diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..e5a7001db --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "modernuoschemagenerator": { + "version": "2.1.4", + "commands": [ + "ModernUOSchemaGenerator" + ] + } + } +} diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 3a2b28871..bfc63eaa8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -14,10 +14,10 @@ jobs: fail-fast: false matrix: include: - - os: macos-latest - name: MacOS 10 - os: macos-11 name: MacOS 11 + - os: macos-12 + name: MacOS 12 steps: - uses: actions/checkout@v2 @@ -26,7 +26,7 @@ jobs: - name: Setup .NET 6 uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.100 + dotnet-version: 6.0.300 - name: Build run: ./publish.cmd - name: Test diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index b2d3a260a..86b55d9d3 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.WORKFLOW_TOKEN }} - name: Install NGBV uses: dotnet/nbgv@master id: nbgv @@ -31,7 +31,11 @@ jobs: git checkout ${{ steps.last_release.outputs.release }} git tag -fa v0.1.0 -m "Fake release" git checkout - - git push --follow-tags + - name: Push git changes + uses: ad-m/github-push-action@master + with: + GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} + tags: true - name: Conventional Changelog id: changelog uses: TriPSs/conventional-changelog-action@v3 @@ -49,14 +53,14 @@ jobs: delete_release: true tag_name: v${{ steps.changelog.outputs.version }} env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - name: Delete v0.1.0 Tag uses: dev-drprasad/delete-tag-and-release@v0.1.3 with: delete_release: true tag_name: v0.1.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - name: Create Release id: create_release uses: actions/create-release@v1 @@ -67,4 +71,4 @@ jobs: draft: false prerelease: false env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} diff --git a/.gitignore b/.gitignore index 325476c99..7022fc9b2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /Distribution/ModernUO.* /Distribution/Assemblies /Distribution/bsdtar +/Distribution/Configuration/assistants.json /Distribution/Configuration/modernuo.json /Distribution/Configuration/email-settings.json /Distribution/Configuration/throttles.json @@ -32,3 +33,4 @@ .DS_Store /packages/* +/Distribution/Configuration/server-access.json diff --git a/Directory.Build.props b/Directory.Build.props index d0e008643..5b23e4a72 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,6 +28,7 @@ NO_LOCAL_INIT MUO $(SolutionDir) + false true @@ -54,13 +55,13 @@ latest - + - 3.4.244 + 3.5.107 all - + diff --git a/Distribution/Data/Decoration/Britannia/wind.cfg b/Distribution/Data/Decoration/Britannia/wind.cfg index 695d46747..93746ee8a 100644 --- a/Distribution/Data/Decoration/Britannia/wind.cfg +++ b/Distribution/Data/Decoration/Britannia/wind.cfg @@ -770,4 +770,4 @@ Static 0x1F3C # Arch Cure Static 0x1F45 -5304 88 19 \ No newline at end of file +5304 88 19 diff --git a/Distribution/Data/categorization.json b/Distribution/Data/categorization.json index 6eff0b152..4ddec6a0a 100644 --- a/Distribution/Data/categorization.json +++ b/Distribution/Data/categorization.json @@ -3337,7 +3337,6 @@ { "type": "DiseasedCat" }, { "type": "Doppleganger" }, { "type": "Drithen" }, - { "type": "DummyThief" }, { "type": "Efreet" }, { "type": "ElderAbbein" }, { "type": "ElderAcob" }, @@ -3630,19 +3629,6 @@ { "type": "SeaSerpent" } ] }, - { - "category": "Mobiles.Dummies", - "objects": [ - { "type": "DummyAssassin" }, - { "type": "DummyFence" }, - { "type": "DummyHealer" }, - { "type": "DummyMace" }, - { "type": "DummyNox" }, - { "type": "DummyStun" }, - { "type": "DummySuper" }, - { "type": "DummySword" } - ] - }, { "category": "Mobiles.Escortables", "objects": [ @@ -3682,8 +3668,7 @@ { "category": "Mobiles.Uncategorized", "objects": [ - { "type": "TownCrier" }, - { "type": "Wanderer" } + { "type": "TownCrier" } ] }, { diff --git a/Distribution/Data/expansion.json b/Distribution/Data/expansion.json index e0b036f61..a89041813 100644 --- a/Distribution/Data/expansion.json +++ b/Distribution/Data/expansion.json @@ -4,7 +4,6 @@ "ClientVersion": null, "ClientFlags": null, "FeatureFlags": { - "None": true, "T2A": false, "UOR": false, "UOTD": false, @@ -13,7 +12,7 @@ "SixthCharacterSlot": false, "SE": false, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -31,7 +30,6 @@ "EJ": false }, "CharacterListFlags": { - "None": true, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -50,7 +48,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": true, "AOS": false, "SE": false, "ML": false, @@ -70,7 +67,6 @@ "ClientVersion": null, "ClientFlags": "Felucca", "FeatureFlags": { - "None": false, "T2A": true, "UOR": false, "UOTD": false, @@ -79,7 +75,7 @@ "SixthCharacterSlot": false, "SE": false, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -97,7 +93,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -116,7 +111,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": true, "AOS": false, "SE": false, "ML": false, @@ -136,7 +130,6 @@ "ClientVersion": null, "ClientFlags": "Trammel", "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, "UOTD": false, @@ -145,7 +138,7 @@ "SixthCharacterSlot": false, "SE": false, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -163,7 +156,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -182,7 +174,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": true, "AOS": false, "SE": false, "ML": false, @@ -202,7 +193,6 @@ "ClientVersion": null, "ClientFlags": "Ilshenar", "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, "UOTD": true, @@ -211,7 +201,7 @@ "SixthCharacterSlot": false, "SE": false, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -229,7 +219,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -248,7 +237,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": true, "AOS": false, "SE": false, "ML": false, @@ -268,7 +256,6 @@ "ClientVersion": null, "ClientFlags": "Ilshenar", "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, "UOTD": true, @@ -277,7 +264,7 @@ "SixthCharacterSlot": false, "SE": false, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -295,7 +282,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -314,7 +300,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": true, "AOS": false, "SE": false, "ML": false, @@ -334,16 +319,15 @@ "ClientVersion": null, "ClientFlags": "Malas", "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": false, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -361,7 +345,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -380,7 +363,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": false, "ML": false, @@ -400,16 +382,15 @@ "ClientVersion": null, "ClientFlags": "Tokuno", "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": true, "ML": false, - "EigthAge": false, + "EighthAge": false, "NinthAge": false, "TenthAge": false, "IncreasedStorage": false, @@ -427,7 +408,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -446,7 +426,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": true, "ML": false, @@ -466,16 +445,15 @@ "ClientVersion": "5.0.0a", "ClientFlags": null, "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": true, "ML": true, - "EigthAge": false, + "EighthAge": false, "NinthAge": true, "TenthAge": false, "IncreasedStorage": false, @@ -493,7 +471,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -512,7 +489,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": true, "ML": true, @@ -532,16 +508,15 @@ "ClientVersion": null, "ClientFlags": "TerMur", "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": true, "ML": true, - "EigthAge": false, + "EighthAge": false, "NinthAge": true, "TenthAge": false, "IncreasedStorage": false, @@ -559,7 +534,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -578,7 +552,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": true, "ML": true, @@ -598,16 +571,15 @@ "ClientVersion": "7.0.9.0", "ClientFlags": null, "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": true, "ML": true, - "EigthAge": false, + "EighthAge": false, "NinthAge": true, "TenthAge": false, "IncreasedStorage": false, @@ -625,7 +597,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -644,7 +615,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": true, "ML": true, @@ -664,16 +634,15 @@ "ClientVersion": "7.0.45.65", "ClientFlags": null, "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": true, "ML": true, - "EigthAge": false, + "EighthAge": false, "NinthAge": true, "TenthAge": false, "IncreasedStorage": false, @@ -691,7 +660,6 @@ "EJ": false }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -710,7 +678,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": true, "ML": true, @@ -730,16 +697,15 @@ "ClientVersion": "7.0.61.0", "ClientFlags": null, "FeatureFlags": { - "None": false, "T2A": true, "UOR": true, - "UOTD": true, + "UOTD": false, "LBR": true, "AOS": true, "SixthCharacterSlot": false, "SE": true, "ML": true, - "EigthAge": false, + "EighthAge": false, "NinthAge": true, "TenthAge": false, "IncreasedStorage": false, @@ -757,7 +723,6 @@ "EJ": true }, "CharacterListFlags": { - "None": false, "Unk1": false, "OverwriteConfigButton": false, "OneCharacterSlot": false, @@ -776,7 +741,6 @@ "NewFeluccaAreas": false }, "HousingFlags": { - "None": false, "AOS": true, "SE": true, "ML": true, diff --git a/Distribution/Data/npc-speeds.json b/Distribution/Data/npc-speeds.json new file mode 100644 index 000000000..5ea8371ae --- /dev/null +++ b/Distribution/Data/npc-speeds.json @@ -0,0 +1,148 @@ +[ + { + "name": "Slow", + "active": 0.3, + "passive": 0.6, + "types": [ + "AntLion", "ArcticOgreLord", "BogThing", + "Bogle", "BoneKnight", "EarthElemental", + "Ettin", "FrostOoze", "FrostTroll", + "GazerLarva", "Ghoul", "Golem", + "HeadlessOne", "Jwilson", "Mummy", + "Ogre", "OgreLord", "PlagueBeast", + "Quagmire", "Rat", "RottingCorpse", + "SewerRat", "Skeleton", "Slime", + "Zombie", "Walrus", "RestlessSoul", + "CrystalElemental", "DarknightCreeper", "MoundOfMaggots", + "Juggernaut", "Yamandon", "Serado", + "GreaterDragon", "PlagueBeastLord" + ] + }, + { + "name": "Medium", + "active": 0.25, + "passive": 0.5, + "types": [ + "AcidElemental", "AgapiteElemental", "Alligator", + "AncientLich", "Betrayer", "Bird", + "BlackBear", "BlackSolenInfiltratorQueen", "BlackSolenInfiltratorWarrior", + "BlackSolenQueen", "BlackSolenWarrior", "BlackSolenWorker", + "BloodElemental", "Boar", "Bogling", + "BoneMagi", "Brigand", "BronzeElemental", + "BrownBear", "Bull", "BullFrog", + "Cat", "Centaur", "ChaosDaemon", + "Chicken", "GolemController", "CopperElemental", + "CopperElemental", "Cougar", "Cow", + "Cyclops", "Daemon", "DeepSeaSerpent", + "DesertOstard", "DireWolf", "Dog", + "Dolphin", "Dragon", "Drake", + "DullCopperElemental", "Eagle", "ElderGazer", + "EvilMage", "EvilMageLord", "Executioner", + "Savage", "FireElemental", "FireGargoyle", + "FireSteed", "ForestOstard", "FrenziedOstard", + "FrostSpider", "Gargoyle", "Gazer", + "IceSerpent", "GiantRat", "GiantSerpent", + "GiantSpider", "GiantToad", "Goat", + "GoldenElemental", "Gorilla", "GreatHart", + "GreyWolf", "GrizzlyBear", "Guardian", + "Harpy", "Harrower", "HellHound", + "Hind", "HordeMinion", "Horse", + "Horse", "IceElemental", "IceFiend", + "IceSnake", "Imp", "JackRabbit", + "Kirin", "Kraken", "PredatorHellCat", + "LavaLizard", "LavaSerpent", "LavaSnake", + "Lizardman", "Llama", "Mongbat", + "StrongMongbat", "MountainGoat", "Orc", + "OrcBomber", "OrcBrute", "OrcCaptain", + "OrcishLord", "OrcishMage", "PackHorse", + "PackLlama", "Panther", "Pig", + "PlagueSpawn", "PolarBear", "Rabbit", + "Ratman", "RatmanArcher", "RatmanMage", + "RedSolenInfiltratorQueen", "RedSolenInfiltratorWarrior", "RedSolenQueen", + "RedSolenWarrior", "RedSolenWorker", "RidableLlama", + "Ridgeback", "Scorpion", "SeaSerpent", + "SerpentineDragon", "Shade", "ShadowIronElemental", + "ShadowWisp", "ShadowWyrm", "Sheep", + "SilverSteed", "SkeletalDragon", "SkeletalMage", + "SkeletalMount", "HellCat", "Snake", + "SnowLeopard", "SpectralArmour", "Spectre", + "StoneGargoyle", "StoneHarpy", "SwampDragon", + "ScaledSwampDragon", "SwampTentacle", "TerathanAvenger", + "TerathanDrone", "TerathanMatriarch", "TerathanWarrior", + "TimberWolf", "Titan", "Troll", + "Unicorn", "ValoriteElemental", "VeriteElemental", + "CoMWarHorse", "MinaxWarHorse", "SLWarHorse", + "TBWarHorse", "WaterElemental", "WhippingVine", + "WhiteWolf", "Wraith", "Wyvern", + "KhaldunZealot", "KhaldunSummoner", "SavageRidgeback", + "LichLord", "SkeletalKnight", "SummonedDaemon", + "SummonedEarthElemental", "SummonedWaterElemental", "SummonedFireElemental", + "MeerWarrior", "MeerEternal", "MeerMage", + "MeerCaptain", "JukaLord", "JukaMage", + "JukaWarrior", "AbysmalHorror", "BoneDemon", + "Devourer", "FleshGolem", "Gibberling", + "GoreFiend", "Impaler", "PatchworkSkeleton", + "Ravager", "ShadowKnight", "SkitteringHopper", + "Treefellow", "VampireBat", "WailingBanshee", + "WandererOfTheVoid", "Cursed", "GrimmochDrummel", + "LysanderGathenwale", "MorgBergen", "ShadowFiend", + "SpectralArmour", "TavaraSewel", "ArcaneDaemon", + "Doppleganger", "EnslavedGargoyle", "ExodusMinion", + "ExodusOverseer", "GargoyleDestroyer", "GargoyleEnforcer", + "Moloch", "BakeKitsune", "DeathwatchBeetleHatchling", + "Kappa", "KazeKemono", "DeathwatchBeetle", + "TsukiWolf", "YomotsuElder", "YomotsuPriest", + "YomotsuWarrior", "RevenantLion", "Oni", + "Gaman", "Crane", "Beetle", + "Parrot", "ElfBrigand", "GreaterMongbat", + "AnimatedWeapon", "Reaper", "Corpser" + ] + }, + { + "name": "Fast", + "active": 0.2, + "passive": 0.4, + "types": [ + "LordOaks", "Silvani", "AirElemental", + "AncientWyrm", "Balron", "BladeSpirits", + "DreadSpider", "Efreet", "EtherealWarrior", + "Lich", "Nightmare", "OphidianArchmage", + "OphidianMage", "OphidianWarrior", "OphidianMatriarch", + "OphidianKnight", "PoisonElemental", + "SandVortex", "SavageRider", "SavageShaman", + "SnowElemental", "WhiteWyrm", "Wisp", + "DemonKnight", "GiantBlackWidow", "SummonedAirElemental", + "LesserHiryu", "Hiryu", "LadyOfTheSnow", + "RaiJu", "Ronin", "RuneBeetle", + "Changeling", "LadyJennifyr", "LadyMarai", "MasterJonath", + "MasterMikael", "MasterTheophilus", "RedDeath", + "SirPatrick", "Miasma", "Rend", + "Grobu", "Gnaw", "Guile", + "Irk", "Spite", "LadyLissith", + "LadySabrix", "Malefic", "Silk", + "Virulent", "SeaHorse", "UnholyFamiliar", + "HolyFamiliar", "GiantIceWorm", "Phoenix", + "Succubus", "EnragedRabbit", "EnragedHind", + "EnragedHart", "EnragedBlackBear", "EnragedEagle", + "RagingGrizzlyBear", "CorrosiveSlime", "DarkWisp", + "DarkGuardian", "HarrowerTentacles", "ServantOfSemidar", + "Reptalon" + ] + }, + { + "name": "Very Fast", + "active": 0.175, + "passive": 0.35, + "types": [ + "Barracoon", "Mephitis", "Neira", + "Rikktor", "Semidar", "EnergyVortex", + "EliteNinja", "Pixie", "SilverSerpent", + "VorpalBunny", "FleshRenderer", "KhaldunRevenant", + "FactionDragoon", "FactionKnight", "FactionPaladin", + "FactionHenchman", "FactionMercenary", "FactionNecromancer", + "FactionSorceress", "FactionWizard", "FactionBerserker", + "FactionPaladin", "Leviathan", "FireBeetle", + "FanDancer", "FactionDeathKnight", "Revenant" + ] + } +] diff --git a/Distribution/Data/teleporters.json b/Distribution/Data/teleporters.json index 82a4fe684..91e70eb0b 100644 --- a/Distribution/Data/teleporters.json +++ b/Distribution/Data/teleporters.json @@ -40,9 +40,9 @@ "back": true }, { - "src": { "map": "Felucca", "loc": [589, 1637, 0] }, - "dst": { "map": "Felucca", "loc": [6575, 888, 0] }, - "back": false + "src": { "map": "Felucca", "loc": [588, 1637, 0] }, + "dst": { "map": "Felucca", "loc": [6472, 868, 26] }, + "back": true }, { "src": { "map": "Felucca", "loc": [766, 1645, 0] }, @@ -2054,16 +2054,6 @@ "dst": { "map": "Felucca", "loc": [6503, 88, 0] }, "back": false }, - { - "src": { "map": "Felucca", "loc": [6488, 849, 40] }, - "dst": { "map": "Felucca", "loc": [6587, 867, 0] }, - "back": false - }, - { - "src": { "map": "Felucca", "loc": [6488, 850, 41] }, - "dst": { "map": "Felucca", "loc": [6587, 868, 0] }, - "back": false - }, { "src": { "map": "Felucca", "loc": [6540, 116, -15] }, "dst": { "map": "Felucca", "loc": [6537, 138, -20] }, @@ -2085,13 +2075,8 @@ "back": false }, { - "src": { "map": "Felucca", "loc": [6574, 889, 0] }, - "dst": { "map": "Felucca", "loc": [6472, 869, 20] }, - "back": false - }, - { - "src": { "map": "Felucca", "loc": [6575, 889, 0] }, - "dst": { "map": "Felucca", "loc": [6473, 869, 20] }, + "src": { "map": "Felucca", "loc": [6477, 860, 9] }, + "dst": { "map": "Felucca", "loc": [6574, 889, 0] }, "back": false }, { @@ -2116,7 +2101,7 @@ }, { "src": { "map": "Felucca", "loc": [6588, 867, 0] }, - "dst": { "map": "Felucca", "loc": [6489, 849, 39] }, + "dst": { "map": "Felucca", "loc": [6488, 849, 40] }, "back": false }, { @@ -2220,8 +2205,13 @@ "back": true }, { - "src": { "map": "Trammel", "loc": [589, 1637, 0] }, - "dst": { "map": "Trammel", "loc": [6575, 888, 0] }, + "src": { "map": "Trammel", "loc": [588, 1637, 0] }, + "dst": { "map": "Trammel", "loc": [6472, 868, 26] }, + "back": true + }, + { + "src": { "map": "Trammel", "loc": [6477, 860, 9] }, + "dst": { "map": "Trammel", "loc": [6574, 889, 0] }, "back": false }, { @@ -4179,16 +4169,6 @@ "dst": { "map": "Trammel", "loc": [6503, 88, 0] }, "back": false }, - { - "src": { "map": "Trammel", "loc": [6488, 849, 40] }, - "dst": { "map": "Trammel", "loc": [6587, 867, 0] }, - "back": false - }, - { - "src": { "map": "Trammel", "loc": [6488, 850, 41] }, - "dst": { "map": "Trammel", "loc": [6587, 868, 0] }, - "back": false - }, { "src": { "map": "Trammel", "loc": [6540, 116, -15] }, "dst": { "map": "Trammel", "loc": [6537, 138, -20] }, @@ -4209,16 +4189,6 @@ "dst": { "map": "Trammel", "loc": [6576, 73, 0] }, "back": false }, - { - "src": { "map": "Trammel", "loc": [6574, 889, 0] }, - "dst": { "map": "Trammel", "loc": [6472, 869, 20] }, - "back": false - }, - { - "src": { "map": "Trammel", "loc": [6575, 889, 0] }, - "dst": { "map": "Trammel", "loc": [6473, 869, 20] }, - "back": false - }, { "src": { "map": "Trammel", "loc": [6577, 159, 13] }, "dst": { "map": "Trammel", "loc": [6577, 177, 30] }, @@ -4241,7 +4211,7 @@ }, { "src": { "map": "Trammel", "loc": [6588, 867, 0] }, - "dst": { "map": "Trammel", "loc": [6489, 849, 39] }, + "dst": { "map": "Trammel", "loc": [6488, 849, 40] }, "back": false }, { diff --git a/FAQ.md b/FAQ.md index 4b7d5c3e4..aa3f178c5 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,21 +1,24 @@ # Frequently Asked Questions -- [Why do I have thousands of compile errors](#why-do-i-have-thousands-of-compile-errors) -- [Where do I go to run ModernUO?](#where-do-i-go-to-run-modernuo) -- [I don't see my scripts compiling, what happened?](#i-dont-see-my-scripts-compiling-what-happened) -- [Why can't I run ModernUO on a 32-bit operating system?](#why-cant-i-run-modernuo-on-a-32-bit-operating-system) + +## What has changed since RunUO 2.7? +We have compiled a non-exhaustive list of the features changes, technical improvements, and new additions. +- See: [RunUO to ModernUO](./RUNUO_TO_MODERNUO.md) ## Why do I have thousands of compile errors? -ModernUO uses a new technique called source generation. Most likely the compile errors are related to that. -To resolve the compile errors make sure you are using Visual Studio 2019 v16.10+, or Rider 2021.2+ for development and the newest .NET available. +ModernUO uses a _very_ new technique called source generation. Most likely the compile errors are related to the source generator crashing. +To resolve the compile errors make sure you are using Visual Studio 2022, or Rider 2022+ for development and the newest .NET available. Specifically for Visual Studio, you must build the entire solution once, restart Visual Studio entirely, and then build again. -This is required every time code changes in the `SerializationGenerator` or `SerializationSchemaGenerator` projects. + +It is possible that while you are developing, many of your files will go red and properties/symbols cannot be resolved. Contact us on discord +and let us know how to reproduce it so we can get it fixed! Under the same vein, we are working on building an analyzer for the IDE so if there +are errors, you are not in the dark. ## Where do I go to run ModernUO? Everything is run from the `Distribution` directory. This folder is portable, so it can be moved to the production server for deployments. ## I don't see my scripts compiling, what happened? -.NET 5 does not support dynamic compiling newer versions of C#. Scripts are now compiled separately and moved to the `Distribution/Assemblies` directory. +Scripts are now compiled separately (UOContent project) and moved to the `Distribution/Assemblies` directory. ## Why can't I run ModernUO on a 32-bit operating system? -ModernUO is optimized for performance and modern hardware. For this reason we no longer support 32-bit operating systems +ModernUO is optimized for modern hardware. We cannot support 32-bit systems, sorry. diff --git a/GOALS.md b/GOALS.md deleted file mode 100644 index 1995b9dc1..000000000 --- a/GOALS.md +++ /dev/null @@ -1,63 +0,0 @@ -# GOALS - -The goal of ModernUO is a complete rewrite of RunUO that focuses on performance. -Some of the many high level goals include: - -### Architecture -- [x] Upgrade Server/Scripts to target .NET Core 3 -- [X] Upgrade the code to use C# 8 standards -- [X] Remove compilation of scripts at startup in favor of loading DLLs. -- [X] Support configuration via a `modernuo.json` file - -### Networking -- [ ] Replace Packet classes with functions -- [X] Improve asynchronous socket handling using Pipes -- [X] Improve socket handling (2-5x) and event loop using libuv - -### Administration -- [ ] Move IP logging and account data to SQL -- [ ] Move account management to web - -### Serialization & World Saves -- [ ] Create a flag to indicate when objects will be serialized. - * Players, all items held by players, houses, etc. -- [ ] Create transactions which handle changes to objects including: - * Transitioning from not serialized to serialized (created) - * Transitioning from serialized to not serialized (deleted) - * Property value changes -- [ ] Create asynchronous request from cold storage - * External system must provide objects and their nested objects (e.g. containers) - -### Optimizations for large shards -- [ ] Eject inactive players and their items after they are serialized to cold storage -- [ ] Create hydration techniques for non-persisted systems (e.g. spawners, decorations, static, champions, etc) -- [ ] Simplify certain items by eliminating all of their individual types and using an enum to reference their individual type - * Items subject to change: reagents, logs, ore, ingots, potions, and containers - * Syntax change: `new MandrakeRoot()` -> `new Reagent(ReagentType.MandrakeRoot)` -- [ ] Create object pools for high availability items such as gold and reagents - * For example, `new MandrakeRoot()` -> `ObjectPool.Get(ReagentType.MandrakeRoot)`. - * Pools should be elastic and adjust according to nominal usage. For example, if thousands of gold objects are created and destroyed in a small period of time, the pool should be expanded and replenished properly so it is never empty, or full. -- [ ] Replace timer system with libuv implementation -- [X] Create `DefaultName` for mobiles - -### Plugins (Separate Repos & Optional) -- [ ] RunUO 2.0 Migration Utility - * Template code for updating serializations - * Convert World file and dump to new serialization technique if there is one -- [ ] Login/Auth Gateway (Like OSI!) - * Use central AuthID via redis or similar - * Support 2FA Auths via at login gump -- [ ] 2FA Auth at Login to prevent password theft - * Support Authy via universal interface - * Support external app/device, magic number email, SMS, and FCM - * Support user identity via web browser to curb flooding -- [ ] Serialization via Database (no world saves) - * Asynchronously offload the transaction to another service for permanent serialization - * Support basic KVP databases such as MongoDB, RocksDB, and Redis, etc. - -### Bugs & Misc -- [X] Rounding errors with new bank system -- [X] Replace specific checks for `Item`/`Mobile` by extended `IEntity` and mainstreaming the `NEWPARENT` code -- [ ] TryDropItem dropping items until it fails when it should be all-or-nothing -- [ ] Consumable items are deleted directly in several places instead of using `Item.Consume()` -- [ ] Stack/Hold checks are broken in certain situations diff --git a/ModernUO.sln b/ModernUO.sln index 75eb761a4..92647cca4 100644 --- a/ModernUO.sln +++ b/ModernUO.sln @@ -10,12 +10,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Tests", "Projects\Se EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UOContent.Tests", "Projects\UOContent.Tests\UOContent.Tests.csproj", "{3C4797F9-603E-44EF-8E8C-9275CC9EA74B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Projects\Benchmarks\Benchmarks.csproj", "{38B7E4A1-FDDD-486C-98EE-BA7B13712528}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerializationGenerator", "Projects\SerializationGenerator\SerializationGenerator.csproj", "{07DDB8CF-F926-44F4-A584-FF2997D2C9D0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerializationSchemaGenerator", "Projects\SerializationSchemaGenerator\SerializationSchemaGenerator.csproj", "{A30150A3-796C-4C6D-B3E4-B7BEB0021701}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Analyze|x64 = Analyze|x64 @@ -47,24 +41,6 @@ Global {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Debug|x64.Build.0 = Debug|x64 {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Release|x64.ActiveCfg = Release|x64 {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Release|x64.Build.0 = Release|x64 - {38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Analyze|x64.ActiveCfg = Release|x64 - {38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Analyze|x64.Build.0 = Release|x64 - {38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Debug|x64.ActiveCfg = Debug|x64 - {38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Debug|x64.Build.0 = Debug|x64 - {38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Release|x64.ActiveCfg = Release|x64 - {38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Release|x64.Build.0 = Release|x64 - {07DDB8CF-F926-44F4-A584-FF2997D2C9D0}.Analyze|x64.ActiveCfg = Analyze|x64 - {07DDB8CF-F926-44F4-A584-FF2997D2C9D0}.Analyze|x64.Build.0 = Analyze|x64 - {07DDB8CF-F926-44F4-A584-FF2997D2C9D0}.Debug|x64.ActiveCfg = Debug|x64 - {07DDB8CF-F926-44F4-A584-FF2997D2C9D0}.Debug|x64.Build.0 = Debug|x64 - {07DDB8CF-F926-44F4-A584-FF2997D2C9D0}.Release|x64.ActiveCfg = Release|x64 - {07DDB8CF-F926-44F4-A584-FF2997D2C9D0}.Release|x64.Build.0 = Release|x64 - {A30150A3-796C-4C6D-B3E4-B7BEB0021701}.Analyze|x64.ActiveCfg = Analyze|x64 - {A30150A3-796C-4C6D-B3E4-B7BEB0021701}.Analyze|x64.Build.0 = Analyze|x64 - {A30150A3-796C-4C6D-B3E4-B7BEB0021701}.Debug|x64.ActiveCfg = Debug|x64 - {A30150A3-796C-4C6D-B3E4-B7BEB0021701}.Debug|x64.Build.0 = Debug|x64 - {A30150A3-796C-4C6D-B3E4-B7BEB0021701}.Release|x64.ActiveCfg = Release|x64 - {A30150A3-796C-4C6D-B3E4-B7BEB0021701}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Projects/Benchmarks/Benchmarks.csproj b/Projects/Benchmarks/Benchmarks.csproj deleted file mode 100644 index eff9ed660..000000000 --- a/Projects/Benchmarks/Benchmarks.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - Exe - net6.0 - x64 - x64 - 9 - true - true - - - - - - - diff --git a/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs b/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs deleted file mode 100644 index b9ec3d676..000000000 --- a/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Collections.Generic; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server.Collections; - -namespace Benchmarks -{ - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkOrderedHashSet - { - private readonly string[] _iterations = new string[16]; - - [IterationSetup] - public void IterationSetup() - { - for (var i = 0; i < _iterations.Length; i++) - { - _iterations[i] = i.ToString(); - } - } - - [Benchmark] - public int UsingList() - { - var list = new List(); - for (int i = 0; i < _iterations.Length / 2; i++) - { - AddIfNotPresent(list, _iterations[i]); - } - - for (int i = 0; i < _iterations.Length; i++) - { - AddIfNotPresent(list, _iterations[i]); - } - - for (int i = 0; i < list.Count; i++) - { - list[i].ToString(); - } - - return list.Count; - } - - private static int AddIfNotPresent(List list, T item) - { - var index = list.IndexOf(item); - if (index > -1) - { - return index; - } - - list.Add(item); - return list.Count - 1; - } - - [Benchmark] - public int UsingOrderedHashSet() - { - var ordered = new OrderedHashSet(); - for (int i = 0; i < _iterations.Length / 2; i++) - { - ordered.GetOrAdd(_iterations[i]).ToString(); - } - - for (int i = 0; i < _iterations.Length; i++) - { - ordered.GetOrAdd(_iterations[i]).ToString(); - } - - foreach (var str in ordered) - { - str.ToString(); - } - - return ordered.Count; - } - - [Benchmark] - public int UsingPooledOrderedHashSet() - { - var ordered = new PooledOrderedHashSet(); - for (int i = 0; i < _iterations.Length / 2; i++) - { - ordered.GetOrAdd(_iterations[i]).ToString(); - } - - for (int i = 0; i < _iterations.Length; i++) - { - ordered.GetOrAdd(_iterations[i]).ToString(); - } - - foreach (var str in ordered) - { - str.ToString(); - } - - return ordered.Count; - } - - [Benchmark] - public int UsingHashSet() - { - var hashSet = new HashSet<(string, int)>(new OrderedStringComparer()); - for (int i = 0; i < _iterations.Length / 2; i++) - { - hashSet.Add((_iterations[i], i)); - } - - for (int i = 0; i < _iterations.Length; i++) - { - hashSet.Add((_iterations[i], i)); - } - - foreach (var str in hashSet) - { - str.ToString(); - } - - return hashSet.Count; - } - - private class OrderedStringComparer : EqualityComparer<(string, int)> - { - public override bool Equals((string, int) x, (string, int) y) => x.Item1.Equals(y.Item1, System.StringComparison.Ordinal); - - public override int GetHashCode((string, int) obj) => obj.Item1.GetHashCode(); - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs b/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs deleted file mode 100644 index f794d64b4..000000000 --- a/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Serilog; -using Serilog.Core; - -namespace Benchmarks -{ - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkConsoleLogging - { - private const string text = "Sample message"; - - private Logger logger; - private Logger asyncLogger; - - [GlobalSetup] - public void GlobalSetup() - { - logger = new LoggerConfiguration() - .WriteTo.Console() - .CreateLogger(); - - asyncLogger = new LoggerConfiguration() - .WriteTo.Async(a => a.Console()) - .CreateLogger(); - } - - [GlobalCleanup] - public void GlobalCleanup() - { - logger = null; - asyncLogger = null; - } - - [Benchmark] - public void TestConsoleWriteLine() - { - for (int i = 0; i < 10000; i++) - { - Console.WriteLine(text); - } - } - - [Benchmark] - public void TestSerilogConsoleSink() - { - for (int i = 0; i < 10000; i++) - { - logger.Information(text); - } - } - - [Benchmark] - public void TestSerilogAsyncConsoleSink() - { - for (int i = 0; i < 10000; i++) - { - asyncLogger.Information(text); - } - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Map/MapSelectors.cs b/Projects/Benchmarks/Benchmarks/Map/MapSelectors.cs deleted file mode 100644 index 4c47b7189..000000000 --- a/Projects/Benchmarks/Benchmarks/Map/MapSelectors.cs +++ /dev/null @@ -1,665 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Benchmarks -{ - [SimpleJob(RuntimeMoniker.NetCoreApp50)] - public class MapSelectors - { - static readonly Sector sector = new Sector(); - static Server.Rectangle2D bounds = new Server.Rectangle2D(0, 0, 100, 100); - - public static void Init() - { - for (int i = 0; i < 50; ++i) - { - sector.Multis.Add(new BaseMulti()); - } - for (int i = 0; i < 1000; ++i) - { - sector.BItems.Add(new BItem()); - } - for (int i = 0; i < 1000; ++i) - { - sector.Mobiles.Add(new Mobile()); - } - } - - #region MultiTiles - [Benchmark] - public void SelectMultiTilesNew() - { - foreach(StaticTile[] tiles in SelectMultiTiles(sector, bounds)) - { - for(int i = 0; i < tiles.Length; ++i) - { - int id = tiles[i].ID; - } - } - } - - [Benchmark] - public void SelectMultiTilesLinq() - { - foreach(StaticTile[] tiles in SelectMultiTilesLinq(sector, bounds)) - { - for(int i = 0; i < tiles.Length; ++i) - { - int id = tiles[i].ID; - } - } - } - - public IEnumerable SelectMultiTilesLinq(Sector s, Server.Rectangle2D bounds) - { - foreach (var o in s.Multis.Where(o => o != null && !o.Deleted)) - { - var c = o.Components; - - int x, y, xo, yo; - StaticTile[] t, r; - - for (x = bounds.Start.X; x < bounds.End.X; x++) - { - xo = x - (o.X + c.Min.X); - - if (xo < 0 || xo >= c.Width) - { - continue; - } - - for (y = bounds.Start.Y; y < bounds.End.Y; y++) - { - yo = y - (o.Y + c.Min.Y); - - if (yo < 0 || yo >= c.Height) - { - continue; - } - - t = c.Tiles[xo][yo]; - - if (t.Length <= 0) - { - continue; - } - - r = new StaticTile[t.Length]; - - for (var i = 0; i < t.Length; i++) - { - r[i] = t[i]; - r[i].Z += o.Z; - } - - yield return r; - } - } - } - } - - public IEnumerable SelectMultiTiles(Sector s, Server.Rectangle2D bounds) - { - for (int l = s.Multis.Count - 1; l >= 0; --l) - { - BaseMulti o = s.Multis[l]; - if (o != null && !o.Deleted) - { - MultiComponentList c = o.Components; - - int x, y, xo, yo; - StaticTile[] t, r; - - for (x = bounds.Start.X; x < bounds.End.X; x++) - { - xo = x - (o.X + c.Min.X); - - if (xo < 0 || xo >= c.Width) - { - continue; - } - - for (y = bounds.Start.Y; y < bounds.End.Y; y++) - { - yo = y - (o.Y + c.Min.Y); - - if (yo < 0 || yo >= c.Height) - { - continue; - } - - t = c.Tiles[xo][yo]; - - if (t.Length <= 0) - { - continue; - } - - r = new StaticTile[t.Length]; - - for (var i = 0; i < t.Length; i++) - { - r[i] = t[i]; - r[i].Z += o.Z; - } - - yield return r; - } - } - } - } - } - - #endregion - - #region Multis - [Benchmark] - public void SelectMultisNew() - { - SelectMultis(sector, bounds); - } - - [Benchmark] - public void SelectMultisLinq() - { - SelectMultisLinq(sector, bounds); - } - - public IEnumerable SelectMultisLinq(Sector s, Server.Rectangle2D bounds) - { - return s.Multis.Where(o => o != null && !o.Deleted && bounds.Contains(o.Location)); - } - - public IEnumerable SelectMultis(Sector s, Server.Rectangle2D bounds) - { - List entities = new List(s.Multis.Count); - for (int i = s.Multis.Count - 1; i >= 0; --i) - { - BaseMulti BItem = s.Multis[i]; - if (BItem != null && !BItem.Deleted && bounds.Contains(BItem.Location)) - entities.Add(BItem); - } - return entities; - } - #endregion - - #region BItems - [Benchmark] - public void SelectBItemsNew() - { - SelectBItems(sector, bounds); - } - - [Benchmark] - public void SelectBItemsLinq() - { - SelectBItemsLinq(sector, bounds); - } - - public IEnumerable SelectBItemsLinq(Sector s, Server.Rectangle2D bounds) where T : BItem - { - return s.BItems.OfType().Where(o => o != null && !o.Deleted && o.Parent == null && bounds.Contains(o.Location)); - } - - public IEnumerable SelectBItems(Sector s, Server.Rectangle2D bounds) where T : BItem - { - List entities = new List(s.BItems.Count); - Type type = typeof(T); - for (int i = s.BItems.Count - 1; i >= 0; --i) - { - BItem BItem = s.BItems[i]; - if (BItem != null && !BItem.Deleted && BItem.Parent == null && bounds.Contains(BItem.Location) && type.IsAssignableFrom(BItem.GetType())) - entities.Add(BItem as T); - } - return entities; - } - #endregion - - #region Mobiles - [Benchmark] - public void SelectMobilesNew() - { - SelectMobiles(sector, bounds); - } - - [Benchmark] - public void SelectMobilesLinq() - { - SelectMobilesLinq(sector, bounds); - } - - public IEnumerable SelectMobilesLinq(Sector s, Server.Rectangle2D bounds) where T : Mobile - { - return s.Mobiles.OfType().Where(o => o != null && !o.Deleted && bounds.Contains(o.Location)); - } - - public IEnumerable SelectMobiles(Sector s, Server.Rectangle2D bounds) where T : Mobile - { - List entities = new List(s.Mobiles.Count); - Type type = typeof(T); - for (int i = s.Mobiles.Count - 1; i >= 0; --i) - { - Mobile mob = s.Mobiles[i]; - if (mob != null && !mob.Deleted && bounds.Contains(mob.Location) && type.IsAssignableFrom(mob.GetType())) - entities.Add(mob as T); - } - return entities; - } - #endregion - - #region Entities - [Benchmark] - public void SelectEntitiesNew() - { - SelectEntities(sector, bounds); - } - - [Benchmark] - public void SelectEntitiesLinq() - { - SelectEntitiesLinq(sector, bounds); - } - - public IEnumerable SelectEntitiesLinq(Sector s, Server.Rectangle2D bounds) - { - return Enumerable.Empty() - .Union(s.Mobiles.Where(o => o != null && !o.Deleted)) - .Union(s.BItems.Where(o => o != null && !o.Deleted && o.Parent == null)) - .Where(o => bounds.Contains(o.Location)); - } - - private readonly List entities = new (10); - public IEnumerable SelectEntities(Sector s, Server.Rectangle2D bounds) - { - entities.Clear(); - entities.Capacity = s.Mobiles.Count + s.BItems.Count; - for (int i = s.Mobiles.Count - 1, j = s.BItems.Count - 1; i >= 0 || j >= 0; --i, --j) - { - if (j >= 0) - { - BItem BItem = s.BItems[j]; - if (BItem != null && !BItem.Deleted && BItem.Parent == null && bounds.Contains(BItem.Location)) - entities.Add(BItem); - } - if (i >= 0) - { - Mobile mob = s.Mobiles[i]; - if (mob != null && !mob.Deleted && bounds.Contains(mob.Location)) - entities.Add(mob); - } - } - return entities; - } - #endregion - } - public class BItem : Server.IPoint3D, IEntity - { - public object Parent { get; set; } = null; - - public bool Deleted { get; set; } = false; - - public int Z { get; set; } = 1; - - public int X { get; set; } = 1; - - public int Y { get; set; } = 1; - - public Serial Serial => throw new System.NotImplementedException(); - - public Point3D Location { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public Map Map { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - - public Region Region => throw new System.NotImplementedException(); - - public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public int Hue { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public Direction Direction { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public DateTime Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public int TypeRef => throw new NotImplementedException(); - - Point3D IEntity.Location => throw new NotImplementedException(); - - Map IEntity.Map => throw new NotImplementedException(); - - int IPoint3D.Z => throw new NotImplementedException(); - - int IPoint2D.X => throw new NotImplementedException(); - - int IPoint2D.Y => throw new NotImplementedException(); - - DateTime ISerializable.Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - DateTime ISerializable.LastSerialized { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - long ISerializable.SavePosition { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - BufferWriter ISerializable.SaveBuffer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - int ISerializable.TypeRef => throw new NotImplementedException(); - - Serial ISerializable.Serial => throw new NotImplementedException(); - - bool ISerializable.Deleted => throw new NotImplementedException(); - - public BItem() - { - - } - - public void Delete() - { - throw new System.NotImplementedException(); - } - - public void ProcessDelta() - { - throw new System.NotImplementedException(); - } - - public void OnStatsQuery(Server.Mobile m) - { - throw new System.NotImplementedException(); - } - - public void InvalidateProperties() - { - throw new System.NotImplementedException(); - } - - public int CompareTo(object obj) - { - throw new System.NotImplementedException(); - } - - public int CompareTo(IEntity other) - { - throw new System.NotImplementedException(); - } - - public void MoveToWorld(Point3D location, Map map) - { - throw new NotImplementedException(); - } - - public bool InRange(Point2D p, int range) - { - throw new NotImplementedException(); - } - - public bool InRange(Point3D p, int range) - { - throw new NotImplementedException(); - } - - public void RemoveBItem(BItem BItem) - { - throw new NotImplementedException(); - } - - public void BeforeSerialize() - { - throw new NotImplementedException(); - } - - public void Deserialize(IGenericReader reader) - { - throw new NotImplementedException(); - } - - public void Serialize(IGenericWriter writer) - { - throw new NotImplementedException(); - } - - public void SetTypeRef(Type type) - { - throw new NotImplementedException(); - } - - void IEntity.MoveToWorld(Point3D location, Map map) - { - throw new NotImplementedException(); - } - - void IEntity.ProcessDelta() - { - throw new NotImplementedException(); - } - - bool IEntity.InRange(Point2D p, int range) - { - throw new NotImplementedException(); - } - - bool IEntity.InRange(Point3D p, int range) - { - throw new NotImplementedException(); - } - - void ISerializable.BeforeSerialize() - { - throw new NotImplementedException(); - } - - void ISerializable.Deserialize(IGenericReader reader) - { - throw new NotImplementedException(); - } - - void ISerializable.Serialize(IGenericWriter writer) - { - throw new NotImplementedException(); - } - - void ISerializable.Delete() - { - throw new NotImplementedException(); - } - - void ISerializable.SetTypeRef(Type type) - { - throw new NotImplementedException(); - } - - public void RemoveItem(Item item) - { - throw new NotImplementedException(); - } - } - - public class Mobile : Server.IPoint3D, IEntity - { - public bool Deleted { get; set; } = false; - - public int Z { get; set; } = 1; - - public int X { get; set; } = 1; - - public int Y { get; set; } = 1; - - public Serial Serial => throw new System.NotImplementedException(); - - public Point3D Location { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public Map Map { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - - public Region Region => throw new System.NotImplementedException(); - - public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public int Hue { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public Direction Direction { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } - public DateTime Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public int TypeRef => throw new NotImplementedException(); - - Point3D IEntity.Location => throw new NotImplementedException(); - - Map IEntity.Map => throw new NotImplementedException(); - - int IPoint3D.Z => throw new NotImplementedException(); - - int IPoint2D.X => throw new NotImplementedException(); - - int IPoint2D.Y => throw new NotImplementedException(); - - DateTime ISerializable.Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - DateTime ISerializable.LastSerialized { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - long ISerializable.SavePosition { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - BufferWriter ISerializable.SaveBuffer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - int ISerializable.TypeRef => throw new NotImplementedException(); - - Serial ISerializable.Serial => throw new NotImplementedException(); - - bool ISerializable.Deleted => throw new NotImplementedException(); - - public Mobile() - { - - } - - public void Delete() - { - throw new System.NotImplementedException(); - } - - public void ProcessDelta() - { - throw new System.NotImplementedException(); - } - - public void OnStatsQuery(Server.Mobile m) - { - throw new System.NotImplementedException(); - } - - public void InvalidateProperties() - { - throw new System.NotImplementedException(); - } - - public int CompareTo(object obj) - { - throw new System.NotImplementedException(); - } - - public int CompareTo(IEntity other) - { - throw new System.NotImplementedException(); - } - - public void MoveToWorld(Point3D location, Map map) - { - throw new NotImplementedException(); - } - - public bool InRange(Point2D p, int range) - { - throw new NotImplementedException(); - } - - public bool InRange(Point3D p, int range) - { - throw new NotImplementedException(); - } - - public void RemoveBItem(BItem BItem) - { - throw new NotImplementedException(); - } - - public void BeforeSerialize() - { - throw new NotImplementedException(); - } - - public void Deserialize(IGenericReader reader) - { - throw new NotImplementedException(); - } - - public void Serialize(IGenericWriter writer) - { - throw new NotImplementedException(); - } - - public void SetTypeRef(Type type) - { - throw new NotImplementedException(); - } - - void IEntity.MoveToWorld(Point3D location, Map map) - { - throw new NotImplementedException(); - } - - void IEntity.ProcessDelta() - { - throw new NotImplementedException(); - } - - bool IEntity.InRange(Point2D p, int range) - { - throw new NotImplementedException(); - } - - bool IEntity.InRange(Point3D p, int range) - { - throw new NotImplementedException(); - } - - void ISerializable.BeforeSerialize() - { - throw new NotImplementedException(); - } - - void ISerializable.Deserialize(IGenericReader reader) - { - throw new NotImplementedException(); - } - - void ISerializable.Serialize(IGenericWriter writer) - { - throw new NotImplementedException(); - } - - void ISerializable.Delete() - { - throw new NotImplementedException(); - } - - void ISerializable.SetTypeRef(Type type) - { - throw new NotImplementedException(); - } - - public void RemoveItem(Item item) - { - throw new NotImplementedException(); - } - } - - public class BaseMulti : BItem - { - public MultiComponentList Components = MultiComponentList.Empty; - - public BaseMulti() - { - for (int i = 0; i < 20; ++i) - for (int j = 0; j < 20; ++j) - for (int z = 0; z < 20; ++z) - Components.Add(123, i, j, z); - } - - } - - public class Sector - { - public List BItems { get; set; } = new List(); - public List Mobiles { get; set; } = new List(); - public List Multis { get; set; } = new List(); - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs b/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs deleted file mode 100644 index 114cac192..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Buffers; -using System.IO; -using System.IO.Compression; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server.Collections; -using Server.Gumps; -using Server.Network; -using Server.Tests.Network; - -namespace Benchmarks -{ - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.Net60)] - public class OutgoingGumpPacketBenchmarks - { - private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray(0x20000); - - public static void CreateDisplayGump(Gump gump, out int switches, out int entries) - { - switches = 0; - entries = 0; - - const bool packed = false; - - var layoutWriter = new SpanWriter(_layoutBuffer); - - if (!gump.Draggable) - { - layoutWriter.Write(Gump.NoMove); - } - - if (!gump.Closable) - { - layoutWriter.Write(Gump.NoClose); - } - - if (!gump.Disposable) - { - layoutWriter.Write(Gump.NoDispose); - } - - if (!gump.Resizable) - { - layoutWriter.Write(Gump.NoResize); - } - - var stringsList = new OrderedHashSet(32); - - foreach (var entry in gump.Entries) - { - entry.AppendTo(ref layoutWriter, stringsList, ref entries, ref switches); - } - - var stringsWriter = new SpanWriter(_stringsBuffer); - - foreach (var str in stringsList) - { - var s = str ?? ""; - stringsWriter.Write((ushort)s.Length); - stringsWriter.WriteBigUni(s); - } - - int maxLength; - if (packed) - { - var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); - var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); - maxLength = 40 + worstLayoutLength + worstStringsLength; - } - else - { - maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; - } - - var writer = new SpanWriter(maxLength); - writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID - writer.Seek(2, SeekOrigin.Current); - - writer.Write(gump.Serial); - writer.Write(gump.TypeID); - writer.Write(gump.X); - writer.Write(gump.Y); - - if (packed) - { - layoutWriter.Write((byte)0); // Layout text terminator - OutgoingGumpPackets.WritePacked(layoutWriter.Span, ref writer); - - writer.Write(stringsList.Count); - OutgoingGumpPackets.WritePacked(stringsWriter.Span, ref writer); - } - else - { - writer.Write((ushort)layoutWriter.BytesWritten); - writer.Write(layoutWriter.Span); - - writer.Write((ushort)stringsList.Count); - writer.Write(stringsWriter.Span); - } - - writer.WritePacketLength(); - - layoutWriter.Dispose(); // Just in case - stringsWriter.Dispose(); // Just in case - } - - public class NameChangeDeedGump : Gump - { - public NameChangeDeedGump() : base(50, 50) - { - Closable = false; - Draggable = false; - Resizable = false; - - AddPage(0); - - AddBlackAlpha(10, 120, 250, 85); - AddHtml(10, 125, 250, 20, Color(Center("Name Change Deed"), 0xFFFFFF)); - - AddLabel(73, 15, 1152, ""); - AddLabel(20, 150, 0x480, "New Name:"); - AddTextField(100, 150, 150, 20, 0); - - AddButtonLabeled(75, 180, 1, "Submit"); - } - - public void AddBlackAlpha(int x, int y, int width, int height) - { - AddImageTiled(x, y, width, height, 2624); - AddAlphaRegion(x, y, width, height); - } - - 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 string Center(string text) => $"
{text}
"; - - public static string Color(string text, int color) => $"{text}"; - - public void AddButtonLabeled(int x, int y, int buttonID, string text) - { - AddButton(x, y - 1, 4005, 4007, buttonID); - AddHtml(x + 35, y, 240, 20, Color(text, 0xFFFFFF)); - } - } - - private static Gump _gump; - - [GlobalSetup] - public void Setup() - { - _gump = new NameChangeDeedGump(); - } - - [Benchmark] - public void TestNewStack() - { - CreateDisplayGump(_gump, out var _, out var _); - } - - [Benchmark] - public void TestOldStack() - { - _gump.Compile().Compile(false, out var _); - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs b/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs deleted file mode 100644 index 4db59d432..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Buffers; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server; -using Server.Network; - -namespace Benchmarks -{ - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkPacketBroadcast - { - public static int SendUnicodeMessage( - ArraySegment[] buffer, - Serial serial, int graphic, MessageType type, int hue, int font, string lang, string name, string text - ) - { - name = name?.Trim() ?? ""; - text = text?.Trim() ?? ""; - lang = lang?.Trim() ?? "ENU"; - - if (hue == 0) - { - hue = 0x3B2; - } - - var writer = new CircularBufferWriter(buffer); - writer.Write((byte)0xAE); - writer.Write((ushort)(50 + text.Length * 2)); - writer.Write(serial.Value); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - writer.WriteAscii(lang, 4); - writer.WriteAscii(name, 30); - writer.WriteBigUniNull(text); - - return writer.Position; - } - - public static int CreateUnicodeMessage( - Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, string lang, string name, string text - ) - { - name = name?.Trim() ?? ""; - text = text?.Trim() ?? ""; - lang = lang?.Trim() ?? "ENU"; - - if (hue == 0) - { - hue = 0x3B2; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xAE); - writer.Write((ushort)(50 + text.Length * 2)); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - writer.WriteAscii(lang, 4); - writer.WriteAscii(name, 30); - writer.WriteBigUniNull(text); - - return writer.Position; - } - - private Pipe[] _pipes = new Pipe[25000]; - - [IterationSetup] - public void SetUp() - { - for (var i = 0; i < _pipes.Length; i++) - { - _pipes[i] = new Pipe(new byte[4096]); - } - } - - [IterationCleanup] - public void CleanUp() - { - for (var i = 0; i < _pipes.Length; i++) - { - _pipes[i] = null; - } - } - - [Benchmark] - public int TestCircularBuffer() - { - var text = "This is some really long text that we want to handle. It should take a little bit to encode this."; - foreach (var pipe in _pipes) - { - var result = pipe.Writer.TryGetMemory(); - var length = SendUnicodeMessage( - result.Buffer, - Serial.MinusOne, -1, MessageType.Regular, 0x3B2, 3, "ENU", "System", text - ); - pipe.Writer.Advance((uint)length); - } - - return _pipes.Length; - } - - [Benchmark] - public int TestSpanWriterFromBuffer() - { - var text = "This is some really long text that we want to handle. It should take a little bit to encode this."; - foreach (var pipe in _pipes) - { - var result = pipe.Writer.TryGetMemory(); - - Span buffer = result.Buffer[0]; - - var length = CreateUnicodeMessage( - buffer, Serial.MinusOne, -1, MessageType.Regular, 0x3B2, 3, "ENU", "System", text - ); - pipe.Writer.Advance((uint)length); - } - - return _pipes.Length; - } - - [Benchmark] - public int TestSpanWriter() - { - var text = "This is some really long text that we want to handle. It should take a little bit to encode this."; - Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)]; - var length = CreateUnicodeMessage( - buffer, Serial.MinusOne, -1, MessageType.Regular, 0x3B2, 3, "ENU", "System", text - ); - - buffer = buffer[..length]; - - foreach (var pipe in _pipes) - { - var result = pipe.Writer.TryGetMemory(); - result.CopyFrom(buffer); - pipe.Writer.Advance((uint)buffer.Length); - } - - return _pipes.Length; - } - - private static void SendUnicodeMessageWithSpan(Pipe pipe, string text) - { - Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)]; - var length = CreateUnicodeMessage( - buffer, Serial.MinusOne, -1, MessageType.Regular, 0x3B2, 3, "ENU", "System", text - ); - - buffer = buffer[..length]; - var result = pipe.Writer.TryGetMemory(); - result.CopyFrom(buffer); - pipe.Writer.Advance((uint)buffer.Length); - } - - [Benchmark] - public int TestSpanWriterLooped() - { - var text = "This is some really long text that we want to handle. It should take a little bit to encode this."; - - foreach (var pipe in _pipes) - { - SendUnicodeMessageWithSpan(pipe, text); - } - - return _pipes.Length; - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/GumpPackets.cs b/Projects/Benchmarks/Benchmarks/Packets/GumpPackets.cs deleted file mode 100644 index 74888fd37..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/GumpPackets.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Text; -using Server.Gumps; -using Server.Network; - -namespace Server.Tests -{ - public interface IGumpWriter - { - int TextEntries { get; set; } - int Switches { get; set; } - - void AppendLayout(bool val); - void AppendLayout(int val); - void AppendLayout(uint val); - void AppendLayout(Serial serial); - void AppendLayoutNS(int val); - void AppendLayout(string text); - void AppendLayoutNS(string text); - void AppendLayout(byte[] buffer); - void WriteStrings(List strings); - void Flush(); - } - - public sealed class CloseGump : Packet - { - public CloseGump(int typeID, int buttonID) : base(0xBF) - { - EnsureCapacity(13); - - Stream.Write((short)0x04); - Stream.Write(typeID); - Stream.Write(buttonID); - } - } - - public sealed class DisplayGumpPacked : Packet, IGumpWriter - { - private static readonly byte[] m_True = Gump.StringToBuffer(" 1"); - private static readonly byte[] m_False = Gump.StringToBuffer(" 0"); - - private static readonly byte[] m_BeginTextSeparator = Gump.StringToBuffer(" @"); - private static readonly byte[] m_EndTextSeparator = Gump.StringToBuffer("@"); - - private static readonly byte[] m_Buffer = new byte[48]; - - private readonly Gump m_Gump; - - private readonly PacketWriter m_Layout; - private readonly PacketWriter m_Strings; - - private int m_StringCount; - - static DisplayGumpPacked() => m_Buffer[0] = (byte)' '; - - public DisplayGumpPacked(Gump gump) - : base(0xDD) - { - m_Gump = gump; - - m_Layout = PacketWriter.CreateInstance(8192); - m_Strings = PacketWriter.CreateInstance(8192); - } - - public int TextEntries { get; set; } - - public int Switches { get; set; } - - public void AppendLayout(bool val) - { - AppendLayout(val ? m_True : m_False); - } - - public void AppendLayout(int val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - - m_Layout.Write(m_Buffer, 0, bytes); - } - - public void AppendLayout(uint val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - - m_Layout.Write(m_Buffer, 0, bytes); - } - - public void AppendLayout(Serial serial) => AppendLayout(serial.Value); - - public void AppendLayoutNS(int val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1); - - m_Layout.Write(m_Buffer, 1, bytes); - } - - public void AppendLayoutNS(string text) - { - m_Layout.WriteAsciiFixed(text, text.Length); - } - - 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 (var i = 0; i < strings.Count; ++i) - { - var v = strings[i] ?? ""; - - 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); - - Stream.Write(m_Gump.Serial); - Stream.Write(m_Gump.TypeID); - Stream.Write(m_Gump.X); - Stream.Write(m_Gump.Y); - - // Note: layout MUST be null terminated (don't listen to krrios) - m_Layout.Write((byte)0); - - WritePacked(m_Layout); - - Stream.Write(m_StringCount); - - WritePacked(m_Strings); - - PacketWriter.ReleaseInstance(m_Layout); - PacketWriter.ReleaseInstance(m_Strings); - } - - private void WritePacked(PacketWriter src) - { - var buffer = src.UnderlyingStream.GetBuffer(); - var length = (int)src.Length; - - if (length == 0) - { - Stream.Write(0); - return; - } - - var wantLength = 1 + length * 1024 / 1000; - - wantLength += 4095; - wantLength &= ~4095; - - var packBuffer = ArrayPool.Shared.Rent(wantLength); - - var packLength = wantLength; - - Zlib.Pack(packBuffer, ref packLength, buffer, length, ZlibQuality.Default); - - Stream.Write(4 + packLength); - Stream.Write(length); - Stream.Write(packBuffer, 0, packLength); - - ArrayPool.Shared.Return(packBuffer); - } - } - - public sealed class DisplayGumpFast : Packet, IGumpWriter - { - private static readonly byte[] m_True = Gump.StringToBuffer(" 1"); - private static readonly byte[] m_False = Gump.StringToBuffer(" 0"); - - private static readonly byte[] m_BeginTextSeparator = Gump.StringToBuffer(" @"); - private static readonly byte[] m_EndTextSeparator = Gump.StringToBuffer("@"); - - private readonly byte[] m_Buffer = new byte[48]; - private int m_LayoutLength; - - public DisplayGumpFast(Gump g) : base(0xB0) - { - m_Buffer[0] = (byte)' '; - - EnsureCapacity(4096); - - Stream.Write(g.Serial); - Stream.Write(g.TypeID); - Stream.Write(g.X); - Stream.Write(g.Y); - Stream.Write((ushort)0xFFFF); - } - - public int TextEntries { get; set; } - - public int Switches { get; set; } - - public void AppendLayout(bool val) - { - AppendLayout(val ? m_True : m_False); - } - - public void AppendLayout(int val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - - Stream.Write(m_Buffer, 0, bytes); - m_LayoutLength += bytes; - } - - public void AppendLayout(uint val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - - Stream.Write(m_Buffer, 0, bytes); - m_LayoutLength += bytes; - } - - public void AppendLayout(Serial serial) => AppendLayout(serial.Value); - - public void AppendLayoutNS(int val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1); - - Stream.Write(m_Buffer, 1, bytes); - m_LayoutLength += bytes; - } - - public void AppendLayoutNS(string text) - { - var length = text.Length; - Stream.WriteAsciiFixed(text, length); - m_LayoutLength += length; - } - - public void AppendLayout(string text) - { - AppendLayout(m_BeginTextSeparator); - - var length = text.Length; - Stream.WriteAsciiFixed(text, length); - m_LayoutLength += length; - - AppendLayout(m_EndTextSeparator); - } - - public void AppendLayout(byte[] buffer) - { - var length = buffer.Length; - Stream.Write(buffer, 0, length); - m_LayoutLength += length; - } - - public void WriteStrings(List text) - { - Stream.Seek(19, SeekOrigin.Begin); - Stream.Write((ushort)m_LayoutLength); - Stream.Seek(0, SeekOrigin.End); - - Stream.Write((ushort)text.Count); - - for (var i = 0; i < text.Count; ++i) - { - var v = text[i] ?? ""; - - int length = (ushort)v.Length; - - Stream.Write((ushort)length); - Stream.WriteBigUniFixed(v, length); - } - } - - public void Flush() - { - } - } - - public sealed class DisplaySignGump : Packet - { - public DisplaySignGump(Serial serial, int gumpID, string unknown, string caption) : base(0x8B) - { - unknown ??= ""; - caption ??= ""; - - EnsureCapacity(15 + unknown.Length + caption.Length); - - Stream.Write(serial); - Stream.Write((short)gumpID); - Stream.Write((short)(unknown.Length + 1)); - Stream.WriteAsciiNull(unknown); - Stream.Write((short)(caption.Length + 1)); - Stream.WriteAsciiNull(caption); - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/GumpUtilities.cs b/Projects/Benchmarks/Benchmarks/Packets/GumpUtilities.cs deleted file mode 100644 index c14ae27a2..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/GumpUtilities.cs +++ /dev/null @@ -1,481 +0,0 @@ -using System.Collections.Generic; -using Server.Gumps; -using Server.Network; - -namespace Server.Tests.Network -{ - public static class GumpUtilities - { - private static readonly byte[] m_BeginLayout = Gump.StringToBuffer("{ "); - private static readonly byte[] m_EndLayout = Gump.StringToBuffer(" }"); - - public static Packet Compile(this Gump g, NetState ns = null) - { - IGumpWriter disp = new DisplayGumpFast(g); - // IGumpWriter disp = new DisplayGumpPacked(g); - - if (g.UseWebRender) - { - disp.AppendLayout(Gump.WebRender); - } - - if (!g.Draggable) - { - disp.AppendLayout(Gump.NoMove); - } - - if (!g.Closable) - { - disp.AppendLayout(Gump.NoClose); - } - - if (!g.Disposable) - { - disp.AppendLayout(Gump.NoDispose); - } - - if (!g.Resizable) - { - disp.AppendLayout(Gump.NoResize); - } - - var count = g.Entries.Count; - var strings = new List(); - - for (var i = 0; i < count; ++i) - { - var e = g.Entries[i]; - - disp.AppendLayout(m_BeginLayout); - e.AppendToByType(disp, strings); - disp.AppendLayout(m_EndLayout); - } - - disp.WriteStrings(strings); - - disp.Flush(); - - return (Packet)disp; - } - - public static int Intern(this List strings, string value) - { - var indexOf = strings.IndexOf(value); - - if (indexOf >= 0) - { - return indexOf; - } - - strings.Add(value); - return strings.Count - 1; - } - - public static void AppendToByType(this GumpEntry e, IGumpWriter disp, List strings) - { - switch (e) - { - case GumpAlphaRegion g: - { - g.AppendTo(disp, strings); - break; - } - case GumpBackground g: - { - g.AppendTo(disp, strings); - break; - } - case GumpButton g: - { - g.AppendTo(disp, strings); - break; - } - case GumpCheck g: - { - g.AppendTo(disp, strings); - break; - } - case GumpGroup g: - { - g.AppendTo(disp, strings); - break; - } - case GumpECHandleInput g: - { - g.AppendTo(disp, strings); - break; - } - case GumpHtml g: - { - g.AppendTo(disp, strings); - break; - } - case GumpHtmlLocalized g: - { - g.AppendTo(disp, strings); - break; - } - case GumpImage g: - { - g.AppendTo(disp, strings); - break; - } - case GumpImageTileButton g: - { - g.AppendTo(disp, strings); - break; - } - case GumpImageTiled g: - { - g.AppendTo(disp, strings); - break; - } - case GumpItem g: - { - g.AppendTo(disp, strings); - break; - } - case GumpItemProperty g: - { - g.AppendTo(disp, strings); - break; - } - case GumpLabel g: - { - g.AppendTo(disp, strings); - break; - } - case GumpLabelCropped g: - { - g.AppendTo(disp, strings); - break; - } - case GumpMasterGump g: - { - g.AppendTo(disp, strings); - break; - } - case GumpPage g: - { - g.AppendTo(disp, strings); - break; - } - case GumpRadio g: - { - g.AppendTo(disp, strings); - break; - } - case GumpSpriteImage g: - { - g.AppendTo(disp, strings); - break; - } - case GumpTextEntry g: - { - g.AppendTo(disp, strings); - break; - } - case GumpTextEntryLimited g: - { - g.AppendTo(disp, strings); - break; - } - case GumpTooltip g: - { - g.AppendTo(disp, strings); - break; - } - } - } - - public static void AppendTo(this GumpAlphaRegion g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpAlphaRegion.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - } - - public static void AppendTo(this GumpBackground g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpBackground.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.GumpID); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - } - - public static void AppendTo(this GumpButton g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpButton.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.NormalID); - disp.AppendLayout(g.PressedID); - disp.AppendLayout((int)g.Type); - disp.AppendLayout(g.Param); - disp.AppendLayout(g.ButtonID); - } - - public static void AppendTo(this GumpCheck g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpButton.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.InactiveID); - disp.AppendLayout(g.ActiveID); - disp.AppendLayout(g.InitialState); - disp.AppendLayout(g.SwitchID); - - disp.Switches++; - } - - public static void AppendTo(this GumpGroup g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpGroup.LayoutName); - disp.AppendLayout(g.Group); - } - - public static void AppendTo(this GumpECHandleInput g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpECHandleInput.LayoutName); - } - - public static void AppendTo(this GumpHtml g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpHtml.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(strings.Intern(g.Text)); - disp.AppendLayout(g.Background); - disp.AppendLayout(g.Scrollbar); - } - - public static void AppendTo(this GumpHtmlLocalized g, IGumpWriter disp, List strings) - { - switch (g.Type) - { - case GumpHtmlLocalizedType.Plain: - { - disp.AppendLayout(GumpHtmlLocalized.LayoutNamePlain); - - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.Number); - disp.AppendLayout(g.Background); - disp.AppendLayout(g.Scrollbar); - - break; - } - - case GumpHtmlLocalizedType.Color: - { - disp.AppendLayout(GumpHtmlLocalized.LayoutNameColor); - - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.Number); - disp.AppendLayout(g.Background); - disp.AppendLayout(g.Scrollbar); - disp.AppendLayout(g.Color); - - break; - } - - case GumpHtmlLocalizedType.Args: - { - disp.AppendLayout(GumpHtmlLocalized.LayoutNameArgs); - - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.Background); - disp.AppendLayout(g.Scrollbar); - disp.AppendLayout(g.Color); - disp.AppendLayout(g.Number); - disp.AppendLayout(g.Args); - - break; - } - } - } - - public static void AppendTo(this GumpImage g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpImage.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.GumpID); - - if (g.Hue != 0) - { - disp.AppendLayout(GumpImage.HueEquals); - disp.AppendLayoutNS(g.Hue); - } - - if (!string.IsNullOrEmpty(g.Class)) - { - disp.AppendLayout(GumpImage.ClassEquals); - disp.AppendLayoutNS(g.Class); - } - } - - public static void AppendTo(this GumpImageTileButton g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpImageTileButton.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.NormalID); - disp.AppendLayout(g.PressedID); - disp.AppendLayout((int)g.Type); - disp.AppendLayout(g.Param); - disp.AppendLayout(g.ButtonID); - - disp.AppendLayout(g.ItemID); - disp.AppendLayout(g.Hue); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - - if (g.LocalizedTooltip > 0) - { - disp.AppendLayout(GumpImageTileButton.LayoutTooltip); - disp.AppendLayout(g.LocalizedTooltip); - } - } - - public static void AppendTo(this GumpImageTiled g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpImageTiled.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.GumpID); - } - - public static void AppendTo(this GumpItem g, IGumpWriter disp, List strings) - { - disp.AppendLayout(g.Hue == 0 ? GumpItem.LayoutName : GumpItem.LayoutNameHue); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.ItemID); - - if (g.Hue != 0) - { - disp.AppendLayout(g.Hue); - } - } - - public static void AppendTo(this GumpItemProperty g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpItemProperty.LayoutName); - disp.AppendLayout(g.Serial); - } - - public static void AppendTo(this GumpLabel g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpLabel.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Hue); - disp.AppendLayout(strings.Intern(g.Text)); - } - - public static void AppendTo(this GumpLabelCropped g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpLabelCropped.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.Hue); - disp.AppendLayout(strings.Intern(g.Text)); - } - - public static void AppendTo(this GumpMasterGump g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpMasterGump.LayoutName); - disp.AppendLayout(g.GumpID); - } - - public static void AppendTo(this GumpPage g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpPage.LayoutName); - disp.AppendLayout(g.Page); - } - - public static void AppendTo(this GumpRadio g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpRadio.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.InactiveID); - disp.AppendLayout(g.ActiveID); - disp.AppendLayout(g.InitialState); - disp.AppendLayout(g.SwitchID); - - disp.Switches++; - } - - public static void AppendTo(this GumpSpriteImage g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpSpriteImage.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.GumpID); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.SX); - disp.AppendLayout(g.SY); - } - - public static void AppendTo(this GumpTextEntry g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpTextEntry.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.Hue); - disp.AppendLayout(g.EntryID); - disp.AppendLayout(strings.Intern(g.InitialText)); - - disp.TextEntries++; - } - - public static void AppendTo(this GumpTextEntryLimited g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpTextEntryLimited.LayoutName); - disp.AppendLayout(g.X); - disp.AppendLayout(g.Y); - disp.AppendLayout(g.Width); - disp.AppendLayout(g.Height); - disp.AppendLayout(g.Hue); - disp.AppendLayout(g.EntryID); - disp.AppendLayout(strings.Intern(g.InitialText)); - disp.AppendLayout(g.Size); - - disp.TextEntries++; - } - - public static void AppendTo(this GumpTooltip g, IGumpWriter disp, List strings) - { - disp.AppendLayout(GumpTooltip.LayoutName); - disp.AppendLayout(g.Number); - - if (!string.IsNullOrEmpty(g.Args)) - { - disp.AppendLayout(g.Args); - } - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/Packet.cs b/Projects/Benchmarks/Benchmarks/Packets/Packet.cs deleted file mode 100644 index 7b634fcce..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/Packet.cs +++ /dev/null @@ -1,265 +0,0 @@ -using System; -using System.Buffers; -using System.Diagnostics; -using System.IO; -using Server.Diagnostics; - -namespace Server.Network -{ - public abstract class Packet - { - private const int CompressorBufferSize = 0x10000; - - private readonly int m_Length; - - private byte[] m_CompiledBuffer; - private int m_CompiledLength; - private State m_State; - - protected Packet(int packetID) - { - PacketID = packetID; - - if (Core.Profiling) - { - var prof = PacketSendProfile.Acquire(PacketID); - prof.Increment(); - } - } - - protected Packet(int packetID, int length) - { - PacketID = packetID; - m_Length = length; - - Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length ); - Stream.Write((byte)packetID); - - if (Core.Profiling) - { - var prof = PacketSendProfile.Acquire(PacketID); - prof.Increment(); - } - } - - public int PacketID { get; } - - public PacketWriter Stream { get; protected set; } - - public void EnsureCapacity(int length) - { - Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length ); - Stream.Write((byte)PacketID); - Stream.Write((short)0); - } - - 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 Packet p) - { - p?.Release(); - p = null; - } - - public static void Release(Packet p) - { - 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) - { - ArrayPool.Shared.Return(m_CompiledBuffer); - } - - m_State &= ~(State.Static | State.Acquired | State.Buffered); - - m_CompiledBuffer = null; - } - - public void Release() - { - if ((m_State & State.Acquired) != 0) - { - Free(); - } - } - - private readonly object _object = new(); - - public byte[] Compile(bool compress, out int length) - { - lock (_object) - { - 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 var op = new StreamWriter("net_opt.log", true); - op.WriteLine("Redundant compile for packet {0}, use Acquire() and Release()", GetType()); - op.WriteLine(new StackTrace()); - } - catch - { - // ignored - } - } - - m_CompiledBuffer = Array.Empty(); - 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) - { - var streamLen = Stream.Length; - - Stream.Seek(1, SeekOrigin.Begin); - Stream.Write((ushort)streamLen); - } - else if (Stream.Length != m_Length) - { - var diff = (int)Stream.Length - m_Length; - - Console.WriteLine( - "Packet: 0x{0:X2}: Bad packet length! ({1}{2} bytes)", - PacketID, - diff >= 0 ? "+" : "", - diff - ); - } - - var ms = Stream.UnderlyingStream; - - m_CompiledBuffer = ms.GetBuffer(); - var length = (int)ms.Length; - - if (compress) - { - var compressorBuffer = new byte[CompressorBufferSize]; - var compressedLength = NetworkCompression.Compress(m_CompiledBuffer.AsSpan(0, length), compressorBuffer); - - if (length <= 0) - { - Console.WriteLine( - "Warning: Compression buffer overflowed on packet 0x{0:X2} ('{1}') (length={2})", - PacketID, - GetType().Name, - length - ); - using var op = new StreamWriter("compression_overflow.log", true); - op.WriteLine( - "{0} Warning: Compression buffer overflowed on packet 0x{1:X2} ('{2}') (length={3})", - Core.Now, - PacketID, - GetType().Name, - length - ); - op.WriteLine(new StackTrace()); - } - else - { - m_CompiledBuffer = compressorBuffer; - m_CompiledLength = compressedLength; - } - } - else - { - m_CompiledLength = length; - } - - if (m_CompiledLength > 0) - { - var old = m_CompiledBuffer; - - if ((m_State & State.Static) != 0) - { - m_CompiledBuffer = new byte[m_CompiledLength]; - } - else - { - // Release it later using Release() - m_CompiledBuffer = ArrayPool.Shared.Rent(m_CompiledLength); - m_State |= State.Buffered; - } - - Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, m_CompiledLength); - - if (compress) - { - ArrayPool.Shared.Return(old); - } - } - - PacketWriter.ReleaseInstance(Stream); - Stream = null; - } - - [Flags] - private enum State - { - Inactive = 0x00, - Static = 0x01, - Acquired = 0x02, - Accessed = 0x04, - Buffered = 0x08, - Warned = 0x10 - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/PacketTestUtilities.cs b/Projects/Benchmarks/Benchmarks/Packets/PacketTestUtilities.cs deleted file mode 100644 index e7a01619f..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/PacketTestUtilities.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Runtime.CompilerServices; -using Server; -using Server.Network; - -namespace Benchmarks -{ - public static class PacketTestUtilities - { - public static Span Compile(this Packet p) => - p.Compile(false, out var length).AsSpan(0, length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Write(this Span data, ref int pos, Serial serial) - { - BinaryPrimitives.WriteUInt32BigEndian(data.Slice(pos, 4), serial.Value); - pos += 4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Write(this Span data, ref int pos, ushort value) - { - BinaryPrimitives.WriteUInt16BigEndian(data.Slice(pos, 2), value); - pos += 2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Write(this Span data, ref int pos, byte value) => data[pos++] = value; - } -} diff --git a/Projects/Benchmarks/Benchmarks/Packets/PacketWriter.cs b/Projects/Benchmarks/Benchmarks/Packets/PacketWriter.cs deleted file mode 100644 index 696dbc7e8..000000000 --- a/Projects/Benchmarks/Benchmarks/Packets/PacketWriter.cs +++ /dev/null @@ -1,354 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Text; - -namespace Server.Network -{ - /// - /// Provides functionality for writing primitive binary data. - /// - public class PacketWriter - { - private static readonly ConcurrentQueue m_Pool = new(); - - /// - /// Internal format buffer. - /// - private readonly byte[] m_Buffer = new byte[4]; - - private int m_Capacity; - - /// - /// Instantiates a new PacketWriter instance with a given capacity. - /// - /// Initial capacity for the internal stream. - public PacketWriter(int capacity = 32) - { - UnderlyingStream = new MemoryStream(capacity); - m_Capacity = capacity; - } - - /// - /// Gets the total stream length. - /// - public long Length => UnderlyingStream.Length; - - /// - /// Gets or sets the current stream position. - /// - public long Position - { - get => UnderlyingStream.Position; - set => UnderlyingStream.Position = value; - } - - /// - /// The internal stream used by this PacketWriter instance. - /// - public MemoryStream UnderlyingStream { get; } - - public static PacketWriter CreateInstance(int capacity = 32) - { - if (m_Pool.TryDequeue(out var pw)) - { - pw.m_Capacity = capacity; - pw.UnderlyingStream.SetLength(0); - return pw; - } - - return new PacketWriter(capacity); - } - - public static void ReleaseInstance(PacketWriter pw) - { - m_Pool.Enqueue(pw); - } - - /// - /// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1. - /// - public void Write(bool value) - { - UnderlyingStream.WriteByte((byte)(value ? 1 : 0)); - } - - /// - /// Writes a 1-byte unsigned integer value to the underlying stream. - /// - public void Write(byte value) - { - UnderlyingStream.WriteByte(value); - } - - /// - /// Writes a 1-byte signed integer value to the underlying stream. - /// - public void Write(sbyte value) - { - UnderlyingStream.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; - - UnderlyingStream.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; - - UnderlyingStream.Write(m_Buffer, 0, 2); - } - - public void Write(Serial serial) => Write(serial.Value); - - /// - /// 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; - - UnderlyingStream.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; - - UnderlyingStream.Write(m_Buffer, 0, 4); - } - - /// - /// Writes a sequence of bytes to the underlying stream - /// - public void Write(byte[] buffer, int offset, int size) - { - UnderlyingStream.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; - } - - var length = value.Length; - - UnderlyingStream.SetLength(UnderlyingStream.Length + size); - - if (length >= size) - { - UnderlyingStream.Position += - Encoding.ASCII.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position); - } - else - { - Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position); - UnderlyingStream.Position += size; - } - } - - /// - /// 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; - } - - var length = value.Length; - - UnderlyingStream.SetLength(UnderlyingStream.Length + length + 1); - - Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position); - UnderlyingStream.Position += length + 1; - } - - /// - /// 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; - } - - var length = value.Length; - - UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2); - - UnderlyingStream.Position += - Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position); - UnderlyingStream.Position += 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; - } - - var length = value.Length; - size *= 2; - - UnderlyingStream.SetLength(UnderlyingStream.Length + size); - - if (length * 2 >= size) - { - UnderlyingStream.Position += - Encoding.Unicode.GetBytes( - value, - 0, - size / 2, - UnderlyingStream.GetBuffer(), - (int)UnderlyingStream.Position - ); - } - else - { - Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position); - UnderlyingStream.Position += size; - } - } - - /// - /// 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; - } - - var length = value.Length; - - UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2); - - UnderlyingStream.Position += - Encoding.BigEndianUnicode.GetBytes( - value, - 0, - length, - UnderlyingStream.GetBuffer(), - (int)UnderlyingStream.Position - ); - UnderlyingStream.Position += 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; - } - - var length = value.Length; - size *= 2; - - UnderlyingStream.SetLength(UnderlyingStream.Length + size); - - if (length * 2 >= size) - { - UnderlyingStream.Position += - Encoding.BigEndianUnicode.GetBytes( - value, - 0, - size / 2, - UnderlyingStream.GetBuffer(), - (int)UnderlyingStream.Position - ); - } - else - { - Encoding.BigEndianUnicode.GetBytes( - value, - 0, - length, - UnderlyingStream.GetBuffer(), - (int)UnderlyingStream.Position - ); - UnderlyingStream.Position += size; - } - } - - /// - /// Fills the stream from the current position up to (capacity) with 0x00's - /// - public void Fill() - { - Fill(m_Capacity - UnderlyingStream.Length); - } - - /// - /// Writes a number of 0x00 byte values to the underlying stream. - /// - public void Fill(long length) - { - if (UnderlyingStream.Position == UnderlyingStream.Length) - { - UnderlyingStream.SetLength(UnderlyingStream.Length + length); - UnderlyingStream.Seek(0, SeekOrigin.End); - } - else - { - UnderlyingStream.Write(new byte[length], 0, (int)length); - } - } - - /// - /// Offsets the current position from an origin. - /// - public long Seek(long offset, SeekOrigin origin) => UnderlyingStream.Seek(offset, origin); - - /// - /// Gets the entire stream content as a byte array. - /// - public byte[] ToArray() => UnderlyingStream.ToArray(); - } -} diff --git a/Projects/Benchmarks/Benchmarks/Rng/BenchmarkDoubleVsFixed.cs b/Projects/Benchmarks/Benchmarks/Rng/BenchmarkDoubleVsFixed.cs deleted file mode 100644 index 53b079cb5..000000000 --- a/Projects/Benchmarks/Benchmarks/Rng/BenchmarkDoubleVsFixed.cs +++ /dev/null @@ -1,28 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server.Random; - -namespace Benchmarks.Benchmarks.Rng -{ - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkDoubleVsFixed - { - private Xoshiro256PlusPlus _xoshiro256PlusPlus; - - [GlobalSetup] - public void Setup() - { - _xoshiro256PlusPlus = new Xoshiro256PlusPlus(); - } - - [Benchmark] - public bool NextDouble() => 50.1 < _xoshiro256PlusPlus.NextDouble() * 100; - - [Benchmark] - public bool NextFixedInt() => 501 < _xoshiro256PlusPlus.Next(1000); - - [Benchmark] - public bool NextHighResDouble() => 50.1 < _xoshiro256PlusPlus.NextDoubleHighRes() * 100; - } -} diff --git a/Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs b/Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs deleted file mode 100644 index caced76b3..000000000 --- a/Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server.Random; - -namespace Benchmarks.Benchmarks.Rng -{ - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkXoshiro - { - private Random _random; - private Xoshiro256PlusPlus _xoshiro256PlusPlus; - - [GlobalSetup] - public void Setup() - { - _xoshiro256PlusPlus = new Xoshiro256PlusPlus(); - _random = new Random(); - } - - [Benchmark] - public int SystemRandomULong() => _random.Next(10000); - - [Benchmark] - public int XoshiroRandomULong() => _xoshiro256PlusPlus.Next(10000); - - [Benchmark] - public double SystemRandomDouble() => _random.NextDouble(); - - [Benchmark] - public double XoshiroRandomDouble() => _xoshiro256PlusPlus.NextDouble(); - - [Benchmark] - public int SystemRandomMinMax() => _random.Next(5000, 85000); - - [Benchmark] - public int XoshiroRandomMinMax() - { - const int min = 5000; - const int max = 85000; - - return min + (int)_xoshiro256PlusPlus.Next((uint)(max - min + 1)); - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs b/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs deleted file mode 100644 index b2f716861..000000000 --- a/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs +++ /dev/null @@ -1,32 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server.Text; - -namespace Benchmarks.BenchmarkText -{ - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkTextEncoding - { - private const string text = - "This is supposed to be a really long text Ƞă¼ÖƄŭȘú😅¥♓ǵDƤĂ😋ġǁ⚕😐'ƿī😪_l" + - "This is supposed to be a really long text Ƞă¼ÖƄŭȘú😅¥♓ǵDƤĂ😋ġǁ⚕😐'ƿī😪_l" + - "This is supposed to be a really long text Ƞă¼ÖƄŭȘú😅¥♓ǵDƤĂ😋ġǁ⚕😐'ƿī😪_l" + - "This is supposed to be a really long text Ƞă¼ÖƄŭȘú😅¥♓ǵDƤĂ😋ġǁ⚕😐'ƿī😪_l" + - "This is supposed to be a really long text Ƞă¼ÖƄŭȘú😅¥♓ǵDƤĂ😋ġǁ⚕😐'ƿī😪_l"; - - [Benchmark] - public byte[] TestEncodingOldReturnBytes() - { - var bytes = TextEncoding.UTF8.GetBytes(text); - return bytes; - } - - [Benchmark] - public byte[] TestEncodingNewReturnBytes() - { - var bytes = text.GetBytesUtf8(); - return bytes; - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs b/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs deleted file mode 100644 index 55e4fd289..000000000 --- a/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Buffers; -using System.Text; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server.Buffers; - -namespace Benchmarks.BenchmarkUtilities -{ - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.Net60)] - public class BenchmarkStringHelpers - { - private readonly string[] names = - { - "Kamron", "Owyn", "Luthius", "Jaedan", "Vorspire", "other people", - "Kamron-2", "Owyn-2", "Luthius-2", "Jaedan-2", "Vorspire-2", "other people too" - }; - - private int length; - - [GlobalSetup] - public void Setup() - { - var chrs = ArrayPool.Shared.Rent(65535); - ArrayPool.Shared.Return(chrs); - length = 0; - - for (int i = 0; i < names.Length; i++) - { - length += names.Length; - } - - length += 2 * (names.Length - 1) + 3; - } - - [Benchmark] - public string BenchmarkStringBuilder() - { - var sb = new StringBuilder(); - for (var i = 0; i < names.Length; i++) - { - if (i > 0) - { - sb.Append(i == names.Length - 1 ? ", and" : ", "); - } - - sb.Append(names[i]); - } - - return sb.ToString(); - } - - [Benchmark] - public string BenchmarkValueStringBuilderWithStack() - { - using var sb = new ValueStringBuilder(stackalloc char[length]); - for (var i = 0; i < names.Length; i++) - { - if (i > 0) - { - sb.Append(i == names.Length - 1 ? ", and" : ", "); - } - - sb.Append(names[i]); - } - - return sb.ToString(); - } - - [Benchmark] - public string BenchmarkValueStringBuilderWithRentedBuffer() - { - using var sb = new ValueStringBuilder(stackalloc char[32]); - for (var i = 0; i < names.Length; i++) - { - if (i > 0) - { - sb.Append(i == names.Length - 1 ? ", and" : ", "); - } - - sb.Append(names[i]); - } - - return sb.ToString(); - } - } -} diff --git a/Projects/Benchmarks/Directory.Build.props b/Projects/Benchmarks/Directory.Build.props deleted file mode 100644 index b9b7c8ad8..000000000 --- a/Projects/Benchmarks/Directory.Build.props +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Projects/Benchmarks/Program.cs b/Projects/Benchmarks/Program.cs deleted file mode 100644 index bfdadb1bc..000000000 --- a/Projects/Benchmarks/Program.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BenchmarkDotNet.Running; -using Benchmarks.Benchmarks.Rng; - -namespace Benchmarks -{ - public static class Program - { - private static void Main(string[] args) - { - // var featureFlags = BenchmarkRunner.Run(); - // var packetConstruction = BenchmarkRunner.Run(); - // var broadcast = BenchmarkRunner.Run(); - // var stringHelpers = BenchmarkRunner.Run(); - // var indexList = BenchmarkRunner.Run(); - // var textEncoding = BenchmarkRunner.Run(); - // var logging = BenchmarkRunner.Run(); - // var gumpPacket = BenchmarkRunner.Run(); - // MapSelectors.Init(); - // var mapSelectors = BenchmarkRunner.Run(); - // var rngTest = BenchmarkRunner.Run(); - var doubleRngText = BenchmarkRunner.Run(); - } - } -} diff --git a/Projects/SerializationGenerator/EntitySerializationGenerator.cs b/Projects/SerializationGenerator/EntitySerializationGenerator.cs deleted file mode 100755 index e5b1e0f0d..000000000 --- a/Projects/SerializationGenerator/EntitySerializationGenerator.cs +++ /dev/null @@ -1,91 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: EntityJsonGenerator.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; -using SerializableMigration; - -namespace SerializationGenerator -{ - [Generator] - public class EntitySerializationGenerator : ISourceGenerator - { - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(() => new SerializerSyntaxReceiver()); - } - - public void Execute(GeneratorExecutionContext context) - { - if (context.SyntaxContextReceiver is not SerializerSyntaxReceiver receiver) - { - return; - } - - var jsonOptions = SerializableMigrationSchema.GetJsonSerializerOptions(); - // List of types that _will_ become ISerializable - var serializableList = receiver.SerializableList; - var embeddedSerializableList = receiver.EmbeddedSerializableList; - - foreach (var (classSymbol, (serializableAttr, fieldsList)) in receiver.ClassAndFields) - { - if (serializableAttr == null) - { - continue; - } - - string classSource = context.GenerateSerializationPartialClass( - classSymbol, - serializableAttr, - false, - fieldsList.ToImmutableArray(), - jsonOptions, - serializableList, - embeddedSerializableList - ); - - if (classSource != null) - { - context.AddSource($"{classSymbol.ToDisplayString()}.Serialization.cs", SourceText.From(classSource, Encoding.UTF8)); - } - } - - foreach (var (classSymbol, (embeddedSerializableAttr, fieldsList)) in receiver.EmbeddedClassAndFields) - { - if (embeddedSerializableAttr == null) - { - continue; - } - - string classSource = context.GenerateSerializationPartialClass( - classSymbol, - embeddedSerializableAttr, - true, - fieldsList.ToImmutableArray(), - jsonOptions, - serializableList, - embeddedSerializableList - ); - - if (classSource != null) - { - context.AddSource($"{classSymbol.ToDisplayString()}.Serialization.cs", SourceText.From(classSource, Encoding.UTF8)); - } - } - } - } -} diff --git a/Projects/SerializationGenerator/IsExternalInit.cs b/Projects/SerializationGenerator/IsExternalInit.cs deleted file mode 100644 index eb2da113f..000000000 --- a/Projects/SerializationGenerator/IsExternalInit.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace System.Runtime.CompilerServices -{ - internal static class IsExternalInit {} -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs deleted file mode 100644 index 7611d5799..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs +++ /dev/null @@ -1,436 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableEntityGeneration.Class.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using Microsoft.CodeAnalysis; -using SerializableMigration; - -namespace SerializationGenerator -{ - public static partial class SerializableEntityGeneration - { - public static string GenerateSerializationPartialClass( - this GeneratorExecutionContext context, - INamedTypeSymbol classSymbol, - AttributeData serializableAttr, - bool embedded, - ImmutableArray fieldsAndProperties, - JsonSerializerOptions jsonSerializerOptions, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes - ) - { - var version = (int)serializableAttr.ConstructorArguments[0].Value!; - - var migrations = context.GetMigrationsByAnalyzerConfig( - classSymbol, - version, - jsonSerializerOptions - ); - - return context.Compilation.GenerateSerializationPartialClass( - classSymbol, - serializableAttr, - null, // Do not generate schema - embedded, - null, - migrations.ToImmutableArray(), - fieldsAndProperties, - serializableTypes, - embeddedSerializableTypes - ); - } - - public static string GenerateSerializationPartialClass( - this Compilation compilation, - INamedTypeSymbol classSymbol, - AttributeData serializableAttr, - string? migrationPath, - bool embedded, - JsonSerializerOptions? jsonSerializerOptions, - ImmutableArray fieldsAndProperties, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes - ) - { - var version = (int)serializableAttr.ConstructorArguments[0].Value!; - - var migrations = SerializableMigrationSchema.GetMigrations( - classSymbol, - version, - migrationPath, - jsonSerializerOptions - ); - - return compilation.GenerateSerializationPartialClass( - classSymbol, - serializableAttr, - migrationPath, - embedded, - jsonSerializerOptions, - migrations.ToImmutableArray(), - fieldsAndProperties, - serializableTypes, - embeddedSerializableTypes - ); - } - - public static string GenerateSerializationPartialClass( - this Compilation compilation, - INamedTypeSymbol classSymbol, - AttributeData serializableAttr, - string? migrationPath, - bool embedded, - JsonSerializerOptions? jsonSerializerOptions, - ImmutableArray migrations, - ImmutableArray fieldsAndProperties, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes - ) - { - var serializableFieldAttribute = - compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_FIELD_ATTRIBUTE); - var serializableFieldAttrAttribute = - compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_FIELD_ATTR_ATTRIBUTE); - var serializableInterface = - compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_INTERFACE); - var parentSerializableAttribute = - compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_PARENT_ATTRIBUTE); - var serializableFieldSaveFlagAttribute = - compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_FIELD_SAVE_FLAG_ATTRIBUTE); - var serializableFieldDefaultAttribute = - compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_FIELD_DEFAULT_ATTRIBUTE); - - // If we have a parent that is or derives from ISerializable, then we are in override - var isOverride = classSymbol.BaseType.ContainsInterface(serializableInterface); - - if (!(embedded || isOverride || classSymbol.ContainsInterface(serializableInterface))) - { - return null; - } - - var isRawSerializable = classSymbol.HasRawSerializableInterface(compilation, ImmutableArray.Empty); - - var version = (int)serializableAttr.ConstructorArguments[0].Value!; - var encodedVersion = (bool)serializableAttr.ConstructorArguments[1].Value!; - - // Let's find out if we need to do serialization flags - var serializableFieldSaveFlags = new SortedDictionary(); - foreach (var m in classSymbol.GetMembers().OfType()) - { - var getSaveFlagAttribute = m.GetAttribute(serializableFieldSaveFlagAttribute); - var getDefaultValueAttribute = m.GetAttribute(serializableFieldDefaultAttribute); - - if (getSaveFlagAttribute == null && getDefaultValueAttribute == null) - { - continue; - } - - var attrCtorArgs = getSaveFlagAttribute?.ConstructorArguments ?? getDefaultValueAttribute.ConstructorArguments; - var order = (int)attrCtorArgs[0].Value!; - - serializableFieldSaveFlags.TryGetValue(order, out var saveFlagMethods); - - serializableFieldSaveFlags[order] = new SerializableFieldSaveFlagMethods - { - DetermineFieldShouldSerialize = getSaveFlagAttribute != null ? m : saveFlagMethods?.DetermineFieldShouldSerialize, - GetFieldDefaultValue = getDefaultValueAttribute != null ? m : saveFlagMethods?.GetFieldDefaultValue - }; - } - - var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); - var className = classSymbol.Name; - - StringBuilder source = new StringBuilder(); - - source.AppendLine("#pragma warning disable\n"); - source.GenerateNamespaceStart(namespaceName); - - var interfaces = !embedded || isRawSerializable - ? Array.Empty() - : new ITypeSymbol[] { compilation.GetTypeByMetadataName(SymbolMetadata.RAW_SERIALIZABLE_INTERFACE) }; - - var indent = " "; - - source.RecursiveGenerateClassStart(classSymbol, interfaces.ToImmutableArray(), ref indent); - - source.GenerateClassField( - indent, - Accessibility.Private, - InstanceModifier.Const, - "int", - "_version", - version.ToString() - ); - source.AppendLine(); - - var parentFieldOrProperty = embedded ? fieldsAndProperties.FirstOrDefault( - fieldOrPropertySymbol => fieldOrPropertySymbol.GetAttributes() - .FirstOrDefault( - attr => - SymbolEqualityComparer.Default.Equals(attr.AttributeClass, parentSerializableAttribute) - ) != null - ) : null; - - var serializablePropertySet = new SortedDictionary(new SerializablePropertyComparer()); - - foreach (var fieldOrPropertySymbol in fieldsAndProperties) - { - var allAttributes = fieldOrPropertySymbol.GetAttributes(); - - var serializableFieldAttr = allAttributes - .FirstOrDefault( - attr => - SymbolEqualityComparer.Default.Equals(attr.AttributeClass, serializableFieldAttribute) - ); - - if (serializableFieldAttr == null) - { - continue; - } - - foreach (var attr in allAttributes) - { - if (!SymbolEqualityComparer.Default.Equals(attr.AttributeClass, serializableFieldAttrAttribute)) - { - continue; - } - - if (attr.AttributeClass == null) - { - continue; - } - - var ctorArgs = attr.ConstructorArguments; - var attrTypeArg = ctorArgs[0]; - - if (attrTypeArg.Kind == TypedConstantKind.Primitive && attrTypeArg.Value is string attrStr) - { - source.AppendLine($"{indent}{attrStr}"); - } - else - { - var attrType = (ITypeSymbol)attrTypeArg.Value; - source.GenerateAttribute(indent, attrType?.Name, ctorArgs[1].Values); - } - } - - var attrCtorArgs = serializableFieldAttr.ConstructorArguments; - - var order = (int)attrCtorArgs[0].Value!; - var getterAccessor = Helpers.GetAccessibility(attrCtorArgs[1].Value?.ToString()); - var setterAccessor = Helpers.GetAccessibility(attrCtorArgs[2].Value?.ToString()); - var virtualProperty = (bool)attrCtorArgs[3].Value!; - - if (fieldOrPropertySymbol is IFieldSymbol fieldSymbol) - { - source.GenerateSerializableProperty( - compilation, - indent, - fieldSymbol, - getterAccessor, - setterAccessor, - virtualProperty, - parentFieldOrProperty - ); - source.AppendLine(); - } - - serializableFieldSaveFlags.TryGetValue(order, out var serializableFieldSaveFlagMethods); - - var serializableProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - fieldOrPropertySymbol, - order, - allAttributes, - serializableTypes, - embeddedSerializableTypes, - classSymbol, - serializableFieldSaveFlagMethods - ); - - serializablePropertySet.Add(serializableProperty, fieldOrPropertySymbol); - } - - var serializableFields = serializablePropertySet.Keys.ToImmutableArray(); - var serializableProperties = serializablePropertySet.Select( - kvp => kvp.Key with - { - Name = (kvp.Value as IFieldSymbol)?.GetPropertyName() ?? ((IPropertySymbol)kvp.Value).Name - } - ).ToImmutableArray(); - - // If we are not inheriting ISerializable, then we need to define some stuff - if (!(isOverride || embedded)) - { - // long ISerializable.SavePosition { get; set; } = -1; - source.GenerateAutoProperty( - Accessibility.NotApplicable, - "long", - "ISerializable.SavePosition", - Accessibility.NotApplicable, - Accessibility.NotApplicable, - indent, - defaultValue: "-1" - ); - - // BufferWriter ISerializable.SaveBuffer { get; set; } - source.GenerateAutoProperty( - Accessibility.NotApplicable, - "BufferWriter", - "ISerializable.SaveBuffer", - Accessibility.NotApplicable, - Accessibility.NotApplicable, - indent - ); - } - - if (!embedded) - { - // Serial constructor - source.GenerateSerialCtor(compilation, className, indent, isOverride); - source.AppendLine(); - } - - if (version > 0) - { - for (var i = 0; i < migrations.Length; i++) - { - var migration = migrations[i]; - if (migration.Version < version) - { - source.GenerateMigrationContentStruct(compilation, indent, migration, classSymbol); - source.AppendLine(); - } - } - } - - // Serialize Method - source.GenerateSerializeMethod( - compilation, - indent, - isOverride, - encodedVersion, - serializableFields, - serializableProperties, - serializableFieldSaveFlags - ); - source.AppendLine(); - - // Deserialize Method - source.GenerateDeserializeMethod( - compilation, - classSymbol, - indent, - isOverride, - version, - encodedVersion, - migrations, - serializableFields, - serializableProperties, - parentFieldOrProperty, - serializableFieldSaveFlags - ); - - // Serialize SaveFlag enum class - if (serializableFieldSaveFlags.Count > 0) - { - source.AppendLine(); - source.GenerateEnumStart( - "SaveFlag", - $"{indent} ", - true, - Accessibility.Private - ); - - source.GenerateEnumValue($"{indent} ", true, "None", -1); - int index = 0; - foreach (var (order, _) in serializableFieldSaveFlags) - { - source.GenerateEnumValue($"{indent} ", true, serializableProperties[order].Name, index++); - } - - source.GenerateEnumEnd($"{indent} "); - } - - source.RecursiveGenerateClassEnd(classSymbol, ref indent); - source.GenerateNamespaceEnd(); - - if (migrationPath != null) - { - // Write the migration file - var newMigration = new SerializableMetadata - { - Version = version, - Type = classSymbol.ToDisplayString(), - Properties = serializableProperties.Length > 0 ? serializableProperties : null - }; - - WriteMigration(migrationPath, newMigration, jsonSerializerOptions); - } - - return source.ToString(); - } - - private static void WriteMigration(string migrationPath, SerializableMetadata metadata, JsonSerializerOptions options) - { - Directory.CreateDirectory(migrationPath); - var filePath = Path.Combine(migrationPath, $"{metadata.Type}.v{metadata.Version}.json"); - File.WriteAllText(filePath, JsonSerializer.Serialize(metadata, options)); - } - - private static void RecursiveGenerateClassStart( - this StringBuilder source, - INamedTypeSymbol classSymbol, - ImmutableArray interfaces, - ref string indent - ) - { - var containingSymbolList = new List(); - - do - { - containingSymbolList.Add(classSymbol); - classSymbol = classSymbol.ContainingSymbol as INamedTypeSymbol; - } while (classSymbol != null); - - containingSymbolList.Reverse(); - - for (var i = 0; i < containingSymbolList.Count; i++) - { - var symbol = containingSymbolList[i]; - source.GenerateClassStart(symbol, indent, i == containingSymbolList.Count - 1 ? interfaces : ImmutableArray.Empty); - indent += " "; - } - } - - private static void RecursiveGenerateClassEnd(this StringBuilder source, INamedTypeSymbol classSymbol, ref string indent) - { - do - { - indent = indent.Substring(0, indent.Length - 4); - source.GenerateClassEnd(indent); - - classSymbol = classSymbol.ContainingSymbol as INamedTypeSymbol; - } while (classSymbol != null); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs deleted file mode 100644 index 59048a125..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs +++ /dev/null @@ -1,213 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableEntityGeneration.DeserializeMethod.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializableMigration; - -namespace SerializationGenerator -{ - public static partial class SerializableEntityGeneration - { - public static void GenerateDeserializeMethod( - this StringBuilder source, - Compilation compilation, - INamedTypeSymbol classSymbol, - string indent, - bool isOverride, - int version, - bool encodedVersion, - ImmutableArray migrations, - ImmutableArray fields, - ImmutableArray properties, - ISymbol parentFieldOrProperty, - SortedDictionary serializableFieldSaveFlagMethodsDictionary - ) - { - var genericReaderInterface = compilation.GetTypeByMetadataName(SymbolMetadata.GENERIC_READER_INTERFACE); - - source.GenerateMethodStart( - indent, - "Deserialize", - Accessibility.Public, - isOverride, - "void", - ImmutableArray.Create<(ITypeSymbol, string)>((genericReaderInterface, "reader")) - ); - - var bodyIndent = $"{indent} "; - var innerIndent = $"{bodyIndent} "; - - if (isOverride) - { - source.AppendLine($"{bodyIndent}base.Deserialize(reader);"); - source.AppendLine(); - } - - var afterDeserialization = classSymbol - .GetMembers() - .OfType() - .Select( - m => - { - if (!m.ReturnsVoid || m.Parameters.Length != 0) - { - return (m, null); - } - - return (m, m.GetAttributes() - .FirstOrDefault( - attr => SymbolEqualityComparer.Default.Equals( - attr.AttributeClass, - compilation.GetTypeByMetadataName(SymbolMetadata.AFTERDESERIALIZATION_ATTRIBUTE) - ) - )); - } - ).Where(m => m.Item2 != null).ToList(); - - // Version - source.AppendLine($"{bodyIndent}var version = reader.{(encodedVersion ? "ReadEncodedInt" : "ReadInt")}();"); - - if (version > 0) - { - var parent = parentFieldOrProperty?.Name ?? "this"; - var nextVersion = 0; - - for (var i = 0; i < migrations.Length; i++) - { - var migrationVersion = migrations[i].Version; - if (migrationVersion == nextVersion) - { - nextVersion++; - } - - source.AppendLine(); - source.AppendLine($"{bodyIndent}if (version == {migrationVersion})"); - source.AppendLine($"{bodyIndent}{{"); - source.AppendLine($"{bodyIndent} MigrateFrom(new V{migrationVersion}Content(reader, this));"); - source.AppendLine($"{bodyIndent} {parent}.MarkDirty();"); - source.GenerateAfterDeserialization($"{bodyIndent} ", afterDeserialization); - source.AppendLine($"{bodyIndent} return;"); - source.AppendLine($"{bodyIndent}}}"); - } - - if (nextVersion < version) - { - source.AppendLine(); - source.AppendLine($"{bodyIndent}if (version < _version)"); - source.AppendLine($"{bodyIndent}{{"); - source.AppendLine($"{bodyIndent} Deserialize(reader, version);"); - source.AppendLine($"{bodyIndent} {parent}.MarkDirty();"); - source.GenerateAfterDeserialization($"{bodyIndent} ", afterDeserialization); - source.AppendLine($"{bodyIndent} return;"); - source.AppendLine($"{bodyIndent}}}"); - } - } - - if (serializableFieldSaveFlagMethodsDictionary.Count > 0) - { - source.AppendLine(); - source.AppendLine($"{bodyIndent}var saveFlags = reader.ReadEnum();"); - } - - for (var i = 0; i < properties.Length; i++) - { - var field = fields[i]; - var property = properties[i]; - var rule = SerializableMigrationRulesEngine.Rules[property.Rule]; - - if (serializableFieldSaveFlagMethodsDictionary.TryGetValue( - property.Order, - out var serializableFieldSaveFlagMethods - )) - { - source.AppendLine(); - // Special case - if (property.Type == "bool") - { - source.AppendLine($"{bodyIndent}{field.Name} = (saveFlags & SaveFlag.{property.Name}) != 0;"); - } - else - { - source.AppendLine($"{bodyIndent}if ((saveFlags & SaveFlag.{property.Name}) != 0)\n{bodyIndent}{{"); - rule.GenerateDeserializationMethod( - source, - innerIndent, - field, - parentFieldOrProperty?.Name ?? "this" - ); - (rule as IPostDeserializeMethod)?.PostDeserializeMethod( - source, - innerIndent, - field, - compilation, - classSymbol - ); - - if (serializableFieldSaveFlagMethods.GetFieldDefaultValue != null) - { - source.AppendLine($"{bodyIndent}}}\n{bodyIndent}else\n{bodyIndent}{{"); - source.AppendLine( - $"{bodyIndent} {field.Name} = {serializableFieldSaveFlagMethods.GetFieldDefaultValue.Name}();" - ); - } - - source.AppendLine($"{bodyIndent}}}"); - } - } - else - { - source.AppendLine(); - rule.GenerateDeserializationMethod( - source, - bodyIndent, - field, - parentFieldOrProperty?.Name ?? "this" - ); - (rule as IPostDeserializeMethod)?.PostDeserializeMethod( - source, - bodyIndent, - field, - compilation, - classSymbol - ); - } - } - - source.GenerateAfterDeserialization($"{bodyIndent}", afterDeserialization); - source.GenerateMethodEnd(indent); - } - - private static void GenerateAfterDeserialization( - this StringBuilder source, string indent, IList<(IMethodSymbol, AttributeData?)> afterDeserialization - ) - { - foreach (var (method, attr) in afterDeserialization) - { - if ((bool)attr.ConstructorArguments[0].Value!) - { - source.AppendLine($"{indent}{method.Name}();"); - } - else - { - source.AppendLine($"{indent}Timer.DelayCall({method.Name});"); - } - } - } - } -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs deleted file mode 100644 index d2457bb90..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs +++ /dev/null @@ -1,82 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableEntityGeneration.Property.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SerializableEntityGeneration - { - public static void GenerateSerializableProperty( - this StringBuilder source, - Compilation compilation, - string indent, - IFieldSymbol fieldSymbol, - Accessibility getter, - Accessibility? setter, - bool isVirtual, - ISymbol? parentFieldOrProperty - ) - { - var fieldName = fieldSymbol.Name; - - var invalidatePropertiesAttribute = fieldSymbol - .GetAttributes() - .OfType() - .FirstOrDefault( - attr => attr.AttributeClass?.Equals( - compilation.GetTypeByMetadataName(SymbolMetadata.INVALIDATEPROPERTIES_ATTRIBUTE), - SymbolEqualityComparer.Default - ) ?? false - ); - - var propertyIndent = $"{indent} "; - var innerIndent = $"{propertyIndent} "; - - var propertyAccessor = setter > getter ? setter : getter; - var getterAccessor = getter == propertyAccessor ? Accessibility.NotApplicable : getter; - - source.GeneratePropertyStart(indent, propertyAccessor.Value, isVirtual, fieldSymbol); - - // Getter - source.GeneratePropertyGetterReturnsField(propertyIndent, fieldSymbol, getterAccessor); - - if (setter != null && setter != Accessibility.NotApplicable) - { - var setterAccessor = setter == propertyAccessor ? Accessibility.NotApplicable : setter; - - var parentSymbol = parentFieldOrProperty?.Name ?? "this"; - - // Setter - source.GeneratePropertySetterStart(propertyIndent, false, setterAccessor.Value); - source.AppendLine($"{innerIndent}if (value != {fieldName})"); - source.AppendLine($"{innerIndent}{{"); - source.AppendLine($"{innerIndent} {fieldName} = value;"); - source.AppendLine($"{innerIndent} {parentSymbol}.MarkDirty();"); - - if (invalidatePropertiesAttribute != null) - { - source.AppendLine($"{innerIndent} InvalidateProperties();"); - } - source.AppendLine($"{innerIndent}}}"); - source.GeneratePropertyGetSetEnd(propertyIndent, false); - } - - source.GeneratePropertyEnd(indent); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs deleted file mode 100644 index 7b7ebcb7f..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableEntityGeneration.SerialCtor.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SerializableEntityGeneration - { - private static readonly ImmutableArray _baseParameters = new[] { "serial" }.ToImmutableArray(); - public static void GenerateSerialCtor( - this StringBuilder source, - Compilation compilation, - string className, - string indent, - bool isOverride - ) - { - var serialType = (ITypeSymbol)compilation.GetTypeByMetadataName("Server.Serial"); - - source.GenerateConstructorStart( - indent, - className, - Accessibility.Public, - new []{ (serialType, "serial") }.ToImmutableArray(), - isOverride ? _baseParameters : ImmutableArray.Empty - ); - - if (!isOverride) - { - source.AppendLine($"{indent} Serial = serial;"); - source.AppendLine($"{indent} SetTypeRef(typeof({className}));"); - } - - source.GenerateMethodEnd(indent); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs deleted file mode 100644 index 6aff93f5e..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs +++ /dev/null @@ -1,112 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableEntityGeneration.SerializeMethod.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializableMigration; - -namespace SerializationGenerator -{ - public static partial class SerializableEntityGeneration - { - public static void GenerateSerializeMethod( - this StringBuilder source, - Compilation compilation, - string indent, - bool isOverride, - bool encodedVersion, - ImmutableArray fields, - ImmutableArray properties, - SortedDictionary serializableFieldSaveFlagMethodsDictionary - ) - { - var genericWriterInterface = compilation.GetTypeByMetadataName(SymbolMetadata.GENERIC_WRITER_INTERFACE); - - source.GenerateMethodStart( - indent, - "Serialize", - Accessibility.Public, - isOverride, - "void", - ImmutableArray.Create<(ITypeSymbol, string)>((genericWriterInterface, "writer")) - ); - - var bodyIndent = $"{indent} "; - var innerIndent = $"{bodyIndent} "; - - if (isOverride) - { - source.AppendLine($"{bodyIndent}base.Serialize(writer);"); - source.AppendLine(); - } - - // Version - source.AppendLine($"{bodyIndent}writer.{(encodedVersion ? "WriteEncodedInt" : "Write")}(_version);"); - - // Let's collect the flags - if (serializableFieldSaveFlagMethodsDictionary.Count > 0) - { - source.AppendLine($"\n{bodyIndent}var saveFlags = SaveFlag.None;"); - - foreach (var (order, saveFlagMethods) in serializableFieldSaveFlagMethodsDictionary) - { - source.AppendLine($"{bodyIndent}if ({saveFlagMethods.DetermineFieldShouldSerialize!.Name}())\n{bodyIndent}{{"); - - var propertyName = properties[order].Name; - source.AppendLine($"{innerIndent}saveFlags |= SaveFlag.{propertyName};"); - - source.AppendLine($"{bodyIndent}}}"); - } - - source.AppendLine($"{bodyIndent}writer.WriteEnum(saveFlags);"); - } - - for (var i = 0; i < properties.Length; i++) - { - var field = fields[i]; - var property = properties[i]; - if (serializableFieldSaveFlagMethodsDictionary.ContainsKey(property.Order)) - { - // Special case - if (property.Type != "bool") - { - source.AppendLine($"\n{bodyIndent}if ((saveFlags & SaveFlag.{property.Name}) != 0)\n{bodyIndent}{{"); - SerializableMigrationRulesEngine.Rules[property.Rule] - .GenerateSerializationMethod( - source, - innerIndent, - field - ); - source.AppendLine($"{bodyIndent}}}"); - } - } - else - { - source.AppendLine(); - SerializableMigrationRulesEngine.Rules[property.Rule] - .GenerateSerializationMethod( - source, - bodyIndent, - field - ); - } - } - - source.GenerateMethodEnd(indent); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableFieldSaveFlagMethods.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableFieldSaveFlagMethods.cs deleted file mode 100644 index 55a087914..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableFieldSaveFlagMethods.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public record SerializableFieldSaveFlagMethods - { - public IMethodSymbol? DetermineFieldShouldSerialize { get; init; } - - public IMethodSymbol? GetFieldDefaultValue { get; init; } - } -} diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializationEntityGeneration.ContentStruct.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializationEntityGeneration.ContentStruct.cs deleted file mode 100644 index 56544ed60..000000000 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializationEntityGeneration.ContentStruct.cs +++ /dev/null @@ -1,127 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializationEntityGeneration.ContentStruct.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializableMigration; - -namespace SerializationGenerator -{ - public static partial class SerializableEntityGeneration - { - public static void GenerateMigrationContentStruct( - this StringBuilder source, - Compilation compilation, - string indent, - SerializableMetadata migration, - INamedTypeSymbol classSymbol - ) - { - source.AppendLine($"{indent}ref struct V{migration.Version}Content"); - source.AppendLine($"{indent}{{"); - var properties = migration.Properties ?? ImmutableArray.Empty; - - foreach (var serializableProperty in properties) - { - SerializableMigrationRulesEngine.Rules[serializableProperty.Rule].GenerateMigrationProperty( - source, compilation, $"{indent} ", serializableProperty - ); - } - - var innerIndent = $"{indent} "; - - var usesSaveFlags = properties.Any(p => p.UsesSaveFlag == true); - - if (usesSaveFlags) - { - source.AppendLine(); - source.GenerateEnumStart( - $"V{migration.Version}SaveFlag", - $"{indent} ", - true, - Accessibility.Private - ); - - source.GenerateEnumValue(innerIndent, true, "None", -1); - int index = 0; - foreach (var property in properties) - { - if (property.UsesSaveFlag == true) - { - source.GenerateEnumValue(innerIndent, true, property.Name, index++); - } - } - - source.GenerateEnumEnd($"{indent} "); - } - - source.AppendLine($"{indent} internal V{migration.Version}Content(IGenericReader reader, {classSymbol.ToDisplayString()} entity)"); - source.AppendLine($"{indent} {{"); - - if (usesSaveFlags) - { - source.AppendLine($"{innerIndent}var saveFlags = reader.ReadEnum();"); - } - - if (properties.Length > 0) - { - foreach (var property in properties) - { - if (property.UsesSaveFlag == true) - { - source.AppendLine(); - // Special case - if (property.Type == "bool") - { - source.AppendLine($"{innerIndent}{property.Name} = (saveFlags & V{migration.Version}SaveFlag.{property.Name}) != 0;"); - } - else - { - source.AppendLine($"{innerIndent}if ((saveFlags & V{migration.Version}SaveFlag.{property.Name}) != 0)\n{innerIndent}{{"); - - SerializableMigrationRulesEngine.Rules[property.Rule].GenerateDeserializationMethod( - source, - $"{innerIndent} ", - property, - "entity", - true - ); - - source.AppendLine($"{innerIndent}}}\n{innerIndent}else\n{innerIndent}{{"); - source.AppendLine($"{innerIndent} {property.Name} = default;"); - source.AppendLine($"{innerIndent}}}"); - } - } - else - { - SerializableMigrationRulesEngine.Rules[property.Rule].GenerateDeserializationMethod( - source, - innerIndent, - property, - "entity", - true - ); - } - } - } - - source.AppendLine($"{indent} }}"); - - source.AppendLine($"{indent}}}"); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/IPostDeserializeMethod.cs b/Projects/SerializationGenerator/SerializableMigration/IPostDeserializeMethod.cs deleted file mode 100644 index 0a48c0117..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/IPostDeserializeMethod.cs +++ /dev/null @@ -1,31 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IPostDeserializeMethod.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializableMigration -{ - public interface IPostDeserializeMethod - { - public void PostDeserializeMethod( - StringBuilder source, - string indent, - SerializableProperty property, - Compilation compilation, - INamedTypeSymbol classSymbol - ); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/ISerializableMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/ISerializableMigrationRule.cs deleted file mode 100644 index a64453e33..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/ISerializableMigrationRule.cs +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializableMigration -{ - public interface ISerializableMigrationRule - { - string RuleName { get; } - - void GenerateMigrationProperty( - StringBuilder source, - Compilation compilation, - string indent, - SerializableProperty serializableProperty - ); - - bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ); - - void GenerateDeserializationMethod( - StringBuilder source, - string indent, - SerializableProperty property, - string? parentReference, - bool isMigration = false - ); - - void GenerateSerializationMethod( - StringBuilder source, - string indent, - SerializableProperty property - ); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/ArrayMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/ArrayMigrationRule.cs deleted file mode 100644 index 99a1e911e..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/ArrayMigrationRule.cs +++ /dev/null @@ -1,135 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ArrayMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializableMigration; - -public class ArrayMigrationRule : MigrationRule -{ - public override string RuleName => nameof(ArrayMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not IArrayTypeSymbol arrayTypeSymbol) - { - ruleArguments = null; - return false; - } - - var serializableArrayType = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "ArrayEntry", - arrayTypeSymbol.ElementType, - 0, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var length = serializableArrayType.RuleArguments?.Length?? 0; - ruleArguments = new string[length + 2]; - ruleArguments[0] = arrayTypeSymbol.ElementType.ToDisplayString(); - ruleArguments[1] = serializableArrayType.Rule; - if (length > 0) - { - Array.Copy(serializableArrayType.RuleArguments!, 0, ruleArguments, 2, length); - } - - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - var ruleArguments = property.RuleArguments; - var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]]; - var arrayElementRuleArguments = new string[ruleArguments.Length - 2]; - Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2); - - var propertyIndex = $"{property.Name}Index"; - source.AppendLine($"{indent}{property.Name} = new {ruleArguments[0]}[reader.ReadEncodedInt()];"); - source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {property.Name}.Length; {propertyIndex}++)"); - source.AppendLine($"{indent}{{"); - - var serializableArrayElement = new SerializableProperty - { - Name = $"{property.Name}[{propertyIndex}]", - Type = ruleArguments[0], - Rule = arrayElementRule.RuleName, - RuleArguments = arrayElementRuleArguments - }; - - arrayElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableArrayElement, parentReference); - - source.AppendLine($"{indent}}}"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]]; - var arrayElementRuleArguments = new string[ruleArguments.Length - 2]; - Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2); - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyIndex = $"{propertyVarPrefix}Index"; - var propertyLength = $"{propertyVarPrefix}Length"; - source.AppendLine($"{indent}var {propertyLength} = {property.Name}?.Length ?? 0;"); - source.AppendLine($"{indent}writer.WriteEncodedInt({propertyLength});"); - source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyLength}; {propertyIndex}++)"); - source.AppendLine($"{indent}{{"); - - var serializableArrayElement = new SerializableProperty - { - Name = $"{property.Name}![{propertyIndex}]", - Type = ruleArguments[0], - Rule = arrayElementRule.RuleName, - RuleArguments = arrayElementRuleArguments - }; - - arrayElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableArrayElement); - - source.AppendLine($"{indent}}}"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/DictionaryMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/DictionaryMigrationRule.cs deleted file mode 100644 index caf0d1dc8..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/DictionaryMigrationRule.cs +++ /dev/null @@ -1,250 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: DictionaryMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class DictionaryMigrationRule : MigrationRule -{ - public override string RuleName => nameof(DictionaryMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsDictionary(compilation)) - { - ruleArguments = null; - return false; - } - - var keySymbolType = namedTypeSymbol.TypeArguments[0]; - - var serializableKeyProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "KeyEntry", - keySymbolType, - 0, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var valueSymbolType = namedTypeSymbol.TypeArguments[1]; - - var serializableValueProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "ValueEntry", - valueSymbolType, - 0, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var extraOptions = ""; - if (attributes.Any(a => a.IsTidy(compilation))) - { - extraOptions += "@Tidy"; - } - - var keyArgumentsLength = serializableKeyProperty.RuleArguments?.Length ?? 0; - var valueArgumentsLength = serializableValueProperty.RuleArguments?.Length ?? 0; - var index = 0; - - ruleArguments = new string[7 + keyArgumentsLength + valueArgumentsLength]; - ruleArguments[index++] = extraOptions; - ruleArguments[index++] = keySymbolType.ToDisplayString(); - ruleArguments[index++] = serializableKeyProperty.Rule; - ruleArguments[index++] = keyArgumentsLength.ToString(); - - if (keyArgumentsLength > 0) - { - Array.Copy(serializableKeyProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength); - index += keyArgumentsLength; - } - - ruleArguments[index++] = valueSymbolType.ToDisplayString(); - ruleArguments[index++] = serializableValueProperty.Rule; - ruleArguments[index++] = valueArgumentsLength.ToString(); - - if (valueArgumentsLength > 0) - { - Array.Copy(serializableValueProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength); - } - - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var index = 1; - var keyType = ruleArguments![index++]; - - var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var keyRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (keyRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length); - index += keyRuleArguments.Length; - } - - var valueType = ruleArguments[index++]; - var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var valueRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (valueRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length); - } - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyIndex = $"{propertyVarPrefix}Index"; - var propertyKeyEntry = $"{propertyVarPrefix}Key"; - var propertyValueEntry = $"{propertyVarPrefix}Value"; - var propertyCount = $"{propertyVarPrefix}Count"; - - source.AppendLine($"{indent}{ruleArguments[1]} {propertyKeyEntry};"); - source.AppendLine($"{indent}{valueType} {propertyValueEntry};"); - source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();"); - source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.Dictionary<{keyType}, {valueType}>({propertyCount});"); - source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)"); - source.AppendLine($"{indent}{{"); - - var serializableKeyElement = new SerializableProperty - { - Name = propertyKeyEntry, - Type = keyType, - Rule = keyElementRule.RuleName, - RuleArguments = keyRuleArguments - }; - - keyElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableKeyElement, parentReference); - - var serializableValueElement = new SerializableProperty - { - Name = propertyValueEntry, - Type = valueType, - Rule = valueElementRule.RuleName, - RuleArguments = valueRuleArguments - }; - - valueElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableValueElement, parentReference); - source.AppendLine($"{indent} {propertyName}.Add({propertyKeyEntry}, {propertyValueEntry});"); - - source.AppendLine($"{indent}}}"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var index = 0; - var shouldTidy = ruleArguments![index++].Contains("@Tidy"); - var keyType = ruleArguments![index++]; - - var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![index++]]; - var keyRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (keyRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length); - index += keyRuleArguments.Length; - } - - var valueType = ruleArguments[index++]; - var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var valueRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (valueRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length); - } - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyKeyEntry = $"{propertyVarPrefix}Key"; - var propertyValueEntry = $"{propertyVarPrefix}Value"; - var propertyCount = $"{propertyVarPrefix}Count"; - - if (shouldTidy) - { - source.AppendLine($"{indent}{property.Name}?.Tidy();"); - } - source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;"); - source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});"); - source.AppendLine($"{indent}if ({propertyCount} > 0)"); - source.AppendLine($"{indent}{{"); - source.AppendLine($"{indent} foreach (var ({propertyKeyEntry}, {propertyValueEntry}) in {property.Name}!)"); - source.AppendLine($"{indent} {{"); - - var serializableKeyElement = new SerializableProperty - { - Name = propertyKeyEntry, - Type = keyType, - Rule = keyElementRule.RuleName, - RuleArguments = keyRuleArguments - }; - - keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableKeyElement); - - var serializableValueElement = new SerializableProperty - { - Name = propertyValueEntry, - Type = valueType, - Rule = valueElementRule.RuleName, - RuleArguments = valueRuleArguments - }; - - valueElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableValueElement); - - source.AppendLine($"{indent} }}"); - source.AppendLine($"{indent}}}"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/EnumMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/EnumMigrationRule.cs deleted file mode 100644 index 5e8d7fd45..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/EnumMigrationRule.cs +++ /dev/null @@ -1,73 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: EnumMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class EnumMigrationRule : MigrationRule -{ - public override string RuleName => nameof(EnumMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not ITypeSymbol typeSymbol || !typeSymbol.IsEnum()) - { - ruleArguments = null; - return false; - } - - ruleArguments = Array.Empty(); - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - source.AppendLine($"{indent}{property.Name} = reader.ReadEnum<{property.Type}>();"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - source.AppendLine($"{indent}writer.WriteEnum<{property.Type}>({property.Name});"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs deleted file mode 100644 index 20fe03d08..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs +++ /dev/null @@ -1,171 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: HashSetMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class HashSetMigrationRule : MigrationRule -{ - public override string RuleName => nameof(HashSetMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsHashSet(compilation)) - { - ruleArguments = null; - return false; - } - - var setTypeSymbol = namedTypeSymbol.TypeArguments[0]; - - var serializableSetType = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "SetEntry", - setTypeSymbol, - 0, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var extraOptions = ""; - if (attributes.Any(a => a.IsTidy(compilation))) - { - extraOptions += "@Tidy"; - } - - var length = serializableSetType.RuleArguments?.Length ?? 0; - ruleArguments = new string[length + 3]; - ruleArguments[0] = extraOptions; - ruleArguments[1] = setTypeSymbol.ToDisplayString(); - ruleArguments[2] = serializableSetType.Rule; - - if (length > 0) - { - Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 3, length); - } - - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); - var argumentsOffset = hasExtraOptions ? 1 : 0; - - var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]]; - var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; - Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyIndex = $"{propertyVarPrefix}Index"; - var propertyEntry = $"{propertyVarPrefix}Entry"; - var propertyCount = $"{propertyVarPrefix}Count"; - - source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};"); - source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();"); - source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[argumentsOffset]}>({propertyCount});"); - source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)"); - source.AppendLine($"{indent}{{"); - - var serializableSetElement = new SerializableProperty - { - Name = propertyEntry, - Type = ruleArguments[argumentsOffset], - Rule = setElementRule.RuleName, - RuleArguments = setElementRuleArguments - }; - - setElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableSetElement, parentReference); - source.AppendLine($"{indent} {property.Name}.Add({propertyEntry});"); - - source.AppendLine($"{indent}}}"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); - var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy"); - var argumentsOffset = hasExtraOptions ? 1 : 0; - - var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]]; - var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; - Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyEntry = $"{propertyVarPrefix}Entry"; - var propertyCount = $"{propertyVarPrefix}Count"; - - if (shouldTidy) - { - source.AppendLine($"{indent}{property.Name}?.Tidy();"); - } - source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;"); - source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});"); - source.AppendLine($"{indent}if ({propertyCount} > 0)"); - source.AppendLine($"{indent}{{"); - source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)"); - source.AppendLine($"{indent} {{"); - - var serializableSetElement = new SerializableProperty - { - Name = propertyEntry, - Type = ruleArguments[argumentsOffset], - Rule = setElementRule.RuleName, - RuleArguments = setElementRuleArguments - }; - - setElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableSetElement); - - source.AppendLine($"{indent} }}"); - source.AppendLine($"{indent}}}"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/KeyValuePairMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/KeyValuePairMigrationRule.cs deleted file mode 100644 index 5b7c4fbef..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/KeyValuePairMigrationRule.cs +++ /dev/null @@ -1,225 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: KeyValuePairMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class KeyValuePairMigrationRule : MigrationRule -{ - public override string RuleName => nameof(KeyValuePairMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsKeyValuePair(compilation)) - { - ruleArguments = null; - return false; - } - - var keySymbolType = namedTypeSymbol.TypeArguments[0]; - - var keySerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "key", - keySymbolType, - 0, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var valueSymbolType = namedTypeSymbol.TypeArguments[1]; - - var valueSerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "value", - valueSymbolType, - 1, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var keyArgumentsLength = keySerializedProperty.RuleArguments?.Length ?? 0; - var valueArgumentsLength = valueSerializedProperty.RuleArguments?.Length ?? 0; - var index = 0; - - // Key - ruleArguments = new string[6 + keyArgumentsLength + valueArgumentsLength]; - ruleArguments[index++] = ""; // Extra options - ruleArguments[index++] = keySymbolType.ToDisplayString(); - ruleArguments[index++] = keySerializedProperty.Rule; - ruleArguments[index++] = keyArgumentsLength.ToString(); - if (keyArgumentsLength > 0) - { - Array.Copy(keySerializedProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength); - index += keyArgumentsLength; - } - - // Value - ruleArguments[index++] = valueSymbolType.ToDisplayString(); - ruleArguments[index++] = valueSerializedProperty.Rule; - - if (valueArgumentsLength > 0) - { - Array.Copy(valueSerializedProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength); - } - - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var index = 1; // skip extra options - var keyType = ruleArguments![index++]; - var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var keyRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (keyRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length); - index += keyRuleArguments.Length; - } - - var serializableKeyProperty = new SerializableProperty - { - Name = "key", - Type = keyType, - Rule = keyRule.RuleName, - RuleArguments = keyRuleArguments - }; - - keyRule.GenerateDeserializationMethod( - source, - indent, - serializableKeyProperty, - parentReference - ); - - var valueType = ruleArguments[index++]; - var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var valueRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (valueRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length); - } - - var serializableValueProperty = new SerializableProperty - { - Name = "value", - Type = valueType, - Rule = valueRule.RuleName, - RuleArguments = valueRuleArguments - }; - - valueRule.GenerateDeserializationMethod( - source, - indent, - serializableValueProperty, - parentReference - ); - - source.AppendLine( - $"{indent}{property.Name} = new {SymbolMetadata.KEYVALUEPAIR_STRUCT}<{keyType}, {valueType}>(key, value);" - ); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var index = 1; // skip extra options - var keyType = ruleArguments![index++]; - var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var keyRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (keyRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length); - index += keyRuleArguments.Length; - } - - var serializableKeyProperty = new SerializableProperty - { - Name = $"{property.Name}.Key", - Type = keyType, - Rule = keyRule.RuleName, - RuleArguments = keyRuleArguments - }; - - keyRule.GenerateSerializationMethod( - source, - indent, - serializableKeyProperty - ); - - var valueType = ruleArguments[index++]; - var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]]; - var valueRuleArguments = new string[int.Parse(ruleArguments[index++])]; - - if (valueRuleArguments.Length > 0) - { - Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length); - } - - var serializableValueProperty = new SerializableProperty - { - Name = $"{property.Name}.Value", - Type = valueType, - Rule = valueRule.RuleName, - RuleArguments = valueRuleArguments - }; - - valueRule.GenerateSerializationMethod( - source, - indent, - serializableValueProperty - ); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs deleted file mode 100644 index 53d2d2c44..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs +++ /dev/null @@ -1,172 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ListMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class ListMigrationRule : MigrationRule -{ - public override string RuleName => nameof(ListMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsList(compilation)) - { - ruleArguments = null; - return false; - } - - var listTypeSymbol = namedTypeSymbol.TypeArguments[0]; - - var serializableListType = SerializableMigrationRulesEngine.GenerateSerializableProperty( - compilation, - "ListEntry", - listTypeSymbol, - 0, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - null - ); - - var extraOptions = ""; - if (attributes.Any(a => a.IsTidy(compilation))) - { - extraOptions += "@Tidy"; - } - - var length = serializableListType.RuleArguments?.Length ?? 0; - ruleArguments = new string[length + 3]; - ruleArguments[0] = extraOptions; - ruleArguments[1] = listTypeSymbol.ToDisplayString(); - ruleArguments[2] = serializableListType.Rule; - - if (length > 0) - { - Array.Copy(serializableListType.RuleArguments!, 0, ruleArguments, 3, length); - } - - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); - var argumentsOffset = hasExtraOptions ? 1 : 0; - - var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[argumentsOffset + 1]]; - - var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; - Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyIndex = $"{propertyVarPrefix}Index"; - var propertyEntry = $"{propertyVarPrefix}Entry"; - var propertyCount = $"{propertyVarPrefix}Count"; - - source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};"); - source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();"); - source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[argumentsOffset]}>({propertyCount});"); - source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)"); - source.AppendLine($"{indent}{{"); - - var serializableListElement = new SerializableProperty - { - Name = propertyEntry, - Type = ruleArguments[argumentsOffset], - Rule = listElementRule.RuleName, - RuleArguments = listElementRuleArguments - }; - - listElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableListElement, parentReference); - source.AppendLine($"{indent} {propertyName}.Add({propertyEntry});"); - - source.AppendLine($"{indent}}}"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var ruleArguments = property.RuleArguments; - var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); - var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy"); - var argumentsOffset = hasExtraOptions ? 1 : 0; - - var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]]; - var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; - Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); - - var propertyName = property.Name; - var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; - var propertyEntry = $"{propertyVarPrefix}Entry"; - var propertyCount = $"{propertyVarPrefix}Count"; - - if (shouldTidy) - { - source.AppendLine($"{indent}{property.Name}?.Tidy();"); - } - source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;"); - source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});"); - source.AppendLine($"{indent}if ({propertyCount} > 0)"); - source.AppendLine($"{indent}{{"); - source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)"); - source.AppendLine($"{indent} {{"); - - var serializableListElement = new SerializableProperty - { - Name = propertyEntry, - Type = ruleArguments[argumentsOffset], - Rule = listElementRule.RuleName, - RuleArguments = listElementRuleArguments - }; - - listElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableListElement); - - source.AppendLine($"{indent} }}"); - source.AppendLine($"{indent}}}"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/MigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/MigrationRule.cs deleted file mode 100644 index 848812b54..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/MigrationRule.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public abstract class MigrationRule : ISerializableMigrationRule -{ - public abstract string RuleName { get; } - - public virtual void GenerateMigrationProperty( - StringBuilder source, Compilation compilation, string indent, SerializableProperty serializableProperty - ) - { - var propertyType = serializableProperty.Type; - var type = compilation.GetTypeByMetadataName(propertyType)?.IsValueType == true - || SymbolMetadata.IsPrimitiveFromTypeDisplayString(propertyType) && propertyType != "bool" - ? $"{propertyType}{(serializableProperty.UsesSaveFlag == true ? "?" : "")}" : propertyType; - - source.AppendLine($"{indent}internal readonly {type} {serializableProperty.Name};"); - } - - public abstract bool GenerateRuleState( - Compilation compilation, ISymbol symbol, ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, ISymbol? parentSymbol, out string[] ruleArguments - ); - - public abstract void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ); - - public abstract void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property); -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs deleted file mode 100644 index 70b34ce06..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs +++ /dev/null @@ -1,149 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PrimitiveTypeMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class PrimitiveTypeMigrationRule : MigrationRule -{ - public override string RuleName => nameof(PrimitiveTypeMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol.IsIpAddress(compilation) || symbol.IsTimeSpan(compilation)) - { - ruleArguments = Array.Empty(); - return true; - } - - if ( - symbol is not ITypeSymbol { - SpecialType: not (not - SpecialType.System_Boolean and not - SpecialType.System_SByte and not - SpecialType.System_Int16 and not - SpecialType.System_Int32 and not - SpecialType.System_Int64 and not - SpecialType.System_Byte and not - SpecialType.System_UInt16 and not - SpecialType.System_UInt32 and not - SpecialType.System_UInt64 and not - SpecialType.System_Single and not - SpecialType.System_Double and not - SpecialType.System_String and not - SpecialType.System_Decimal and not - SpecialType.System_DateTime) - } typeSymbol - ) - { - ruleArguments = null; - return false; - } - - ruleArguments = typeSymbol.SpecialType switch - { - SpecialType.System_Int32 when attributes.Any(a => a.IsEncodedInt(compilation)) => - new[] { "EncodedInt" }, - SpecialType.System_DateTime when attributes.Any(a => a.IsDeltaDateTime(compilation)) => - new[] { "DeltaTime" }, - SpecialType.System_String when attributes.Any(a => a.IsInternString(compilation)) => - new[] { "InternString" }, - _ => new[] { "" } - }; - - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null; - - const string ipAddress = SymbolMetadata.IPADDRESS_CLASS; - const string timeSpan = SymbolMetadata.TIMESPAN_STRUCT; - const string date = "System.DateTime"; - - var readMethod = property.Type switch - { - "bool" => "ReadBool", - "sbyte" => "ReadSByte", - "short" => "ReadShort", - "int" when argument == "EncodedInt" => "ReadEncodedInt", - "int" => "ReadInt", - "long" => "ReadLong", - "byte" => "ReadByte", - "ushort" => "ReadUShort", - "uint" => "ReadUInt", - "ulong" => "ReadULong", - "float" => "ReadFloat", - "double" => "ReadDouble", - "string" => "ReadString", - "decimal" => "ReadDecimal", - date when argument == "DeltaTime" => "ReadDeltaTime", - date => "ReadDateTime", - ipAddress => "ReadIPAddress", - timeSpan => "ReadTimeSpan" - }; - - var readArgument = readMethod == "ReadString" && argument == "InternString" ? "true" : ""; - - source.AppendLine($"{indent}{propertyName} = reader.{readMethod}({readArgument});"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null; - - var writeMethod = property.Type switch - { - "System.DateTime" when argument == "DeltaTime" => "WriteDeltaTime", - "int" when argument == "EncodedInt" => "WriteEncodedInt", - _ => "Write" - }; - - source.AppendLine($"{indent}writer.{writeMethod}({propertyName});"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveUOTypeMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveUOTypeMigrationRule.cs deleted file mode 100644 index ec2ea15d3..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveUOTypeMigrationRule.cs +++ /dev/null @@ -1,80 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PrimitiveUOTypeMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class PrimitiveUOTypeMigrationRule : MigrationRule -{ - public override string RuleName => nameof(PrimitiveUOTypeMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - ruleArguments = symbol switch - { - _ when symbol.IsPoint2D(compilation) => new[] { "Point2D" }, - _ when symbol.IsPoint3D(compilation) => new[] { "Point3D" }, - _ when symbol.IsRectangle2D(compilation) => new[] { "Rect2D" }, - _ when symbol.IsRectangle3D(compilation) => new[] { "Rect3D" }, - _ when symbol.IsRace(compilation) => new[] { "Race" }, - _ when symbol.IsMap(compilation) => new[] { "Map" }, - _ when symbol.IsBitArray(compilation) => new[] { "BitArray" }, - _ => null - }; - - return ruleArguments != null; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}{propertyName} = reader.Read{property.RuleArguments?[0] ?? ""}();"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}writer.Write({propertyName});"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/RawSerializableMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/RawSerializableMigrationRule.cs deleted file mode 100644 index 8f0944f1d..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/RawSerializableMigrationRule.cs +++ /dev/null @@ -1,82 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: RawSerializableMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class RawSerializableMigrationRule : MigrationRule -{ - public override string RuleName => nameof(RawSerializableMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is not ITypeSymbol typeSymbol) - { - ruleArguments = null; - return false; - } - - if (!typeSymbol.HasRawSerializableInterface(compilation, embeddedSerializableTypes)) - { - ruleArguments = null; - return false; - } - - ruleArguments = new[] { "" }; - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}{propertyName} = new {property.Type}({parentReference ?? "this"});"); - source.AppendLine($"{indent}{propertyName}.Deserialize(reader);"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}{propertyName}.Serialize(writer);"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/SerializableInterfaceMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/SerializableInterfaceMigrationRule.cs deleted file mode 100644 index 2326af110..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/SerializableInterfaceMigrationRule.cs +++ /dev/null @@ -1,75 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableInterfaceMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class SerializableInterfaceMigrationRule : MigrationRule -{ - public override string RuleName => nameof(SerializableInterfaceMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (symbol is ITypeSymbol typeSymbol && typeSymbol.HasSerializableInterface(compilation, serializableTypes)) - { - ruleArguments = Array.Empty(); - return true; - } - - ruleArguments = null; - return false; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}{propertyName} = reader.ReadEntity<{property.Type}>();"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}writer.Write({propertyName});"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/SerializationMethodSignatureMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/SerializationMethodSignatureMigrationRule.cs deleted file mode 100644 index 4e9859170..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/SerializationMethodSignatureMigrationRule.cs +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializationMethodSignatureMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class SerializationMethodSignatureMigrationRule : MigrationRule -{ - public override string RuleName => nameof(SerializationMethodSignatureMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if ((symbol as ITypeSymbol)?.HasPublicSerializeMethod(compilation, serializableTypes) != true) - { - ruleArguments = null; - return false; - } - - if (symbol is not INamedTypeSymbol namedTypeSymbol || - !namedTypeSymbol.HasGenericReaderCtor(compilation, parentSymbol, out var requiresParent)) - { - ruleArguments = null; - return false; - } - - ruleArguments = new[] { requiresParent ? "DeserializationRequiresParent" : "" }; - return true; - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - var argument = property.RuleArguments?.Length >= 1 && - property.RuleArguments[0] == "DeserializationRequiresParent" ? ", this" : ""; - - source.AppendLine($"{indent}{propertyName} = new {property.Type}(reader{argument});"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - source.AppendLine($"{indent}{propertyName}.Serialize(writer);"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/TimerMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/TimerMigrationRule.cs deleted file mode 100644 index 7d97dfd6e..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/TimerMigrationRule.cs +++ /dev/null @@ -1,136 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: TimerMigrationRule.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration; - -public class TimerMigrationRule : MigrationRule, IPostDeserializeMethod -{ - public override string RuleName => nameof(TimerMigrationRule); - - public override bool GenerateRuleState( - Compilation compilation, - ISymbol symbol, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - out string[] ruleArguments - ) - { - if (!(symbol is ITypeSymbol typeSymbol && typeSymbol.IsTimer(compilation))) - { - ruleArguments = null; - return false; - } - - ruleArguments = attributes.Any(a => a.IsTimerDrift(compilation)) - ? new[] { "@TimerDrift" } - : new[] { "" }; - - return true; - } - - public override void GenerateMigrationProperty( - StringBuilder source, Compilation compilation, string indent, SerializableProperty serializableProperty - ) - { - source.AppendLine($"{indent}internal readonly System.DateTime {serializableProperty.Name}Next;"); - source.AppendLine($"{indent}internal readonly System.TimeSpan {serializableProperty.Name}Delay;"); - } - - public override void GenerateDeserializationMethod( - StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false - ) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - var ruleArguments = property.RuleArguments; - var driftTimer = ruleArguments![0].Contains("@TimerDrift"); - - var readTimer = driftTimer ? "reader.ReadDeltaTime()" : "reader.ReadDateTime()"; - var useVar = isMigration ? "" : "var "; - source.AppendLine($"{indent}{useVar}{propertyName}Next = {readTimer};"); - source.AppendLine($"{indent}{useVar}{propertyName}Delay = {propertyName}Next == System.DateTime.MinValue ? System.TimeSpan.MinValue : {propertyName}Next - Core.Now;"); - } - - public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) - { - var expectedRule = RuleName; - var ruleName = property.Rule; - if (expectedRule != ruleName) - { - throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); - } - - var propertyName = property.Name; - var ruleArguments = property.RuleArguments; - var driftTimer = ruleArguments![0].Contains("@TimerDrift"); - - var writerMethod = driftTimer ? "WriteDeltaTime" : "Write"; - source.AppendLine($"{indent}writer.{writerMethod}({propertyName}?.Next ?? System.DateTime.MinValue);"); - } - - public void PostDeserializeMethod( - StringBuilder source, string indent, SerializableProperty property, Compilation compilation, INamedTypeSymbol classSymbol - ) - { - var deserializeTimerMethod = classSymbol - .GetMembers() - .OfType() - .FirstOrDefault( - m => - { - if (!m.ReturnsVoid || m.Parameters.Length != 1 || !m.Parameters[0].Type.IsTimeSpan(compilation)) - { - return false; - } - - return m.GetAttributes() - .FirstOrDefault( - attr => - { - if (!SymbolEqualityComparer.Default.Equals( - attr.AttributeClass, - compilation.GetTypeByMetadataName( - SymbolMetadata.DESERIALIZE_TIMER_FIELD_ATTRIBUTE - ) - )) - { - return false; - } - - var order = (int)attr.ConstructorArguments[0].Value!; - return order == property.Order; - } - ) != null; - } - ) ?? throw new Exception("Serializing a timer requires a method with the DeserializeTimerField attribute to handle creating the timer itself."); - - source.AppendLine($"{indent}{deserializeTimerMethod.Name}({property.Name}Delay);"); - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableMetadataComparer.cs b/Projects/SerializationGenerator/SerializableMigration/SerializableMetadataComparer.cs deleted file mode 100644 index 13fd600b2..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableMetadataComparer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; - -namespace SerializableMigration -{ - public class SerializableMetadataComparer : IComparer - { - public int Compare(SerializableMetadata x, SerializableMetadata y) - { - if (ReferenceEquals(x, y)) - { - return 0; - } - - if (ReferenceEquals(null, y)) - { - return 1; - } - - if (ReferenceEquals(null, x)) - { - return -1; - } - - return x.Version.CompareTo(y.Version); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs b/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs deleted file mode 100644 index 1fe3e32f5..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs +++ /dev/null @@ -1,133 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableMigrationRulesEngine.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using SerializationGenerator; - -namespace SerializableMigration -{ - public static class SerializableMigrationRulesEngine - { - public static readonly Dictionary Rules = new(); - - static SerializableMigrationRulesEngine() - { - var rules = new ISerializableMigrationRule[] - { - new EnumMigrationRule(), - new ListMigrationRule(), - new ArrayMigrationRule(), - new HashSetMigrationRule(), - new DictionaryMigrationRule(), - new KeyValuePairMigrationRule(), - new PrimitiveTypeMigrationRule(), - new PrimitiveUOTypeMigrationRule(), - new SerializableInterfaceMigrationRule(), - new SerializationMethodSignatureMigrationRule(), - new RawSerializableMigrationRule(), - new TimerMigrationRule() - }; - - foreach (var rule in rules) - { - Rules.Add(rule.RuleName, rule); - } - } - - public static SerializableProperty? GenerateSerializableProperty( - Compilation compilation, - ISymbol fieldOrPropertySymbol, - int order, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - SerializableFieldSaveFlagMethods? serializableFieldSaveFlagMethods - ) - { - string propertyName; - ITypeSymbol propertyType; - - if (fieldOrPropertySymbol is IFieldSymbol fieldSymbol) - { - propertyName = fieldSymbol.Name; - propertyType = fieldSymbol.Type; - } - else if (fieldOrPropertySymbol is IPropertySymbol propertySymbol) - { - propertyName = fieldOrPropertySymbol.Name; - propertyType = propertySymbol.Type; - } - else - { - return null; - } - - return GenerateSerializableProperty( - compilation, - propertyName, - propertyType, - order, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - serializableFieldSaveFlagMethods - ); - } - - public static SerializableProperty GenerateSerializableProperty( - Compilation compilation, - string propertyName, - ISymbol propertyType, - int order, - ImmutableArray attributes, - ImmutableArray serializableTypes, - ImmutableArray embeddedSerializableTypes, - ISymbol? parentSymbol, - SerializableFieldSaveFlagMethods? serializableFieldSaveFlagMethods - ) - { - foreach (var rule in Rules.Values) - { - if (rule.GenerateRuleState( - compilation, - propertyType, - attributes, - serializableTypes, - embeddedSerializableTypes, - parentSymbol, - out var ruleArguments - )) - { - return new SerializableProperty - { - Name = propertyName, - Type = propertyType.ToDisplayString(), - Order = order, - UsesSaveFlag = serializableFieldSaveFlagMethods?.DetermineFieldShouldSerialize != null ? true : null, - Rule = rule.RuleName, - RuleArguments = ruleArguments.Length > 0 ? ruleArguments : null - }; - } - } - - throw new Exception($"No rule found for property {propertyName} of type {propertyType} ({Rules.Count})"); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs b/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs deleted file mode 100644 index 2f8613456..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs +++ /dev/null @@ -1,112 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableMigrationSchema.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.RegularExpressions; -using Microsoft.CodeAnalysis; - -namespace SerializableMigration -{ - public static class SerializableMigrationSchema - { - public static JsonSerializerOptions GetJsonSerializerOptions() => - new() - { - WriteIndented = true, - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - ReadCommentHandling = JsonCommentHandling.Skip - }; - - private static Dictionary _cache = new(); - - private static readonly Regex _fileRegex = new(@"\S+\.v\d+\.json$"); - - public static List GetMigrations( - INamedTypeSymbol typeSymbol, - int version, - string migrationPath, - JsonSerializerOptions options - ) - { - var typeName = typeSymbol.ToDisplayString(); - var migrations = new SortedSet(new SerializableMetadataComparer()); - - var migrationFiles = Directory.GetFiles(migrationPath, $"{typeName}.v*.json"); - - foreach (var file in migrationFiles) - { - var fi = new FileInfo(file); - if (!_cache.TryGetValue(fi.Name, out var migration)) - { - var text = File.ReadAllText(file, Encoding.UTF8); - migration = JsonSerializer.Deserialize(text, options); - _cache[fi.Name] = migration; - } - - if (typeName == migration!.Type && version > migration.Version) - { - migrations.Add(migration); - } - } - - return migrations.ToList(); - } - - public static List GetMigrationsByAnalyzerConfig( - this GeneratorExecutionContext context, - INamedTypeSymbol typeSymbol, - int version, - JsonSerializerOptions options - ) - { - var typeName = typeSymbol.ToDisplayString(); - var migrations = new SortedSet(new SerializableMetadataComparer()); - - foreach (var additionalText in context.AdditionalFiles) - { - var fi = new FileInfo(additionalText.Path); - if (!_fileRegex.IsMatch(fi.Name)) - { - continue; - } - - if (!_cache.TryGetValue(fi.Name, out var migration)) - { - var text = additionalText.GetText(context.CancellationToken)?.ToString(); - if (text == null) - { - continue; - } - - migration = JsonSerializer.Deserialize(text, options); - _cache[fi.Name] = migration; - } - - if (typeName == migration!.Type && version > migration.Version) - { - migrations.Add(migration); - } - } - - return migrations.ToList(); - } - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableProperty.cs b/Projects/SerializationGenerator/SerializableMigration/SerializableProperty.cs deleted file mode 100644 index 1f6f0c55e..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableProperty.cs +++ /dev/null @@ -1,40 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableProperty.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Text.Json.Serialization; - -namespace SerializableMigration -{ - public record SerializableProperty - { - [JsonPropertyName("name")] - public string Name { get; init; } - - [JsonPropertyName("type")] - public string Type { get; init; } - - [JsonPropertyName("usesSaveFlag")] - public bool? UsesSaveFlag { get; init; } - - [JsonPropertyName("rule")] - public string Rule { get; init; } - - [JsonPropertyName("ruleArguments")] - public string[]? RuleArguments { get; init; } - - [JsonIgnore] - public int Order { get; init; } - } -} diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializablePropertyComparer.cs b/Projects/SerializationGenerator/SerializableMigration/SerializablePropertyComparer.cs deleted file mode 100644 index d4b736978..000000000 --- a/Projects/SerializationGenerator/SerializableMigration/SerializablePropertyComparer.cs +++ /dev/null @@ -1,42 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializablePropertyComparer.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; - -namespace SerializableMigration -{ - public class SerializablePropertyComparer : IComparer - { - public int Compare(SerializableProperty x, SerializableProperty y) - { - if (Equals(x, y)) - { - return 0; - } - - if (Equals(null, y)) - { - return 1; - } - - if (Equals(null, x)) - { - return -1; - } - - return x.Order.CompareTo(y.Order); - } - } -} diff --git a/Projects/SerializationGenerator/SerializationGenerator.csproj b/Projects/SerializationGenerator/SerializationGenerator.csproj deleted file mode 100755 index 10448f42f..000000000 --- a/Projects/SerializationGenerator/SerializationGenerator.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - netstandard2.0 - preview - analyzers - - - - - - - - - - - - - - $(GetTargetPathDependsOn);GetDependencyTargetPaths - - - - - - - - - - - - diff --git a/Projects/SerializationGenerator/SerializerSyntaxReceiver.cs b/Projects/SerializationGenerator/SerializerSyntaxReceiver.cs deleted file mode 100755 index fea02e69d..000000000 --- a/Projects/SerializationGenerator/SerializerSyntaxReceiver.cs +++ /dev/null @@ -1,133 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SyntaxReceiver.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace SerializationGenerator -{ - public class SerializerSyntaxReceiver : ISyntaxContextReceiver - { -#pragma warning disable RS1024 - public Dictionary)> ClassAndFields { get; } = new(SymbolEqualityComparer.Default); - public Dictionary)> EmbeddedClassAndFields { get; } = new(SymbolEqualityComparer.Default); -#pragma warning restore RS1024 - - public ImmutableArray SerializableList => ClassAndFields.Keys.ToImmutableArray(); - - public ImmutableArray EmbeddedSerializableList => EmbeddedClassAndFields.Keys.ToImmutableArray(); - - public void OnVisitSyntaxNode(SyntaxNode node, SemanticModel semanticModel) - { - var compilation = semanticModel.Compilation; - - if (node is ClassDeclarationSyntax { AttributeLists: { Count: > 0 } } classDeclarationSyntax) - { - if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) - { - return; - } - - if (classSymbol.IsEmbeddedSerializable(compilation, out var attrData)) - { - if (EmbeddedClassAndFields.TryGetValue(classSymbol, out var value)) - { - var (_, fieldsList) = value; - EmbeddedClassAndFields[classSymbol] = (attrData, fieldsList); - } - else - { - EmbeddedClassAndFields.Add(classSymbol, (attrData, new List())); - } - } - else if (classSymbol.WillBeSerializable(compilation, out attrData)) - { - if (ClassAndFields.TryGetValue(classSymbol, out var value)) - { - var (_, fieldsList) = value; - ClassAndFields[classSymbol] = (attrData, fieldsList); - } - else - { - ClassAndFields.Add(classSymbol, (attrData, new List())); - } - } - - return; - } - - if (node is FieldDeclarationSyntax { AttributeLists: { Count: > 0 } } fieldDeclarationSyntax) - { - foreach (var variable in fieldDeclarationSyntax.Declaration.Variables) - { - if (semanticModel.GetDeclaredSymbol(variable) is IFieldSymbol fieldSymbol) - { - AddFieldOrProperty(fieldSymbol, compilation); - } - } - - return; - } - - if (node is PropertyDeclarationSyntax { AttributeLists: { Count: > 0 } } propertyDeclarationSyntax) - { - if (semanticModel.GetDeclaredSymbol(propertyDeclarationSyntax) is IPropertySymbol propertySymbol) - { - AddFieldOrProperty(propertySymbol, compilation); - } - } - } - - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) => - OnVisitSyntaxNode(context.Node, context.SemanticModel); - - private void AddFieldOrProperty(ISymbol symbol, Compilation compilation) - { - var serializableFieldAttr = compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_FIELD_ATTRIBUTE); - var parentAttr = compilation.GetTypeByMetadataName(SymbolMetadata.SERIALIZABLE_PARENT_ATTRIBUTE); - - if (symbol.GetAttribute(serializableFieldAttr) == null && symbol.GetAttribute(parentAttr) == null) - { - return; - } - - var classSymbol = symbol.ContainingType; - if (ClassAndFields.TryGetValue(classSymbol, out var value)) - { - var (_, fieldsList) = value; - fieldsList.Add(symbol); - return; - } - - if (EmbeddedClassAndFields.TryGetValue(classSymbol, out value)) - { - var (_, fieldsList) = value; - fieldsList.Add(symbol); - return; - } - - if (classSymbol.WillBeSerializable(compilation, out var attrData)) - { - ClassAndFields.Add(classSymbol, (attrData, new List { symbol })); - } - else if (classSymbol.IsEmbeddedSerializable(compilation, out attrData)) - { - EmbeddedClassAndFields.Add(classSymbol, (attrData, new List { symbol })); - } - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/Helpers.cs b/Projects/SerializationGenerator/SourceGeneration/Helpers.cs deleted file mode 100644 index f1a9a88e5..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/Helpers.cs +++ /dev/null @@ -1,65 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Helpers.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; - -namespace SerializationGenerator -{ - public static class Helpers - { - public static bool ContainsInterface(this ITypeSymbol symbol, ISymbol interfaceSymbol) => - symbol.Interfaces.Any(i => i.ConstructedFrom.Equals(interfaceSymbol, SymbolEqualityComparer.Default)) || - symbol.AllInterfaces.Any(i => i.ConstructedFrom.Equals(interfaceSymbol, SymbolEqualityComparer.Default)); - - public static ImmutableArray GetAllMethods(this ITypeSymbol symbol, string name) - { - var methods = symbol.GetMembers(name).OfType().ToImmutableArray(); - if (symbol.ContainingSymbol is not ITypeSymbol typeSymbol) - { - return methods; - } - - var list = new List(); - list.AddRange(methods.ToList()); - list.AddRange(GetAllMethods(typeSymbol, name).ToList()); - - return list.ToImmutableArray(); - } - - public static string ToFriendlyString(this Accessibility accessibility) => SyntaxFacts.GetText(accessibility); - - public static Accessibility GetAccessibility(string? value) => - value switch - { - "private" => Accessibility.Private, - "protected" => Accessibility.Protected, - "internal" => Accessibility.Internal, - "public" => Accessibility.Public, - "protected internal" => Accessibility.ProtectedOrInternal, - "private protected" => Accessibility.ProtectedAndInternal, - _ => Accessibility.NotApplicable - }; - - public static bool CanBeConstructedFrom(this ITypeSymbol? symbol, ISymbol classSymbol) => - symbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.ConstructedFrom.Equals( - classSymbol, - SymbolEqualityComparer.Default - ) || symbol != null && CanBeConstructedFrom(symbol.BaseType, classSymbol); - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs deleted file mode 100644 index 53dc5823c..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs +++ /dev/null @@ -1,125 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Arguments.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static void GetTypesFromTypedConstant(TypedConstant arg, List list) - { - if (arg.Kind == TypedConstantKind.Type) - { - list.Add((ITypeSymbol)arg.Value); - } - else if (arg.Kind == TypedConstantKind.Array) - { - for (var i = 0; i < arg.Values.Length; i++) - { - GetTypesFromTypedConstant(arg.Values[i], list); - } - } - } - - public static void GenerateSignatureArguments(this StringBuilder source, ImmutableArray<(ITypeSymbol, string)> parameters) - { - for (var i = 0; i < parameters.Length; i++) - { - var (t, v) = parameters[i]; - source.AppendFormat("{0} {1}", t.ToDisplayString(), v); - if (i < parameters.Length - 1) - { - source.Append(", "); - } - } - } - - public static void GenerateNamedArgument(this StringBuilder source, KeyValuePair namedArg) - { - source.AppendFormat("{0} = ", namedArg.Key); - source.GenerateTypedConstant(namedArg.Value); - } - - public static void GenerateTypedConstants(this StringBuilder source, ImmutableArray args) - { - source.Append("new []{"); - for (var i = 0; i < args.Length; i++) - { - source.GenerateTypedConstant(args[i]); - if (i < args.Length - 1) - { - source.Append(", "); - } - } - source.Append('}'); - } - - public static void GenerateTypedConstant(this StringBuilder source, TypedConstant arg) - { - if (arg.IsNull) - { - source.Append("null"); - return; - } - - switch (arg.Kind) - { - default: - { - return; - } - case TypedConstantKind.Primitive: - { - - if (arg.Value is string str) - { - source.AppendFormat("\"{0}\"", str); - } - else - { - source.Append(arg.Value); - } - break; - } - case TypedConstantKind.Enum: - { - if (arg.Type == null || arg.Value == null) - { - source.Append("null"); - } - else - { - source.AppendFormat("({0}){1}", arg.Type.ToDisplayString(), arg.Value); - } - break; - } - case TypedConstantKind.Type: - { - source.AppendFormat("typeof({0})", ((ITypeSymbol)arg.Value)?.Name); - break; - } - case TypedConstantKind.Array: - { - source.GenerateTypedConstants(arg.Values); - break; - } - } - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs deleted file mode 100644 index eda4cf08c..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs +++ /dev/null @@ -1,102 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Attribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static void GenerateAttribute( - this StringBuilder source, - string indent, - string attrClassName, - ImmutableArray args - ) - { - source.Append($"{indent}[{attrClassName}"); - var hasArgs = args.Length > 0; - - if (hasArgs) - { - source.Append("("); - } - - for (var i = 0; i < args.Length; i++) - { - var arg = args[i]; - source.GenerateTypedConstant(arg); - if (i < args.Length - 1) - { - source.Append(", "); - } - } - - if (hasArgs) - { - source.Append(")"); - } - - source.AppendLine("]"); - } - - public static void GenerateAttribute(this StringBuilder source, AttributeData attr) - { - source.Append($" [{attr.AttributeClass?.Name}"); - var ctorArgs = attr.ConstructorArguments; - var namedArgs = attr.NamedArguments; - var hasArgs = ctorArgs.Length + namedArgs.Length > 0; - - if (hasArgs) - { - source.Append("("); - } - - for (var i = 0; i < ctorArgs.Length; i++) - { - var arg = ctorArgs[i]; - source.GenerateTypedConstant(arg); - if (i < ctorArgs.Length - 1) - { - source.Append(", "); - } - } - - for (var i = 0; i < namedArgs.Length; i++) - { - var arg = namedArgs[i]; - source.GenerateNamedArgument(arg); - if (i < namedArgs.Length - 1) - { - source.Append(", "); - } - } - - if (hasArgs) - { - source.Append(")"); - } - - source.AppendLine("]"); - } - - public static void AggressiveInline(this StringBuilder source, string indent) => - source.AppendLine( - $"{indent}[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]" - ); - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Class.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Class.cs deleted file mode 100644 index c25999cd8..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Class.cs +++ /dev/null @@ -1,72 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Class.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static void GenerateClassStart( - this StringBuilder source, - INamedTypeSymbol classSymbol, - string indent, - ImmutableArray interfaces, - bool isPartial = true - ) - { - var accessor = classSymbol.DeclaredAccessibility; - source.Append($"{indent}{accessor.ToFriendlyString()} {(isPartial ? "partial " : "")}class {classSymbol.Name}"); - if (!interfaces.IsEmpty) - { - source.Append(" : "); - for (var i = 0; i < interfaces.Length; i++) - { - source.Append(interfaces[i].ToDisplayString()); - if (i < interfaces.Length - 1) - { - source.Append(", "); - } - } - } - - source.AppendLine($"\n{indent}{{"); - } - - public static void GenerateClassEnd(this StringBuilder source, string indent) - { - source.AppendLine($"{indent}}}"); - } - - // TODO: Generalize this to any field using dynamic indentation - public static void GenerateClassField( - this StringBuilder source, - string indent, - Accessibility accessors, - InstanceModifier instance, - string type, - string variableName, - string value - ) - { - var instanceStr = instance == InstanceModifier.None ? "" : $"{instance.ToFriendlyString()} "; - var accessorStr = accessors == Accessibility.NotApplicable ? "" : $"{accessors.ToFriendlyString()} "; - var valueStr = value == null ? "" : $" = {value}"; - source.AppendLine($"{indent}{accessorStr}{instanceStr}{type} {variableName}{valueStr};"); - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Enum.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Enum.cs deleted file mode 100644 index 6f8213561..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Enum.cs +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Enum.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static void GenerateEnumStart( - this StringBuilder source, - string enumName, - string indent, - bool useFlags, - Accessibility accessor = Accessibility.Public - ) - { - if (useFlags) - { - source.AppendLine($"{indent}[System.Flags]"); - } - source.AppendLine($"{indent}{accessor.ToFriendlyString()} enum {enumName}\n{indent}{{"); - } - - public static void GenerateEnumValue(this StringBuilder source, string indent, bool isFlag, string name, int value) - { - var number = value < 0 ? 0 : 1 << value; - var valueStr = isFlag ? $"0x{number:X8}" : value.ToString(); - source.AppendLine($"{indent}{name} = {valueStr},"); - } - - public static void GenerateEnumEnd(this StringBuilder source, string indent) - { - source.AppendLine($"{indent}}}"); - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.InstanceModifier.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.InstanceModifier.cs deleted file mode 100644 index 2fdfef6da..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.InstanceModifier.cs +++ /dev/null @@ -1,39 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.InstanceModifier.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace SerializationGenerator -{ - public enum InstanceModifier - { - None, - Const, - ReadOnly, - Static, - StaticReadOnly - } - - public static partial class SourceGeneration - { - public static string ToFriendlyString(this InstanceModifier modifier) => - modifier switch - { - InstanceModifier.Const => "const", - InstanceModifier.ReadOnly => "readonly", - InstanceModifier.Static => "static", - InstanceModifier.StaticReadOnly => "static readonly", - _ => "" - }; - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs deleted file mode 100644 index 407fd3e32..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs +++ /dev/null @@ -1,62 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Method.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static void GenerateMethodStart( - this StringBuilder source, string indent, string methodName, Accessibility accessors, bool isOverride, - string returnType, ImmutableArray<(ITypeSymbol, string)> parameters - ) - { - source.Append($"{indent}{accessors.ToFriendlyString()}{(isOverride ? " override" : "")} {returnType} {methodName}("); - source.GenerateSignatureArguments(parameters); - source.AppendLine($")\n{indent}{{"); - } - - public static void GenerateMethodEnd(this StringBuilder source, string indent) => source.AppendLine($"{indent}}}"); - - public static void GenerateConstructorStart( - this StringBuilder source, string indent, string className, Accessibility accessors, ImmutableArray<(ITypeSymbol, string)> parameters, - ImmutableArray baseParameters, bool isOverload = false - ) - { - source.Append($"{indent}{accessors.ToFriendlyString()} {className}("); - source.GenerateSignatureArguments(parameters); - source.Append(')'); - bool hasBaseParams = baseParameters.Length > 0; - if (hasBaseParams) - { - source.AppendFormat(" : {0}(", isOverload ? "this" : "base"); - for (int i = 0; i < baseParameters.Length; i++) - { - source.Append(baseParameters[i]); - if (i < baseParameters.Length - 1) - { - source.Append(','); - } - } - source.Append(')'); - } - - source.AppendLine($"\n{indent}{{"); - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Namespace.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Namespace.cs deleted file mode 100644 index 24ced2544..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Namespace.cs +++ /dev/null @@ -1,33 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Namespace.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Text; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static void GenerateNamespaceStart(this StringBuilder source, string namespaceName) - { - source.AppendLine($@"namespace {namespaceName} -{{"); - } - - public static void GenerateNamespaceEnd(this StringBuilder source) - { - source.AppendLine("}"); - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs deleted file mode 100644 index fc66bc985..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs +++ /dev/null @@ -1,144 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceGeneration.Property.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Text; -using Humanizer; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SourceGeneration - { - public static string GetPropertyName(this IFieldSymbol fieldSymbol) - { - var fieldName = fieldSymbol.Name; - - var propertyName = fieldName; - - if (propertyName.StartsWith("m_", StringComparison.OrdinalIgnoreCase)) - { - propertyName = propertyName.Substring(2); - } - else if (propertyName.StartsWith("_", StringComparison.OrdinalIgnoreCase)) - { - propertyName = propertyName.Substring(1); - } - - return propertyName.Dehumanize(); - } - - public static void GeneratePropertyStart( - this StringBuilder source, - string indent, - Accessibility accessors, - bool isVirtual, - IFieldSymbol fieldSymbol - ) - { - var propertyName = fieldSymbol.GetPropertyName(); - var virt = isVirtual ? "virtual " : ""; - - source.AppendLine($"{indent}{accessors.ToFriendlyString()} {virt}{fieldSymbol.Type} {propertyName}"); - source.AppendLine($"{indent}{{"); - } - - public static void GenerateAutoProperty( - this StringBuilder source, - Accessibility accessors, - string type, - string propertyName, - Accessibility? getAccessor, - Accessibility? setAccessor, - string indent, - bool useInit = false, - string defaultValue = null, - bool isOverride = false - ) - { - if (getAccessor == null && setAccessor == null) - { - throw new ArgumentNullException($"Must specify a {nameof(getAccessor)} or {nameof(setAccessor)} parameter"); - } - - var getter = getAccessor == null ? - "" : - $"{(getAccessor != Accessibility.NotApplicable ? $"{getAccessor.Value.ToFriendlyString()} " : "")}get;"; - - var getterSpace = getAccessor != null ? " " : ""; - var setOrInit = useInit ? "init;" : "set;"; - - var setterAccessor = setAccessor is null or Accessibility.NotApplicable - ? "" - : $"{setAccessor.Value.ToFriendlyString() ?? ""} "; - - var setter = setAccessor == null ? "" : $"{getterSpace}{setterAccessor}{setOrInit}"; - - var propertyAccessor = accessors == Accessibility.NotApplicable ? "" : $"{accessors.ToFriendlyString()} "; - var printOverride = isOverride ? "override " : ""; - var printDefaultValue = defaultValue != null ? $"{(setAccessor != null ? " =" : "")} {defaultValue};" : ""; - var printGetterSetter = setAccessor == null ? "=>" : $"{{ {getter}{setter} }}"; - - source.AppendLine($"{indent}{propertyAccessor}{printOverride}{type} {propertyName} {printGetterSetter}{printDefaultValue}"); - } - - public static void GeneratePropertyEnd(this StringBuilder source, string indent) => source.AppendLine($"{indent}}}"); - - public static void GeneratePropertyGetterReturnsField( - this StringBuilder source, - string indent, - IFieldSymbol fieldSymbol, - Accessibility Accessibility - ) - { - var accessor = Accessibility != Accessibility.NotApplicable ? $"{Accessibility.ToFriendlyString()} " : ""; - source.AppendLine($"{indent}{accessor}get => {fieldSymbol.Name};"); - } - - public static void GeneratePropertyGetterStart( - this StringBuilder source, - string indent, - bool useExpression, - Accessibility Accessibility - ) - { - var accessor = Accessibility != Accessibility.NotApplicable ? $"{Accessibility.ToFriendlyString()} " : ""; - var expression = useExpression ? " => " : $"\n{indent}{{"; - source.AppendLine($"{indent}{accessor}get{expression}"); - } - - public static void GeneratePropertyGetSetEnd(this StringBuilder source, string indent, bool useExpression) - { - if (!useExpression) - { - source.AppendLine($"{indent}}}"); - } - } - - public static void GeneratePropertySetterStart( - this StringBuilder source, - string indent, - bool useExpression, - Accessibility Accessibility, - bool useInit = false - ) - { - var init = useInit ? "init" : "set"; - var expression = useExpression ? " => " : $"\n{indent}{{"; - var accessor = Accessibility != Accessibility.NotApplicable ? $"{Accessibility.ToFriendlyString()} " : ""; - source.AppendLine($"{indent}{accessor}{init}{expression}"); - } - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.Builtin.cs b/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.Builtin.cs deleted file mode 100644 index 28241d463..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.Builtin.cs +++ /dev/null @@ -1,69 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SymbolMetadata.Builtin.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SymbolMetadata - { - public const string DICTIONARY_CLASS = "System.Collections.Generic.Dictionary`2"; - public const string LIST_CLASS = "System.Collections.Generic.List`1"; - public const string HASHSET_CLASS = "System.Collections.Generic.HashSet`1"; - public const string IPADDRESS_CLASS = "System.Net.IPAddress"; - public const string KEYVALUEPAIR_STRUCT = "System.Collections.Generic.KeyValuePair"; - public const string TIMESPAN_STRUCT = "System.TimeSpan"; - - public static bool IsTimeSpan(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(TIMESPAN_STRUCT), - SymbolEqualityComparer.Default - ); - - public static bool IsIpAddress(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(IPADDRESS_CLASS), - SymbolEqualityComparer.Default - ); - - public static bool IsKeyValuePair(this ISymbol symbol, Compilation compilation) => - (symbol as INamedTypeSymbol)?.ConstructedFrom.Equals( - compilation.GetTypeByMetadataName(KEYVALUEPAIR_STRUCT), - SymbolEqualityComparer.Default - ) == true; - - public static bool IsDictionary(this ISymbol symbol, Compilation compilation) => - (symbol as INamedTypeSymbol)?.ConstructedFrom.Equals( - compilation.GetTypeByMetadataName(DICTIONARY_CLASS), - SymbolEqualityComparer.Default - ) == true; - - public static bool IsHashSet(this ISymbol symbol, Compilation compilation) => - (symbol as INamedTypeSymbol)?.ConstructedFrom.Equals( - compilation.GetTypeByMetadataName(HASHSET_CLASS), - SymbolEqualityComparer.Default - ) == true; - - public static bool IsList(this ISymbol symbol, Compilation compilation) => - (symbol as INamedTypeSymbol)?.ConstructedFrom.Equals( - compilation.GetTypeByMetadataName(LIST_CLASS), - SymbolEqualityComparer.Default - ) == true; - - public static bool IsPrimitiveFromTypeDisplayString(string type) => - type is "bool" or "sbyte" or "short" or "int" or "long" or "byte" or "ushort" - or "uint" or "ulong" or "float" or "double" or "string" or "decimal"; - } -} diff --git a/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs b/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs deleted file mode 100644 index 2fdf8793c..000000000 --- a/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs +++ /dev/null @@ -1,228 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SymbolMetadata.UO.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Immutable; -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace SerializationGenerator -{ - public static partial class SymbolMetadata - { - public const string INVALIDATEPROPERTIES_ATTRIBUTE = "Server.InvalidatePropertiesAttribute"; - public const string AFTERDESERIALIZATION_ATTRIBUTE = "Server.AfterDeserializationAttribute"; - public const string SERIALIZABLE_ATTRIBUTE = "Server.SerializableAttribute"; - public const string EMBEDDED_SERIALIZABLE_ATTRIBUTE = "Server.EmbeddedSerializableAttribute"; - public const string SERIALIZABLE_PARENT_ATTRIBUTE = "Server.SerializableParentAttribute"; - public const string SERIALIZABLE_FIELD_ATTRIBUTE = "Server.SerializableFieldAttribute"; - public const string SERIALIZABLE_FIELD_ATTR_ATTRIBUTE = "Server.SerializableFieldAttrAttribute"; - public const string SERIALIZABLE_INTERFACE = "Server.ISerializable"; - public const string GENERIC_WRITER_INTERFACE = "Server.IGenericWriter"; - public const string GENERIC_READER_INTERFACE = "Server.IGenericReader"; - public const string DELTA_DATE_TIME_ATTRIBUTE = "Server.DeltaDateTimeAttribute"; - public const string INTERN_STRING_ATTRIBUTE = "Server.InternStringAttribute"; - public const string ENCODED_INT_ATTRIBUTE = "Server.EncodedIntAttribute"; - public const string TIDY_ATTRIBUTE = "Server.TidyAttribute"; - public const string POINT2D_STRUCT = "Server.Point2D"; - public const string POINT3D_STRUCT = "Server.Point3D"; - public const string RECTANGLE2D_STRUCT = "Server.Rectangle2D"; - public const string RECTANGLE3D_STRUCT = "Server.Rectangle3D"; - public const string RACE_CLASS = "Server.Race"; - public const string MAP_CLASS = "Server.Map"; - public const string TIMER_CLASS = "Server.Timer"; - public const string TIMER_DRIFT_ATTRIBUTE = "Server.TimerDriftAttribute"; - public const string DESERIALIZE_TIMER_FIELD_ATTRIBUTE = "Server.DeserializeTimerFieldAttribute"; - public const string SERIALIZABLE_FIELD_SAVE_FLAG_ATTRIBUTE = "Server.SerializableFieldSaveFlagAttribute"; - public const string SERIALIZABLE_FIELD_DEFAULT_ATTRIBUTE = "Server.SerializableFieldDefaultAttribute"; - public const string RAW_SERIALIZABLE_INTERFACE = "Server.IRawSerializable"; - // ModernUO modified BitArray - public const string SERVER_BITARRAY_CLASS = "Server.Collections.BitArray"; - - public static bool IsTimerDrift(this AttributeData attr, Compilation compilation) => - attr?.IsAttribute(compilation.GetTypeByMetadataName(TIMER_DRIFT_ATTRIBUTE)) == true; - - public static bool IsTimer(this ITypeSymbol symbol, Compilation compilation) => - symbol.CanBeConstructedFrom(compilation.GetTypeByMetadataName(TIMER_CLASS)); - - public static bool IsEncodedInt(this AttributeData attr, Compilation compilation) => - attr?.IsAttribute(compilation.GetTypeByMetadataName(ENCODED_INT_ATTRIBUTE)) == true; - - public static bool IsDeltaDateTime(this AttributeData attr, Compilation compilation) => - attr?.IsAttribute(compilation.GetTypeByMetadataName(DELTA_DATE_TIME_ATTRIBUTE)) == true; - - public static bool IsInternString(this AttributeData attr, Compilation compilation) => - attr?.IsAttribute(compilation.GetTypeByMetadataName(INTERN_STRING_ATTRIBUTE)) == true; - - public static bool IsTidy(this AttributeData attr, Compilation compilation) => - attr?.IsAttribute(compilation.GetTypeByMetadataName(TIDY_ATTRIBUTE)) == true; - - public static bool IsAttribute(this AttributeData attr, ISymbol symbol) => - attr?.AttributeClass?.Equals(symbol, SymbolEqualityComparer.Default) == true; - - public static bool IsEnum(this ITypeSymbol symbol) => - symbol.SpecialType == SpecialType.System_Enum || symbol.TypeKind == TypeKind.Enum; - - public static bool HasSerializableInterface( - this ITypeSymbol symbol, - Compilation compilation, - ImmutableArray serializableTypes - ) => - symbol.ContainsInterface(compilation.GetTypeByMetadataName(SERIALIZABLE_INTERFACE)) || - serializableTypes.Contains(symbol); - - public static bool HasRawSerializableInterface( - this ITypeSymbol symbol, - Compilation compilation, - ImmutableArray embeddedSerializableTypes - ) => - symbol.ContainsInterface(compilation.GetTypeByMetadataName(RAW_SERIALIZABLE_INTERFACE)) || - embeddedSerializableTypes.Contains(symbol); - - public static bool Contains(this ImmutableArray symbols, ITypeSymbol? symbol) => - symbol is INamedTypeSymbol namedSymbol && - symbols.Contains(namedSymbol, SymbolEqualityComparer.Default) || symbols.Contains(symbol?.BaseType); - - public static bool HasGenericReaderCtor( - this INamedTypeSymbol symbol, - Compilation compilation, - ISymbol? parentSymbol, - out bool requiresParent - ) - { - var genericReaderInterface = compilation.GetTypeByMetadataName(GENERIC_READER_INTERFACE); - var genericCtor = symbol.Constructors.FirstOrDefault( - m => !m.IsStatic && - m.MethodKind == MethodKind.Constructor && - m.Parameters.Length >= 1 && - m.Parameters.Length <= 2 && - SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, genericReaderInterface) - ); - - requiresParent = genericCtor?.Parameters.Length == 2 && SymbolEqualityComparer.Default.Equals(genericCtor.Parameters[1].Type, parentSymbol); - return genericCtor != null; - } - - public static bool HasPublicSerializeMethod( - this ITypeSymbol symbol, - Compilation compilation, - ImmutableArray serializableTypes - ) - { - var genericWriterInterface = compilation.GetTypeByMetadataName(GENERIC_WRITER_INTERFACE); - - return symbol.GetAllMethods("Serialize") - .Any( - m => !m.IsStatic && - m.ReturnsVoid && - m.Parameters.Length == 1 && - SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, genericWriterInterface) && - m.DeclaredAccessibility == Accessibility.Public - ); - } - - public static bool HasPublicDeserializeMethod( - this ITypeSymbol symbol, - Compilation compilation, - ImmutableArray serializableTypes - ) - { - var genericReaderInterface = compilation.GetTypeByMetadataName(GENERIC_READER_INTERFACE); - - return symbol.GetAllMethods("Deserialize") - .Any( - m => !m.IsStatic && - m.ReturnsVoid && - m.Parameters.Length == 1 && - SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, genericReaderInterface) && - m.DeclaredAccessibility == Accessibility.Public - ); - } - - public static bool IsPoint2D(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(POINT2D_STRUCT), - SymbolEqualityComparer.Default - ); - - public static bool IsPoint3D(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(POINT3D_STRUCT), - SymbolEqualityComparer.Default - ); - - public static bool IsRectangle2D(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(RECTANGLE2D_STRUCT), - SymbolEqualityComparer.Default - ); - - public static bool IsRectangle3D(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(RECTANGLE3D_STRUCT), - SymbolEqualityComparer.Default - ); - - public static bool IsRace(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(RACE_CLASS), - SymbolEqualityComparer.Default - ); - - public static bool IsMap(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(MAP_CLASS), - SymbolEqualityComparer.Default - ); - - public static bool IsBitArray(this ISymbol symbol, Compilation compilation) => - symbol.Equals( - compilation.GetTypeByMetadataName(SERVER_BITARRAY_CLASS), - SymbolEqualityComparer.Default - ); - - public static AttributeData? GetAttribute(this ISymbol symbol, ISymbol attrSymbol) => - symbol - .GetAttributes() - .FirstOrDefault( - ad => ad.AttributeClass != null && SymbolEqualityComparer.Default.Equals(ad.AttributeClass, attrSymbol) - ); - - public static bool WillBeSerializable(this INamedTypeSymbol classSymbol, Compilation compilation, out AttributeData? attributeData) - { - var serializableInterface = compilation.GetTypeByMetadataName(SERIALIZABLE_INTERFACE); - - if (!classSymbol.ContainsInterface(serializableInterface)) - { - attributeData = null; - return false; - } - - var serializableEntityAttribute = - compilation.GetTypeByMetadataName(SERIALIZABLE_ATTRIBUTE); - - attributeData = classSymbol.GetAttribute(serializableEntityAttribute); - return attributeData != null; - } - - public static bool IsEmbeddedSerializable(this INamedTypeSymbol classSymbol, Compilation compilation, out AttributeData? attributeData) - { - var embeddedSerializableEntityAttribute = - compilation.GetTypeByMetadataName(EMBEDDED_SERIALIZABLE_ATTRIBUTE); - - attributeData = classSymbol.GetAttribute(embeddedSerializableEntityAttribute); - return attributeData != null; - } - } -} diff --git a/Projects/SerializationGenerator/Utility.cs b/Projects/SerializationGenerator/Utility.cs deleted file mode 100644 index ad3f9c9c8..000000000 --- a/Projects/SerializationGenerator/Utility.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Utility.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; - -namespace SerializationGenerator -{ - public static class Utility - { - public static void Deconstruct(this KeyValuePair tuple, out T1 key, out T2 value) - { - key = tuple.Key; - value = tuple.Value; - } - } -} diff --git a/Projects/SerializationSchemaGenerator/.gitignore b/Projects/SerializationSchemaGenerator/.gitignore deleted file mode 100644 index 2fe317a80..000000000 --- a/Projects/SerializationSchemaGenerator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Output/ diff --git a/Projects/SerializationSchemaGenerator/Application.cs b/Projects/SerializationSchemaGenerator/Application.cs deleted file mode 100644 index eb6946787..000000000 --- a/Projects/SerializationSchemaGenerator/Application.cs +++ /dev/null @@ -1,103 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Application.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Immutable; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using SerializationGenerator; - -namespace SerializationSchemaGenerator -{ - public static class Application - { - public static void Main(string[] args) - { - if (args.Length < 1) - { - throw new ArgumentException("Usage: dotnet SerializationSchemaGenerator.dll "); - } - - var solutionPath = args[0]; - - Parallel.ForEach( - SourceCodeAnalysis.GetCompilation(solutionPath), - (projectCompilation) => - { - var (project, compilation) = projectCompilation; - if (project.Name.EndsWith(".Tests", StringComparison.Ordinal) || project.Name == "Benchmarks") - { - return; - } - - var projectFile = new FileInfo(project.FilePath!); - var projectPath = projectFile.Directory?.FullName; - var migrationPath = Path.Join(projectPath, "Migrations"); - Directory.CreateDirectory(migrationPath); - - var syntaxReceiver = new SerializerSyntaxReceiver(); - - foreach (var syntaxTree in compilation.SyntaxTrees) - { - var root = syntaxTree.GetRoot(); - var syntaxVisitor = new SyntaxVisitor(compilation.GetSemanticModel(syntaxTree), syntaxReceiver); - syntaxVisitor.Visit(root); - } - - var jsonOptions = new JsonSerializerOptions - { - WriteIndented = true, - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - ReadCommentHandling = JsonCommentHandling.Skip - }; - - var serializableTypes = syntaxReceiver.SerializableList; - var embeddedSerializableTypes = syntaxReceiver.EmbeddedSerializableList; - - foreach (var (classSymbol, (attributeData, fieldsList)) in syntaxReceiver.ClassAndFields) - { - var source = compilation.GenerateSerializationPartialClass( - classSymbol, - attributeData, - migrationPath, - false, - jsonOptions, - fieldsList.ToImmutableArray(), - serializableTypes, - embeddedSerializableTypes - ); - } - - foreach (var (classSymbol, (attributeData, fieldsList)) in syntaxReceiver.EmbeddedClassAndFields) - { - var source = compilation.GenerateSerializationPartialClass( - classSymbol, - attributeData, - migrationPath, - true, - jsonOptions, - fieldsList.ToImmutableArray(), - serializableTypes, - embeddedSerializableTypes - ); - } - } - ); - } - } -} diff --git a/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj b/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj deleted file mode 100755 index 1df2df68a..000000000 --- a/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - Exe - Output - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - diff --git a/Projects/SerializationSchemaGenerator/SourceCodeAnalysis.cs b/Projects/SerializationSchemaGenerator/SourceCodeAnalysis.cs deleted file mode 100644 index ac578b406..000000000 --- a/Projects/SerializationSchemaGenerator/SourceCodeAnalysis.cs +++ /dev/null @@ -1,49 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SourceCodeAnalysis.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.Build.Locator; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.MSBuild; - -namespace SerializationSchemaGenerator -{ - public static class SourceCodeAnalysis - { - public static List<(Project, Compilation)> GetCompilation(string solutionPath) - { - if (!File.Exists(solutionPath) || !solutionPath.EndsWith(".sln", StringComparison.Ordinal)) - { - throw new FileNotFoundException($"Could not open a valid solution at location {solutionPath}"); - } - - MSBuildLocator.RegisterDefaults(); - - var workspace = MSBuildWorkspace.Create(); - - var solutionToAnalyze = workspace.OpenSolutionAsync(solutionPath).Result; - - var results = solutionToAnalyze.Projects.AsParallel() - .Select((project) => (project, project?.GetCompilationAsync().Result)) - .Where((value) => value.Result != null) - .ToList(); - - return results; - } - } -} diff --git a/Projects/SerializationSchemaGenerator/SyntaxVisitor.cs b/Projects/SerializationSchemaGenerator/SyntaxVisitor.cs deleted file mode 100644 index 1e47733be..000000000 --- a/Projects/SerializationSchemaGenerator/SyntaxVisitor.cs +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SyntaxVisitor.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using SerializationGenerator; - -namespace SerializationSchemaGenerator -{ - public class SyntaxVisitor : CSharpSyntaxWalker - { - private readonly SemanticModel _semanticModel; - private readonly SerializerSyntaxReceiver _syntaxReceiver; - - public SyntaxVisitor(SemanticModel semanticModel, SerializerSyntaxReceiver syntaxReceiver) - { - _semanticModel = semanticModel; - _syntaxReceiver = syntaxReceiver; - } - - public override void VisitClassDeclaration(ClassDeclarationSyntax node) - { - base.VisitClassDeclaration(node); - _syntaxReceiver.OnVisitSyntaxNode(node, _semanticModel); - } - - public override void VisitFieldDeclaration(FieldDeclarationSyntax node) - { - base.VisitFieldDeclaration(node); - _syntaxReceiver.OnVisitSyntaxNode(node, _semanticModel); - } - - public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) - { - base.VisitPropertyDeclaration(node); - _syntaxReceiver.OnVisitSyntaxNode(node, _semanticModel); - } - } -} diff --git a/Projects/Server.Tests/Helpers/GumpUtilities.cs b/Projects/Server.Tests/Helpers/GumpUtilities.cs index 70e7c69af..98ef15d59 100644 --- a/Projects/Server.Tests/Helpers/GumpUtilities.cs +++ b/Projects/Server.Tests/Helpers/GumpUtilities.cs @@ -193,7 +193,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpAlphaRegion g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpAlphaRegion.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("checkertrans")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Width); @@ -202,7 +202,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpBackground g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpBackground.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("resizepic")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.GumpID); @@ -212,7 +212,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpButton g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpButton.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("button")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.NormalID); @@ -224,7 +224,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpCheck g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpButton.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("checkbox")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.InactiveID); @@ -237,18 +237,18 @@ namespace Server.Tests.Network public static void AppendTo(this GumpGroup g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpGroup.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("group")); disp.AppendLayout(g.Group); } public static void AppendTo(this GumpECHandleInput g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpECHandleInput.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("echandleinput")); } public static void AppendTo(this GumpHtml g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpHtml.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("htmlgump")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Width); @@ -264,7 +264,7 @@ namespace Server.Tests.Network { case GumpHtmlLocalizedType.Plain: { - disp.AppendLayout(GumpHtmlLocalized.LayoutNamePlain); + disp.AppendLayout(Gump.StringToBuffer("xmfhtmlgump")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); @@ -279,7 +279,7 @@ namespace Server.Tests.Network case GumpHtmlLocalizedType.Color: { - disp.AppendLayout(GumpHtmlLocalized.LayoutNameColor); + disp.AppendLayout(Gump.StringToBuffer("xmfhtmlgumpcolor")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); @@ -295,7 +295,7 @@ namespace Server.Tests.Network case GumpHtmlLocalizedType.Args: { - disp.AppendLayout(GumpHtmlLocalized.LayoutNameArgs); + disp.AppendLayout(Gump.StringToBuffer("xmfhtmltok")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); @@ -314,27 +314,27 @@ namespace Server.Tests.Network public static void AppendTo(this GumpImage g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpImage.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("gumppic")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.GumpID); if (g.Hue != 0) { - disp.AppendLayout(GumpImage.HueEquals); + disp.AppendLayoutNS(" hue="); disp.AppendLayoutNS(g.Hue); } if (!string.IsNullOrEmpty(g.Class)) { - disp.AppendLayout(GumpImage.ClassEquals); - disp.AppendLayoutNS(g.Class); + disp.AppendLayoutNS(" class="); + disp.AppendLayout(Gump.StringToBuffer(g.Class)); } } public static void AppendTo(this GumpImageTileButton g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpImageTileButton.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("buttontileart")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.NormalID); @@ -347,17 +347,11 @@ namespace Server.Tests.Network disp.AppendLayout(g.Hue); disp.AppendLayout(g.Width); disp.AppendLayout(g.Height); - - if (g.LocalizedTooltip > 0) - { - disp.AppendLayout(GumpImageTileButton.LayoutTooltip); - disp.AppendLayout(g.LocalizedTooltip); - } } public static void AppendTo(this GumpImageTiled g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpImageTiled.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("gumppictiled")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Width); @@ -367,7 +361,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpItem g, IGumpWriter disp, List strings) { - disp.AppendLayout(g.Hue == 0 ? GumpItem.LayoutName : GumpItem.LayoutNameHue); + disp.AppendLayout(Gump.StringToBuffer(g.Hue == 0 ? "tilepic" : "tilepichue")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.ItemID); @@ -380,13 +374,13 @@ namespace Server.Tests.Network public static void AppendTo(this GumpItemProperty g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpItemProperty.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("itemproperty")); disp.AppendLayout(g.Serial); } public static void AppendTo(this GumpLabel g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpLabel.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("text")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Hue); @@ -395,7 +389,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpLabelCropped g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpLabelCropped.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("croppedtext")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Width); @@ -406,19 +400,19 @@ namespace Server.Tests.Network public static void AppendTo(this GumpMasterGump g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpMasterGump.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("mastergump")); disp.AppendLayout(g.GumpID); } public static void AppendTo(this GumpPage g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpPage.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("page")); disp.AppendLayout(g.Page); } public static void AppendTo(this GumpRadio g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpRadio.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("radio")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.InactiveID); @@ -431,7 +425,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpSpriteImage g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpSpriteImage.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("picinpic")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.GumpID); @@ -443,7 +437,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpTextEntry g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpTextEntry.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("textentry")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Width); @@ -457,7 +451,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpTextEntryLimited g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpTextEntryLimited.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("textentrylimited")); disp.AppendLayout(g.X); disp.AppendLayout(g.Y); disp.AppendLayout(g.Width); @@ -472,7 +466,7 @@ namespace Server.Tests.Network public static void AppendTo(this GumpTooltip g, IGumpWriter disp, List strings) { - disp.AppendLayout(GumpTooltip.LayoutName); + disp.AppendLayout(Gump.StringToBuffer("tooltip")); disp.AppendLayout(g.Number); if (!string.IsNullOrEmpty(g.Args)) diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index b33bf4c7d..db30edf70 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -3,17 +3,13 @@ false - - + + - - + - - Data\Professions\LBR\prof.txt - diff --git a/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs b/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs new file mode 100644 index 000000000..0b7ee8937 --- /dev/null +++ b/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs @@ -0,0 +1,72 @@ +using System; +using Server.Buffers; +using Xunit; + +namespace Server.Tests.Tests.Buffers; + +[Collection("Sequential Tests")] +public class STArrayPoolTests +{ + [Theory] + [InlineData(0, 0)] + [InlineData(2, 16)] + [InlineData(56, 64)] + [InlineData(120, 128)] + [InlineData(65535, 65536)] + [InlineData(1024 * 1024 * 15, 1024 * 1024 * 16)] + public void ValidMinimumLengths(int requestedLength, int expectedLength) + { + var arr = STArrayPool.Shared.Rent(requestedLength); + Assert.Equal(expectedLength, arr.Length); + } + + [Fact] + public void NegativeLengthThrows() + { + Assert.Throws( + () => + { + var arr = STArrayPool.Shared.Rent(-1); + } + ); + } + + [Fact] + public void CachesOnlyUpToCPUCountPerBucket() + { + STArrayPool.Shared.ResetForTesting(); + + var cores = Environment.ProcessorCount; + var arrays1 = new byte[cores * 8 + 2][]; // 1 for the cache, and 8 * CPU for the stacks + var weakReferences1 = new WeakReference[cores * 8 + 2]; + + var arrays2 = new byte[cores * 8 + 2][]; // 1 for the cache, and 8 * CPU for the stacks + var weakReferences2 = new WeakReference[cores * 8 + 2]; + + for (var i = 0; i < arrays1.Length; i++) + { + arrays1[i] = STArrayPool.Shared.Rent(32); + weakReferences1[i] = new WeakReference(arrays1[i]); + + arrays2[i] = STArrayPool.Shared.Rent(64); + weakReferences2[i] = new WeakReference(arrays2[i]); + } + + for (var i = 0; i < arrays1.Length; i++) + { + STArrayPool.Shared.Return(arrays1[i]); + arrays1[i] = null; + + STArrayPool.Shared.Return(arrays2[i]); + arrays2[i] = null; + } + + GC.Collect(); + for (var i = 0; i < weakReferences1.Length; i++) + { + // When the last one is returned, the one right before it is dropped. + Assert.Equal(i != weakReferences1.Length - 2, weakReferences1[i].IsAlive); + Assert.Equal(i != weakReferences2.Length - 2, weakReferences2[i].IsAlive); + } + } +} diff --git a/Projects/Server.Tests/Tests/Collections/BitArrayTests.cs b/Projects/Server.Tests/Tests/Collections/BitArrayTests.cs new file mode 100644 index 000000000..31d2fb2e1 --- /dev/null +++ b/Projects/Server.Tests/Tests/Collections/BitArrayTests.cs @@ -0,0 +1,29 @@ +using Server.Collections; +using Xunit; + +namespace Server.Tests; + +public class BitArrayTests +{ + [Fact] + public void TestBitArray() + { + var bitArray = new BitArray(700); // Restricted Spells; + bitArray.Set(5, true); + bitArray.Set(39, true); + bitArray.Set(125, true); + + // Simulate World Saving + var writer = new BufferWriter(1024, false); + writer.Write(bitArray); // Save it to a file + + // Simulate World Loading + var reader = new BufferReader(writer.Buffer); + var bitArrayTest = reader.ReadBitArray(); + Assert.Equal(700, bitArrayTest.Length); + for (var i = 0; i < bitArrayTest.Length; i++) + { + Assert.Equal(i is 5 or 39 or 125, bitArrayTest.Get(i)); + } + } +} diff --git a/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs b/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs new file mode 100644 index 000000000..a9d3576b0 --- /dev/null +++ b/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs @@ -0,0 +1,102 @@ +using System; +using Moq; +using Server.Collections; +using Server.Random; +using Xunit; + +namespace Server.Tests; + +public sealed class PooledRefQueueTests : IDisposable +{ + public void Dispose() => RandomSources.SetRng(null); + + private static void PrepareRng(int queueCount, int rngValue) + { + Mock mockRng = new Mock(); + mockRng + .Setup(rng => rng.Next(It.IsAny())) + .Returns( + (int size) => + { + Assert.Equal(queueCount, size); + return rngValue; + } + ); + + RandomSources.SetRng(mockRng.Object); + } + + [Fact] + public void TestPeekRandom1() + { + // Random value for _head = 0, _tail = 5, _size = 5, + using var queue = PooledRefQueue.Create(10); + queue.Enqueue(0); + queue.Enqueue(1); + queue.Enqueue(2); + queue.Enqueue(3); // <----- + queue.Enqueue(4); + queue.Enqueue(5); + + PrepareRng(6, 3); + Assert.Equal(3, queue.PeekRandom()); + } + + [Fact] + public void TestPeekRandom2() + { + // Random value for _head = 3, _tail = 10, _size = 7, + using var queue = PooledRefQueue.Create(10); + queue.Enqueue(0); + queue.Enqueue(1); + queue.Enqueue(2); + + queue.Enqueue(3); + queue.Enqueue(4); + queue.Enqueue(5); + queue.Enqueue(6); // <--- + queue.Enqueue(7); + queue.Enqueue(8); + queue.Enqueue(9); + + queue.Dequeue(); + queue.Dequeue(); + queue.Dequeue(); + + PrepareRng(7, 3); + Assert.Equal(6, queue.PeekRandom()); + } + + [Theory] + [InlineData(3, 6)] + [InlineData(8, 11)] + [InlineData(6, 9)] + [InlineData(7, 10)] + public void TestPeekRandom3(int rngValue, int expectedIndex) + { + // Random value for _head = 3, _tail = 2, _size = 10, + using var queue = PooledRefQueue.Create(10); + queue.Enqueue(0); + queue.Enqueue(1); + queue.Enqueue(2); + + queue.Enqueue(3); + queue.Enqueue(4); + queue.Enqueue(5); + queue.Enqueue(6); + queue.Enqueue(7); + queue.Enqueue(8); + queue.Enqueue(9); + + queue.Dequeue(); + queue.Dequeue(); + queue.Dequeue(); + + queue.Enqueue(10); + queue.Enqueue(11); + queue.Enqueue(12); + + PrepareRng(10, rngValue); + Assert.Equal(expectedIndex, queue.PeekRandom()); + } +} diff --git a/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs b/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs new file mode 100644 index 000000000..d37046591 --- /dev/null +++ b/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs @@ -0,0 +1,19 @@ +using Xunit; + +namespace Server.Tests; + +public class LocalizationEntryTests +{ + [Fact] + public void TestClilocAsParameter() + { + Localization.Add("enu", 500002, "This tests ~1_NUMBER~ as parameters."); + Localization.Add("enu", 500003, "clilocs"); + + string numericFormatter = Localization.Format(500002, "enu", $"{500003:#}"); + string stringParam = Localization.Format(500002, "enu", $"{"#500003"}"); + + Assert.Equal("This tests clilocs as parameters.", numericFormatter); + Assert.Equal("This tests clilocs as parameters.", stringParam); + } +} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs index fd8aeba6c..61332a2a0 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs @@ -24,9 +24,9 @@ namespace Server.Tests.Network public void TestDisplaySignGump() { Serial gumpSerial = (Serial)0x1000; - var gumpId = 100; - var unknownString = "This is an unknown string"; - var caption = "This is a caption"; + const int gumpId = 100; + const string unknownString = "This is an unknown string"; + const string caption = "This is a caption"; var expected = new DisplaySignGump(gumpSerial, gumpId, unknownString, caption).Compile(); diff --git a/Projects/Server.Tests/Tests/Network/PipeTests.cs b/Projects/Server.Tests/Tests/Network/PipeTests.cs index ab4556dc6..31b9baaa3 100644 --- a/Projects/Server.Tests/Tests/Network/PipeTests.cs +++ b/Projects/Server.Tests/Tests/Network/PipeTests.cs @@ -11,7 +11,7 @@ namespace Server.Tests.Network { private async void DelayedExecute(Action action) { - await Task.Delay(5); + await Task.Delay(1); action(); } @@ -130,8 +130,12 @@ namespace Server.Tests.Network continue; } - result.CopyFrom(new[] { expected_value, expected_value, expected_value, expected_value, expected_value, expected_value, expected_value, expected_value, - expected_value, expected_value, expected_value, expected_value, expected_value, expected_value, expected_value, expected_value }); + result.CopyFrom(new[] { + expected_value, expected_value, expected_value, expected_value, + expected_value, expected_value, expected_value, expected_value, + expected_value, expected_value, expected_value, expected_value, + expected_value, expected_value, expected_value, expected_value + }); writer.Advance(16); count += 16; diff --git a/Projects/Server.Tests/Tests/Network/PollGroupTests.cs b/Projects/Server.Tests/Tests/Network/PollGroupTests.cs new file mode 100644 index 000000000..34d97e9a2 --- /dev/null +++ b/Projects/Server.Tests/Tests/Network/PollGroupTests.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network; + +public class PollGroupTests +{ + [Fact] + public void TestPollGroup() + { + // var group = new KQueuePollGroup(); + var nss = new NetState[2048]; + var handles = new IntPtr[2048]; + for (var i = 0; i < nss.Length; i++) + { + nss[i] = PacketTestUtilities.CreateTestNetState(); + handles[i] = (IntPtr)nss[i].Handle; + } + + GC.AddMemoryPressure(10000000000); + GC.Collect(); + GC.RemoveMemoryPressure(10000000000); + GC.Collect(); + + Thread.Sleep(1000); + + for (var i = 0; i < nss.Length; i++) + { + Assert.Equal(nss[i].Handle, (GCHandle)handles[i]); + } + + // group.Dispose(); + } +} diff --git a/Projects/Server.Tests/Tests/SerialTests.cs b/Projects/Server.Tests/Tests/SerialTests.cs new file mode 100644 index 000000000..60ff3d370 --- /dev/null +++ b/Projects/Server.Tests/Tests/SerialTests.cs @@ -0,0 +1,37 @@ +using System; +using Xunit; + +namespace Server.Tests; + +public class SerialTests +{ + [Fact] + public void TestSerialTryFormatDefault() + { + var serial = (Serial)0xABCD1234u; + const string serialStr = "0xABCD1234"; + Span buffer = stackalloc char[serialStr.Length]; + var result = serial.TryFormat(buffer, out var charsWritten, null, null); + Assert.True(result); + Assert.Equal(serialStr.Length, charsWritten); + Assert.Equal(serialStr, buffer.ToString()); + + var interpolated = $"{serial}"; + Assert.Equal(serialStr, interpolated); + } + + [Fact] + public void TestSerialTryFormatCustom() + { + var serial = (Serial)0xABCD1234u; + const string serialStr = "2882343476"; + Span buffer = stackalloc char[serialStr.Length]; + var result = serial.TryFormat(buffer, out var charsWritten, "##", null); + Assert.True(result); + Assert.Equal(serialStr.Length, charsWritten); + Assert.Equal(serialStr, buffer.ToString()); + + var interpolated = $"{serial:##}"; + Assert.Equal(serialStr, interpolated); + } +} diff --git a/Projects/Server/Buffers/CircularBufferReader.cs b/Projects/Server/Buffers/CircularBufferReader.cs index 40eeafc2f..aa8b9aa08 100644 --- a/Projects/Server/Buffers/CircularBufferReader.cs +++ b/Projects/Server/Buffers/CircularBufferReader.cs @@ -245,7 +245,7 @@ namespace Server.Network [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1) { - int sizeT = TextEncoding.GetByteLengthForEncoding(encoding); + int byteLength = encoding.GetByteLengthForEncoding(); bool isFixedLength = fixedLength > -1; @@ -254,7 +254,7 @@ namespace Server.Network if (isFixedLength) { - size = fixedLength * sizeT; + size = fixedLength * byteLength; if (size > Remaining) { throw new OutOfMemoryException(); @@ -262,7 +262,7 @@ namespace Server.Network } else { - size = remaining - (remaining & (sizeT - 1)); + size = remaining - (remaining & (byteLength - 1)); } ReadOnlySpan span; @@ -273,7 +273,7 @@ namespace Server.Network var firstLength = Math.Min(_first.Length - Position, size); // Find terminator - index = _first.Slice(Position, firstLength).IndexOfTerminator(sizeT); + index = _first.Slice(Position, firstLength).IndexOfTerminator(byteLength); if (index < 0) { @@ -285,7 +285,7 @@ namespace Server.Network } else { - index = _second[..remaining].IndexOfTerminator(sizeT); + index = _second[..remaining].IndexOfTerminator(byteLength); int secondLength = index < 0 ? remaining : index; int length = firstLength + secondLength; @@ -295,7 +295,7 @@ namespace Server.Network _first[Position..].CopyTo(bytes); _second[..secondLength].CopyTo(bytes[firstLength..]); - Position += length + (index >= 0 ? sizeT : 0); + Position += length + (index >= 0 ? byteLength : 0); return TextEncoding.GetString(bytes, encoding, safeString); } } @@ -306,7 +306,7 @@ namespace Server.Network { size = Math.Min(remaining, size); span = _second.Slice( Position - _first.Length, size); - index = span.IndexOfTerminator(sizeT); + index = span.IndexOfTerminator(byteLength); if (index >= 0) { @@ -318,7 +318,7 @@ namespace Server.Network } } - Position += isFixedLength ? size : index + sizeT; + Position += isFixedLength ? size : index + byteLength; return TextEncoding.GetString(span, encoding, safeString); } diff --git a/Projects/Server/Buffers/PooledArraySpanFormattable.cs b/Projects/Server/Buffers/PooledArraySpanFormattable.cs new file mode 100644 index 000000000..7290f250a --- /dev/null +++ b/Projects/Server/Buffers/PooledArraySpanFormattable.cs @@ -0,0 +1,70 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PooledArraySpanFormattable.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +#nullable enable +using System; + +namespace Server.Buffers; + +public struct PooledArraySpanFormattable : ISpanFormattable, IDisposable +{ + private char[] _arrayToReturnToPool; + private int _pos; + private string _value; + + public PooledArraySpanFormattable(char[] arrayToReturnToPool, int length) + { + _arrayToReturnToPool = arrayToReturnToPool; + _pos = length; + _value = null; + } + + public ReadOnlySpan Chars => _arrayToReturnToPool.AsSpan(.._pos); + + public static implicit operator string(PooledArraySpanFormattable f) => f.ToString(); + + public string ToString(string? format = null, IFormatProvider formatProvider = null) + { + _value ??= new string(_arrayToReturnToPool.AsSpan(0, _pos)); + + STArrayPool.Shared.Return(_arrayToReturnToPool); + _arrayToReturnToPool = null; + + return _value; + } + + public bool TryFormat( + Span destination, out int charsWritten, ReadOnlySpan format = default, + IFormatProvider provider = null + ) + { + if (destination.Length < _pos) + { + charsWritten = 0; + return false; + } + + _arrayToReturnToPool.AsSpan(0, _pos).CopyTo(destination); + charsWritten = _pos; + return true; + } + + public void Dispose() + { + STArrayPool.Shared.Return(_arrayToReturnToPool); + _arrayToReturnToPool = null; + this = default; // Defensive clear + } +} diff --git a/Projects/Server/Buffers/RawInterpolatedStringHandler.cs b/Projects/Server/Buffers/RawInterpolatedStringHandler.cs new file mode 100644 index 000000000..699b61792 --- /dev/null +++ b/Projects/Server/Buffers/RawInterpolatedStringHandler.cs @@ -0,0 +1,600 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace Server.Buffers; + +/// Provides a handler to interpolate strings which UNSAFELY exposes it's internal character span. +[InterpolatedStringHandler] +public ref struct RawInterpolatedStringHandler +{ + // Implementation note: + // As this type lives in CompilerServices and is only intended to be targeted by the compiler, + // public APIs eschew argument validation logic in a variety of places, e.g. allowing a null input + // when one isn't expected to produce a NullReferenceException rather than an ArgumentNullException. + + /// Expected average length of formatted data used for an individual interpolation expression result. + /// + /// This is inherited from string.Format, and could be changed based on further data. + /// string.Format actually uses `format.Length + args.Length * 8`, but format.Length + /// includes the format items themselves, e.g. "{0}", and since it's rare to have double-digit + /// numbers of items, we bump the 8 up to 11 to account for the three extra characters in "{d}", + /// since the compiler-provided base length won't include the equivalent character count. + /// + private const int GuessedLengthPerHole = 11; + /// Minimum size array to rent from the pool. + /// Same as stack-allocation size used today by string.Format. + private const int MinimumArrayPoolLength = 256; + + /// Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls. + private readonly IFormatProvider? _provider; + /// Array rented from the array pool and used to back . + private char[]? _arrayToReturnToPool; + /// The span to write into. + private Span _chars; + /// Position at which to write the next character. + private int _pos; + /// Whether provides an ICustomFormatter. + /// + /// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive + /// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field + /// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider + /// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a + /// formatter, we pay for the extra interface call on each AppendFormatted that needs it. + /// + private readonly bool _hasCustomFormatter; + + /// Creates a handler used to translate an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public RawInterpolatedStringHandler(int literalLength, int formattedCount) + { + _provider = null; + _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); + _pos = 0; + _hasCustomFormatter = false; + } + + /// Creates a handler used to translate an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// An object that supplies culture-specific formatting information. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public RawInterpolatedStringHandler(int literalLength, int formattedCount, IFormatProvider? provider) + { + _provider = provider; + _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); + _pos = 0; + _hasCustomFormatter = provider is not null && HasCustomFormatter(provider); + } + + /// Derives a default length with which to seed the handler. + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // becomes a constant when inputs are constant + internal static int GetDefaultLength(int literalLength, int formattedCount) => + Math.Max(MinimumArrayPoolLength, literalLength + formattedCount * GuessedLengthPerHole); + + /// Clears the handler, returning any rented array to the pool. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // used only on a few hot paths + public void Clear() + { + char[]? toReturn = _arrayToReturnToPool; + this = default; // defensive clear + if (toReturn is not null) + { + STArrayPool.Shared.Return(toReturn); + } + } + + /// Gets a span of the written characters thus far. + public ReadOnlySpan Text => _chars[.._pos]; + + /// Writes the specified string to the handler. + /// The string to write. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendLiteral(string value) + { + if (value.Length == 1) + { + Span chars = _chars; + int pos = _pos; + if ((uint)pos < (uint)chars.Length) + { + chars[pos] = value[0]; + _pos = pos + 1; + } + else + { + GrowThenCopyString(value); + } + return; + } + + AppendStringDirect(value); + } + + /// Writes the specified string to the handler. + /// The string to write. + private void AppendStringDirect(string value) + { + if (value.TryCopyTo(_chars[_pos..])) + { + _pos += value.Length; + } + else + { + GrowThenCopyString(value); + } + } + + #region AppendFormatted + // Design note: + // The compiler requires a AppendFormatted overload for anything that might be within an interpolation expression; + // if it can't find an appropriate overload, for handlers in general it'll simply fail to compile. + // (For target-typing to string where it uses DefaultInterpolatedStringHandler implicitly, it'll instead fall back to + // its other mechanisms, e.g. using string.Format. This fallback has the benefit that if we miss a case, + // interpolated strings will still work, but it has the downside that a developer generally won't know + // if the fallback is happening and they're paying more.) + // + // At a minimum, then, we would need an overload that accepts: + // (object value, int alignment = 0, string? format = null) + // Such an overload would provide the same expressiveness as string.Format. However, this has several + // shortcomings: + // - Every value type in an interpolation expression would be boxed. + // - ReadOnlySpan could not be used in interpolation expressions. + // - Every AppendFormatted call would have three arguments at the call site, bloating the IL further. + // - Every invocation would be more expensive, due to lack of specialization, every call needing to account + // for alignment and format, etc. + // + // To address that, we could just have overloads for T and ReadOnlySpan: + // (T) + // (T, int alignment) + // (T, string? format) + // (T, int alignment, string? format) + // (ReadOnlySpan) + // (ReadOnlySpan, int alignment) + // (ReadOnlySpan, string? format) + // (ReadOnlySpan, int alignment, string? format) + // but this also has shortcomings: + // - Some expressions that would have worked with an object overload will now force a fallback to string.Format + // (or fail to compile if the handler is used in places where the fallback isn't provided), because the compiler + // can't always target type to T, e.g. `b switch { true => 1, false => null }` where `b` is a bool can successfully + // be passed as an argument of type `object` but not of type `T`. + // - Reference types get no benefit from going through the generic code paths, and actually incur some overheads + // from doing so. + // - Nullable value types also pay a heavy price, in particular around interface checks that would generally evaporate + // at compile time for value types but don't (currently) if the Nullable goes through the same code paths + // (see https://github.com/dotnet/runtime/issues/50915). + // + // We could try to take a more elaborate approach for DefaultInterpolatedStringHandler, since it is the most common handler + // and we want to minimize overheads both at runtime and in IL size, e.g. have a complete set of overloads for each of: + // (T, ...) where T : struct + // (T?, ...) where T : struct + // (object, ...) + // (ReadOnlySpan, ...) + // (string, ...) + // but this also has shortcomings, most importantly: + // - If you have an unconstrained T that happens to be a value type, it'll now end up getting boxed to use the object overload. + // This also necessitates the T? overload, since nullable value types don't meet a T : struct constraint, so without those + // they'd all map to the object overloads as well. + // - Any reference type with an implicit cast to ROS will fail to compile due to ambiguities between the overloads. string + // is one such type, hence needing dedicated overloads for it that can be bound to more tightly. + // + // A middle ground we've settled on, which is likely to be the right approach for most other handlers as well, would be the set: + // (T, ...) with no constraint + // (ReadOnlySpan) and (ReadOnlySpan, int) + // (object, int alignment = 0, string? format = null) + // (string) and (string, int) + // This would address most of the concerns, at the expense of: + // - Most reference types going through the generic code paths and so being a bit more expensive. + // - Nullable types being more expensive until https://github.com/dotnet/runtime/issues/50915 is addressed. + // We could choose to add a T? where T : struct set of overloads if necessary. + // Strings don't require their own overloads here, but as they're expected to be very common and as we can + // optimize them in several ways (can copy the contents directly, don't need to do any interface checks, don't + // need to pay the shared generic overheads, etc.) we can add overloads specifically to optimize for them. + // + // Hole values are formatted according to the following policy: + // 1. If an IFormatProvider was supplied and it provides an ICustomFormatter, use ICustomFormatter.Format (even if the value is null). + // 2. If the type implements ISpanFormattable, use ISpanFormattable.TryFormat. + // 3. If the type implements IFormattable, use IFormattable.ToString. + // 4. Otherwise, use object.ToString. + // This matches the behavior of string.Format, StringBuilder.AppendFormat, etc. The only overloads for which this doesn't + // apply is ReadOnlySpan, which isn't supported by either string.Format nor StringBuilder.AppendFormat, but more + // importantly which can't be boxed to be passed to ICustomFormatter.Format. + + #region AppendFormatted T + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(T value) + { + // This method could delegate to AppendFormatted with a null format, but explicitly passing + // default as the format to TryFormat helps to improve code quality in some cases when TryFormat is inlined, + // e.g. for Int32 it enables the JIT to eliminate code in the inlined method based on a length check on the format. + + // If there's a custom formatter, always use it. + if (_hasCustomFormatter) + { + AppendCustomFormatter(value, format: null); + return; + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, default, _provider)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format: null, _provider); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public void AppendFormatted(T value, string? format) + { + // If there's a custom formatter, always use it. + if (_hasCustomFormatter) + { + AppendCustomFormatter(value, format); + return; + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, format, _provider)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format, _provider); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment) + { + int startingPos = _pos; + AppendFormatted(value); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment, string? format) + { + int startingPos = _pos; + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + #endregion + + #region AppendFormatted ReadOnlySpan + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) + { + // Fast path for when the value fits in the current buffer + if (value.TryCopyTo(_chars[_pos..])) + { + _pos += value.Length; + } + else + { + GrowThenCopySpan(value); + } + } + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + // The value is as large or larger than the required amount of padding, + // so just write the value. + AppendFormatted(value); + return; + } + + // Write the value along with the appropriate padding. + EnsureCapacityForAdditionalChars(value.Length + paddingRequired); + if (leftAlign) + { + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + _chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + } + else + { + _chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + } + #endregion + + #region AppendFormatted string + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) + { + // Fast-path for no custom formatter and a non-null string that fits in the current destination buffer. + if (!_hasCustomFormatter && value?.TryCopyTo(_chars[_pos..]) == true) + { + _pos += value.Length; + } + else + { + AppendFormattedSlow(value); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// + /// Slow path to handle a custom formatter, potentially null value, + /// or a string that doesn't fit in the current buffer. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendFormattedSlow(string? value) + { + if (_hasCustomFormatter) + { + AppendCustomFormatter(value, format: null); + } + else if (value is not null) + { + EnsureCapacityForAdditionalChars(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => + // Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload + // simply to disambiguate between ROS and object, just in case someone does specify a format, as + // string is implicitly convertible to both. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + + #region AppendFormatted object + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => + // This overload is expected to be used rarely, only if either a) something strongly typed as object is + // formatted with both an alignment and a format, or b) the compiler is unable to target type to T. It + // exists purely to help make cases from (b) compile. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + #endregion + + /// Gets whether the provider provides a custom formatter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used in a few hot path call sites + internal static bool HasCustomFormatter(IFormatProvider provider) + { + Debug.Assert(provider is not null); + Debug.Assert(provider is not CultureInfo || provider.GetFormat(typeof(ICustomFormatter)) is null, "Expected CultureInfo to not provide a custom formatter"); + return + provider.GetType() != typeof(CultureInfo) && // optimization to avoid GetFormat in the majority case + provider.GetFormat(typeof(ICustomFormatter)) != null; + } + + /// Formats the value using the custom formatter from the provider. + /// The value to write. + /// The format string. + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendCustomFormatter(T value, string? format) + { + // This case is very rare, but we need to handle it prior to the other checks in case + // a provider was used that supplied an ICustomFormatter which wanted to intercept the particular value. + // We do the cast here rather than in the ctor, even though this could be executed multiple times per + // formatting, to make the cast pay for play. + Debug.Assert(_hasCustomFormatter); + Debug.Assert(_provider != null); + + ICustomFormatter? formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); + Debug.Assert(formatter != null, "An incorrectly written provider said it implemented ICustomFormatter, and then didn't"); + + if (formatter?.Format(format, value, _provider) is string customFormatted) + { + AppendStringDirect(customFormatted); + } + } + + /// Handles adding any padding required for aligning a formatted value in an interpolation expression. + /// The position at which the written value started. + /// Non-zero minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) + { + Debug.Assert(startingPos >= 0 && startingPos <= _pos); + Debug.Assert(alignment != 0); + + int charsWritten = _pos - startingPos; + + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingNeeded = alignment - charsWritten; + if (paddingNeeded > 0) + { + EnsureCapacityForAdditionalChars(paddingNeeded); + + if (leftAlign) + { + _chars.Slice(_pos, paddingNeeded).Fill(' '); + } + else + { + _chars.Slice(startingPos, charsWritten).CopyTo(_chars[(startingPos + paddingNeeded)..]); + _chars.Slice(startingPos, paddingNeeded).Fill(' '); + } + + _pos += paddingNeeded; + } + } + + /// Ensures has the capacity to store beyond . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureCapacityForAdditionalChars(int additionalChars) + { + if (_chars.Length - _pos < additionalChars) + { + Grow(additionalChars); + } + } + + /// Fallback for fast path in when there's not enough space in the destination. + /// The string to write. + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopyString(string value) + { + Grow(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + + /// Fallback for for when not enough space exists in the current buffer. + /// The span to write. + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopySpan(ReadOnlySpan value) + { + Grow(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + + /// Grows to have the capacity to store at least beyond . + [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible + private void Grow(int additionalChars) + { + // This method is called when the remaining space (_chars.Length - _pos) is + // insufficient to store a specific number of additional characters. Thus, we + // need to grow to at least that new total. GrowCore will handle growing by more + // than that if possible. + Debug.Assert(additionalChars > _chars.Length - _pos); + GrowCore((uint)_pos + (uint)additionalChars); + } + + /// Grows the size of . + [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible + private void Grow() + { + // This method is called when the remaining space in _chars isn't sufficient to continue + // the operation. Thus, we need at least one character beyond _chars.Length. GrowCore + // will handle growing by more than that if possible. + GrowCore((uint)_chars.Length + 1); + } + + /// Grow the size of to at least the specified . + [MethodImpl(MethodImplOptions.AggressiveInlining)] // but reuse this grow logic directly in both of the above grow routines + private void GrowCore(uint requiredMinCapacity) + { + // We want the max of how much space we actually required and doubling our capacity (without going beyond the max allowed length). We + // also want to avoid asking for small arrays, to reduce the number of times we need to grow, and since we're working with unsigned + // ints that could technically overflow if someone tried to, for example, append a huge string to a huge string, we also clamp to int.MaxValue. + // Even if the array creation fails in such a case, we may later fail in ToStringAndClear. + + uint newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_chars.Length * 2, 0x3FFFFFDF)); + int arraySize = (int)Math.Clamp(newCapacity, MinimumArrayPoolLength, int.MaxValue); + + char[] newArray = STArrayPool.Shared.Rent(arraySize); + _chars[.._pos].CopyTo(newArray); + + char[]? toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = newArray; + + if (toReturn is not null) + { + STArrayPool.Shared.Return(toReturn); + } + } +} diff --git a/Projects/Server/Buffers/STArrayPool.cs b/Projects/Server/Buffers/STArrayPool.cs new file mode 100644 index 000000000..a9ff65492 --- /dev/null +++ b/Projects/Server/Buffers/STArrayPool.cs @@ -0,0 +1,337 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Server.Buffers; + +/** + * Adaptation of the ArrayPool.Shared (TlsOverPerCoreLockedStacksArrayPool) for single threaded *unsafe* usage. + */ +public class STArrayPool : ArrayPool +{ + private const int StackArraySize = 8; + private const int BucketCount = 27; // SelectBucketIndex(1024 * 1024 * 1024 + 1) + private static readonly STArrayPool _shared = new(); + + public static STArrayPool Shared => _shared; + + private int _trimCallbackCreated; + private static STArray[] _cacheBuckets; + private STArrayStack[] _buckets = new STArrayStack[BucketCount]; + + private STArrayPool() {} + + public override T[] Rent(int minimumLength) + { + T[] buffer; + + var bucketIndex = SelectBucketIndex(minimumLength); + var cachedBuckets = _cacheBuckets; + if (cachedBuckets is not null && (uint)bucketIndex < (uint)cachedBuckets.Length) + { + buffer = cachedBuckets[bucketIndex].Array; + if (buffer is not null) + { + cachedBuckets[bucketIndex].Array = null; + return buffer; + } + } + + var buckets = _buckets; + if ((uint)bucketIndex < (uint)buckets.Length) + { + var b = buckets[bucketIndex]; + if (b is not null) + { + buffer = b.TryPop(); + if (buffer is not null) + { + return buffer; + } + } + + minimumLength = GetMaxSizeForBucket(bucketIndex); + } + + if (minimumLength == 0) + { + // We aren't renting. + return Array.Empty(); + } + + if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + } + + buffer = GC.AllocateUninitializedArray(minimumLength); + return buffer; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (array is null) + { + return; + } + + var bucketIndex = SelectBucketIndex(array.Length); + var cacheBuckets = _cacheBuckets ?? InitializeBuckets(); + + if ((uint)bucketIndex < (uint)_cacheBuckets!.Length) + { + if (clearArray) + { + Array.Clear(array); + } + + if (array.Length != GetMaxSizeForBucket(bucketIndex)) + { + throw new ArgumentException("Buffer is not from the pool", nameof(array)); + } + + ref var bucketArray = ref cacheBuckets[bucketIndex]; + var prev = bucketArray.Array; + bucketArray = new STArray(array); + if (prev is not null) + { + var bucket = _buckets[bucketIndex] ?? CreateBucketStack(bucketIndex); + bucket.TryPush(prev); + } + } + } + + public void ResetForTesting() + { + if (Core.IsRunningFromXUnit) + { + _cacheBuckets = null; + _buckets = new STArrayStack[BucketCount]; + } + } + + public bool Trim() + { + var ticks = Core.TickCount; + var pressure = GetMemoryPressure(); + + var buckets = _buckets; + for (var i = 0; i < buckets.Length; i++) + { + buckets[i]?.Trim(ticks, pressure, GetMaxSizeForBucket(i)); + } + + if (_cacheBuckets == null) + { + return true; + } + + // Under high pressure, release all cached buckets + if (pressure == MemoryPressure.High) + { + Array.Clear(_cacheBuckets); + } + else + { + uint threshold = pressure switch + { + MemoryPressure.Medium => 10000, + _ => 30000, + }; + + var cacheBuckets = _cacheBuckets; + for (var i = 0; i < cacheBuckets.Length; i++) + { + ref var b = ref cacheBuckets[i]; + + if (b.Array is null) + { + continue; + } + + var lastSeen = b.Ticks; + if (lastSeen == 0) + { + b.Ticks = ticks; + } + else if (ticks - lastSeen >= threshold) + { + b.Array = null; + } + } + } + + return true; + } + + private STArrayStack CreateBucketStack(int bucketIndex) + { + return _buckets[bucketIndex] = new STArrayStack(); + } + + private STArray[] InitializeBuckets() + { + Debug.Assert(_cacheBuckets is null, $"Non-null {nameof(_cacheBuckets)}"); + var buckets = new STArray[BucketCount]; + + if (Interlocked.Exchange(ref _trimCallbackCreated, 1) == 0) + { + Gen2GcCallback.Register(o => ((STArrayPool)o).Trim(), this); + } + + return _cacheBuckets = buckets; + } + + // Buffers are bucketed so that a request between 2^(n-1) + 1 and 2^n is given a buffer of 2^n + // Bucket index is log2(bufferSize - 1) with the exception that buffers between 1 and 16 bytes + // are combined, and the index is slid down by 3 to compensate. + // Zero is a valid bufferSize, and it is assigned the highest bucket index so that zero-length + // buffers are not retained by the pool. The pool will return the Array.Empty singleton for these. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SelectBucketIndex(int bufferSize) => BitOperations.Log2((uint)bufferSize - 1 | 15) - 3; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int GetMaxSizeForBucket(int binIndex) + { + int maxSize = 16 << binIndex; + Debug.Assert(maxSize >= 0); + return maxSize; + } + + internal enum MemoryPressure + { + Low, + Medium, + High + } + + internal static MemoryPressure GetMemoryPressure() + { + GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); + + if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * 0.90) + { + return MemoryPressure.High; + } + + if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * 0.70) + { + return MemoryPressure.Medium; + } + + return MemoryPressure.Low; + } + + private sealed class STArrayStack + { + // Maximum buffers we will store in our stack + private readonly T[][] _arrays = new T[StackArraySize * Environment.ProcessorCount][]; + private int _count; + private long _ticks; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPush(T[] array) + { + var arrays = _arrays; + var count = _count; + if ((uint)count < (uint)_arrays.Length) + { + arrays[count] = array; + _count = count + 1; + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] TryPop() + { + var arrays = _arrays; + var count = _count - 1; + if ((uint)count < (uint)arrays.Length) + { + var arr = arrays[count]; + arrays[count] = null; + _count = count; + return arr; + } + + return null; + } + + public void Trim(long now, MemoryPressure pressure, int bucketSize) + { + if (_count == 0) + { + return; + } + + // 10 seconds under high pressure, otherwise 60 seconds + var threshold = pressure == MemoryPressure.High ? 10000 : 60000; + + if (_ticks == 0) + { + _ticks = now; + return; + } + + if (now - _ticks <= threshold) + { + return; + } + + int trimCount = 1; + switch (pressure) + { + case MemoryPressure.Medium: + { + trimCount = 2; + break; + } + case MemoryPressure.High: + { + if (bucketSize > 16384) + { + trimCount++; + } + + var size = Unsafe.SizeOf(); + if (size > 32) + { + trimCount += 2; + } + else if (size > 16) + { + trimCount++; + } + + break; + } + } + + while (_count > 0 && trimCount-- > 0) + { + _arrays[--_count] = null; + } + } + } + + private struct STArray + { + public T[] Array; + public long Ticks; + + public STArray(T[] array) + { + Array = array; + Ticks = 0; + } + } +} diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index d9459c8f2..cbff16597 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -166,7 +166,7 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1) { - int sizeT = TextEncoding.GetByteLengthForEncoding(encoding); + int byteLength = encoding.GetByteLengthForEncoding(); bool isFixedLength = fixedLength > -1; @@ -174,7 +174,7 @@ namespace System.Buffers int size; if (isFixedLength) { - size = fixedLength * sizeT; + size = fixedLength * byteLength; if (size > Remaining) { throw new OutOfMemoryException(); @@ -183,8 +183,8 @@ namespace System.Buffers else { // In case the remaining is not evenly divisible - size = remaining - (remaining & (sizeT - 1)); - int index = _buffer.Slice(Position, size).IndexOfTerminator(sizeT); + size = remaining - (remaining & (byteLength - 1)); + int index = _buffer.Slice(Position, size).IndexOfTerminator(byteLength); size = index < 0 ? size : index; } diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs index 607e226a2..937db1c3e 100644 --- a/Projects/Server/Buffers/SpanWriter.cs +++ b/Projects/Server/Buffers/SpanWriter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpanWriter.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using System.Buffers.Binary; -using System.Data; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; @@ -22,40 +21,41 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.Toolkit.HighPerformance; using Server; +using Server.Buffers; using Server.Text; -namespace System.Buffers +namespace System.Buffers; + +public ref struct SpanWriter { - public ref struct SpanWriter + private readonly bool _resize; + private byte[] _arrayToReturnToPool; + private Span _buffer; + private int _position; + + public int BytesWritten { get; private set; } + + public int Position { - private readonly bool _resize; - private byte[] _arrayToReturnToPool; - private Span _buffer; - private int _position; - - public int BytesWritten { get; private set; } - - public int Position + get => _position; + private set { - get => _position; - private set - { - _position = value; + _position = value; - if (value > BytesWritten) - { - BytesWritten = value; - } + if (value > BytesWritten) + { + BytesWritten = value; } } + } - public int Capacity => _buffer.Length; + public int Capacity => _buffer.Length; - public ReadOnlySpan Span => _buffer[..Position]; + public ReadOnlySpan Span => _buffer[..Position]; - public Span RawBuffer => _buffer; + public Span RawBuffer => _buffer; - /** + /** * Converts the writer to a Span using a SpanOwner. * If the buffer was stackalloc, it will be copied to a rented buffer. * Otherwise the existing rented buffer is used. @@ -64,395 +64,425 @@ namespace System.Buffers * Do not use the SpanWriter after calling this method. * This method will effectively dispose of the SpanWriter and is therefore considered terminal. */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SpanOwner ToSpan() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SpanOwner ToSpan() + { + var toReturn = _arrayToReturnToPool; + + SpanOwner apo; + if (_position == 0) { - var toReturn = _arrayToReturnToPool; - - SpanOwner apo; - if (_position == 0) - { - apo = new SpanOwner(_position, Array.Empty()); - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } - } - else if (toReturn != null) - { - apo = new SpanOwner(_position, toReturn); - } - else - { - var buffer = ArrayPool.Shared.Rent(_position); - _buffer.CopyTo(buffer); - apo = new SpanOwner(_position, buffer); - } - - this = default; // Don't allow two references to the same buffer - return apo; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SpanWriter(Span initialBuffer, bool resize = false) - { - _resize = resize; - _buffer = initialBuffer; - _position = 0; - BytesWritten = 0; - _arrayToReturnToPool = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SpanWriter(int initialCapacity, bool resize = false) - { - _resize = resize; - _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity); - _buffer = _arrayToReturnToPool; - _position = 0; - BytesWritten = 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void Grow(int additionalCapacity) - { - var newSize = Math.Max(BytesWritten + additionalCapacity, _buffer.Length * 2); - byte[] poolArray = ArrayPool.Shared.Rent(newSize); - - _buffer[..BytesWritten].CopyTo(poolArray); - - byte[] toReturn = _arrayToReturnToPool; - _buffer = _arrayToReturnToPool = poolArray; + apo = new SpanOwner(_position, Array.Empty()); if (toReturn != null) { - ArrayPool.Shared.Return(toReturn); + STArrayPool.Shared.Return(toReturn); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void GrowIfNeeded(int count) + else if (toReturn != null) { - if (_position + count > _buffer.Length) + apo = new SpanOwner(_position, toReturn); + } + else + { + var buffer = STArrayPool.Shared.Rent(_position); + _buffer.CopyTo(buffer); + apo = new SpanOwner(_position, buffer); + } + + this = default; // Don't allow two references to the same buffer + return apo; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SpanWriter(Span initialBuffer, bool resize = false) + { + _resize = resize; + _buffer = initialBuffer; + _position = 0; + BytesWritten = 0; + _arrayToReturnToPool = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SpanWriter(int initialCapacity, bool resize = false) + { + _resize = resize; + _arrayToReturnToPool = STArrayPool.Shared.Rent(initialCapacity); + _buffer = _arrayToReturnToPool; + _position = 0; + BytesWritten = 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacity) + { + var newSize = Math.Max(BytesWritten + additionalCapacity, _buffer.Length * 2); + byte[] poolArray = STArrayPool.Shared.Rent(newSize); + + _buffer[..BytesWritten].CopyTo(poolArray); + + byte[] toReturn = _arrayToReturnToPool; + _buffer = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + STArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void GrowIfNeeded(int count) + { + if (_position + count > _buffer.Length) + { + if (!_resize) { - if (!_resize) - { - throw new OutOfMemoryException(); - } - - Grow(count); - } - } - - public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(_buffer); - - public void EnsureCapacity(int capacity) - { - if (capacity > _buffer.Length) - { - if (!_resize) - { - throw new OutOfMemoryException(); - } - - Grow(capacity - BytesWritten); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void Write(bool value) - { - GrowIfNeeded(1); - _buffer[Position++] = *(byte*)&value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(byte value) - { - GrowIfNeeded(1); - _buffer[Position++] = value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(sbyte value) - { - GrowIfNeeded(1); - _buffer[Position++] = (byte)value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(short value) - { - GrowIfNeeded(2); - BinaryPrimitives.WriteInt16BigEndian(_buffer[_position..], value); - Position += 2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLE(short value) - { - GrowIfNeeded(2); - BinaryPrimitives.WriteInt16LittleEndian(_buffer[_position..], value); - Position += 2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(ushort value) - { - GrowIfNeeded(2); - BinaryPrimitives.WriteUInt16BigEndian(_buffer[_position..], value); - Position += 2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLE(ushort value) - { - GrowIfNeeded(2); - BinaryPrimitives.WriteUInt16LittleEndian(_buffer[_position..], value); - Position += 2; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(int value) - { - GrowIfNeeded(4); - BinaryPrimitives.WriteInt32BigEndian(_buffer[_position..], value); - Position += 4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLE(int value) - { - GrowIfNeeded(4); - BinaryPrimitives.WriteInt32LittleEndian(_buffer[_position..], value); - Position += 4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(uint value) - { - GrowIfNeeded(4); - BinaryPrimitives.WriteUInt32BigEndian(_buffer[_position..], value); - Position += 4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Serial serial) => Write(serial.Value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLE(uint value) - { - GrowIfNeeded(4); - BinaryPrimitives.WriteUInt32LittleEndian(_buffer[_position..], value); - Position += 4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(long value) - { - GrowIfNeeded(8); - BinaryPrimitives.WriteInt64BigEndian(_buffer[_position..], value); - Position += 8; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(ulong value) - { - GrowIfNeeded(8); - BinaryPrimitives.WriteUInt64BigEndian(_buffer[_position..], value); - Position += 8; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(ReadOnlySpan buffer) - { - var count = buffer.Length; - GrowIfNeeded(count); - buffer.CopyTo(_buffer[_position..]); - Position += count; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAscii(char chr) => Write((byte)chr); - - public void WriteString(string value, Encoding encoding, int fixedLength = -1) where T : struct, IEquatable - { - int sizeT = Unsafe.SizeOf(); - - if (sizeT > 2) - { - throw new InvalidConstraintException("WriteString only accepts byte, sbyte, char, short, and ushort as a constraint"); + throw new OutOfMemoryException(); } - value ??= string.Empty; + Grow(count); + } + } - var charLength = Math.Min(fixedLength > -1 ? fixedLength : value.Length, value.Length); - var src = value.AsSpan(0, charLength); + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(_buffer); - var byteCount = fixedLength > -1 ? fixedLength * sizeT : encoding.GetByteCount(value); - if (byteCount == 0) + public void EnsureCapacity(int capacity) + { + if (capacity > _buffer.Length) + { + if (!_resize) { - return; + throw new OutOfMemoryException(); } - GrowIfNeeded(byteCount); + Grow(capacity - BytesWritten); + } + } - var bytesWritten = encoding.GetBytes(src, _buffer[_position..]); - Position += bytesWritten; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Write(bool value) + { + GrowIfNeeded(1); + _buffer[Position++] = *(byte*)&value; + } - if (fixedLength > -1) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(byte value) + { + GrowIfNeeded(1); + _buffer[Position++] = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(sbyte value) + { + GrowIfNeeded(1); + _buffer[Position++] = (byte)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(short value) + { + GrowIfNeeded(2); + BinaryPrimitives.WriteInt16BigEndian(_buffer[_position..], value); + Position += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLE(short value) + { + GrowIfNeeded(2); + BinaryPrimitives.WriteInt16LittleEndian(_buffer[_position..], value); + Position += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ushort value) + { + GrowIfNeeded(2); + BinaryPrimitives.WriteUInt16BigEndian(_buffer[_position..], value); + Position += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLE(ushort value) + { + GrowIfNeeded(2); + BinaryPrimitives.WriteUInt16LittleEndian(_buffer[_position..], value); + Position += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(int value) + { + GrowIfNeeded(4); + BinaryPrimitives.WriteInt32BigEndian(_buffer[_position..], value); + Position += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLE(int value) + { + GrowIfNeeded(4); + BinaryPrimitives.WriteInt32LittleEndian(_buffer[_position..], value); + Position += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(uint value) + { + GrowIfNeeded(4); + BinaryPrimitives.WriteUInt32BigEndian(_buffer[_position..], value); + Position += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(Serial serial) => Write(serial.Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLE(uint value) + { + GrowIfNeeded(4); + BinaryPrimitives.WriteUInt32LittleEndian(_buffer[_position..], value); + Position += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(long value) + { + GrowIfNeeded(8); + BinaryPrimitives.WriteInt64BigEndian(_buffer[_position..], value); + Position += 8; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ulong value) + { + GrowIfNeeded(8); + BinaryPrimitives.WriteUInt64BigEndian(_buffer[_position..], value); + Position += 8; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ReadOnlySpan buffer) + { + var count = buffer.Length; + GrowIfNeeded(count); + buffer.CopyTo(_buffer[_position..]); + Position += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteAscii(char chr) => Write((byte)chr); + + public void WriteAscii( + ref RawInterpolatedStringHandler handler) + { + Write(handler.Text, Encoding.ASCII); + handler.Clear(); + } + + public void WriteAscii( + IFormatProvider? formatProvider, + [InterpolatedStringHandlerArgument("formatProvider")] + ref RawInterpolatedStringHandler handler) + { + Write(handler.Text, Encoding.ASCII); + handler.Clear(); + } + + public void Write( + Encoding encoding, + ref RawInterpolatedStringHandler handler) + { + Write(handler.Text, encoding); + handler.Clear(); + } + + public void Write( + Encoding encoding, + IFormatProvider? formatProvider, + [InterpolatedStringHandlerArgument("formatProvider")] + ref RawInterpolatedStringHandler handler) + { + Write(handler.Text, encoding); + handler.Clear(); + } + + public void Write(ReadOnlySpan value, Encoding encoding, int fixedLength = -1) + { + var charLength = Math.Min(fixedLength > -1 ? fixedLength : value.Length, value.Length); + var src = value[..charLength]; + + var byteLength = encoding.GetByteLengthForEncoding(); + var byteCount = encoding.GetByteCount(src); + if (fixedLength > src.Length) + { + byteCount += (fixedLength - src.Length) * byteLength; + } + + if (byteCount == 0) + { + return; + } + + GrowIfNeeded(byteCount); + + var bytesWritten = encoding.GetBytes(src, _buffer[_position..]); + Position += bytesWritten; + + if (fixedLength > -1) + { + var extra = fixedLength * byteLength - bytesWritten; + if (extra > 0) { - var extra = fixedLength * sizeT - bytesWritten; - if (extra > 0) - { - Clear(extra); - } + Clear(extra); } } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLittleUni(string value) => WriteString(value, TextEncoding.UnicodeLE); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLittleUni(string value) => Write(value, TextEncoding.UnicodeLE); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLittleUniNull(string value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLittleUniNull(string value) + { + Write(value, TextEncoding.UnicodeLE); + Write((ushort)0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLittleUni(string value, int fixedLength) => Write(value, TextEncoding.UnicodeLE, fixedLength); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBigUni(string value) => Write(value, TextEncoding.Unicode); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBigUniNull(string value) + { + Write(value, TextEncoding.Unicode); + Write((ushort)0); // '\0' + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBigUni(string value, int fixedLength) => Write(value, TextEncoding.Unicode, fixedLength); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUTF8(string value) => Write(value, TextEncoding.UTF8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUTF8Null(string value) + { + Write(value, TextEncoding.UTF8); + Write((byte)0); // '\0' + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteAscii(string value) => Write(value, Encoding.ASCII); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteAsciiNull(string value) + { + Write(value, Encoding.ASCII); + Write((byte)0); // '\0' + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteAscii(string value, int fixedLength) => Write(value, Encoding.ASCII, fixedLength); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear(int count) + { + GrowIfNeeded(count); + _buffer.Slice(_position, count).Clear(); + Position += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Seek(int offset, SeekOrigin origin) + { + Debug.Assert( + origin != SeekOrigin.End || _resize || offset <= 0, + "Attempting to seek to a position beyond capacity using SeekOrigin.End without resize" + ); + + Debug.Assert( + origin != SeekOrigin.End || offset >= -_buffer.Length, + + "Attempting to seek to a negative position using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || _resize || offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin without resize" + ); + + Debug.Assert( + origin != SeekOrigin.Current || _position + offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Current" + ); + + Debug.Assert( + origin != SeekOrigin.Current || _resize || _position + offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Current without resize" + ); + + var newPosition = Math.Max(0, origin switch { - WriteString(value, TextEncoding.UnicodeLE); - Write((ushort)0); + SeekOrigin.Current => _position + offset, + SeekOrigin.End => BytesWritten + offset, + _ => offset // Begin + }); + + if (newPosition >= _buffer.Length) + { + Grow(newPosition - _buffer.Length + 1); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLittleUni(string value, int fixedLength) => WriteString(value, TextEncoding.UnicodeLE, fixedLength); + return Position = newPosition; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBigUni(string value) => WriteString(value, TextEncoding.Unicode); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBigUniNull(string value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + byte[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) { - WriteString(value, TextEncoding.Unicode); - Write((ushort)0); // '\0' + STArrayPool.Shared.Return(toReturn); + } + } + + public struct SpanOwner : IDisposable + { + private readonly int _length; + private readonly byte[] _arrayToReturnToPool; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal SpanOwner(int length, byte[] buffer) + { + _length = length; + _arrayToReturnToPool = buffer; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBigUni(string value, int fixedLength) => WriteString(value, TextEncoding.Unicode, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUTF8(string value) => WriteString(value, TextEncoding.UTF8); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUTF8Null(string value) + public Span Span { - WriteString(value, TextEncoding.UTF8); - Write((byte)0); // '\0' - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAscii(string value) => WriteString(value, Encoding.ASCII); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAsciiNull(string value) - { - WriteString(value, Encoding.ASCII); - Write((byte)0); // '\0' - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAscii(string value, int fixedLength) => WriteString(value, Encoding.ASCII, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear(int count) - { - GrowIfNeeded(count); - _buffer.Slice(_position, count).Clear(); - Position += count; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Seek(int offset, SeekOrigin origin) - { - Debug.Assert( - origin != SeekOrigin.End || _resize || offset <= 0, - "Attempting to seek to a position beyond capacity using SeekOrigin.End without resize" - ); - - Debug.Assert( - origin != SeekOrigin.End || offset >= -_buffer.Length, - - "Attempting to seek to a negative position using SeekOrigin.End" - ); - - Debug.Assert( - origin != SeekOrigin.Begin || offset >= 0, - "Attempting to seek to a negative position using SeekOrigin.Begin" - ); - - Debug.Assert( - origin != SeekOrigin.Begin || _resize || offset <= _buffer.Length, - "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin without resize" - ); - - Debug.Assert( - origin != SeekOrigin.Current || _position + offset >= 0, - "Attempting to seek to a negative position using SeekOrigin.Current" - ); - - Debug.Assert( - origin != SeekOrigin.Current || _resize || _position + offset <= _buffer.Length, - "Attempting to seek to a position beyond the capacity using SeekOrigin.Current without resize" - ); - - var newPosition = Math.Max(0, origin switch - { - SeekOrigin.Current => _position + offset, - SeekOrigin.End => BytesWritten + offset, - _ => offset // Begin - }); - - if (newPosition >= _buffer.Length) - { - Grow(newPosition - _buffer.Length + 1); - } - - return Position = newPosition; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => MemoryMarshal.CreateSpan(ref _arrayToReturnToPool.DangerousGetReference(), _length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { byte[] toReturn = _arrayToReturnToPool; - this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again - if (toReturn != null) + this = default; + if (_length > 0) { - ArrayPool.Shared.Return(toReturn); - } - } - - public struct SpanOwner : IDisposable - { - private readonly int _length; - private readonly byte[] _arrayToReturnToPool; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal SpanOwner(int length, byte[] buffer) - { - _length = length; - _arrayToReturnToPool = buffer; - } - - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => MemoryMarshal.CreateSpan(ref _arrayToReturnToPool.DangerousGetReference(), _length); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - byte[] toReturn = _arrayToReturnToPool; - this = default; - if (_length > 0) - { - ArrayPool.Shared.Return(toReturn); - } + STArrayPool.Shared.Return(toReturn); } } } diff --git a/Projects/Server/Buffers/ValueStringBuilder.cs b/Projects/Server/Buffers/ValueStringBuilder.cs index 1beda97f8..79e1b687a 100644 --- a/Projects/Server/Buffers/ValueStringBuilder.cs +++ b/Projects/Server/Buffers/ValueStringBuilder.cs @@ -1,474 +1,613 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable using System; using System.Buffers; -using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Server.Buffers +namespace Server.Buffers; + +public ref struct ValueStringBuilder { - public ref struct ValueStringBuilder + private char[] _arrayToReturnToPool; + private Span _chars; + private int _length; + private bool _mt; + + private ArrayPool ArrayPool { - private char[] _arrayToReturnToPool; - private Span _chars; - private int _length; - - // If this ctor is used, you cannot pass in stackalloc ROS for append/replace. - public ValueStringBuilder(ReadOnlySpan initialString) : this(initialString.Length) - { - Append(initialString); - } - - public ValueStringBuilder(ReadOnlySpan initialString, Span initialBuffer) : this(initialBuffer) - { - Append(initialString); - } - - public ValueStringBuilder(Span initialBuffer) - { - _arrayToReturnToPool = null; - _chars = initialBuffer; - _length = 0; - } - - // If this ctor is used, you cannot pass in stackalloc ROS for append/replace. - public ValueStringBuilder(int initialCapacity) - { - _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity); - _chars = _arrayToReturnToPool; - _length = 0; - } - - public int Length => _length; - - public int Capacity => _chars.Length; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Reset() + get => _mt ? ArrayPool.Shared : STArrayPool.Shared; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueStringBuilder Create(int capacity = 64, bool mt = false) => new(capacity, mt); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueStringBuilder CreateMT(int capacity = 64) => new(capacity, true); + + // If this ctor is used, you cannot pass in stackalloc ROS for append/replace. + public ValueStringBuilder(ReadOnlySpan initialString, bool mt = false) : this(initialString.Length, mt) + { + Append(initialString); + } + + public ValueStringBuilder(ReadOnlySpan initialString, Span initialBuffer, bool mt = false) : this(initialBuffer, mt) + { + Append(initialString); + } + + public ValueStringBuilder(Span initialBuffer, bool mt = false) + { + _mt = mt; + _arrayToReturnToPool = null; + _chars = initialBuffer; + _length = 0; + } + + // If this ctor is used, you cannot pass in stackalloc ROS for append/replace. + public ValueStringBuilder(int initialCapacity, bool mt = false) + { + _mt = mt; + _length = 0; + _arrayToReturnToPool = (_mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(initialCapacity); + _chars = _arrayToReturnToPool; + } + + public int Length => _length; + + public int Capacity => _chars.Length; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() + { + _length = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureCapacity(int capacity) + { + if (capacity > _chars.Length) { - _length = 0; + Grow(capacity - Length); + } + } + + /// + /// Get a pinnable reference to the builder. + /// Does not ensure there is a null char after + /// This overload is pattern matched in the C# 7.3+ compiler so you can omit + /// the explicit method call, and write eg "fixed (char* c = builder)" + /// + public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(_chars); + + /// + /// Get a pinnable reference to the builder. + /// + /// Ensures that the builder has a null char after + public ref char GetPinnableReference(bool terminate) + { + if (terminate) + { + EnsureCapacity(_length + 1); + _chars[_length] = '\0'; + } + return ref MemoryMarshal.GetReference(_chars); + } + + public ref char this[int index] => ref _chars[index]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() => _chars[.._length].ToString(); + + /// Returns the underlying storage of the builder. + public Span RawChars => _chars; + + /// + /// Returns a span around the contents of the builder. + /// + /// Ensures that the builder has a null char after + public ReadOnlySpan AsSpan(bool terminate) + { + if (terminate) + { + EnsureCapacity(_length + 1); + _chars[_length] = '\0'; + } + return _chars[.._length]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan AsSpan() => _chars[.._length]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan AsSpan(int start) => _chars[start..]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (_chars[.._length].TryCopyTo(destination)) + { + charsWritten = _length; + return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void EnsureCapacity(int capacity) + charsWritten = 0; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Insert(int index, char value, int count) + { + if (_length > _chars.Length - count) { - if (capacity > _chars.Length) - { - Grow(capacity - Length); - } + Grow(count); } - /// - /// Get a pinnable reference to the builder. - /// Does not ensure there is a null char after - /// This overload is pattern matched in the C# 7.3+ compiler so you can omit - /// the explicit method call, and write eg "fixed (char* c = builder)" - /// - public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(_chars); + int remaining = _length - index; + _chars.Slice(index, remaining).CopyTo(_chars[(index + count)..]); + _chars.Slice(index, count).Fill(value); + _length += count; + } - /// - /// Get a pinnable reference to the builder. - /// - /// Ensures that the builder has a null char after - public ref char GetPinnableReference(bool terminate) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Insert(int index, string? s) + { + if (s == null) { - if (terminate) - { - EnsureCapacity(_length + 1); - _chars[_length] = '\0'; - } - return ref MemoryMarshal.GetReference(_chars); + return; } - public ref char this[int index] => ref _chars[index]; + int count = s.Length; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => _chars[.._length].ToString(); - - /// Returns the underlying storage of the builder. - public Span RawChars => _chars; - - /// - /// Returns a span around the contents of the builder. - /// - /// Ensures that the builder has a null char after - public ReadOnlySpan AsSpan(bool terminate) + if (_length > _chars.Length - count) { - if (terminate) - { - EnsureCapacity(_length + 1); - _chars[_length] = '\0'; - } - return _chars[.._length]; + Grow(count); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan AsSpan() => _chars[.._length]; + int remaining = _length - index; + _chars.Slice(index, remaining).CopyTo(_chars[(index + count)..]); + s.AsSpan().CopyTo(_chars[index..]); + _length += count; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan AsSpan(int start) => _chars[start..]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryCopyTo(Span destination, out int charsWritten) + public void Append(T value, string? format = null) + { + if (value is IFormattable) { - if (_chars[.._length].TryCopyTo(destination)) + if (value is ISpanFormattable) { - charsWritten = _length; - return true; - } - - charsWritten = 0; - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Insert(int index, char value, int count) - { - if (_length > _chars.Length - count) - { - Grow(count); - } - - int remaining = _length - index; - _chars.Slice(index, remaining).CopyTo(_chars[(index + count)..]); - _chars.Slice(index, count).Fill(value); - _length += count; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Insert(int index, string s) - { - if (s == null) - { - return; - } - - int count = s.Length; - - if (_length > _chars.Length - count) - { - Grow(count); - } - - int remaining = _length - index; - _chars.Slice(index, remaining).CopyTo(_chars[(index + count)..]); - s.AsSpan().CopyTo(_chars[index..]); - _length += count; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Append(char c) - { - int pos = _length; - if ((uint)pos < (uint)_chars.Length) - { - _chars[pos] = c; - _length = pos + 1; - } - else - { - GrowAndAppend(c); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Append(int value, NumberFormatInfo info = null) - { - if (value >= 0) - { - Append((uint)value); - return; - } - - Append((info ?? NumberFormatInfo.CurrentInfo).NegativeSign); - Append((uint)-value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void Append(uint value) - { - int bufferLength = value.CountDigits(); - - int pos = _length; - if ((uint)pos + (uint)bufferLength >= _chars.Length) - { - Grow(bufferLength); - } - - if (bufferLength == 1) - { - _chars[pos] = (char)(value + '0'); - _length = pos + 1; - return; - } - - fixed (char* buffer = _chars[pos..]) - { - char* p = buffer + bufferLength; - do + Span destination = _chars[_length..]; + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(destination, out charsWritten, format, default)) { - value = Utility.DivRem(value, 10, out uint remainder); - *--p = (char)(remainder + '0'); - } while (value != 0); - } + Grow(1); + } - _length = pos + bufferLength; - } + if ((uint)charsWritten > (uint)destination.Length) + { + throw new FormatException("Invalid string"); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Append(string s) - { - if (s == null) - { - return; - } - - int pos = _length; - if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. - { - _chars[pos] = s[0]; - _length = pos + 1; + _length += charsWritten; } else { - AppendSlow(s); + Append(((IFormattable)value).ToString(format, default)); // constrained call avoiding boxing for value types } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AppendLine(string s) + else if (value is not null) { - if (s == null) - { - return; - } - - // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. - if (s.Length == 1) - { - Append(s[0]); - } - else - { - AppendSlow(s); - } - - Append(Environment.NewLine); + Append(value.ToString()); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void AppendSlow(string s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string? s) + { + if (s == null) { - int pos = _length; - if (pos > _chars.Length - s.Length) - { - Grow(s.Length); - } - - s.AsSpan().CopyTo(_chars[pos..]); - _length += s.Length; + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Append(char c, int count) + int pos = _length; + if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. { - if (_length > _chars.Length - count) - { - Grow(count); - } - - Span dst = _chars.Slice(_length, count); - for (int i = 0; i < dst.Length; i++) - { - dst[i] = c; - } - _length += count; + _chars[pos] = s[0]; + _length = pos + 1; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void Append(char* value, int length) + else { - int pos = _length; - if (pos > _chars.Length - length) - { - Grow(length); - } - - Span dst = _chars.Slice(_length, length); - for (int i = 0; i < dst.Length; i++) - { - dst[i] = *value++; - } - _length += length; + AppendSlow(s); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Append(ReadOnlySpan value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendLine(string? s) + { + if (s == null) { - int pos = _length; - if (pos > _chars.Length - value.Length) - { - Grow(value.Length); - } - - value.CopyTo(_chars[_length..]); - _length += value.Length; + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span AppendSpan(int length) + // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + if (s.Length == 1) { - int origPos = _length; - if (origPos > _chars.Length - length) - { - Grow(length); - } - - _length = origPos + length; - return _chars.Slice(origPos, length); + Append(s[0]); } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void GrowAndAppend(char c) + else { - Grow(1); - Append(c); + AppendSlow(s); } -#nullable enable - /// - /// Resize the internal buffer either by doubling current buffer size or - /// by adding to - /// whichever is greater. - /// - /// - /// Number of chars requested beyond current position. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private void Grow(int additionalCapacityBeyondPos) + Append(Environment.NewLine); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AppendSlow(string? s) + { + int pos = _length; + if (pos > _chars.Length - s.Length) { - char[] poolArray = ArrayPool.Shared.Rent(Math.Max(_length + additionalCapacityBeyondPos, _chars.Length * 2)); - - _chars[.._length].CopyTo(poolArray); - - char[] toReturn = _arrayToReturnToPool; - _chars = _arrayToReturnToPool = poolArray; - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } + Grow(s.Length); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() + s.AsSpan().CopyTo(_chars[pos..]); + _length += s.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c, int count) + { + if (_length > _chars.Length - count) { - char[] toReturn = _arrayToReturnToPool; - this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } + Grow(count); } + + Span dst = _chars.Slice(_length, count); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + _length += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Append(char* value, int length) + { + int pos = _length; + if (pos > _chars.Length - length) + { + Grow(length); + } + + Span dst = _chars.Slice(_length, length); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = *value++; + } + _length += length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(ReadOnlySpan value) + { + int pos = _length; + if (pos > _chars.Length - value.Length) + { + Grow(value.Length); + } + + value.CopyTo(_chars[_length..]); + _length += value.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + int origPos = _length; + if (origPos > _chars.Length - length) + { + Grow(length); + } + + _length = origPos + length; + return _chars.Slice(origPos, length); + } + + /// + /// Resize the internal buffer either by doubling current buffer size or + /// by adding to + /// whichever is greater. + /// + /// + /// Number of chars requested beyond current position. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacityBeyondPos) + { + char[] poolArray = ArrayPool.Rent(Math.Max(_length + additionalCapacityBeyondPos, _chars.Length * 2)); + + _chars[.._length].CopyTo(poolArray); + + char[] toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + if (_arrayToReturnToPool != null) + { + ArrayPool.Return(_arrayToReturnToPool); + } + + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + } #nullable restore - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ReplaceAny(ReadOnlySpan oldChars, ReadOnlySpan newChars, int startIndex, int count) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReplaceAny(ReadOnlySpan oldChars, ReadOnlySpan newChars, int startIndex, int count) + { + int currentLength = _length; + if ((uint)startIndex > (uint)currentLength) { - int currentLength = _length; - if ((uint)startIndex > (uint)currentLength) - { - throw new ArgumentOutOfRangeException(nameof(startIndex)); - } - - if (count < 0 || startIndex > currentLength - count) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - var slice = _chars; - - while (true) - { - var indexOf = slice.IndexOfAny(oldChars); - if (indexOf == -1) - { - break; - } - - var chr = slice[indexOf]; - - slice[indexOf] = newChars[oldChars.IndexOf(chr)]; - slice = slice[(indexOf + 1)..]; - } + throw new ArgumentOutOfRangeException(nameof(startIndex)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Replace(char oldChar, char newChar, int startIndex, int count) + if (count < 0 || startIndex > currentLength - count) { - int currentLength = _length; - if ((uint)startIndex > (uint)currentLength) - { - throw new ArgumentOutOfRangeException(nameof(startIndex)); - } - - if (count < 0 || startIndex > currentLength - count) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - var slice = _chars; - - while (true) - { - var indexOf = slice.IndexOf(oldChar); - if (indexOf == -1) - { - break; - } - - slice[indexOf] = newChar; - slice = slice[(indexOf + 1)..]; - } + throw new ArgumentOutOfRangeException(nameof(count)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Remove(int startIndex, int length) + var slice = _chars; + + while (true) { - if (length < 0) + var indexOf = slice.IndexOfAny(oldChars); + if (indexOf == -1) { - throw new ArgumentOutOfRangeException(nameof(length)); + break; } - if (startIndex < 0) + var chr = slice[indexOf]; + + slice[indexOf] = newChars[oldChars.IndexOf(chr)]; + slice = slice[(indexOf + 1)..]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Replace(char oldChar, char newChar, int startIndex, int count) + { + int currentLength = _length; + if ((uint)startIndex > (uint)currentLength) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + + if (count < 0 || startIndex > currentLength - count) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + var slice = _chars; + + while (true) + { + var indexOf = slice.IndexOf(oldChar); + if (indexOf == -1) { - throw new ArgumentOutOfRangeException(nameof(startIndex)); + break; } - if (length > _length - startIndex) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } + slice[indexOf] = newChar; + slice = slice[(indexOf + 1)..]; + } + } - if (startIndex == 0) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Remove(int startIndex, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + + if (length > _length - startIndex) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + if (startIndex == 0) + { + _chars = _chars[length..]; + } + else if (startIndex + length == _length) + { + _chars = _chars[..startIndex]; + } + else + { + // Somewhere in the middle, this will be slow + _chars[(startIndex + length)..].CopyTo(_chars[startIndex..]); + } + + _length -= length; + } + + /// Provides a handler used by the language compiler to append interpolated strings into instances. + [InterpolatedStringHandler] + public ref struct AppendInterpolatedStringHandler + { + // Implementation note: + // As this type is only intended to be targeted by the compiler, public APIs eschew argument validation logic + // in a variety of places, e.g. allowing a null input when one isn't expected to produce a NullReferenceException rather + // than an ArgumentNullException. + + /// The associated StringBuilder to which to append. + internal ValueStringBuilder _stringBuilder; + + /// Creates a handler used to append an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The associated StringBuilder to which to append. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public AppendInterpolatedStringHandler(int literalLength, int formattedCount, ValueStringBuilder stringBuilder) + { + _stringBuilder = stringBuilder; + } + + /// Writes the specified string to the handler. + /// The string to write. + public void AppendLiteral(string value) => _stringBuilder.Append(value); + + // Design note: + // This provides the same set of overloads and semantics as DefaultInterpolatedStringHandler. + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(T value) => _stringBuilder.Append(value); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public void AppendFormatted(T value, string? format) => _stringBuilder.Append(value, format); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment) => + AppendFormatted(value, alignment, format: null); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment, string? format) + { + if (alignment == 0) { - _chars = _chars[length..]; + // This overload is used as a fallback from several disambiguation overloads, so special-case 0. + AppendFormatted(value, format); } - else if (startIndex + length == _length) + else if (alignment < 0) { - _chars = _chars[..startIndex]; + // Left aligned: format into the handler, then append any additional padding required. + int start = _stringBuilder.Length; + AppendFormatted(value, format); + int paddingRequired = -alignment - (_stringBuilder.Length - start); + if (paddingRequired > 0) + { + _stringBuilder.Append(' ', paddingRequired); + } } else { - // Somewhere in the middle, this will be slow - _chars[(startIndex + length)..].CopyTo(_chars[startIndex..]); - } + var startingPos = _stringBuilder._length; + AppendFormatted(value, format); - _length -= length; + InsertAlignment(startingPos, alignment); + } + } + + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) => _stringBuilder.Append(value); + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + if (alignment == 0) + { + _stringBuilder.Append(value); + } + else + { + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + _stringBuilder.Append(value); + } + else if (leftAlign) + { + _stringBuilder.Append(value); + _stringBuilder.Append(' ', paddingRequired); + } + else + { + _stringBuilder.Append(' ', paddingRequired); + _stringBuilder.Append(value); + } + } + } + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) => _stringBuilder.Append(value); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => + // Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload + // simply to disambiguate between ROS and object, just in case someone does specify a format, as + // string is implicitly convertible to both. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => + // This overload is expected to be used rarely, only if either a) something strongly typed as object is + // formatted with both an alignment and a format, or b) the compiler is unable to target type to T. It + // exists purely to help make cases from (b) compile. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + + private void InsertAlignment(int startingPos, int alignment) + { + var charsWritten = _stringBuilder._length - startingPos; + + var paddingNeeded = alignment - charsWritten; + if (paddingNeeded > 0) + { + var chars = _stringBuilder._chars; + if (chars.Length - _stringBuilder._length < paddingNeeded) + { + _stringBuilder.Grow(paddingNeeded); + } + + chars.Slice(startingPos, charsWritten).CopyTo(chars[(startingPos + paddingNeeded)..]); + chars.Slice(startingPos, paddingNeeded).Fill(' '); + + _stringBuilder._length += paddingNeeded; + } } } } diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableMetadata.cs b/Projects/Server/Buffers/ValueStringBuilderExtensions.cs similarity index 60% rename from Projects/SerializationGenerator/SerializableMigration/SerializableMetadata.cs rename to Projects/Server/Buffers/ValueStringBuilderExtensions.cs index a7a8a6b81..1ec588e80 100644 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableMetadata.cs +++ b/Projects/Server/Buffers/ValueStringBuilderExtensions.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: SerializableMigration.cs * + * File: ValueStringBuilderExtensions.cs * * * * 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 * @@ -13,20 +13,19 @@ * along with this program. If not, see . * *************************************************************************/ -using System.Collections.Immutable; -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; -namespace SerializableMigration +namespace Server.Buffers; + +public static class ValueStringBuilderExtensions { - public record SerializableMetadata + // Compiler generated + public static void Append( + this ref ValueStringBuilder stringBuilder, + [InterpolatedStringHandlerArgument("stringBuilder")] + ref ValueStringBuilder.AppendInterpolatedStringHandler handler) { - [JsonPropertyName("version")] - public int Version { get; init; } - - [JsonPropertyName("type")] - public string Type { get; init; } - - [JsonPropertyName("properties")] - public ImmutableArray? Properties { get; init; } + // Reassign since the string builder stored on the interpolated handler is by-value + stringBuilder = handler._stringBuilder; } } diff --git a/Projects/Server/Client/ClientVersion.cs b/Projects/Server/Client/ClientVersion.cs new file mode 100644 index 000000000..412eb53bd --- /dev/null +++ b/Projects/Server/Client/ClientVersion.cs @@ -0,0 +1,262 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ClientVersion.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Server.Buffers; + +namespace Server; + +public enum ClientType +{ + Regular, + UOTD, + God, + SA +} + +public class ClientVersion : IComparable, IComparer +{ + public static readonly ClientVersion Version400a = new("4.0.0a"); + public static readonly ClientVersion Version407a = new("4.0.7a"); + public static readonly ClientVersion Version500a = new("5.0.0a"); + public static readonly ClientVersion Version502b = new("5.0.2b"); + public static readonly ClientVersion Version6000 = new("6.0.0.0"); + public static readonly ClientVersion Version6017 = new("6.0.1.7"); + public static readonly ClientVersion Version60142 = new("6.0.14.2"); + public static readonly ClientVersion Version7000 = new("7.0.0.0"); + public static readonly ClientVersion Version7090 = new("7.0.9.0"); + public static readonly ClientVersion Version70120 = new("7.0.12.0"); // Plant localization change + public static readonly ClientVersion Version70130 = new("7.0.13.0"); + public static readonly ClientVersion Version70160 = new("7.0.16.0"); + public static readonly ClientVersion Version70300 = new("7.0.30.0"); + public static readonly ClientVersion Version70331 = new("7.0.33.1"); + public static readonly ClientVersion Version704565 = new("7.0.45.65"); + public static readonly ClientVersion Version70500 = new("7.0.50.0"); + public static readonly ClientVersion Version70610 = new("7.0.61.0"); + + public ClientVersion(int maj, int min, int rev, int pat, ClientType type = ClientType.Regular) + { + Major = maj; + Minor = min; + Revision = rev; + Patch = pat; + Type = type; + + SourceString = Utility.Intern(ToStringImpl()); + } + + public ClientVersion(string fmt) + { + fmt = fmt.ToLower(); + SourceString = Utility.Intern(fmt); + + try + { + var br1 = fmt.IndexOfOrdinal('.'); + var br2 = fmt.IndexOf('.', br1 + 1); + + var br3 = br2 + 1; + while (br3 < fmt.Length && char.IsDigit(fmt, br3)) + { + br3++; + } + + Major = Utility.ToInt32(fmt.AsSpan()[..br1]); + Minor = Utility.ToInt32(fmt.AsSpan(br1 + 1, br2 - br1 - 1)); + Revision = Utility.ToInt32(fmt.AsSpan(br2 + 1, br3 - br2 - 1)); + + if (br3 < fmt.Length) + { + if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 + { + if (!char.IsWhiteSpace(fmt, br3)) + { + Patch = fmt[br3] - 'a' + 1; + } + } + else + { + Patch = Utility.ToInt32(fmt.AsSpan(br3 + 1, fmt.Length - br3 - 1)); + } + } + + if (fmt.InsensitiveContains("god") || fmt.InsensitiveContains("gq")) + { + Type = ClientType.God; + } + else if (fmt.InsensitiveContains("third dawn") || + fmt.InsensitiveContains("uo:td") || + fmt.InsensitiveContains("uotd") || + fmt.InsensitiveContains("uo3d") || + fmt.InsensitiveContains("uo:3d")) + { + Type = ClientType.UOTD; + } + else + { + Type = ClientType.Regular; + } + } + catch + { + Major = 0; + Minor = 0; + Revision = 0; + Patch = 0; + Type = ClientType.Regular; + } + } + + public int Major { get; } + + public int Minor { get; } + + public int Revision { get; } + + public int Patch { get; } + + public ClientType Type { get; } + + public string SourceString { get; } + + public int CompareTo(ClientVersion o) + { + if (o == null) + { + return 1; + } + + if (Major > o.Major) + { + return 1; + } + + if (Major < o.Major) + { + return -1; + } + + if (Minor > o.Minor) + { + return 1; + } + + if (Minor < o.Minor) + { + return -1; + } + + if (Revision > o.Revision) + { + return 1; + } + + if (Revision < o.Revision) + { + return -1; + } + + if (Patch > o.Patch) + { + return 1; + } + + if (Patch < o.Patch) + { + return -1; + } + + return 0; + } + + int IComparer.Compare(ClientVersion x, ClientVersion y) => Compare(x, y); + + public static bool operator ==(ClientVersion l, ClientVersion r) => Compare(l, r) == 0; + + public static bool operator !=(ClientVersion l, ClientVersion r) => Compare(l, r) != 0; + + public static bool operator >=(ClientVersion l, ClientVersion r) => Compare(l, r) >= 0; + + public static bool operator >(ClientVersion l, ClientVersion r) => Compare(l, r) > 0; + + public static bool operator <=(ClientVersion l, ClientVersion r) => Compare(l, r) <= 0; + + public static bool operator <(ClientVersion l, ClientVersion r) => Compare(l, r) < 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(Major, Minor, Revision, Patch, Type); + + public override bool Equals(object obj) + { + var v = obj as ClientVersion; + + return Major == v?.Major + && Minor == v.Minor + && Revision == v.Revision + && Patch == v.Patch + && Type == v.Type; + } + + private string ToStringImpl() + { + using var builder = new ValueStringBuilder(stackalloc char[32]); + + if (Major > 5 || Minor > 0 || Revision > 6) + { + builder.Append($"{Major}.{Minor}.{Revision}.{Patch}"); + } + else if (Patch > 0) + { + builder.Append($"{Major}.{Minor}.{Revision}{(char)('a' + (Patch - 1))}"); + } + else + { + builder.Append($"{Major}.{Minor}.{Revision}"); + } + + if (Type == ClientType.UOTD) + { + builder.Append(" uotd"); + } + + return builder.ToString(); + } + + public override string ToString() => SourceString; + + public static bool IsNull(object x) => ReferenceEquals(x, null); + + public static int Compare(ClientVersion a, ClientVersion b) + { + if (IsNull(a) && IsNull(b)) + { + return 0; + } + + if (IsNull(a)) + { + return -1; + } + + if (IsNull(b)) + { + return 1; + } + + return a.CompareTo(b); + } +} diff --git a/Projects/Server/Client/UOClient.cs b/Projects/Server/Client/UOClient.cs new file mode 100644 index 000000000..19e62b18c --- /dev/null +++ b/Projects/Server/Client/UOClient.cs @@ -0,0 +1,135 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: UOClient.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Buffers.Binary; +using System.IO; +using System.Text.Json.Serialization; +using Server.Json; +using Server.Logging; + +namespace Server; + +public static class UOClient +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(UOClient)); + + private static bool _automaticallyDetected; + + public static CUOSettings CuoSettings { get; private set; } + public static ClientVersion ServerClientVersion { get; private set; } + + public static void Load() + { + ServerClientVersion = ServerConfiguration.GetSetting("clientData.clientVersion", (ClientVersion)null); + + if (ServerClientVersion == null) + { + ServerClientVersion = DetectCUOClient() ?? DetectClassicClient(); + _automaticallyDetected = true; + } + } + + public static void Configure() + { + if (ServerClientVersion == null) + { + logger.Warning("Could not detect client version. This may cause data files to load improperly."); + return; + } + + if (_automaticallyDetected) + { + logger.Information( + CuoSettings?.ClientVersion == ServerClientVersion + ? "Automatically detected client version {ServerClientVersion} from CUO settings." + : "Automatically detected client version {ServerClientVersion}", + ServerClientVersion + ); + return; + } + + logger.Information("Manually configured to use client version {ServerClientVersion}", ServerClientVersion); + } + + private static ClientVersion DetectCUOClient() + { + var path = Core.FindDataFile("settings.json", false); + if (File.Exists(path)) + { + var settings = JsonConfig.Deserialize(path); + var file = new FileInfo(path); + + if (settings.UltimaOnlineDirectory != null) + { + settings.UltimaOnlineDirectory = PathUtility.GetFullPath(settings.UltimaOnlineDirectory, file.DirectoryName); + if (Directory.Exists(settings.UltimaOnlineDirectory)) + { + CuoSettings = settings; + } + } + + return settings.ClientVersion; + } + + return null; + } + + private static ClientVersion DetectClassicClient() + { + var path = Core.FindDataFile("client.exe", false); + + if (File.Exists(path)) + { + using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + var buffer = GC.AllocateUninitializedArray((int)fs.Length, true); + fs.Read(buffer); + // VS_VERSION_INFO (unicode) + Span vsVersionInfo = stackalloc byte[] + { + 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, + 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, + 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00, + 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00 + }; + + for (var i = 0; i < buffer.Length; i++) + { + if (vsVersionInfo.SequenceEqual(buffer.AsSpan(i, 30))) + { + var offset = i + 42; // 30 + 12 + + var minorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset)); + var majorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 2)); + var privatePart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 4)); + var buildPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 6)); + + return new ClientVersion(majorPart, minorPart, buildPart, privatePart); + } + } + } + + return null; + } + + public record CUOSettings + { + [JsonPropertyName("clientversion")] + public ClientVersion ClientVersion { get; set; } + + [JsonPropertyName("ultimaonlinedirectory")] + public string UltimaOnlineDirectory { get; set; } + } +} diff --git a/Projects/Server/ClientVersion.cs b/Projects/Server/ClientVersion.cs deleted file mode 100644 index 8325eb06a..000000000 --- a/Projects/Server/ClientVersion.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Buffers; - -namespace Server -{ - public enum ClientType - { - Regular, - UOTD, - God, - SA - } - - public class ClientVersion : IComparable, IComparer - { - public ClientVersion(int maj, int min, int rev, int pat, ClientType type = ClientType.Regular) - { - Major = maj; - Minor = min; - Revision = rev; - Patch = pat; - Type = type; - - SourceString = Utility.Intern(ToStringImpl()); - } - - public ClientVersion(string fmt) - { - fmt = fmt.ToLower(); - SourceString = Utility.Intern(fmt); - - try - { - var br1 = fmt.IndexOfOrdinal('.'); - var br2 = fmt.IndexOf('.', br1 + 1); - - var br3 = br2 + 1; - while (br3 < fmt.Length && char.IsDigit(fmt, br3)) - { - br3++; - } - - Major = Utility.ToInt32(fmt.AsSpan()[..br1]); - Minor = Utility.ToInt32(fmt.Substring(br1 + 1, br2 - br1 - 1)); - Revision = Utility.ToInt32(fmt.Substring(br2 + 1, br3 - br2 - 1)); - - if (br3 < fmt.Length) - { - if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 - { - if (!char.IsWhiteSpace(fmt, br3)) - { - Patch = fmt[br3] - 'a' + 1; - } - } - else - { - Patch = Utility.ToInt32(fmt.Substring(br3 + 1, fmt.Length - br3 - 1)); - } - } - - if (fmt.InsensitiveContains("god") || fmt.InsensitiveContains("gq")) - { - Type = ClientType.God; - } - else if (fmt.InsensitiveContains("third dawn") || - fmt.InsensitiveContains("uo:td") || - fmt.InsensitiveContains("uotd") || - fmt.InsensitiveContains("uo3d") || - fmt.InsensitiveContains("uo:3d")) - { - Type = ClientType.UOTD; - } - else - { - Type = ClientType.Regular; - } - } - catch - { - Major = 0; - Minor = 0; - Revision = 0; - Patch = 0; - Type = ClientType.Regular; - } - } - - public int Major { get; } - - public int Minor { get; } - - public int Revision { get; } - - public int Patch { get; } - - public ClientType Type { get; } - - public string SourceString { get; } - - public int CompareTo(ClientVersion o) - { - if (o == null) - { - return 1; - } - - if (Major > o.Major) - { - return 1; - } - - if (Major < o.Major) - { - return -1; - } - - if (Minor > o.Minor) - { - return 1; - } - - if (Minor < o.Minor) - { - return -1; - } - - if (Revision > o.Revision) - { - return 1; - } - - if (Revision < o.Revision) - { - return -1; - } - - if (Patch > o.Patch) - { - return 1; - } - - if (Patch < o.Patch) - { - return -1; - } - - return 0; - } - - int IComparer.Compare(ClientVersion x, ClientVersion y) => Compare(x, y); - - public static bool operator ==(ClientVersion l, ClientVersion r) => Compare(l, r) == 0; - - public static bool operator !=(ClientVersion l, ClientVersion r) => Compare(l, r) != 0; - - public static bool operator >=(ClientVersion l, ClientVersion r) => Compare(l, r) >= 0; - - public static bool operator >(ClientVersion l, ClientVersion r) => Compare(l, r) > 0; - - public static bool operator <=(ClientVersion l, ClientVersion r) => Compare(l, r) <= 0; - - public static bool operator <(ClientVersion l, ClientVersion r) => Compare(l, r) < 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(Major, Minor, Revision, Patch, Type); - - public override bool Equals(object obj) - { - var v = obj as ClientVersion; - - return Major == v?.Major - && Minor == v.Minor - && Revision == v.Revision - && Patch == v.Patch - && Type == v.Type; - } - - private string ToStringImpl() - { - using var builder = new ValueStringBuilder(stackalloc char[32]); - - builder.Append(Major.ToString()); - builder.Append('.'); - builder.Append(Minor.ToString()); - builder.Append('.'); - builder.Append(Revision.ToString()); - - if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 - { - if (Patch > 0) - { - builder.Append((char)('a' + (Patch - 1))); - } - } - else - { - builder.Append('.'); - builder.Append(Patch.ToString()); - } - - if (Type != ClientType.Regular) - { - builder.Append(' '); - builder.Append(Type.ToString().ToLower()); - } - - return builder.ToString(); - } - - public override string ToString() => SourceString; - - public static bool IsNull(object x) => ReferenceEquals(x, null); - - public static int Compare(ClientVersion a, ClientVersion b) - { - if (IsNull(a) && IsNull(b)) - { - return 0; - } - - if (IsNull(a)) - { - return -1; - } - - if (IsNull(b)) - { - return 1; - } - - return a.CompareTo(b); - } - } -} diff --git a/Projects/Server/Collections/BitArray.cs b/Projects/Server/Collections/BitArray.cs index 9df8f5441..2ea09a4c2 100644 --- a/Projects/Server/Collections/BitArray.cs +++ b/Projects/Server/Collections/BitArray.cs @@ -15,7 +15,7 @@ namespace Server.Collections; // A vector of bits. Use this to store bits efficiently, without having to do bit // shifting yourself. -[System.Serializable] +[Serializable] public sealed class BitArray : ICollection, ICloneable { private int[] m_array; // Do not rename (binary serialization) @@ -63,7 +63,7 @@ public sealed class BitArray : ICollection, ICloneable ** ** Exceptions: ArgumentException if bytes == null. =========================================================================*/ - public BitArray(byte[] bytes) + public BitArray(ReadOnlySpan bytes, int length = -1) { if (bytes == null) { @@ -79,7 +79,7 @@ public sealed class BitArray : ICollection, ICloneable } m_array = new int[GetInt32ArrayLengthFromByteLength(bytes.Length)]; - m_length = bytes.Length * BitsPerByte; + m_length = length == -1 ? bytes.Length * BitsPerByte : length; uint totalCount = (uint)bytes.Length / 4; @@ -96,16 +96,22 @@ public sealed class BitArray : ICollection, ICloneable switch (byteSpan.Length) { case 3: - last = byteSpan[2] << 16; - goto case 2; + { + last = byteSpan[2] << 16; + goto case 2; + } // fall through case 2: - last |= byteSpan[1] << 8; - goto case 1; + { + last |= byteSpan[1] << 8; + goto case 1; + } // fall through case 1: - m_array[totalCount] = last | byteSpan[0]; - break; + { + m_array[totalCount] = last | byteSpan[0]; + break; + } } _version = 0; @@ -119,63 +125,7 @@ public sealed class BitArray : ICollection, ICloneable ** ** Exceptions: ArgumentException if bytes == null. =========================================================================*/ - public BitArray(ReadOnlySpan bytes) - { - if (bytes == null) - { - throw new ArgumentNullException(nameof(bytes)); - } - - // this value is chosen to prevent overflow when computing m_length. - // m_length is of type int32 and is exposed as a property, so - // type of m_length can't be changed to accommodate. - if (bytes.Length > int.MaxValue / BitsPerByte) - { - throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_ArrayTooLarge, BitsPerByte), nameof(bytes)); - } - - m_array = new int[GetInt32ArrayLengthFromByteLength(bytes.Length)]; - m_length = bytes.Length * BitsPerByte; - - uint totalCount = (uint)bytes.Length / 4; - - ReadOnlySpan byteSpan = bytes; - for (int i = 0; i < totalCount; i++) - { - m_array[i] = BinaryPrimitives.ReadInt32LittleEndian(byteSpan); - byteSpan = byteSpan[4..]; - } - - Debug.Assert(byteSpan.Length >= 0 && byteSpan.Length < 4); - - int last = 0; - switch (byteSpan.Length) - { - case 3: - last = byteSpan[2] << 16; - goto case 2; - // fall through - case 2: - last |= byteSpan[1] << 8; - goto case 1; - // fall through - case 1: - m_array[totalCount] = last | byteSpan[0]; - break; - } - - _version = 0; - } - - /*========================================================================= - ** Allocates space to hold the bit values in bytes. bytes[0] represents - ** bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte - ** represents the lowest index value; bytes[0] & 1 represents bit 0, - ** bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc. - ** - ** Exceptions: ArgumentException if bytes == null. - =========================================================================*/ - public BitArray(BinaryReader reader, int length) + public BitArray(BinaryReader reader, int bitLength) { if (reader == null) { @@ -185,13 +135,15 @@ public sealed class BitArray : ICollection, ICloneable // this value is chosen to prevent overflow when computing m_length. // m_length is of type int32 and is exposed as a property, so // type of m_length can't be changed to accommodate. - if (length > int.MaxValue / BitsPerByte) + if (bitLength > int.MaxValue / BitsPerByte) { throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_ArrayTooLarge, BitsPerByte), nameof(reader)); } + var length = GetByteArrayLengthFromBitLength(bitLength); + m_array = new int[GetInt32ArrayLengthFromByteLength(length)]; - m_length = length * BitsPerByte; + m_length = length; uint totalCount = (uint)length / 4; @@ -207,16 +159,22 @@ public sealed class BitArray : ICollection, ICloneable switch (length) { case 3: - last = reader.ReadInt16(); - goto case 2; + { + last = reader.ReadInt16(); + goto case 2; + } // fall through case 2: - last |= reader.ReadByte(); - goto case 1; + { + last |= reader.ReadByte(); + goto case 1; + } // fall through case 1: - m_array[totalCount] = last | reader.ReadByte(); - break; + { + m_array[totalCount] = last | reader.ReadByte(); + break; + } } _version = 0; @@ -499,14 +457,38 @@ public sealed class BitArray : ICollection, ICloneable // Unroll loop for count less than Vector256 size. switch (count) { - case 7: thisArray[6] &= valueArray[6]; goto case 6; - case 6: thisArray[5] &= valueArray[5]; goto case 5; - case 5: thisArray[4] &= valueArray[4]; goto case 4; - case 4: thisArray[3] &= valueArray[3]; goto case 3; - case 3: thisArray[2] &= valueArray[2]; goto case 2; - case 2: thisArray[1] &= valueArray[1]; goto case 1; - case 1: thisArray[0] &= valueArray[0]; goto Done; - case 0: goto Done; + case 7: + { + thisArray[6] &= valueArray[6]; goto case 6; + } + case 6: + { + thisArray[5] &= valueArray[5]; goto case 5; + } + case 5: + { + thisArray[4] &= valueArray[4]; goto case 4; + } + case 4: + { + thisArray[3] &= valueArray[3]; goto case 3; + } + case 3: + { + thisArray[2] &= valueArray[2]; goto case 2; + } + case 2: + { + thisArray[1] &= valueArray[1]; goto case 1; + } + case 1: + { + thisArray[0] &= valueArray[0]; goto Done; + } + case 0: + { + goto Done; + } } uint i = 0; @@ -597,14 +579,38 @@ public sealed class BitArray : ICollection, ICloneable // Unroll loop for count less than Vector256 size. switch (count) { - case 7: thisArray[6] |= valueArray[6]; goto case 6; - case 6: thisArray[5] |= valueArray[5]; goto case 5; - case 5: thisArray[4] |= valueArray[4]; goto case 4; - case 4: thisArray[3] |= valueArray[3]; goto case 3; - case 3: thisArray[2] |= valueArray[2]; goto case 2; - case 2: thisArray[1] |= valueArray[1]; goto case 1; - case 1: thisArray[0] |= valueArray[0]; goto Done; - case 0: goto Done; + case 7: + { + thisArray[6] |= valueArray[6]; goto case 6; + } + case 6: + { + thisArray[5] |= valueArray[5]; goto case 5; + } + case 5: + { + thisArray[4] |= valueArray[4]; goto case 4; + } + case 4: + { + thisArray[3] |= valueArray[3]; goto case 3; + } + case 3: + { + thisArray[2] |= valueArray[2]; goto case 2; + } + case 2: + { + thisArray[1] |= valueArray[1]; goto case 1; + } + case 1: + { + thisArray[0] |= valueArray[0]; goto Done; + } + case 0: + { + goto Done; + } } uint i = 0; @@ -695,14 +701,38 @@ public sealed class BitArray : ICollection, ICloneable // Unroll loop for count less than Vector256 size. switch (count) { - case 7: thisArray[6] ^= valueArray[6]; goto case 6; - case 6: thisArray[5] ^= valueArray[5]; goto case 5; - case 5: thisArray[4] ^= valueArray[4]; goto case 4; - case 4: thisArray[3] ^= valueArray[3]; goto case 3; - case 3: thisArray[2] ^= valueArray[2]; goto case 2; - case 2: thisArray[1] ^= valueArray[1]; goto case 1; - case 1: thisArray[0] ^= valueArray[0]; goto Done; - case 0: goto Done; + case 7: + { + thisArray[6] ^= valueArray[6]; goto case 6; + } + case 6: + { + thisArray[5] ^= valueArray[5]; goto case 5; + } + case 5: + { + thisArray[4] ^= valueArray[4]; goto case 4; + } + case 4: + { + thisArray[3] ^= valueArray[3]; goto case 3; + } + case 3: + { + thisArray[2] ^= valueArray[2]; goto case 2; + } + case 2: + { + thisArray[1] ^= valueArray[1]; goto case 1; + } + case 1: + { + thisArray[0] ^= valueArray[0]; goto Done; + } + case 0: + { + goto Done; + } } uint i = 0; @@ -781,14 +811,38 @@ public sealed class BitArray : ICollection, ICloneable // Unroll loop for count less than Vector256 size. switch (count) { - case 7: thisArray[6] = ~thisArray[6]; goto case 6; - case 6: thisArray[5] = ~thisArray[5]; goto case 5; - case 5: thisArray[4] = ~thisArray[4]; goto case 4; - case 4: thisArray[3] = ~thisArray[3]; goto case 3; - case 3: thisArray[2] = ~thisArray[2]; goto case 2; - case 2: thisArray[1] = ~thisArray[1]; goto case 1; - case 1: thisArray[0] = ~thisArray[0]; goto Done; - case 0: goto Done; + case 7: + { + thisArray[6] = ~thisArray[6]; goto case 6; + } + case 6: + { + thisArray[5] = ~thisArray[5]; goto case 5; + } + case 5: + { + thisArray[4] = ~thisArray[4]; goto case 4; + } + case 4: + { + thisArray[3] = ~thisArray[3]; goto case 3; + } + case 3: + { + thisArray[2] = ~thisArray[2]; goto case 2; + } + case 2: + { + thisArray[1] = ~thisArray[1]; goto case 1; + } + case 1: + { + thisArray[0] = ~thisArray[0]; goto Done; + } + case 0: + { + goto Done; + } } uint i = 0; @@ -964,10 +1018,7 @@ public sealed class BitArray : ICollection, ICloneable public int Length { - get - { - return m_length; - } + get => m_length; set { if (value < 0) @@ -1035,16 +1086,22 @@ public sealed class BitArray : ICollection, ICloneable switch (remainder) { case 3: - span[2] = (byte)(m_array[quotient] >> 16); - goto case 2; + { + span[2] = (byte)(m_array[quotient] >> 16); + goto case 2; + } // fall through case 2: - span[1] = (byte)(m_array[quotient] >> 8); - goto case 1; + { + span[1] = (byte)(m_array[quotient] >> 8); + goto case 1; + } // fall through case 1: - span[0] = (byte)m_array[quotient]; - break; + { + span[0] = (byte)m_array[quotient]; + break; + } } } @@ -1084,54 +1141,6 @@ public sealed class BitArray : ICollection, ICloneable intArray[index + last] = m_array[last] & unchecked((1 << extraBits) - 1); } } - else if (array is byte[] byteArray) - { - int arrayLength = GetByteArrayLengthFromBitLength(m_length); - if (array.Length - index < arrayLength) - { - throw new ArgumentException(CollectionThrowStrings.Argument_InvalidOffLen); - } - - // equivalent to m_length % BitsPerByte, since BitsPerByte is a power of 2 - uint extraBits = (uint)m_length & (BitsPerByte - 1); - if (extraBits > 0) - { - // last byte is not aligned, we will directly copy one less byte - arrayLength -= 1; - } - - Span span = byteArray.AsSpan(index); - - int quotient = Div4Rem(arrayLength, out int remainder); - for (int i = 0; i < quotient; i++) - { - BinaryPrimitives.WriteInt32LittleEndian(span, m_array[i]); - span = span[4..]; - } - - if (extraBits > 0) - { - Debug.Assert(span.Length > 0); - Debug.Assert(m_array.Length > quotient); - // mask the final byte - span[remainder] = (byte)((m_array[quotient] >> (remainder * 8)) & ((1 << (int)extraBits) - 1)); - } - - switch (remainder) - { - case 3: - span[2] = (byte)(m_array[quotient] >> 16); - goto case 2; - // fall through - case 2: - span[1] = (byte)(m_array[quotient] >> 8); - goto case 1; - // fall through - case 1: - span[0] = (byte)m_array[quotient]; - break; - } - } else if (array is bool[] boolArray) { if (array.Length - index < m_length) diff --git a/Projects/Server/Collections/PooledOrderedHashSet.cs b/Projects/Server/Collections/PooledOrderedHashSet.cs index 4a6e51f1b..8853918ba 100644 --- a/Projects/Server/Collections/PooledOrderedHashSet.cs +++ b/Projects/Server/Collections/PooledOrderedHashSet.cs @@ -14,590 +14,589 @@ *************************************************************************/ using System; -using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.Collections.Extensions; +using Server.Buffers; -namespace Server.Collections +namespace Server.Collections; + +[DebuggerDisplay("Count = {Count}")] +public class PooledOrderedHashSet : IList, IDisposable { - [DebuggerDisplay("Count = {Count}")] - public class PooledOrderedHashSet : IList, IDisposable + private struct Entry { - private struct Entry - { - public uint HashCode; - public TValue Value; - public int Next; // the index of the next item in the same bucket, -1 if last - } + public uint HashCode; + public TValue Value; + public int Next; // the index of the next item in the same bucket, -1 if last + } - private static readonly Entry[] InitialEntries = new Entry[1]; - private int[] _buckets = HashHelpers.SizeOneIntArray; - private int _bucketsLength = 1; - private Entry[] _entries = InitialEntries; - private int _entriesLength = 1; - private ulong _fastModMultiplier; - private int _count; - private int _version; + private static readonly Entry[] InitialEntries = new Entry[1]; + private int[] _buckets = HashHelpers.SizeOneIntArray; + private int _bucketsLength = 1; + private Entry[] _entries = InitialEntries; + private int _entriesLength = 1; + private ulong _fastModMultiplier; + private int _count; + private int _version; #nullable enable - private readonly IEqualityComparer? _comparer; + private readonly IEqualityComparer? _comparer; #nullable disable - public int Count => _count; + public int Count => _count; #nullable enable - public IEqualityComparer? Comparer => _comparer; + public IEqualityComparer? Comparer => _comparer; #nullable disable - public PooledOrderedHashSet() - : this(0) + public PooledOrderedHashSet() + : this(0) + { + } + + public PooledOrderedHashSet(IEqualityComparer comparer) + : this(0, comparer) + { + } + + public PooledOrderedHashSet(int capacity, IEqualityComparer comparer = null) + { + if (capacity < 0) { + throw new ArgumentOutOfRangeException(nameof(capacity)); } - public PooledOrderedHashSet(IEqualityComparer comparer) - : this(0, comparer) + if (capacity > 0) { + int newSize = HashHelpers.GetPrime(capacity); + _buckets = STArrayPool.Shared.Rent(newSize); + _bucketsLength = newSize; + _entries = STArrayPool.Shared.Rent(newSize); + _entriesLength = newSize; + _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); } - public PooledOrderedHashSet(int capacity, IEqualityComparer comparer = null) + if (comparer != EqualityComparer.Default) { - if (capacity < 0) - { - throw new ArgumentOutOfRangeException(nameof(capacity)); - } + _comparer = comparer; + } + } - if (capacity > 0) - { - int newSize = HashHelpers.GetPrime(capacity); - _buckets = ArrayPool.Shared.Rent(newSize); - _bucketsLength = newSize; - _entries = ArrayPool.Shared.Rent(newSize); - _entriesLength = newSize; - _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); - } - - if (comparer != EqualityComparer.Default) - { - _comparer = comparer; - } + public PooledOrderedHashSet(IEnumerable collection, IEqualityComparer comparer = null) + : this((collection as ICollection)?.Count ?? 0, comparer) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); } - public PooledOrderedHashSet(IEnumerable collection, IEqualityComparer comparer = null) - : this((collection as ICollection)?.Count ?? 0, comparer) + foreach (TValue value in collection) { - if (collection == null) - { - throw new ArgumentNullException(nameof(collection)); - } - - foreach (TValue value in collection) - { - Add(value); - } + Add(value); } + } - public bool Contains(TValue item) => TryGetValue(item, out var value) && EqualityComparer.Default.Equals(value); + public bool Contains(TValue item) => TryGetValue(item, out var value) && EqualityComparer.Default.Equals(value); - public void Clear() + public void Clear() + { + if (_count > 0) { - if (_count > 0) - { - Array.Clear(_buckets, 0, _bucketsLength); - Array.Clear(_entries, 0, _count); - _count = 0; - ++_version; - } - } - - public Enumerator GetEnumerator() => new(this); - - void ICollection.Add(TValue item) => TryAdd(item); - - public bool Add(TValue item) => TryAdd(item); - - public int GetOrAdd(TValue value) => TryInsert(null, value); - - public int IndexOf(TValue value) => IndexOf(value, out _); - - public void Insert(int index, TValue value) - { - if ((uint)index > (uint)Count) - { - throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); - } - - TryInsert(index, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ref int GetBucketRef(uint hashCode) - { - int[] buckets = _buckets!; - return ref buckets[HashHelpers.FastMod(hashCode, (uint)_bucketsLength, _fastModMultiplier)]; - } - - public bool Remove(TValue value) - { - int index = IndexOf(value); - if (index >= 0) - { - RemoveAt(index); - return true; - } - - return false; - } - - public void RemoveAt(int index) - { - int count = Count; - if ((uint)index >= (uint)count) - { - throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); - } - - // Remove the entry from the bucket - RemoveEntryFromBucket(index); - - // Decrement the indices > index - Entry[] entries = _entries; - for (int i = index + 1; i < count; ++i) - { - entries[i - 1] = entries[i]; - UpdateBucketIndex(i, incrementAmount: -1); - } - --_count; - entries[_count] = default; + Array.Clear(_buckets, 0, _bucketsLength); + Array.Clear(_entries, 0, _count); + _count = 0; ++_version; } + } - public bool TryAdd(TValue value) => TryInsert(null, value) != _count - 1; + public Enumerator GetEnumerator() => new(this); - public bool TryGetValue(TValue value, out TValue actualValue) + void ICollection.Add(TValue item) => TryAdd(item); + + public bool Add(TValue item) => TryAdd(item); + + public int GetOrAdd(TValue value) => TryInsert(null, value); + + public int IndexOf(TValue value) => IndexOf(value, out _); + + public void Insert(int index, TValue value) + { + if ((uint)index > (uint)Count) { - int index = IndexOf(value); - if (index >= 0) - { - actualValue = _entries[index].Value; - return true; - } - - actualValue = default; - return false; + throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); } - public TValue this[int index] + TryInsert(index, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref int GetBucketRef(uint hashCode) + { + int[] buckets = _buckets!; + return ref buckets[HashHelpers.FastMod(hashCode, (uint)_bucketsLength, _fastModMultiplier)]; + } + + public bool Remove(TValue value) + { + int index = IndexOf(value); + if (index >= 0) { - get - { - if ((uint)index >= (uint)Count) - { - throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); - } - - return _entries[index].Value; - } - set - { - if ((uint)index >= (uint)Count) - { - throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); - } - - TValue v = value; - int foundIndex = IndexOf(v, out uint hashCode); - if (foundIndex < 0) - { - RemoveEntryFromBucket(index); - Entry entry = new Entry { HashCode = hashCode, Value = value }; - AddEntryToBucket(ref entry, index, _buckets, _bucketsLength); - _entries[index] = entry; - ++_version; - } - else if (foundIndex == index) - { - ref Entry entry = ref _entries[index]; - entry.Value = value; - } - else - { - throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_AddingDuplicate, v.ToString())); - } - } + RemoveAt(index); + return true; } - public bool IsReadOnly => false; + return false; + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public void CopyTo(TValue[] array, int arrayIndex) + public void RemoveAt(int index) + { + int count = Count; + if ((uint)index >= (uint)count) { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if ((uint)arrayIndex > (uint)array.Length) - { - throw new ArgumentOutOfRangeException(nameof(arrayIndex), CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum); - } - - int count = Count; - if (array.Length - arrayIndex < count) - { - throw new ArgumentException(CollectionThrowStrings.Arg_ArrayPlusOffTooSmall); - } - - Entry[] entries = _entries; - for (int i = 0; i < count; ++i) - { - Entry entry = entries[i]; - array[i + arrayIndex] = entry.Value; - } + throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Entry[] Resize(int newSize) + // Remove the entry from the bucket + RemoveEntryFromBucket(index); + + // Decrement the indices > index + Entry[] entries = _entries; + for (int i = index + 1; i < count; ++i) { - int[] newBuckets = _buckets.Length < newSize ? ArrayPool.Shared.Rent(newSize) : _buckets; - Entry[] newEntries = _entries.Length < newSize ? ArrayPool.Shared.Rent(newSize) : _entries; + entries[i - 1] = entries[i]; + UpdateBucketIndex(i, incrementAmount: -1); + } + --_count; + entries[_count] = default; + ++_version; + } - int count = Count; - Array.Copy(_entries, newEntries, count); + public bool TryAdd(TValue value) => TryInsert(null, value) != _count - 1; - _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); - - for (int i = 0; i < count; ++i) - { - AddEntryToBucket(ref newEntries[i], i, newBuckets, newSize); - } - - var oldBuckets = _buckets; - var oldEntries = _entries; - - if (oldBuckets.Length > 1 && oldBuckets != newBuckets) - { - ArrayPool.Shared.Return(oldBuckets, true); - } - - if (oldEntries.Length > 1 && oldEntries != newEntries) - { - ArrayPool.Shared.Return(oldEntries, true); - } - - _buckets = newBuckets; - _bucketsLength = newSize; - _entries = newEntries; - _entriesLength = newSize; - return newEntries; + public bool TryGetValue(TValue value, out TValue actualValue) + { + int index = IndexOf(value); + if (index >= 0) + { + actualValue = _entries[index].Value; + return true; } -#nullable enable - private int IndexOf(TValue value, out uint hashCode) + actualValue = default; + return false; + } + + public TValue this[int index] + { + get { - ref int bucket = ref Unsafe.NullRef(); - int i; - - IEqualityComparer? comparer = _comparer; - if (comparer == null) + if ((uint)index >= (uint)Count) { - hashCode = (uint)value.GetHashCode(); - bucket = ref GetBucketRef(hashCode); - i = bucket - 1; + throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); + } - if (i >= 0) - { - if (typeof(TValue).IsValueType) - { - // ValueType: Devirtualize with EqualityComparer.Default intrinsic - Entry[] entries = _entries; - int collisionCount = 0; - do - { - Entry entry = entries[i]; - if (entry.HashCode == hashCode && EqualityComparer.Default.Equals(entry.Value, value)) - { - break; - } + return _entries[index].Value; + } + set + { + if ((uint)index >= (uint)Count) + { + throw new ArgumentOutOfRangeException(nameof(index), CollectionThrowStrings.ArgumentOutOfRange_Index); + } - i = entry.Next; - if (collisionCount >= _entriesLength) - { - // The chain of entries forms a loop; which means a concurrent update has happened. - // Break out of the loop and throw, rather than looping forever. - throw new InvalidOperationException( - CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported - ); - } - - ++collisionCount; - } while (i >= 0); - } - else - { - // Object type: Shared Generic, EqualityComparer.Default won't devirtualize (https://github.com/dotnet/runtime/issues/10050), - // so cache in a local rather than get EqualityComparer per loop iteration. - var defaultComparer = EqualityComparer.Default; - Entry[] entries = _entries; - int collisionCount = 0; - do - { - Entry entry = entries[i]; - if (entry.HashCode == hashCode && defaultComparer.Equals(entry.Value, value)) - { - break; - } - - i = entry.Next; - if (collisionCount >= _entriesLength) - { - // The chain of entries forms a loop; which means a concurrent update has happened. - // Break out of the loop and throw, rather than looping forever. - throw new InvalidOperationException( - CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported - ); - } - - ++collisionCount; - } while (i >= 0); - } - } + TValue v = value; + int foundIndex = IndexOf(v, out uint hashCode); + if (foundIndex < 0) + { + RemoveEntryFromBucket(index); + Entry entry = new Entry { HashCode = hashCode, Value = value }; + AddEntryToBucket(ref entry, index, _buckets, _bucketsLength); + _entries[index] = entry; + ++_version; + } + else if (foundIndex == index) + { + ref Entry entry = ref _entries[index]; + entry.Value = value; } else { - hashCode = (uint)comparer.GetHashCode(value); - bucket = ref GetBucketRef(hashCode); - i = bucket - 1; - if (i >= 0) + throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_AddingDuplicate, v.ToString())); + } + } + } + + public bool IsReadOnly => false; + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void CopyTo(TValue[] array, int arrayIndex) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if ((uint)arrayIndex > (uint)array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex), CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum); + } + + int count = Count; + if (array.Length - arrayIndex < count) + { + throw new ArgumentException(CollectionThrowStrings.Arg_ArrayPlusOffTooSmall); + } + + Entry[] entries = _entries; + for (int i = 0; i < count; ++i) + { + Entry entry = entries[i]; + array[i + arrayIndex] = entry.Value; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Entry[] Resize(int newSize) + { + int[] newBuckets = _buckets.Length < newSize ? STArrayPool.Shared.Rent(newSize) : _buckets; + Entry[] newEntries = _entries.Length < newSize ? STArrayPool.Shared.Rent(newSize) : _entries; + + int count = Count; + Array.Copy(_entries, newEntries, count); + + _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); + + for (int i = 0; i < count; ++i) + { + AddEntryToBucket(ref newEntries[i], i, newBuckets, newSize); + } + + var oldBuckets = _buckets; + var oldEntries = _entries; + + if (oldBuckets.Length > 1 && oldBuckets != newBuckets) + { + STArrayPool.Shared.Return(oldBuckets, true); + } + + if (oldEntries.Length > 1 && oldEntries != newEntries) + { + STArrayPool.Shared.Return(oldEntries, true); + } + + _buckets = newBuckets; + _bucketsLength = newSize; + _entries = newEntries; + _entriesLength = newSize; + return newEntries; + } + +#nullable enable + private int IndexOf(TValue value, out uint hashCode) + { + ref int bucket = ref Unsafe.NullRef(); + int i; + + IEqualityComparer? comparer = _comparer; + if (comparer == null) + { + hashCode = (uint)value.GetHashCode(); + bucket = ref GetBucketRef(hashCode); + i = bucket - 1; + + if (i >= 0) + { + if (typeof(TValue).IsValueType) { + // ValueType: Devirtualize with EqualityComparer.Default intrinsic Entry[] entries = _entries; int collisionCount = 0; do { Entry entry = entries[i]; - if (entry.HashCode == hashCode && comparer.Equals(entry.Value, value)) + if (entry.HashCode == hashCode && EqualityComparer.Default.Equals(entry.Value, value)) { break; } + i = entry.Next; if (collisionCount >= _entriesLength) { // The chain of entries forms a loop; which means a concurrent update has happened. // Break out of the loop and throw, rather than looping forever. - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported); + throw new InvalidOperationException( + CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported + ); } + + ++collisionCount; + } while (i >= 0); + } + else + { + // Object type: Shared Generic, EqualityComparer.Default won't devirtualize (https://github.com/dotnet/runtime/issues/10050), + // so cache in a local rather than get EqualityComparer per loop iteration. + var defaultComparer = EqualityComparer.Default; + Entry[] entries = _entries; + int collisionCount = 0; + do + { + Entry entry = entries[i]; + if (entry.HashCode == hashCode && defaultComparer.Equals(entry.Value, value)) + { + break; + } + + i = entry.Next; + if (collisionCount >= _entriesLength) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + throw new InvalidOperationException( + CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported + ); + } + ++collisionCount; } while (i >= 0); } } - - return i; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int TryInsert(int? index, TValue value) + else { - int i = IndexOf(value, out uint hashCode); - return i >= 0 ? i : AddInternal(index, value, hashCode); + hashCode = (uint)comparer.GetHashCode(value); + bucket = ref GetBucketRef(hashCode); + i = bucket - 1; + if (i >= 0) + { + Entry[] entries = _entries; + int collisionCount = 0; + do + { + Entry entry = entries[i]; + if (entry.HashCode == hashCode && comparer.Equals(entry.Value, value)) + { + break; + } + i = entry.Next; + if (collisionCount >= _entriesLength) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported); + } + ++collisionCount; + } while (i >= 0); + } } - private int AddInternal(int? index, TValue value, uint hashCode) + return i; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int TryInsert(int? index, TValue value) + { + int i = IndexOf(value, out uint hashCode); + return i >= 0 ? i : AddInternal(index, value, hashCode); + } + + private int AddInternal(int? index, TValue value, uint hashCode) + { + Entry[] entries = _entries; + // Check if resize is needed + int count = Count; + if (_entriesLength == count || entries.Length == 1) { - Entry[] entries = _entries; - // Check if resize is needed - int count = Count; - if (_entriesLength == count || entries.Length == 1) - { - entries = Resize(HashHelpers.ExpandPrime(_entriesLength)); - } - - // Increment indices >= index; - int actualIndex = index ?? count; - for (int i = count - 1; i >= actualIndex; --i) - { - entries[i + 1] = entries[i]; - UpdateBucketIndex(i, incrementAmount: 1); - } - - ref Entry entry = ref entries[actualIndex]; - entry.HashCode = hashCode; - entry.Value = value; - AddEntryToBucket(ref entry, actualIndex, _buckets, _bucketsLength); - ++_count; - ++_version; - return actualIndex; + entries = Resize(HashHelpers.ExpandPrime(_entriesLength)); } + + // Increment indices >= index; + int actualIndex = index ?? count; + for (int i = count - 1; i >= actualIndex; --i) + { + entries[i + 1] = entries[i]; + UpdateBucketIndex(i, incrementAmount: 1); + } + + ref Entry entry = ref entries[actualIndex]; + entry.HashCode = hashCode; + entry.Value = value; + AddEntryToBucket(ref entry, actualIndex, _buckets, _bucketsLength); + ++_count; + ++_version; + return actualIndex; + } #nullable restore - // Returns the index of the next entry in the bucket - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddEntryToBucket(ref Entry entry, int entryIndex, int[] buckets, int bucketsLength) - { - ref int b = ref buckets[(int)(entry.HashCode % (uint)bucketsLength)]; - entry.Next = b - 1; - b = entryIndex + 1; - } + // Returns the index of the next entry in the bucket + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AddEntryToBucket(ref Entry entry, int entryIndex, int[] buckets, int bucketsLength) + { + ref int b = ref buckets[(int)(entry.HashCode % (uint)bucketsLength)]; + entry.Next = b - 1; + b = entryIndex + 1; + } - private void RemoveEntryFromBucket(int entryIndex) + private void RemoveEntryFromBucket(int entryIndex) + { + Entry[] entries = _entries; + Entry entry = entries[entryIndex]; + ref int bucket = ref GetBucketRef(entry.HashCode); + // Bucket was pointing to removed entry. Update it to point to the next in the chain + if (bucket == entryIndex + 1) { - Entry[] entries = _entries; - Entry entry = entries[entryIndex]; - ref int bucket = ref GetBucketRef(entry.HashCode); - // Bucket was pointing to removed entry. Update it to point to the next in the chain - if (bucket == entryIndex + 1) + bucket = entry.Next + 1; + } + else + { + // Start at the entry the bucket points to, and walk the chain until we find the entry with the index we want to remove, then fix the chain + int i = bucket - 1; + int collisionCount = 0; + while (true) { - bucket = entry.Next + 1; - } - else - { - // Start at the entry the bucket points to, and walk the chain until we find the entry with the index we want to remove, then fix the chain - int i = bucket - 1; - int collisionCount = 0; - while (true) + ref Entry e = ref entries[i]; + if (e.Next == entryIndex) { - ref Entry e = ref entries[i]; - if (e.Next == entryIndex) - { - e.Next = entry.Next; - return; - } - i = e.Next; - if (collisionCount >= _entriesLength) - { - // The chain of entries forms a loop; which means a concurrent update has happened. - // Break out of the loop and throw, rather than looping forever. - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported); - } - ++collisionCount; + e.Next = entry.Next; + return; } + i = e.Next; + if (collisionCount >= _entriesLength) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported); + } + ++collisionCount; } } + } - private void UpdateBucketIndex(int entryIndex, int incrementAmount) + private void UpdateBucketIndex(int entryIndex, int incrementAmount) + { + Entry[] entries = _entries; + Entry entry = entries[entryIndex]; + ref int bucket = ref GetBucketRef(entry.HashCode); + // Bucket was pointing to entry. Increment the index by incrementAmount. + if (bucket == entryIndex + 1) { - Entry[] entries = _entries; - Entry entry = entries[entryIndex]; - ref int bucket = ref GetBucketRef(entry.HashCode); - // Bucket was pointing to entry. Increment the index by incrementAmount. - if (bucket == entryIndex + 1) + bucket += incrementAmount; + } + else + { + // Start at the entry the bucket points to, and walk the chain until we find the entry with the index we want to increment. + int i = bucket - 1; + int collisionCount = 0; + while (true) { - bucket += incrementAmount; - } - else - { - // Start at the entry the bucket points to, and walk the chain until we find the entry with the index we want to increment. - int i = bucket - 1; - int collisionCount = 0; - while (true) + ref Entry e = ref entries[i]; + if (e.Next == entryIndex) { - ref Entry e = ref entries[i]; - if (e.Next == entryIndex) - { - e.Next += incrementAmount; - return; - } - i = e.Next; - if (collisionCount >= _entriesLength) - { - // The chain of entries forms a loop; which means a concurrent update has happened. - // Break out of the loop and throw, rather than looping forever. - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported); - } - ++collisionCount; + e.Next += incrementAmount; + return; } + i = e.Next; + if (collisionCount >= _entriesLength) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_ConcurrentOperationsNotSupported); + } + ++collisionCount; } } + } - public struct Enumerator : IEnumerator + public struct Enumerator : IEnumerator + { + private readonly PooledOrderedHashSet _PooledOrderedHashSet; + private readonly int _version; + private int _index; + private TValue _current; + + public TValue Current => _current; + + object IEnumerator.Current => _current; + + internal Enumerator(PooledOrderedHashSet PooledOrderedHashSet) { - private readonly PooledOrderedHashSet _PooledOrderedHashSet; - private readonly int _version; - private int _index; - private TValue _current; - - public TValue Current => _current; - - object IEnumerator.Current => _current; - - internal Enumerator(PooledOrderedHashSet PooledOrderedHashSet) - { - _PooledOrderedHashSet = PooledOrderedHashSet; - _version = PooledOrderedHashSet._version; - _index = 0; - _current = default; - } - - public void Dispose() - { - } - - public bool MoveNext() - { - if (_version != _PooledOrderedHashSet._version) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - if (_index < _PooledOrderedHashSet.Count) - { - Entry entry = _PooledOrderedHashSet._entries[_index]; - _current = entry.Value; - ++_index; - return true; - } - _current = default; - return false; - } - - void IEnumerator.Reset() - { - if (_version != _PooledOrderedHashSet._version) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - _index = 0; - _current = default; - } + _PooledOrderedHashSet = PooledOrderedHashSet; + _version = PooledOrderedHashSet._version; + _index = 0; + _current = default; } public void Dispose() { - if (_buckets.Length > 1) - { - ArrayPool.Shared.Return(_buckets, true); - } - - if (_entries.Length > 1) - { - ArrayPool.Shared.Return(_entries, true); - } - - _buckets = HashHelpers.SizeOneIntArray; - _entries = InitialEntries; - _count = 0; - - GC.SuppressFinalize(this); } - ~PooledOrderedHashSet() + public bool MoveNext() { - if (_buckets.Length > 1) + if (_version != _PooledOrderedHashSet._version) { - ArrayPool.Shared.Return(_buckets, true); + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); } - if (_entries.Length > 1) + if (_index < _PooledOrderedHashSet.Count) { - ArrayPool.Shared.Return(_entries, true); + Entry entry = _PooledOrderedHashSet._entries[_index]; + _current = entry.Value; + ++_index; + return true; + } + _current = default; + return false; + } + + void IEnumerator.Reset() + { + if (_version != _PooledOrderedHashSet._version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); } - _buckets = HashHelpers.SizeOneIntArray; - _entries = InitialEntries; - _count = 0; + _index = 0; + _current = default; } } + + public void Dispose() + { + if (_buckets.Length > 1) + { + STArrayPool.Shared.Return(_buckets, true); + } + + if (_entries.Length > 1) + { + STArrayPool.Shared.Return(_entries, true); + } + + _buckets = HashHelpers.SizeOneIntArray; + _entries = InitialEntries; + _count = 0; + + GC.SuppressFinalize(this); + } + + ~PooledOrderedHashSet() + { + if (_buckets.Length > 1) + { + STArrayPool.Shared.Return(_buckets, true); + } + + if (_entries.Length > 1) + { + STArrayPool.Shared.Return(_entries, true); + } + + _buckets = HashHelpers.SizeOneIntArray; + _entries = InitialEntries; + _count = 0; + } } diff --git a/Projects/Server/Collections/PooledRefList.cs b/Projects/Server/Collections/PooledRefList.cs new file mode 100644 index 000000000..fccdff986 --- /dev/null +++ b/Projects/Server/Collections/PooledRefList.cs @@ -0,0 +1,1154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Server.Buffers; + +namespace Server.Collections; + +// Implements a variable-size List that uses an array of objects to store the +// elements. A List has a capacity, which is the allocated length +// of the internal array. As elements are added to a List, the capacity +// of the List is automatically increased as required by reallocating the +// internal array. +// +[DebuggerDisplay("Count = {Count}")] +public ref struct PooledRefList +{ + private const int MaxLength = int.MaxValue; + private const int DefaultCapacity = 4; + + internal T[] _items; // Do not rename (binary serialization) + internal int _size; // Do not rename (binary serialization) + private int _version; // Do not rename (binary serialization) + private bool _mt; + +#pragma warning disable CA1825 // avoid the extra generic instantiation for Array.Empty() + private static readonly T[] s_emptyArray = new T[0]; +#pragma warning restore CA1825 + + private ArrayPool ArrayPool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _mt ? ArrayPool.Shared : STArrayPool.Shared; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledRefList Create(int capacity = 32, bool mt = false) => new(capacity, mt); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledRefList CreateMT(int capacity = 32) => new(capacity, true); + + // Constructs a List. The list is initially empty and has a capacity + // of zero. Upon adding the first element to the list the capacity is + // increased to DefaultCapacity, and then increased in multiples of two + // as required. + public PooledRefList(int capacity, bool mt = false) + { + _mt = mt; + _size = 0; + _version = 0; + _items = capacity switch + { + < 0 => throw new ArgumentOutOfRangeException(nameof(capacity), capacity, CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum), + 0 => Array.Empty(), + _ => (_mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(capacity) + }; + + } + + // Constructs a List, copying the contents of the given collection. The + // size and capacity of the new list will both be equal to the size of the + // given collection. + // + public PooledRefList(PooledRefList collection, bool mt = false) + { + _version = 0; + _mt = mt; + + int count = collection.Count; + if (count == 0) + { + _items = s_emptyArray; + _size = 0; + } + else + { + _items = (mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(count); + collection.CopyTo(_items, 0); + _size = count; + } + } + + // Constructs a List, copying the contents of the given collection. The + // size and capacity of the new list will both be equal to the size of the + // given collection. + // + public PooledRefList(IEnumerable collection, bool mt = false) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + _version = 0; + _mt = mt; + + if (collection is ICollection c) + { + int count = c.Count; + if (count == 0) + { + _items = s_emptyArray; + _size = 0; + } + else + { + _items = (mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(count); + c.CopyTo(_items, 0); + _size = count; + } + } + else + { + _size = 0; + _items = s_emptyArray; + using IEnumerator en = collection!.GetEnumerator(); + while (en.MoveNext()) + { + Add(en.Current); + } + } + } + + // Gets and sets the capacity of this list. The capacity is the size of + // the internal array used to hold items. When set, the internal + // array of the list is reallocated to the given capacity. + // + public int Capacity + { + get => _items.Length; + set + { + if (value < _size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + if (value != _items.Length) + { + if (value > 0) + { + T[] newItems = ArrayPool.Rent(_size); + if (_size > 0) + { + Array.Copy(_items, newItems, _size); + } + + if (_items.Length > 0) + { + Clear(); + ArrayPool.Return(_items); + } + _items = newItems; + } + else + { + Clear(); + ArrayPool.Return(_items); + _items = s_emptyArray; + } + } + } + } + + // Read-only property describing how many elements are in the List. + public int Count => _size; + + // Sets or Gets the element at the given index. + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + // Following trick can reduce the range check by one + if ((uint)index >= (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return _items[index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + if ((uint)index >= (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _items[index] = value; + _version++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsCompatibleObject(object? value) + { + // Non-null values are fine. Only accept nulls if T is a class or Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. + return value is T || value == null && default(T) == null; + } + + // Adds the given object to the end of this list. The size of the list is + // increased by one. If required, the capacity of the list is doubled + // before adding the new element. + // + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T item) + { + _version++; + T[] array = _items; + int size = _size; + if ((uint)size < (uint)array.Length) + { + _size = size + 1; + array[size] = item; + } + else + { + AddWithResize(item); + } + } + + // Non-inline from List.Add to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddWithResize(T item) + { + Debug.Assert(_size == _items.Length); + int size = _size; + Grow(size + 1); + _size = size + 1; + _items[size] = item; + } + + // Adds the elements of the given collection to the end of this list. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. + // + public void AddRange(IEnumerable collection) => InsertRange(_size, collection); + + // Searches a section of the list for a given element using a binary search + // algorithm. Elements of the list are compared to the search value using + // the given IComparer interface. If comparer is null, elements of + // the list are compared to the search value using the IComparable + // interface, which in that case must be implemented by all elements of the + // list and the given search value. This method assumes that the given + // section of the list is already sorted; if this is not the case, the + // result will be incorrect. + // + // The method returns the index of the given value in the list. If the + // list does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. This is also the index at which + // the search value should be inserted into the list in order for the list + // to remain sorted. + // + // The method uses the Array.BinarySearch method to perform the + // search. + // + public int BinarySearch(int index, int count, T item, IComparer? comparer) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_size - index < count) + { + throw new ArgumentException("Length must be greater than zero"); + } + + return Array.BinarySearch(_items, index, count, item, comparer); + } + + public int BinarySearch(T item) => BinarySearch(0, Count, item, null); + + public int BinarySearch(T item, IComparer? comparer) => BinarySearch(0, Count, item, comparer); + + // Clears the contents of List. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _version++; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + int size = _size; + _size = 0; + if (size > 0) + { + Array.Clear(_items, 0, size); // Clear the elements so that the gc can reclaim the references. + } + } + else + { + _size = 0; + } + } + + // Contains returns true if the specified element is in the List. + // It does a linear, O(n) search. Equality is determined by calling + // EqualityComparer.Default.Equals(). + // + public bool Contains(T item) + { + // PERF: IndexOf calls Array.IndexOf, which internally + // calls EqualityComparer.Default.IndexOf, which + // is specialized for different types. This + // boosts performance since instead of making a + // virtual method call each iteration of the loop, + // via EqualityComparer.Default.Equals, we + // only make one virtual call to EqualityComparer.IndexOf. + + return _size != 0 && IndexOf(item) != -1; + } + + public PooledRefList ConvertAll(Converter converter) + { + if (converter == null) + { + throw new ArgumentNullException(nameof(converter)); + } + + PooledRefList list = new PooledRefList(_size); + for (int i = 0; i < _size; i++) + { + list._items[i] = converter(_items[i]); + } + list._size = _size; + + return list; + } + + // Copies this List into array, which must be of a + // compatible array type. + public void CopyTo(T[] array) => CopyTo(array, 0); + + // Copies a section of this list to the given array at the given index. + // + // The method uses the Array.Copy method to copy the elements. + // + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (_size - index < count) + { + throw new ArgumentException("Length must be greater than zero"); + } + + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, index, array, arrayIndex, count); + } + + public void CopyTo(T[] array, int arrayIndex) + { + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, 0, array, arrayIndex, _size); + } + + /// + /// Ensures that the capacity of this list is at least the specified . + /// If the current capacity of the list is less than specified , + /// the capacity is increased by continuously twice current capacity until it is at least the specified . + /// + /// The minimum capacity to ensure. + public int EnsureCapacity(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + if (_items.Length < capacity) + { + Grow(capacity); + _version++; + } + + return _items.Length; + } + + /// + /// Increase the capacity of this list to at least the specified . + /// + /// The minimum capacity to ensure. + private void Grow(int capacity) + { + Debug.Assert(_items.Length < capacity); + + int newcapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length; + + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newcapacity > MaxLength) + { + newcapacity = MaxLength; + } + + // If the computed capacity is still less than specified, set to the original argument. + // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. + if (newcapacity < capacity) + { + newcapacity = capacity; + } + + Capacity = newcapacity; + } + + public bool Exists(Predicate match) => FindIndex(match) != -1; + + public T? Find(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + for (int i = 0; i < _size; i++) + { + if (match(_items[i])) + { + return _items[i]; + } + } + return default; + } + + public PooledRefList FindAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + PooledRefList list = new PooledRefList(); + for (int i = 0; i < _size; i++) + { + if (match(_items[i])) + { + list.Add(_items[i]); + } + } + return list; + } + + public int FindIndex(Predicate match) => FindIndex(0, _size, match); + + public int FindIndex(int startIndex, Predicate match) => FindIndex(startIndex, _size - startIndex, match); + + public int FindIndex(int startIndex, int count, Predicate match) + { + if ((uint)startIndex > (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + + if (count < 0 || startIndex > _size - count) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) + { + if (match(_items[i])) + { + return i; + } + } + return -1; + } + + public T? FindLast(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + for (int i = _size - 1; i >= 0; i--) + { + if (match(_items[i])) + { + return _items[i]; + } + } + return default; + } + + public int FindLastIndex(Predicate match) => FindLastIndex(_size - 1, _size, match); + + public int FindLastIndex(int startIndex, Predicate match) => FindLastIndex(startIndex, startIndex + 1, match); + + public int FindLastIndex(int startIndex, int count, Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + if (_size == 0) + { + // Special case for 0 length List + if (startIndex != -1) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + } + else + { + // Make sure we're not out of range + if ((uint)startIndex >= (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + } + + // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. + if (count < 0 || startIndex - count + 1 < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + int endIndex = startIndex - count; + for (int i = startIndex; i > endIndex; i--) + { + if (match(_items[i])) + { + return i; + } + } + return -1; + } + + public void ForEach(Action action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + int version = _version; + + for (int i = 0; i < _size; i++) + { + if (version != _version) + { + break; + } + action(_items[i]); + } + + if (version != _version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + } + + // Returns an enumerator for this list with the given + // permission for removal of elements. If modifications made to the list + // while an enumeration is in progress, the MoveNext and + // GetObject methods of the enumerator will throw an exception. + // + public Enumerator GetEnumerator() => new(this); + + public PooledRefList GetRange(int index, int count) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_size - index < count) + { + throw new ArgumentException("Length must be greater than zero"); + } + + PooledRefList list = new PooledRefList(count); + Array.Copy(_items, index, list._items, 0, count); + list._size = count; + return list; + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards from beginning to end. + // The elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item) => Array.IndexOf(_items, item, 0, _size); + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and ending at count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index) + { + if (index > _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return Array.IndexOf(_items, item, index, _size - index); + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and upto count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index, int count) + { + if (index > _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0 || index > _size - count) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + return Array.IndexOf(_items, item, index, count); + } + + // Inserts an element into this list at a given index. The size of the list + // is increased by one. If required, the capacity of the list is doubled + // before inserting the new element. + // + public void Insert(int index, T item) + { + // Note that insertions at the end are legal. + if ((uint)index > (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + if (_size == _items.Length) + { + Grow(_size + 1); + } + + if (index < _size) + { + Array.Copy(_items, index, _items, index + 1, _size - index); + } + _items[index] = item; + _size++; + _version++; + } + + // Inserts the elements of the given collection at a given index. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. Ranges may be added + // to the end of the list by setting index to the List's size. + // + public void InsertRange(int index, IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + if ((uint)index > (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (collection is ICollection c) + { + int count = c.Count; + if (count > 0) + { + if (_items.Length - _size < count) + { + Grow(_size + count); + } + if (index < _size) + { + Array.Copy(_items, index, _items, index + count, _size - index); + } + + c.CopyTo(_items, index); + _size += count; + } + } + else + { + using IEnumerator en = collection.GetEnumerator(); + while (en.MoveNext()) + { + Insert(index++, en.Current); + } + } + _version++; + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at the end + // and ending at the first element in the list. The elements of the list + // are compared to the given value using the Object.Equals method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item) + { + if (_size == 0) + { // Special case for empty list + return -1; + } + + return LastIndexOf(item, _size - 1, _size); + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at index + // index and ending at the first element in the list. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item, int index) + { + if (index >= _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return LastIndexOf(item, index, index + 1); + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at index + // index and upto count elements. The elements of + // the list are compared to the given value using the Object.Equals + // method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item, int index, int count) + { + if (Count != 0 && index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (Count != 0 && count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_size == 0) + { // Special case for empty list + return -1; + } + + if (index >= _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count > index + 1) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + return Array.LastIndexOf(_items, item, index, count); + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + public bool Remove(T item) + { + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; + } + + // This method removes all items which matches the predicate. + // The complexity is O(n). + public int RemoveAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + int freeIndex = 0; // the first free slot in items array + + // Find the first item which needs to be removed. + while (freeIndex < _size && !match(_items[freeIndex])) + { + freeIndex++; + } + + if (freeIndex >= _size) + { + return 0; + } + + int current = freeIndex + 1; + while (current < _size) + { + // Find the first item which needs to be kept. + while (current < _size && match(_items[current])) + { + current++; + } + + if (current < _size) + { + // copy item to the free slot. + _items[freeIndex++] = _items[current++]; + } + } + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(_items, freeIndex, _size - freeIndex); // Clear the elements so that the gc can reclaim the references. + } + + int result = _size - freeIndex; + _size = freeIndex; + _version++; + return result; + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + public void RemoveAt(int index) + { + if ((uint)index >= (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _size--; + if (index < _size) + { + Array.Copy(_items, index + 1, _items, index, _size - index); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + _items[_size] = default!; + } + _version++; + } + + // Removes a range of elements from this list. + public void RemoveRange(int index, int count) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_size - index < count) + { + throw new ArgumentException("Length must be greater than zero"); + } + + if (count > 0) + { + _size -= count; + if (index < _size) + { + Array.Copy(_items, index + count, _items, index, _size - index); + } + + _version++; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(_items, _size, count); + } + } + } + + // Reverses the elements in this list. + public void Reverse() => Reverse(0, Count); + + // Reverses the elements in a range of this list. Following a call to this + // method, an element in the range given by index and count + // which was previously located at index i will now be located at + // index index + (index + count - i - 1). + // + public void Reverse(int index, int count) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_size - index < count) + { + throw new ArgumentException("Length must be greater than zero"); + } + + if (count > 1) + { + Array.Reverse(_items, index, count); + } + _version++; + } + + // Sorts the elements in this list. Uses the default comparer and + // Array.Sort. + public void Sort() => Sort(0, Count, null); + + // Sorts the elements in this list. Uses Array.Sort with the + // provided comparer. + public void Sort(IComparer? comparer) => Sort(0, Count, comparer); + + // Sorts the elements in a section of this list. The sort compares the + // elements to each other using the given IComparer interface. If + // comparer is null, the elements are compared to each other using + // the IComparable interface, which in that case must be implemented by all + // elements of the list. + // + // This method uses the Array.Sort method to sort the elements. + // + public void Sort(int index, int count, IComparer? comparer) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_size - index < count) + { + throw new ArgumentException("Length must be greater than zero"); + } + + if (count > 1) + { + Array.Sort(_items, index, count, comparer); + } + _version++; + } + + public void Sort(Comparison comparison) + { + if (comparison == null) + { + throw new ArgumentNullException(nameof(comparison)); + } + + if (_size > 1) + { + Array.Sort(_items, comparison); + } + _version++; + } + + // ToArray returns an array containing the contents of the List. + // This requires copying the List, which is an O(n) operation. + public T[] ToArray() + { + if (_size == 0) + { + return s_emptyArray; + } + + T[] array = new T[_size]; + Array.Copy(_items, array, _size); + return array; + } + + public T[] ToPooledArray() + { + if (_size == 0) + { + return s_emptyArray; + } + + T[] array = ArrayPool.Rent(_size); + Array.Copy(_items, array, _size); + return array; + } + + // Sets the capacity of this list to the size of the list. This method can + // be used to minimize a list's memory overhead once it is known that no + // new elements will be added to the list. To completely clear a list and + // release all memory referenced by the list, execute the following + // statements: + // + // list.Clear(); + // list.TrimExcess(); + // + public void TrimExcess() + { + int threshold = (int)(_items.Length * 0.9); + if (_size < threshold) + { + Capacity = _size; + } + } + + public bool TrueForAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + for (int i = 0; i < _size; i++) + { + if (!match(_items[i])) + { + return false; + } + } + return true; + } + + public ref struct Enumerator + { + private readonly PooledRefList _list; + private int _index; + private readonly int _version; + private T? _current; + + internal Enumerator(PooledRefList list) + { + _list = list; + _index = 0; + _version = list._version; + _current = default; + } + + public void Dispose() + { + _index = -2; + _current = default; + } + + public bool MoveNext() + { + PooledRefList localList = _list; + + if (_version == localList._version && (uint)_index < (uint)localList._size) + { + _current = localList._items[_index]; + _index++; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() + { + if (_version != _list._version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + _index = _list._size + 1; + _current = default; + return false; + } + + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_index == 0 || _index == _list._size + 1) + { + ThrowEnumerationNotStartedOrEnded(); + } + return Current; + } + } + + private void ThrowEnumerationNotStartedOrEnded() + { + Debug.Assert(_index is -1 or -2); + throw new InvalidOperationException(_index == -1 ? CollectionThrowStrings.InvalidOperation_EnumNotStarted : CollectionThrowStrings.InvalidOperation_EnumEnded); + } + + public void Reset() + { + if (_version != _list._version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + _index = -1; + _current = default; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + var array = _items; + + if (array.Length > 0) + { + Clear(); + ArrayPool.Return(_items); + } + + this = default; + } +} diff --git a/Projects/Server/Collections/PooledRefQueue.cs b/Projects/Server/Collections/PooledRefQueue.cs index 577e7f6f4..00ff4b505 100644 --- a/Projects/Server/Collections/PooledRefQueue.cs +++ b/Projects/Server/Collections/PooledRefQueue.cs @@ -6,465 +6,492 @@ using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using Server.Buffers; -namespace Server.Collections +namespace Server.Collections; + +// A simple Queue of generic objects. Internally it is implemented as a +// circular buffer, so Enqueue can be O(n). Dequeue is O(1). +[DebuggerDisplay("Count = {Count}")] +public ref struct PooledRefQueue { - // A simple Queue of generic objects. Internally it is implemented as a - // circular buffer, so Enqueue can be O(n). Dequeue is O(1). - [DebuggerDisplay("Count = {Count}")] - [System.Serializable] - public ref struct PooledRefQueue + private T[] _array; + private int _head; // The index from which to dequeue if the queue isn't empty. + private int _tail; // The index at which to enqueue if the queue isn't full. + private int _size; // Number of elements. + private bool _mt; + private int _version; + +#pragma warning disable CA1825 // avoid the extra generic instantiation for Array.Empty() + private static readonly T[] s_emptyArray = new T[0]; +#pragma warning restore CA1825 + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledRefQueue Create(int capacity = 32, bool mt = false) => new(capacity, mt); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PooledRefQueue CreateMT(int capacity = 32) => new(capacity, true); + + // Creates a queue with room for capacity objects. The default grow factor + // is used. + public PooledRefQueue(int capacity, bool mt = false) { - private T[] _array; - private int _head; // The index from which to dequeue if the queue isn't empty. - private int _tail; // The index at which to enqueue if the queue isn't full. - private int _size; // Number of elements. - private int _version; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PooledRefQueue Create(int capacity = 32) => new(capacity); - - // Creates a queue with room for capacity objects. The default grow factor - // is used. - public PooledRefQueue(int capacity) + _mt = mt; + _array = capacity switch { - _array = capacity switch - { - < 0 => throw new ArgumentOutOfRangeException(nameof(capacity), capacity, CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum), - 0 => Array.Empty(), - _ => ArrayPool.Shared.Rent(capacity) - }; + < 0 => throw new ArgumentOutOfRangeException(nameof(capacity), capacity, CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum), + 0 => s_emptyArray, + _ => (mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(capacity) + }; - _head = 0; - _tail = 0; - _size = 0; - _version = 0; - } + _head = 0; + _tail = 0; + _size = 0; + _version = 0; + } - public int Count => _size; + public int Count => _size; - // Removes all Objects from the queue. - public void Clear() + // Removes all Objects from the queue. + public void Clear() + { + if (_size != 0) { - if (_size != 0) - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - if (_head < _tail) - { - Array.Clear(_array, _head, _size); - } - else - { - Array.Clear(_array, _head, _array.Length - _head); - Array.Clear(_array, 0, _tail); - } - } - - _size = 0; - } - - _head = 0; - _tail = 0; - _version++; - } - - // CopyTo copies a collection into an Array, starting at a particular - // index into the array. - public void CopyTo(T[] array, int arrayIndex) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (arrayIndex < 0 || arrayIndex > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, CollectionThrowStrings.ArgumentOutOfRange_Index); - } - - if (array.Length - arrayIndex < _size) - { - throw new ArgumentException(CollectionThrowStrings.Argument_InvalidOffLen); - } - - int numToCopy = _size; - if (numToCopy == 0) - { - return; - } - - int firstPart = Math.Min(_array.Length - _head, numToCopy); - Array.Copy(_array, _head, array, arrayIndex, firstPart); - numToCopy -= firstPart; - if (numToCopy > 0) - { - Array.Copy(_array, 0, array, arrayIndex + _array.Length - _head, numToCopy); - } - } - - // Adds item to the tail of the queue. - public void Enqueue(T item) - { - if (_size == _array.Length) - { - Grow(_size + 1); - } - - _array[_tail] = item; - MoveNext(ref _tail); - _size++; - _version++; - } - - // GetEnumerator returns an IEnumerator over this Queue. This - // Enumerator will support removing. - public Enumerator GetEnumerator() => new(this); - - // Removes the object at the head of the queue and returns it. If the queue - // is empty, this method throws an - // InvalidOperationException. - public T Dequeue() - { - int head = _head; - T[] array = _array; - - if (_size == 0) - { - ThrowForEmptyQueue(); - } - - T removed = array[head]; if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - array[head] = default!; - } - MoveNext(ref _head); - _size--; - _version++; - return removed; - } - - public bool TryDequeue([MaybeNullWhen(false)] out T result) - { - int head = _head; - T[] array = _array; - - if (_size == 0) - { - result = default!; - return false; - } - - result = array[head]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - array[head] = default!; - } - MoveNext(ref _head); - _size--; - _version++; - return true; - } - - // Returns the object at the head of the queue. The object remains in the - // queue. If the queue is empty, this method throws an - // InvalidOperationException. - public T Peek() - { - if (_size == 0) - { - ThrowForEmptyQueue(); - } - - return _array[_head]; - } - - public bool TryPeek([MaybeNullWhen(false)] out T result) - { - if (_size == 0) - { - result = default!; - return false; - } - - result = _array[_head]; - return true; - } - - // Returns true if the queue contains at least one object equal to item. - // Equality is determined using EqualityComparer.Default.Equals(). - public bool Contains(T item) - { - if (_size == 0) - { - return false; - } - - if (_head < _tail) - { - return Array.IndexOf(_array, item, _head, _size) >= 0; - } - - // We've wrapped around. Check both partitions, the least recently enqueued first. - return - Array.IndexOf(_array, item, _head, _array.Length - _head) >= 0 || - Array.IndexOf(_array, item, 0, _tail) >= 0; - } - - // Iterates over the objects in the queue, returning an array of the - // objects in the Queue, or an empty array if the queue is empty. - // The order of elements in the array is first in to last in, the same - // order produced by successive calls to Dequeue. - public T[] ToArray() - { - if (_size == 0) - { - return Array.Empty(); - } - - T[] arr = new T[_size]; - - if (_head < _tail) - { - Array.Copy(_array, _head, arr, 0, _size); - } - else - { - Array.Copy(_array, _head, arr, 0, _array.Length - _head); - Array.Copy(_array, 0, arr, _array.Length - _head, _tail); - } - - return arr; - } - - public T[] ToPooledArray() - { - if (_size == 0) - { - return Array.Empty(); - } - - T[] arr = ArrayPool.Shared.Rent(_size); - - if (_head < _tail) - { - Array.Copy(_array, _head, arr, 0, _size); - } - else - { - Array.Copy(_array, _head, arr, 0, _array.Length - _head); - Array.Copy(_array, 0, arr, _array.Length - _head, _tail); - } - - return arr; - } - - // PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity - // must be >= _size. - private void SetCapacity(int capacity) - { - T[] newarray = ArrayPool.Shared.Rent(capacity); - if (_size > 0) { if (_head < _tail) { - Array.Copy(_array, _head, newarray, 0, _size); + Array.Clear(_array, _head, _size); } else { - Array.Copy(_array, _head, newarray, 0, _array.Length - _head); - Array.Copy(_array, 0, newarray, _array.Length - _head, _tail); + Array.Clear(_array, _head, _array.Length - _head); + Array.Clear(_array, 0, _tail); } } - if (_array.Length > 0) - { - ArrayPool.Shared.Return(_array, true); - } - - _array = newarray; - _head = 0; - _tail = _size == capacity ? 0 : _size; - _version++; + _size = 0; } - // Increments the index wrapping it if necessary. - private void MoveNext(ref int index) + _head = 0; + _tail = 0; + _version++; + } + + // CopyTo copies a collection into an Array, starting at a particular + // index into the array. + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) { - // It is tempting to use the remainder operator here but it is actually much slower - // than a simple comparison and a rarely taken branch. - // JIT produces better code than with ternary operator ?: - int tmp = index + 1; - if (tmp == _array.Length) - { - tmp = 0; - } - index = tmp; + throw new ArgumentNullException(nameof(array)); } - private void ThrowForEmptyQueue() + if (arrayIndex < 0 || arrayIndex > array.Length) { - Debug.Assert(_size == 0); - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EmptyQueue); + throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, CollectionThrowStrings.ArgumentOutOfRange_Index); } - /// - /// Ensures that the capacity of this Queue is at least the specified . - /// - /// The minimum capacity to ensure. - public int EnsureCapacity(int capacity) + if (array.Length - arrayIndex < _size) { - if (capacity < 0) - { - throw new ArgumentOutOfRangeException(nameof(capacity), capacity, CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum); - } - - if (_array.Length < capacity) - { - Grow(capacity); - } - - return _array.Length; + throw new ArgumentException(CollectionThrowStrings.Argument_InvalidOffLen); } - private void Grow(int capacity) + int numToCopy = _size; + if (numToCopy == 0) { - const int GrowFactor = 2; - const int MinimumGrow = 4; - - int newcapacity = GrowFactor * _array.Length; - - // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. - // Note that this check works even when _items.Length overflowed thanks to the (uint) cast - if ((uint)newcapacity > int.MaxValue) - { - newcapacity = int.MaxValue; - } - - // Ensure minimum growth is respected. - newcapacity = Math.Max(newcapacity, _array.Length + MinimumGrow); - - // If the computed capacity is still less than specified, set to the original argument. - // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. - if (newcapacity < capacity) - { - newcapacity = capacity; - } - - SetCapacity(newcapacity); + return; + } + + int firstPart = Math.Min(_array.Length - _head, numToCopy); + Array.Copy(_array, _head, array, arrayIndex, firstPart); + numToCopy -= firstPart; + if (numToCopy > 0) + { + Array.Copy(_array, 0, array, arrayIndex + _array.Length - _head, numToCopy); + } + } + + // Adds item to the tail of the queue. + public void Enqueue(T item) + { + if (_size == _array.Length) + { + Grow(_size + 1); + } + + _array[_tail] = item; + MoveNext(ref _tail); + _size++; + _version++; + } + + // GetEnumerator returns an IEnumerator over this Queue. This + // Enumerator will support removing. + public Enumerator GetEnumerator() => new(this); + + // Removes the object at the head of the queue and returns it. If the queue + // is empty, this method throws an + // InvalidOperationException. + public T Dequeue() + { + int head = _head; + T[] array = _array; + + if (_size == 0) + { + ThrowForEmptyQueue(); + } + + T removed = array[head]; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + array[head] = default!; + } + MoveNext(ref _head); + _size--; + _version++; + return removed; + } + + public bool TryDequeue([MaybeNullWhen(false)] out T result) + { + int head = _head; + T[] array = _array; + + if (_size == 0) + { + result = default!; + return false; + } + + result = array[head]; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + array[head] = default!; + } + MoveNext(ref _head); + _size--; + _version++; + return true; + } + + // Returns the object at the head of the queue. The object remains in the + // queue. If the queue is empty, this method throws an + // InvalidOperationException. + public T Peek() + { + if (_size == 0) + { + ThrowForEmptyQueue(); + } + + return _array[_head]; + } + + public T PeekRandom() + { + if (_size == 0) + { + ThrowForEmptyQueue(); + } + + var index = _head + Utility.Random(_size); + if (index >= _array.Length) + { + index -= _array.Length; + } + + return _array[index]; + } + + public bool TryPeek([MaybeNullWhen(false)] out T result) + { + if (_size == 0) + { + result = default!; + return false; + } + + result = _array[_head]; + return true; + } + + // Returns true if the queue contains at least one object equal to item. + // Equality is determined using EqualityComparer.Default.Equals(). + public bool Contains(T item) + { + if (_size == 0) + { + return false; + } + + if (_head < _tail) + { + return Array.IndexOf(_array, item, _head, _size) >= 0; + } + + // We've wrapped around. Check both partitions, the least recently enqueued first. + return + Array.IndexOf(_array, item, _head, _array.Length - _head) >= 0 || + Array.IndexOf(_array, item, 0, _tail) >= 0; + } + + // Iterates over the objects in the queue, returning an array of the + // objects in the Queue, or an empty array if the queue is empty. + // The order of elements in the array is first in to last in, the same + // order produced by successive calls to Dequeue. + public T[] ToArray() + { + if (_size == 0) + { + return s_emptyArray; + } + + T[] arr = new T[_size]; + + if (_head < _tail) + { + Array.Copy(_array, _head, arr, 0, _size); + } + else + { + Array.Copy(_array, _head, arr, 0, _array.Length - _head); + Array.Copy(_array, 0, arr, _array.Length - _head, _tail); + } + + return arr; + } + + public T[] ToPooledArray(bool mt = false) + { + if (_size == 0) + { + return s_emptyArray; + } + + T[] arr = (mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(_size); + + if (_head < _tail) + { + Array.Copy(_array, _head, arr, 0, _size); + } + else + { + Array.Copy(_array, _head, arr, 0, _array.Length - _head); + Array.Copy(_array, 0, arr, _array.Length - _head, _tail); + } + + return arr; + } + + // PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity + // must be >= _size. + private void SetCapacity(int capacity) + { + T[] newarray = (_mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(capacity); + if (_size > 0) + { + if (_head < _tail) + { + Array.Copy(_array, _head, newarray, 0, _size); + } + else + { + Array.Copy(_array, _head, newarray, 0, _array.Length - _head); + Array.Copy(_array, 0, newarray, _array.Length - _head, _tail); + } + } + + if (_array.Length > 0) + { + Clear(); + (_mt ? ArrayPool.Shared : STArrayPool.Shared).Return(_array); + } + + _array = newarray; + _head = 0; + _tail = _size == capacity ? 0 : _size; + _version++; + } + + // Increments the index wrapping it if necessary. + private void MoveNext(ref int index) + { + // It is tempting to use the remainder operator here but it is actually much slower + // than a simple comparison and a rarely taken branch. + // JIT produces better code than with ternary operator ?: + int tmp = index + 1; + if (tmp == _array.Length) + { + tmp = 0; + } + index = tmp; + } + + private void ThrowForEmptyQueue() + { + Debug.Assert(_size == 0); + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EmptyQueue); + } + + /// + /// Ensures that the capacity of this Queue is at least the specified . + /// + /// The minimum capacity to ensure. + public int EnsureCapacity(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), capacity, CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_array.Length < capacity) + { + Grow(capacity); + } + + return _array.Length; + } + + private void Grow(int capacity) + { + const int GrowFactor = 2; + const int MinimumGrow = 4; + + int newcapacity = GrowFactor * _array.Length; + + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newcapacity > int.MaxValue) + { + newcapacity = int.MaxValue; + } + + // Ensure minimum growth is respected. + newcapacity = Math.Max(newcapacity, _array.Length + MinimumGrow); + + // If the computed capacity is still less than specified, set to the original argument. + // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. + if (newcapacity < capacity) + { + newcapacity = capacity; + } + + SetCapacity(newcapacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + var array = _array; + if (array.Length > 0) + { + Clear(); + (_mt ? ArrayPool.Shared : STArrayPool.Shared).Return(array); + } + + this = default; + } + + // Implements an enumerator for a Queue. The enumerator uses the + // internal version number of the list to ensure that no modifications are + // made to the list while an enumeration is in progress. + public ref struct Enumerator + { + private readonly PooledRefQueue _q; + private readonly int _version; + private int _index; // -1 = not started, -2 = ended/disposed + private T? _currentElement; + + internal Enumerator(PooledRefQueue q) + { + _q = q; + _version = q._version; + _index = -1; + _currentElement = default; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - var array = _array; - if (array.Length > 0) - { - ArrayPool.Shared.Return(array, true); - } - - this = default; + _index = -2; + _currentElement = default; } - // Implements an enumerator for a Queue. The enumerator uses the - // internal version number of the list to ensure that no modifications are - // made to the list while an enumeration is in progress. - public ref struct Enumerator + public bool MoveNext() { - private readonly PooledRefQueue _q; - private readonly int _version; - private int _index; // -1 = not started, -2 = ended/disposed - private T? _currentElement; - - internal Enumerator(PooledRefQueue q) + if (_version != _q._version) { - _q = q; - _version = q._version; - _index = -1; - _currentElement = default; + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); } - public void Dispose() + if (_index == -2) { + return false; + } + + _index++; + + if (_index == _q._size) + { + // We've run past the last element _index = -2; _currentElement = default; + return false; } - public bool MoveNext() + // Cache some fields in locals to decrease code size + T[] array = _q._array; + int capacity = array.Length; + + // _index represents the 0-based index into the queue, however the queue + // doesn't have to start from 0 and it may not even be stored contiguously in memory. + + int arrayIndex = _q._head + _index; // this is the actual index into the queue's backing array + if (arrayIndex >= capacity) { - if (_version != _q._version) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } + // NOTE: Originally we were using the modulo operator here, however + // on Intel processors it has a very high instruction latency which + // was slowing down the loop quite a bit. + // Replacing it with simple comparison/subtraction operations sped up + // the average foreach loop by 2x. - if (_index == -2) - { - return false; - } - - _index++; - - if (_index == _q._size) - { - // We've run past the last element - _index = -2; - _currentElement = default; - return false; - } - - // Cache some fields in locals to decrease code size - T[] array = _q._array; - int capacity = array.Length; - - // _index represents the 0-based index into the queue, however the queue - // doesn't have to start from 0 and it may not even be stored contiguously in memory. - - int arrayIndex = _q._head + _index; // this is the actual index into the queue's backing array - if (arrayIndex >= capacity) - { - // NOTE: Originally we were using the modulo operator here, however - // on Intel processors it has a very high instruction latency which - // was slowing down the loop quite a bit. - // Replacing it with simple comparison/subtraction operations sped up - // the average foreach loop by 2x. - - arrayIndex -= capacity; // wrap around if needed - } - - _currentElement = array[arrayIndex]; - return true; + arrayIndex -= capacity; // wrap around if needed } - public T Current + _currentElement = array[arrayIndex]; + return true; + } + + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - get + if (_index < 0) { - if (_index < 0) - { - ThrowEnumerationNotStartedOrEnded(); - } - - return _currentElement!; - } - } - - private void ThrowEnumerationNotStartedOrEnded() - { - Debug.Assert(_index is -1 or -2); - throw new InvalidOperationException(_index == -1 ? CollectionThrowStrings.InvalidOperation_EnumNotStarted : CollectionThrowStrings.InvalidOperation_EnumEnded); - } - - public void Reset() - { - if (_version != _q._version) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + ThrowEnumerationNotStartedOrEnded(); } - _index = -1; - _currentElement = default; + return _currentElement!; } } + + private void ThrowEnumerationNotStartedOrEnded() + { + Debug.Assert(_index is -1 or -2); + throw new InvalidOperationException(_index == -1 ? CollectionThrowStrings.InvalidOperation_EnumNotStarted : CollectionThrowStrings.InvalidOperation_EnumEnded); + } + + public void Reset() + { + if (_version != _q._version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + _index = -1; + _currentElement = default; + } } } diff --git a/Projects/Server/Configuration/ServerConfiguration.cs b/Projects/Server/Configuration/ServerConfiguration.cs index d32519748..1045a55ea 100644 --- a/Projects/Server/Configuration/ServerConfiguration.cs +++ b/Projects/Server/Configuration/ServerConfiguration.cs @@ -15,424 +15,303 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Net; using Server.Json; using Server.Logging; -namespace Server +namespace Server; + +public static class ServerConfiguration { - public static class ServerConfiguration + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerConfiguration)); + + private const string _relPath = "Configuration/modernuo.json"; + private static readonly string m_FilePath = Path.Join(Core.BaseDirectory, _relPath); + private static ServerSettings m_Settings; + private static bool m_Mocked; + + public static List AssemblyDirectories => m_Settings.AssemblyDirectories; + + public static HashSet DataDirectories => m_Settings.DataDirectories; + + public static List Listeners => m_Settings.Listeners; + + public static ClientVersion GetSetting(string key, ClientVersion defaultValue) => + m_Settings.Settings.TryGetValue(key, out var value) ? new ClientVersion(value) : defaultValue; + + public static string GetSetting(string key, string defaultValue) => + m_Settings.Settings.TryGetValue(key, out var value) ? value : defaultValue; + + public static int GetSetting(string key, int defaultValue) { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerConfiguration)); + m_Settings.Settings.TryGetValue(key, out var strValue); + return int.TryParse(strValue, out var value) ? value : defaultValue; + } - private const string _relPath = "Configuration/modernuo.json"; - private static readonly string m_FilePath = Path.Join(Core.BaseDirectory, _relPath); - private static ServerSettings m_Settings; - private static bool m_Mocked; + public static long GetSetting(string key, long defaultValue) + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return long.TryParse(strValue, out var value) ? value : defaultValue; + } - public static List AssemblyDirectories => m_Settings.AssemblyDirectories; + public static bool GetSetting(string key, bool defaultValue) + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return bool.TryParse(strValue, out var value) ? value : defaultValue; + } - public static List DataDirectories => m_Settings.DataDirectories; + public static T GetSetting(string key, T defaultValue) where T : struct, Enum + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return Enum.TryParse(strValue, out T value) ? value : defaultValue; + } - public static List Listeners => m_Settings.Listeners; + public static double GetSetting(string key, double defaultValue) + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return double.TryParse(strValue, out var value) ? value : defaultValue; + } - public static ClientVersion GetSetting(string key, ClientVersion defaultValue) => - m_Settings.Settings.TryGetValue(key, out var value) ? new ClientVersion(value) : defaultValue; - - public static string GetSetting(string key, string defaultValue) => - m_Settings.Settings.TryGetValue(key, out var value) ? value : defaultValue; - - public static int GetSetting(string key, int defaultValue) + public static T? GetSetting(string key) where T : struct, Enum + { + if (!m_Settings.Settings.TryGetValue(key, out var strValue)) { - m_Settings.Settings.TryGetValue(key, out var strValue); - return int.TryParse(strValue, out var value) ? value : defaultValue; + return null; } - public static long GetSetting(string key, long defaultValue) + return Enum.TryParse(strValue, out T value) ? value : null; + } + + public static string GetOrUpdateSetting(string key, string defaultValue) + { + if (m_Settings.Settings.TryGetValue(key, out var value)) { - m_Settings.Settings.TryGetValue(key, out var strValue); - return long.TryParse(strValue, out var value) ? value : defaultValue; - } - - public static bool GetSetting(string key, bool defaultValue) - { - m_Settings.Settings.TryGetValue(key, out var strValue); - return bool.TryParse(strValue, out var value) ? value : defaultValue; - } - - public static T GetSetting(string key, T defaultValue) where T : struct, Enum - { - m_Settings.Settings.TryGetValue(key, out var strValue); - return Enum.TryParse(strValue, out T value) ? value : defaultValue; - } - - public static T? GetSetting(string key) where T : struct, Enum - { - if (!m_Settings.Settings.TryGetValue(key, out var strValue)) - { - return null; - } - - return Enum.TryParse(strValue, out T value) ? value : null; - } - - public static string GetOrUpdateSetting(string key, string defaultValue) - { - if (m_Settings.Settings.TryGetValue(key, out var value)) - { - return value; - } - - SetSetting(key, value = defaultValue); return value; } - public static int GetOrUpdateSetting(string key, int defaultValue) + SetSetting(key, value = defaultValue); + return value; + } + + public static int GetOrUpdateSetting(string key, int defaultValue) + { + int value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - int value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = int.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = int.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static long GetOrUpdateSetting(string key, long defaultValue) + return value; + } + + public static long GetOrUpdateSetting(string key, long defaultValue) + { + long value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - long value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = long.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = long.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static bool GetOrUpdateSetting(string key, bool defaultValue) + return value; + } + + public static bool GetOrUpdateSetting(string key, bool defaultValue) + { + bool value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - bool value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = bool.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = bool.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static TimeSpan GetOrUpdateSetting(string key, TimeSpan defaultValue) + return value; + } + + public static TimeSpan GetOrUpdateSetting(string key, TimeSpan defaultValue) + { + TimeSpan value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - TimeSpan value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = TimeSpan.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = TimeSpan.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static T GetOrUpdateSetting(string key, T defaultValue) where T : struct, Enum + return value; + } + + public static T GetOrUpdateSetting(string key, T defaultValue) where T : struct, Enum + { + T value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - T value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = Enum.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = Enum.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static void SetSetting(string key, TimeSpan value) => SetSetting(key, value.ToString()); + return value; + } - public static void SetSetting(string key, int value) => SetSetting(key, value.ToString()); + public static double GetOrUpdateSetting(string key, double defaultValue) + { + double value; - public static void SetSetting(string key, long value) => SetSetting(key, value.ToString()); - - public static void SetSetting(string key, bool value) => SetSetting(key, value.ToString()); - - public static void SetSetting(string key, T value) where T : struct, Enum => - SetSetting(key, value.ToString()); - - public static void SetSetting(string key, string value) + if (m_Settings.Settings.TryGetValue(key, out var strValue)) + { + value = double.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); + } + + return value; + } + + public static void SetSetting(string key, double value) => SetSetting(key, value.ToString()); + + public static void SetSetting(string key, TimeSpan value) => SetSetting(key, value.ToString()); + + public static void SetSetting(string key, int value) => SetSetting(key, value.ToString()); + + public static void SetSetting(string key, long value) => SetSetting(key, value.ToString()); + + public static void SetSetting(string key, bool value) => SetSetting(key, value.ToString()); + + public static void SetSetting(string key, T value) where T : struct, Enum => + SetSetting(key, value.ToString()); + + public static void SetSetting(string key, string value) + { + m_Settings.Settings[key] = value; + Save(); + } + + // If mock is enabled we skip the console readline. + public static void Load(bool mocked = false) + { + m_Mocked = mocked; + var updated = false; + + if (File.Exists(m_FilePath)) + { + logger.Information("Reading server configuration from {Path}...", _relPath); + m_Settings = JsonConfig.Deserialize(m_FilePath); + + if (m_Settings == null) + { + logger.Error("Reading server configuration failed"); + throw new FileNotFoundException($"Failed to deserialize {m_FilePath}."); + } + + logger.Information("Reading server configuration done"); + } + else + { + updated = true; + m_Settings = new ServerSettings(); + } + + if (mocked) + { + return; + } + + if (m_Settings.DataDirectories.Count == 0) + { + updated = true; + foreach (var directory in ServerConfigurationPrompts.GetDataDirectories()) + { + m_Settings.DataDirectories.Add(directory); + } + } + + UOClient.Load(); + var cuoClientFiles = UOClient.CuoSettings?.UltimaOnlineDirectory; + + if (cuoClientFiles != null) + { + DataDirectories.Add(cuoClientFiles); + } + + if (m_Settings.Listeners.Count == 0) + { + updated = true; + m_Settings.Listeners.AddRange(ServerConfigurationPrompts.GetListeners()); + } + + bool? isPre60000 = null; + + if (m_Settings.Expansion == null) + { + var expansion = GetSetting("currentExpansion"); + var hasExpansion = expansion != null; + + expansion ??= ServerConfigurationPrompts.GetExpansion(); + + if (expansion <= Expansion.ML && !hasExpansion) + { + isPre60000 = ServerConfigurationPrompts.GetIsClientPre6000(); + if (isPre60000 == true) + { + SetSetting("maps.enablePre6000Trammel", true); + } + } + + updated = true; + m_Settings.Expansion = expansion; + } + + if (isPre60000 != true) + { + if (ServerConfigurationPrompts.GetIsClient7090()) + { + updated = true; + SetSetting("maps.enablePostHSMultiComponentFormat", true); + } + } + + Core.Expansion = m_Settings.Expansion.Value; + + if (updated) { - m_Settings.Settings[key] = value; Save(); - } - - // If mock is enabled we skip the console readline. - public static void Load(bool mocked = false) - { - m_Mocked = mocked; - var updated = false; - - if (File.Exists(m_FilePath)) - { - logger.Information($"Reading server configuration from {_relPath}..."); - m_Settings = JsonConfig.Deserialize(m_FilePath); - - if (m_Settings == null) - { - logger.Error("Reading server configuration failed"); - throw new FileNotFoundException($"Failed to deserialize {m_FilePath}."); - } - - logger.Information("Reading server configuration done"); - } - else - { - updated = true; - m_Settings = new ServerSettings(); - } - - if (mocked) - { - return; - } - - if (m_Settings.DataDirectories.Count == 0) - { - updated = true; - m_Settings.DataDirectories.AddRange(GetDataDirectories()); - } - - if (m_Settings.Listeners.Count == 0) - { - updated = true; - m_Settings.Listeners.AddRange(GetListeners()); - } - - if (m_Settings.Expansion == null) - { - var expansion = GetSetting("currentExpansion"); - var hasExpansion = expansion != null; - - expansion ??= GetExpansion(); - - if (expansion <= Expansion.ML && !hasExpansion) - { - SetPre6000Support(); - } - - updated = true; - m_Settings.Expansion = expansion; - } - - Core.Expansion = m_Settings.Expansion.Value; - - if (updated) - { - Save(); - Console.Write("Server configuration saved to "); - Utility.PushColor(ConsoleColor.Green); - Console.WriteLine($"{_relPath}."); - Utility.PopColor(); - } - } - - private static void SetPre6000Support() - { - Console.WriteLine("Will you be using a client version older than 6.0.0.0?"); - - do - { - Console.Write("y or [n]> "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("n")) - { - Utility.PushColor(ConsoleColor.Yellow); - Console.WriteLine("Client >= 6.0.0.0 chosen."); - Utility.PopColor(); - return; - } - - if (input.InsensitiveStartsWith("y")) - { - SetSetting("maps.enablePre6000Trammel", true.ToString()); - - Utility.PushColor(ConsoleColor.Yellow); - Console.WriteLine("Client <= 5.0.9.1 chosen."); - Utility.PopColor(); - return; - } - - Console.Write("Invalid option "); - Utility.PushColor(ConsoleColor.Red); - Console.Write(input); - Utility.PopColor(); - Console.WriteLine(". Press y for yes or n for no."); - } while (true); - } - - private static Expansion GetExpansion() - { - Console.WriteLine("Please choose an expansion by typing the number or short name:"); - var expansions = ExpansionInfo.Table; - - for (int i = 0; i < expansions.Length; i++) - { - var info = expansions[i]; - Console.WriteLine(" - {0,2}: {1} ({2})", i, ((Expansion)info.ID).ToString(), info.Name); - } - - var maxExpansion = (Expansion)expansions[^1].ID; - var maxExpansionName = maxExpansion.ToString(); - - do - { - Console.Write("[enter for {0}]> ", maxExpansionName); - var input = Console.ReadLine(); - Expansion expansion; - - if (string.IsNullOrWhiteSpace(input)) - { - expansion = maxExpansion; - } - else if (int.TryParse(input, NumberStyles.Integer, null, out var number) && - number >= 0 && number < expansions.Length) - { - expansion = (Expansion)number; - } - else if (!Enum.TryParse(input, out expansion)) - { - Utility.PushColor(ConsoleColor.Red); - Console.Write(input); - Utility.PopColor(); - Console.WriteLine(" is an invalid expansion option."); - continue; - } - - Console.Write("Expansion set to "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(ExpansionInfo.GetInfo(expansion).Name); - Utility.PopColor(); - Console.WriteLine("."); - return expansion; - } while (true); - } - - private static List GetDataDirectories() - { - Console.WriteLine("Please enter the absolute path to the Ultima Online data:"); - - var directories = new List(); - - do - { - Console.Write("{0}> ", directories.Count > 0 ? "[enter to finish]" : " "); - var directory = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(directory)) - { - break; - } - - if (Directory.Exists(directory)) - { - directories.Add(directory); - Console.Write("Added "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(directory); - Utility.PopColor(); - Console.WriteLine("."); - } - else - { - Utility.PushColor(ConsoleColor.Red); - Console.Write(directory); - Utility.PopColor(); - Console.WriteLine(" does not exist."); - } - } while (true); - - return directories; - } - - private static List GetListeners() - { - Console.WriteLine("Please enter the IP and ports to listen:"); - Console.WriteLine(" - Only enter IP addresses directly bound to this machine"); - Console.WriteLine(" - To listen to all IP addresses enter 0.0.0.0"); - - var ips = new List(); - - do - { - // IP:Port? - Console.Write("[{0}]> ", ips.Count > 0 ? "enter to finish" : "0.0.0.0:2593"); - var ipStr = Console.ReadLine(); - - IPEndPoint ip; - if (string.IsNullOrWhiteSpace(ipStr)) - { - if (ips.Count > 0) - { - break; - } - - ip = new IPEndPoint(IPAddress.Any, 2593); - } - else - { - if (!ipStr.ContainsOrdinal(':')) - { - ipStr += ":2593"; - } - - if (!IPEndPoint.TryParse(ipStr, out ip)) - { - Utility.PushColor(ConsoleColor.Red); - Console.Write(ipStr); - Utility.PopColor(); - Console.WriteLine(" is not a valid IP or port."); - continue; - } - } - - ips.Add(ip); - Console.Write("Added "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(ip); - Utility.PopColor(); - Console.WriteLine("."); - } while (true); - - return ips; - } - - public static void Save() - { - if (m_Mocked) - { - return; - } - - JsonConfig.Serialize(m_FilePath, m_Settings); + Console.Write("Server configuration saved to "); + Utility.PushColor(ConsoleColor.Green); + Console.WriteLine($"{_relPath}."); + Utility.PopColor(); } } + + public static void Save() + { + if (m_Mocked) + { + return; + } + + JsonConfig.Serialize(m_FilePath, m_Settings); + } } diff --git a/Projects/Server/Configuration/ServerConfigurationPrompts.cs b/Projects/Server/Configuration/ServerConfigurationPrompts.cs new file mode 100644 index 000000000..7976870a6 --- /dev/null +++ b/Projects/Server/Configuration/ServerConfigurationPrompts.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; + +namespace Server; + +public static class ServerConfigurationPrompts +{ + internal static bool GetIsClient7090() + { + if (UOClient.ServerClientVersion != null) + { + return UOClient.ServerClientVersion >= ClientVersion.Version7090; + } + + Console.WriteLine("Will you be using a client version 7.0.9.0 or newer?"); + + do + { + Console.Write("[y] or n> "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("y")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client >= 7.0.9.0 chosen."); + Utility.PopColor(); + return true; + } + + if (input.InsensitiveStartsWith("n")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client < 7.0.9.0 chosen."); + Utility.PopColor(); + return false; + } + + Console.Write("Invalid option "); + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(". Press y for yes or n for no."); + } while (true); + } + + + internal static bool GetIsClientPre6000() + { + if (UOClient.ServerClientVersion != null) + { + return UOClient.ServerClientVersion < ClientVersion.Version6000; + } + + Console.WriteLine("Will you be using a client version older than 6.0.0.0?"); + + do + { + Console.Write("y or [n]> "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("n")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client >= 6.0.0.0 chosen."); + Utility.PopColor(); + return false; + } + + if (input.InsensitiveStartsWith("y")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client < 6.0.0.0 chosen."); + Utility.PopColor(); + return true; + } + + Console.Write("Invalid option "); + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(". Press y for yes or n for no."); + } while (true); + } + + internal static Expansion GetExpansion() + { + Console.WriteLine("Please choose an expansion by typing the number or short name:"); + var expansions = ExpansionInfo.Table; + + for (int i = 0; i < expansions.Length; i++) + { + var info = expansions[i]; + Console.WriteLine(" - {0,2}: {1} ({2})", i, ((Expansion)info.ID).ToString(), info.Name); + } + + var maxExpansion = (Expansion)expansions[^1].ID; + var maxExpansionName = maxExpansion.ToString(); + + do + { + Console.Write("[enter for {0}]> ", maxExpansionName); + var input = Console.ReadLine(); + Expansion expansion; + + if (string.IsNullOrWhiteSpace(input)) + { + expansion = maxExpansion; + } + else if (int.TryParse(input, NumberStyles.Integer, null, out var number) && + number >= 0 && number < expansions.Length) + { + expansion = (Expansion)number; + } + else if (!Enum.TryParse(input, out expansion)) + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(" is an invalid expansion option."); + continue; + } + + Console.Write("Expansion set to "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(ExpansionInfo.GetInfo(expansion).Name); + Utility.PopColor(); + Console.WriteLine("."); + return expansion; + } while (true); + } + + internal static List GetDataDirectories() + { + Console.WriteLine("Please enter the absolute path to your ClassicUO or Ultima Online data:"); + + var directories = new List(); + + do + { + Console.Write("{0}> ", directories.Count > 0 ? "[enter to finish]" : " "); + var directory = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(directory)) + { + break; + } + + if (Directory.Exists(directory)) + { + directories.Add(directory); + Console.Write("Added "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(directory); + Utility.PopColor(); + Console.WriteLine("."); + } + else + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(directory); + Utility.PopColor(); + Console.WriteLine(" does not exist."); + } + } while (true); + + return directories; + } + + internal static List GetListeners() + { + Console.WriteLine("Please enter the IP and ports to listen:"); + Console.WriteLine(" - Only enter IP addresses directly bound to this machine"); + Console.WriteLine(" - To listen to all IP addresses enter 0.0.0.0"); + + var ips = new List(); + + do + { + // IP:Port? + Console.Write("[{0}]> ", ips.Count > 0 ? "enter to finish" : "0.0.0.0:2593"); + var ipStr = Console.ReadLine(); + + IPEndPoint ip; + if (string.IsNullOrWhiteSpace(ipStr)) + { + if (ips.Count > 0) + { + break; + } + + ip = new IPEndPoint(IPAddress.Any, 2593); + } + else + { + if (!ipStr.ContainsOrdinal(':')) + { + ipStr += ":2593"; + } + + if (!IPEndPoint.TryParse(ipStr, out ip)) + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(ipStr); + Utility.PopColor(); + Console.WriteLine(" is not a valid IP or port."); + continue; + } + } + + ips.Add(ip); + Console.Write("Added "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(ip); + Utility.PopColor(); + Console.WriteLine("."); + } while (true); + + return ips; + } +} diff --git a/Projects/Server/Configuration/ServerSettings.cs b/Projects/Server/Configuration/ServerSettings.cs index 87f69a595..23eddb6de 100644 --- a/Projects/Server/Configuration/ServerSettings.cs +++ b/Projects/Server/Configuration/ServerSettings.cs @@ -25,7 +25,7 @@ namespace Server public List AssemblyDirectories { get; set; } = new(); [JsonPropertyName("dataDirectories")] - public List DataDirectories { get; set; } = new(); + public HashSet DataDirectories { get; set; } = new(); [JsonPropertyName("listeners")] public List Listeners { get; set; } = new(); diff --git a/Projects/Server/Events/EventSink.cs b/Projects/Server/Events/EventSink.cs index 9e168c742..29f8fda70 100644 --- a/Projects/Server/Events/EventSink.cs +++ b/Projects/Server/Events/EventSink.cs @@ -64,6 +64,9 @@ namespace Server public static event Action Connected; public static void InvokeConnected(Mobile m) => Connected?.Invoke(m); + public static event Action BeforeDisconnected; + public static void InvokeBeforeDisconnected(Mobile m) => BeforeDisconnected?.Invoke(m); + public static event Action Disconnected; public static void InvokeDisconnected(Mobile m) => Disconnected?.Invoke(m); diff --git a/Projects/Server/ExpansionInfo.cs b/Projects/Server/ExpansionInfo.cs index 18407c052..909658add 100644 --- a/Projects/Server/ExpansionInfo.cs +++ b/Projects/Server/ExpansionInfo.cs @@ -57,17 +57,17 @@ namespace Server { None = 0x00000000, T2A = 0x00000001, - UOR = 0x00000002, + UOR = 0x00000002, // In later clients, the T2A/UOR flags are negative feature flags to disable body replacement of Pre-AOS graphics. UOTD = 0x00000004, LBR = 0x00000008, AOS = 0x00000010, SixthCharacterSlot = 0x00000020, SE = 0x00000040, ML = 0x00000080, - EigthAge = 0x00000100, - NinthAge = 0x00000200, /* Crystal/Shadow Custom House Tiles */ + EighthAge = 0x00000100, + NinthAge = 0x00000200, // Crystal/Shadow Custom House Tiles TenthAge = 0x00000400, - IncreasedStorage = 0x00000800, /* Increased Housing/Bank Storage */ + IncreasedStorage = 0x00000800, // Increased Housing/Bank Storage SeventhCharacterSlot = 0x00001000, RoleplayFaces = 0x00002000, TrialAccount = 0x00004000, @@ -86,7 +86,7 @@ namespace Server ExpansionUOR = ExpansionT2A | UOR, ExpansionUOTD = ExpansionUOR | UOTD, ExpansionLBR = ExpansionUOTD | LBR, - ExpansionAOS = ExpansionLBR | AOS | LiveAccount, + ExpansionAOS = LBR | AOS | LiveAccount, ExpansionSE = ExpansionAOS | SE, ExpansionML = ExpansionSE | ML | NinthAge, ExpansionSA = ExpansionML | SA | Gothic | Rustic, @@ -158,6 +158,12 @@ namespace Server public class ExpansionInfo { + public static bool ForceOldAnimations { get; private set; } + public static void Configure() + { + ForceOldAnimations = ServerConfiguration.GetSetting("expansion.forceOldAnimations", false); + } + public static string GetEraFolder(string parentDirectory) { var expansion = Core.Expansion; @@ -264,32 +270,6 @@ namespace Server public ClientVersion RequiredClient { get; set; } public HousingFlags CustomHousingFlag { get; set; } - public static FeatureFlags GetFeatures(Expansion ex) - { - var info = GetInfo(ex); - - if (info != null) - { - return info.SupportedFeatures; - } - - return ex switch - { - Expansion.T2A => FeatureFlags.ExpansionT2A, - Expansion.UOR => FeatureFlags.ExpansionUOR, - Expansion.UOTD => FeatureFlags.ExpansionUOTD, - Expansion.LBR => FeatureFlags.ExpansionLBR, - Expansion.AOS => FeatureFlags.ExpansionAOS, - Expansion.SE => FeatureFlags.ExpansionSE, - Expansion.ML => FeatureFlags.ExpansionML, - Expansion.SA => FeatureFlags.ExpansionSA, - Expansion.HS => FeatureFlags.ExpansionHS, - Expansion.TOL => FeatureFlags.ExpansionTOL, - Expansion.EJ => FeatureFlags.EJ, - _ => FeatureFlags.ExpansionNone - }; - } - public static ExpansionInfo GetInfo(Expansion ex) => GetInfo((int)ex); public static ExpansionInfo GetInfo(int ex) diff --git a/Projects/Server/GarbageCollection/Gen2GcCallback.cs b/Projects/Server/GarbageCollection/Gen2GcCallback.cs new file mode 100644 index 000000000..fc23db8ce --- /dev/null +++ b/Projects/Server/GarbageCollection/Gen2GcCallback.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace System; + +/// +/// Schedules a callback roughly every gen 2 GC (you may see a Gen 0 an Gen 1 but only once) +/// (We can fix this by capturing the Gen 2 count at startup and testing, but I mostly don't care) +/// +internal sealed class Gen2GcCallback : CriticalFinalizerObject +{ + private readonly Func _callback; + private GCHandle _weakTargetObj; + + private Gen2GcCallback(Func callback, object targetObj) + { + _callback = callback; + _weakTargetObj = GCHandle.Alloc(targetObj, GCHandleType.Weak); + } + + /// + /// Schedule 'callback' to be called in the next GC. If the callback returns true it is + /// rescheduled for the next Gen 2 GC. Otherwise the callbacks stop. + /// + /// NOTE: This callback will be kept alive until either the callback function returns false, + /// or the target object dies. + /// + public static void Register(Func callback, object targetObj) + { + // Create a unreachable object that remembers the callback function and target object. + new Gen2GcCallback(callback, targetObj); + } + + ~Gen2GcCallback() + { + if (_weakTargetObj.IsAllocated) + { + // Check to see if the target object is still alive. + object? targetObj = _weakTargetObj.Target; + if (targetObj == null) + { + // The target object is dead, so this callback object is no longer needed. + _weakTargetObj.Free(); + return; + } + + // Execute the callback method. + try + { + Debug.Assert(_callback != null); + if (_callback?.Invoke(targetObj) != true) + { + // If the callback returns false, this callback object is no longer needed. + _weakTargetObj.Free(); + return; + } + } + catch + { + // Ensure that we still get a chance to resurrect this object, even if the callback throws an exception. +#if DEBUG + // Except in DEBUG, as we really shouldn't be hitting any exceptions here. + throw; +#endif + } + } + + // Resurrect ourselves by re-registering for finalization. + GC.ReRegisterForFinalize(this); + } +} diff --git a/Projects/Server/Geometry/Point2D.cs b/Projects/Server/Geometry/Point2D.cs index 8e42236d3..efee210dd 100644 --- a/Projects/Server/Geometry/Point2D.cs +++ b/Projects/Server/Geometry/Point2D.cs @@ -47,7 +47,7 @@ namespace Server m_Y = y; } - public Point2D(IPoint2D p) : this(p.X, p.Y) + public Point2D(Point2D p) : this(p.X, p.Y) { } @@ -58,12 +58,12 @@ namespace Server var start = value.IndexOfOrdinal('('); var end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var x); start = end; end = value.IndexOf(')', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var y); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var y); return new Point2D(x, y); } diff --git a/Projects/Server/Geometry/Point3D.cs b/Projects/Server/Geometry/Point3D.cs index 80588ef6d..96ce2f94e 100644 --- a/Projects/Server/Geometry/Point3D.cs +++ b/Projects/Server/Geometry/Point3D.cs @@ -14,6 +14,7 @@ *************************************************************************/ using System; +using System.Runtime.CompilerServices; namespace Server { @@ -49,11 +50,16 @@ namespace Server set => m_Z = value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Point3D(IPoint3D p) : this(p.X, p.Y, p.Z) { } - public Point3D(IPoint2D p, int z) : this(p.X, p.Y, z) + public Point3D(Point3D p) : this(p.X, p.Y, p.Z) + { + } + + public Point3D(Point2D p, int z) : this(p.X, p.Y, z) { } @@ -80,17 +86,17 @@ namespace Server var start = value.IndexOfOrdinal('('); var end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var x); start = end; end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var y); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var y); start = end; end = value.IndexOf(')', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var z); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var z); return new Point3D(x, y, z); } diff --git a/Projects/Server/Geometry/Rectangle2D.cs b/Projects/Server/Geometry/Rectangle2D.cs index 901cea1c2..3eaefc70a 100644 --- a/Projects/Server/Geometry/Rectangle2D.cs +++ b/Projects/Server/Geometry/Rectangle2D.cs @@ -13,6 +13,8 @@ * along with this program. If not, see . * *************************************************************************/ +using System; + namespace Server { [NoSort] @@ -46,22 +48,22 @@ namespace Server var start = value.IndexOfOrdinal('('); var end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var x); start = end; end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var y); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var y); start = end; end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var w); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var w); start = end; end = value.IndexOf(')', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var h); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var h); return new Rectangle2D(x, y, w, h); } diff --git a/Projects/Server/Geometry/WorldLocation.cs b/Projects/Server/Geometry/WorldLocation.cs index 466f8ef82..f2e28eaac 100644 --- a/Projects/Server/Geometry/WorldLocation.cs +++ b/Projects/Server/Geometry/WorldLocation.cs @@ -145,22 +145,22 @@ namespace Server var start = value.IndexOfOrdinal('('); var end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var x); start = end; end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var y); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var y); start = end; end = value.IndexOf(',', start + 1); - Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var z); + Utility.ToInt32(value.AsSpan(start + 1, end - (start + 1)).Trim(), out var z); start = end; end = value.IndexOf(')', start + 1); - var map = Map.Parse(value.Substring(start + 1, end - (start + 1)).Trim()); + var map = Map.Parse(value.AsSpan(start + 1, end - (start + 1)).Trim()); return new WorldLocation(x, y, z, map); } diff --git a/Projects/Server/Guild.cs b/Projects/Server/Guild.cs index 5ff38abd9..27fb2015b 100644 --- a/Projects/Server/Guild.cs +++ b/Projects/Server/Guild.cs @@ -135,6 +135,6 @@ namespace Server.Guilds return results; } - public override string ToString() => $"0x{Serial.Value:X} \"{Name} [{Abbreviation}]\""; + public override string ToString() => $"{Serial} \"{Name} [{Abbreviation}]\""; } } diff --git a/Projects/Server/Gumps/Grid/Grid.cs b/Projects/Server/Gumps/Grid/Grid.cs new file mode 100644 index 000000000..2cdb8b075 --- /dev/null +++ b/Projects/Server/Gumps/Grid/Grid.cs @@ -0,0 +1,77 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Grid.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Collections.Generic; + +namespace Server.Gumps; + +public enum GridHues +{ + White = 2049, // Change to 0x480 if you have old hues.mul + Black = 0, +} + +public static class GridColors +{ + public const string Gold = "#ffc959"; + public const string White = "#ffffff"; + public const string Blue = "#1D63DC"; + public const string LightBlue = "#7799EE"; + public const string Green = "#80E732"; + public const string Orange = "#F28B00"; + public const string Purple = "#F3ACF6"; + public const string Red = "#B52B2D"; + public const string LightGreen = "#E6FFC0"; + public const string Yellow = "#FFFFBB"; +} + +public class ListItem +{ + public int X { get; set; } + public int Y { get; set; } + public int Index { get; set; } + public Col[] Cols { get; set; } +} + +public class Col +{ + public int X { get; set; } + public int Width { get; set; } + public int HCenter => X + Width / 2; + public int HEnd => X + Width; +} + +public class Row +{ + public int Y { get; set; } + public int Height { get; set; } + public int VCenter => Y + Height / 2; + public int VEnd => Y + Height; +} + +public class Grid +{ + public string Name { get; set; } + public int Height { get; set; } + public int Width { get; set; } + public List Columns { get; set; } = new(); + public List Rows { get; set; } = new(); +} + +public class Swap +{ + public int Index { get; set; } + public int Size { get; set; } +} diff --git a/Projects/Server/Gumps/Grid/GumpGrid.cs b/Projects/Server/Gumps/Grid/GumpGrid.cs new file mode 100644 index 000000000..1c5d87118 --- /dev/null +++ b/Projects/Server/Gumps/Grid/GumpGrid.cs @@ -0,0 +1,525 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: GumpGrid.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using Server.Collections; + +namespace Server.Gumps; + +public class GumpGrid : Gump +{ + private Dictionary _grids = new(); + + public GumpGrid(int x, int y) : base(x, y) + { + } + + private void AddColumn(string name, int x, int width) + { + _grids[name].Columns.Add(new Col { X = x, Width = width, }); + } + + private void AddRow(string name, int y, int height) + { + _grids[name].Rows.Add(new Row { Y = y, Height = height }); + } + + private void InitColumn(string name, int column, int w, int x) + { + var width = w / column; + for (int i = 0; i < column; i++) + { + AddColumn(name, i * width + x, width); + } + } + + private void InitRow(string name, int row, int h, int y) + { + var height = h / row; + for (int i = 0; i < row; i++) + { + AddRow(name, i * height + y, height); + } + } + + private static Swap Exist(ref PooledRefList list, int index) + { + for (int i = 0; i < list.Count; i++) + { + var item = list[i]; + if (item.Index == index) + { + return item; + } + } + + return null; + } + + private static int CalculateCustom(int column, int len, string[] sizes, ref PooledRefList list) + { + var cropSize = 0; + + for (int i = 0; i < column; i++) + { + if (sizes[i] == "*") + { + continue; + } + + //percent + var percent = sizes[i].Replace("*", ""); + var size = sizes[i] != percent ? len / 100 * int.Parse(percent) : int.Parse(percent); + + list.Add(new Swap { Index = i, Size = size }); + cropSize += size; + } + + return cropSize; + } + private void InitCustomSizeRow(string name, int height, int row, string[] sizes, int y) + { + var list = PooledRefList.Create(); + var cropHeight = CalculateCustom(row, height, sizes, ref list); + var Height = height - cropHeight; + var factor = row - list.Count; + for (int i = 0; i < row; i++) + { + var swap = Exist(ref list, i); + if (swap != null) + { + var size = swap.Size; + if (i == 0) + { + AddRow(name, y, size); + } + else + { + var col = _grids[name].Rows[i - 1]; + AddRow(name, col.Height + col.Y + y, size); + } + } + else + { + var size = Height / factor; + if (i == 0) + { + AddRow(name, y, size); + } + else + { + var col = _grids[name].Rows[i - 1]; + AddRow(name, col.Height + col.Y + y, size); + } + } + } + + list.Dispose(); + } + + private void InitCustomSizeColumn(string name, int width, int column, string[] sizes, int x) + { + var list = PooledRefList.Create(); + var cropWidth = CalculateCustom(column, width, sizes, ref list); + var Width = width - cropWidth; + var factor = column - list.Count; + + for (int i = 0; i < column; i++) + { + var swap = Exist(ref list, i); + if (swap != null) + { + var size = swap.Size; + if (i == 0) + { + AddColumn(name, x, size); + } + else + { + var col = _grids[name].Columns[i - 1]; + AddColumn(name, col.Width + col.X + x, size); + } + } + else + { + var size = Width / factor; + if (i == 0) + { + AddColumn(name, x, size); + } + else + { + var col = _grids[name].Columns[i - 1]; + AddColumn(name, col.Width + col.X + x, size); + } + } + } + } + + public Grid SubGrid( + string name, + string destGridName, + int columnStart, + int rowStart, + int createColumnsCount, + int createRowsCount, + int columnSpan = 0, + int rowSpan = 0, + string createColumnSize = "", + string createRowSize = "", + int marginX = 0, + int marginY = 0 + ) + { + if (CalculateCord( + destGridName, + columnStart, + rowStart, + columnSpan, + rowSpan, + out var x, + out var y, + out var width, + out var height + )) + { + Grid( + name, + width, + height, + createColumnsCount, + createRowsCount, + createColumnSize, + createRowSize, + x + marginX, + y + marginY + ); + } + + return CreateGrid(name, 0, 0); + } + + public Grid CreateGrid(string name, int width, int height) + { + if (!_grids.TryGetValue(name, out var grid)) + { + grid = new Grid { Name = name, Width = width, Height = height }; + _grids.Add(name, grid); + } + + return grid; + } + + public Grid Grid( + string name, + int width, + int height, + int columns, + int rows, + string columnSize = "", + string rowSize = "", + int x = 0, + int y = 0 + ) + { + if (columns < 1) + { + throw new Exception(nameof(columns)); + } + + if (rows < 1) + { + throw new Exception(nameof(columns)); + } + + var Grid = CreateGrid(name, width, height); + + if (columnSize.Length > 0) + { + var buffer = columnSize.Split(' '); + if (buffer.Length == columns) + { + InitCustomSizeColumn(name, width, columns, buffer, x); + } + else + { + throw new Exception(nameof(columnSize)); + } + } + else + { + InitColumn(name, columns, width, x); + } + + if (rowSize.Length > 0) + { + var buffer = rowSize.Split(' '); + if (buffer.Length == rows) + { + InitCustomSizeRow(name, height, rows, buffer, y); + } + else + { + throw new Exception(nameof(rowSize)); + } + } + else + { + InitRow(name, rows, height, y); + } + + return Grid; + } + + public bool CalculateCord( + string name, + int column, + int row, + int columnSpan, + int rowSpan, + out int x, + out int y, + out int width, + out int height + ) + { + x = 0; y = 0; width = 0; height = 0; + if (_grids.TryGetValue(name, out var grid)) + { + var count = columnSpan == 0 ? column + 1 : row + columnSpan; + for (int i = column; i < count; i++) + { + if (i >= grid.Columns.Count) + { + break; + } + + var dcol = grid.Columns[i]; + if (i == column) + { + x = dcol.X; + } + + width += dcol.Width; + } + + count = rowSpan == 0 ? row + 1 : row + rowSpan; + for (int i = row; i < count; i++) + { + if (i >= grid.Rows.Count) + { + break; + } + + var drow = grid.Rows[i]; + if (i == row) + { + y = drow.Y; + } + + height += drow.Height; + } + + if (width == 0) + { + width = grid.Width; + } + + if (height == 0) + { + height = grid.Height; + } + + return true; + + } + + return false; + } + + public class ListView + { + public int LineCount { get; } + public int ItemsCount { get; } + public ListItem[] Items { get; } + public ListItem Header { get; } + public int Page { get; } + public int TotalPages { get; } + public int ColHeight { get; } + public bool CanNext { get; } + public bool CanBack { get; } + public int ColsCount { get; } + + public ListView( + int itemsCount, + int columns, + int heightPerItem, + int page, + int x, + int y, + int height, + int pageItemCount, + int[] ColWidth, + int marginX = 0, + int marginY = 0, + int headerHeight = 0 + ) + { + ItemsCount = itemsCount; + var itemsPerPageCount = pageItemCount == 0 ? (height - marginY) / heightPerItem : pageItemCount; + LineCount = itemsPerPageCount; + + if (itemsCount > 0) + { + TotalPages = itemsCount / itemsPerPageCount; + if (itemsCount % itemsPerPageCount == 0) + { + TotalPages--; + } + } + + Page = page > TotalPages ? TotalPages : page; + ColsCount = columns; + ColHeight = heightPerItem; + + var index = Page * itemsPerPageCount > 0 ? Page * itemsPerPageCount : 0; + var nowPageItems = index + itemsPerPageCount; + + if (index > 0) + { + CanBack = true; + } + + if (nowPageItems < itemsCount) + { + CanNext = true; + } + + if (nowPageItems > itemsCount) + { + itemsPerPageCount = Page > 0 ? itemsCount - Page * itemsPerPageCount : itemsCount; + } + + //header + Header = new ListItem + { + X = x, + Y = y + marginY, + Index = -1, + Cols = new Col[columns], + }; + + var length = x + marginX; + for (int col = 0; col < columns; col++) + { + Header.Cols[col] = new Col(); + Header.Cols[col].Width = ColWidth[col]; + Header.Cols[col].X = length; + length += ColWidth[col]; + } + + Items = new ListItem[itemsPerPageCount]; + + for (int i = 0; i < itemsPerPageCount; i++) + { + Items[i] = new ListItem + { + X = x, + Y = y + i * heightPerItem + marginY + headerHeight, + Index = index + i, + Cols = Header.Cols + }; + } + } + } + + public ListView AddListView( + string name, + int column, + int row, + int itemsCount, + int page, + int heightPerItem, + int createColumn, + int columnSpan = 0, + int rowSpan = 0, + int width = 0, + int height = 0, + int pageItemCount = 0, + int marginX = 0, + int marginY = 0, + int headerHeight = 0, + string colSize = "" + ) + { + if (CalculateCord(name, column, row, columnSpan, rowSpan, out var x, out var y, out var w, out var h)) + { + w = width > 0 ? width : w; + h = height > 0 ? height : h; + + int[] colWidth = new int[createColumn]; + + if (colSize == string.Empty) + { + for (int i = 0; i < createColumn; i++) + { + colWidth[i] = w / createColumn; + } + } + else + { + var buffer = colSize.Split(' '); + + if (buffer.Length > 0) + { + const string listName = "listView"; + _grids.Add(listName, new Grid()); + + if (createColumn < buffer.Length) + { + Array.Resize(ref buffer, createColumn); + } + + InitCustomSizeColumn(listName, w, createColumn, buffer, x); + var cols = _grids[listName].Columns; + for (var i = 0; i < createColumn; i++) + { + colWidth[i] = cols[i].Width; + } + + _grids.Remove(listName); + } + } + + return new ListView( + itemsCount, + createColumn, + heightPerItem, + page, + x, + y, + h, + pageItemCount, + colWidth, + marginX, + marginY, + headerHeight + ); + } + + return null; + } +} diff --git a/Projects/Server/Gumps/Gump.cs b/Projects/Server/Gumps/Gump.cs index 29129be0c..e366c34ef 100644 --- a/Projects/Server/Gumps/Gump.cs +++ b/Projects/Server/Gumps/Gump.cs @@ -4,276 +4,283 @@ using System.Text; using System.Text.Json; using Server.Network; using Server.Text; +using Server.Utilities; -namespace Server.Gumps +namespace Server.Gumps; + +public partial class Gump { - public class Gump + private static Serial _nextSerial = (Serial)1; + + public static readonly byte[] WebRender = StringToBuffer("{ webrender }"); + public static readonly byte[] NoMove = StringToBuffer("{ nomove }"); + public static readonly byte[] NoClose = StringToBuffer("{ noclose }"); + public static readonly byte[] NoDispose = StringToBuffer("{ nodispose }"); + public static readonly byte[] NoResize = StringToBuffer("{ noresize }"); + + internal int m_TextEntries, m_Switches; + + public Gump(int x, int y) { - private static Serial _nextSerial = (Serial)1; - public static readonly byte[] WebRender = StringToBuffer("{ webrender }"); - public static readonly byte[] NoMove = StringToBuffer("{ nomove }"); - public static readonly byte[] NoClose = StringToBuffer("{ noclose }"); - public static readonly byte[] NoDispose = StringToBuffer("{ nodispose }"); - public static readonly byte[] NoResize = StringToBuffer("{ noresize }"); - - internal int m_TextEntries, m_Switches; - - public Gump(int x, int y) + do { - do - { - Serial = _nextSerial++; - } while (Serial == 0); // standard client apparently doesn't send a gump response packet if serial == 0 + Serial = _nextSerial++; + } while (Serial == 0); // standard client apparently doesn't send a gump response packet if serial == 0 - X = x; - Y = y; + X = x; + Y = y; - TypeID = GetTypeID(GetType()); + TypeID = GetTypeID(GetType()); - Entries = new List(); - Strings = new List(); + Entries = new List(); + Strings = new List(); + } + + public List Strings { get; } + + public int TypeID { get; } + + public List Entries { get; } + + public Serial Serial { get; set; } + + public int X { get; set; } + + public int Y { get; set; } + + public bool Disposable { get; set; } = true; + + public bool Resizable { get; set; } = true; + + public bool Draggable { get; set; } = true; + + public bool Closable { get; set; } = true; + + public bool UseWebRender { get; set; } = false; + + public static int GetTypeID(Type type) => type?.FullName?.GetHashCode(StringComparison.Ordinal) ?? -1; + + 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 SendData(Mobile m, string responseMethod, object value) + { + Entries.Clear(); + + var jtext = JsonSerializer.Serialize(value, new JsonSerializerOptions() { WriteIndented = true }); + Add(new GumpBodyUpdate(responseMethod, + Convert.ToBase64String(Encoding.UTF8.GetBytes(jtext)))); + + m.SendGump(this); + } + + public void AddBody(string body) + { + Add(new GumpBody(Convert.ToBase64String(Encoding.UTF8.GetBytes(body)))); + } + + public void AddButton( + int x, int y, int normalID, int pressedID, int buttonID, + GumpButtonType type = GumpButtonType.Reply, int param = 0 + ) + { + 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, string args = null) + { + Add(new GumpTooltip(number, args)); + } + + public void AddHtml( + int x, int y, int width, int height, string text, bool background = false, bool scrollbar = false + ) + { + 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 = false, + bool scrollbar = false + ) + { + 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 = false, + bool scrollbar = false + ) + { + 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 = false, bool scrollbar = false + ) + { + Add(new GumpHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar)); + } + + public void AddImage(int x, int y, int gumpID, int hue = 0) + { + 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 AddItem(int x, int y, int itemID, int hue = 0) + { + 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 AddLabelHtml(int x, int y, int width, int height, string text, string hue, int size = 4, bool center = true) + { + AddHtml(x, y, width, height, center ? text.Center(hue, size) : text.Color(hue, size)); + } + + 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 AddTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText, int size) + { + Add(new GumpTextEntryLimited(x, y, width, height, hue, entryID, initialText, size)); + } + + public void AddItemProperty(Serial serial) + { + Add(new GumpItemProperty(serial)); + } + + public void AddSpriteImage(int x, int y, int gumpID, int width, int height, int sx, int sy) + { + Add(new GumpSpriteImage(x, y, gumpID, width, height, sx, sy)); + } + + public void AddECHandleInput() + { + Add(new GumpECHandleInput()); + } + + public void AddGumpIDOverride(int gumpID) + { + Add(new GumpMasterGump(gumpID)); + } + + public void Add(GumpEntry g) + { + if (g.Parent != this) + { + g.Parent = this; } - - public List Strings { get; } - - public int TypeID { get; } - - public List Entries { get; } - - public Serial Serial { get; set; } - - public int X { get; set; } - - public int Y { get; set; } - - public bool Disposable { get; set; } = true; - - public bool Resizable { get; set; } = true; - - public bool Draggable { get; set; } = true; - - public bool Closable { get; set; } = true; - - public bool UseWebRender { get; set; } = false; - - public static int GetTypeID(Type type) => type?.FullName?.GetHashCode(StringComparison.Ordinal) ?? -1; - - 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 SendData(Mobile m, string responseMethod, object value) - { - Entries.Clear(); - - var jtext = JsonSerializer.Serialize(value, new JsonSerializerOptions() { WriteIndented = true }); - Add(new GumpBodyUpdate(responseMethod, - Convert.ToBase64String(Encoding.UTF8.GetBytes(jtext)))); - - m.SendGump(this); - } - - public void AddBody(string body) - { - Add(new GumpBody(Convert.ToBase64String(Encoding.UTF8.GetBytes(body)))); - } - public void AddButton( - int x, int y, int normalID, int pressedID, int buttonID, - GumpButtonType type = GumpButtonType.Reply, int param = 0 - ) - { - 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, string args = null) - { - Add(new GumpTooltip(number, args)); - } - - public void AddHtml( - int x, int y, int width, int height, string text, bool background = false, bool scrollbar = false - ) - { - 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 = false, - bool scrollbar = false - ) - { - 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 = false, - bool scrollbar = false - ) - { - 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 = false, bool scrollbar = false - ) - { - Add(new GumpHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar)); - } - - public void AddImage(int x, int y, int gumpID, int hue = 0) - { - 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, int localizedTooltip = -1 - ) - { - Add( - new GumpImageTileButton( - x, - y, - normalID, - pressedID, - buttonID, - type, - param, - itemID, - hue, - width, - height, - localizedTooltip - ) - ); - } - - public void AddItem(int x, int y, int itemID, int hue = 0) - { - 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 AddTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText, int size) - { - Add(new GumpTextEntryLimited(x, y, width, height, hue, entryID, initialText, size)); - } - - public void AddItemProperty(Serial serial) - { - Add(new GumpItemProperty(serial)); - } - - public void AddSpriteImage(int x, int y, int gumpID, int width, int height, int sx, int sy) - { - Add(new GumpSpriteImage(x, y, gumpID, width, height, sx, sy)); - } - - public void AddECHandleInput() - { - Add(new GumpECHandleInput()); - } - - public void AddGumpIDOverride(int gumpID) - { - Add(new GumpMasterGump(gumpID)); - } - - public void Add(GumpEntry g) - { - if (g.Parent != this) - { - g.Parent = this; - } - else if (!Entries.Contains(g)) - { - Entries.Add(g); - } - } - - public void Remove(GumpEntry g) - { - if (g == null || !Entries.Contains(g)) - { - return; - } - - Entries.Remove(g); - g.Parent = null; - } - - public int Intern(string value) - { - var indexOf = Strings.IndexOf(value); - - if (indexOf >= 0) - { - return indexOf; - } - - Strings.Add(value); - return Strings.Count - 1; - } - - public void SendTo(NetState state) - { - state.AddGump(this); - state.SendDisplayGump(this, out m_Switches, out m_TextEntries); - } - - public static byte[] StringToBuffer(string str) => str.GetBytesAscii(); - - public virtual void OnResponse(NetState sender, RelayInfo info) - { - } - - public virtual void OnServerClose(NetState owner) + else if (!Entries.Contains(g)) { + Entries.Add(g); } } + + public void Remove(GumpEntry g) + { + if (g == null || !Entries.Contains(g)) + { + return; + } + + Entries.Remove(g); + g.Parent = null; + } + + public int Intern(string value) + { + var indexOf = Strings.IndexOf(value); + + if (indexOf >= 0) + { + return indexOf; + } + + Strings.Add(value); + return Strings.Count - 1; + } + + public void SendTo(NetState state) + { + state.AddGump(this); + state.SendDisplayGump(this, out m_Switches, out m_TextEntries); + } + + public static byte[] StringToBuffer(string str) => str.GetBytesAscii(); + + public virtual void OnResponse(NetState sender, RelayInfo info) + { + } + + public virtual void OnServerClose(NetState owner) + { + } } diff --git a/Projects/Server/Gumps/GumpAlphaRegion.cs b/Projects/Server/Gumps/GumpAlphaRegion.cs index f58acfcb7..376aacd0d 100644 --- a/Projects/Server/Gumps/GumpAlphaRegion.cs +++ b/Projects/Server/Gumps/GumpAlphaRegion.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpAlphaRegion.cs * * * @@ -16,43 +16,28 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpAlphaRegion : GumpEntry { - public class GumpAlphaRegion : GumpEntry + public GumpAlphaRegion(int x, int y, int width, int height) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("checkertrans"); + X = x; + Y = y; + Width = width; + Height = height; + } - public GumpAlphaRegion(int x, int y, int width, int height) - { - X = x; - Y = y; - Width = width; - Height = height; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } - - public override string Compile(OrderedHashSet strings) => $"{{ checkertrans {X} {Y} {Width} {Height} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii($"{{ checkertrans {X} {Y} {Width} {Height} }}"); } } diff --git a/Projects/Server/Gumps/GumpBackground.cs b/Projects/Server/Gumps/GumpBackground.cs index 7232a43f2..422fe063a 100644 --- a/Projects/Server/Gumps/GumpBackground.cs +++ b/Projects/Server/Gumps/GumpBackground.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpBackground.cs * * * @@ -16,48 +16,31 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpBackground : GumpEntry { - public class GumpBackground : GumpEntry + public GumpBackground(int x, int y, int width, int height, int gumpID) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("resizepic"); + X = x; + Y = y; + Width = width; + Height = height; + GumpID = gumpID; + } - public GumpBackground(int x, int y, int width, int height, int gumpID) - { - X = x; - Y = y; - Width = width; - Height = height; - GumpID = gumpID; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public int GumpID { get; set; } - public int GumpID { get; set; } - - public override string Compile(OrderedHashSet strings) => $"{{ resizepic {X} {Y} {GumpID} {Width} {Height} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(GumpID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii($"{{ resizepic {X} {Y} {GumpID} {Width} {Height} }}"); } } diff --git a/Projects/Server/Gumps/GumpButton.cs b/Projects/Server/Gumps/GumpButton.cs index b87991762..679bffbf9 100644 --- a/Projects/Server/Gumps/GumpButton.cs +++ b/Projects/Server/Gumps/GumpButton.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpButton.cs * * * @@ -16,68 +16,46 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public enum GumpButtonType { - public enum GumpButtonType + Page = 0, + Reply = 1 +} + +public class GumpButton : GumpEntry +{ + public GumpButton( + int x, int y, int normalID, int pressedID, int buttonID, + GumpButtonType type = GumpButtonType.Reply, int param = 0 + ) { - Page = 0, - Reply = 1 + X = x; + Y = y; + NormalID = normalID; + PressedID = pressedID; + ButtonID = buttonID; + Type = type; + Param = param; } - public class GumpButton : GumpEntry + public int X { get; set; } + + public int Y { get; set; } + + public int NormalID { get; set; } + + public int PressedID { get; set; } + + public int ButtonID { get; set; } + + public GumpButtonType Type { get; set; } + + public int Param { get; set; } + + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("button"); - - public GumpButton( - int x, int y, int normalID, int pressedID, int buttonID, - GumpButtonType type = GumpButtonType.Reply, int param = 0 - ) - { - X = x; - Y = y; - NormalID = normalID; - PressedID = pressedID; - ButtonID = buttonID; - Type = type; - Param = param; - } - - public int X { get; set; } - - public int Y { get; set; } - - public int NormalID { get; set; } - - public int PressedID { get; set; } - - public int ButtonID { get; set; } - - public GumpButtonType Type { get; set; } - - public int Param { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ button {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(NormalID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(PressedID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(((int)Type).ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Param.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(ButtonID.ToString()); - writer.Write((ushort)0x207D); // " }" - } + writer.WriteAscii($"{{ button {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} }}"); } } diff --git a/Projects/Server/Gumps/GumpCheck.cs b/Projects/Server/Gumps/GumpCheck.cs index 21b5e0d71..bcfa289d4 100644 --- a/Projects/Server/Gumps/GumpCheck.cs +++ b/Projects/Server/Gumps/GumpCheck.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpCheck.cs * * * @@ -16,56 +16,36 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpCheck : GumpEntry { - public class GumpCheck : GumpEntry + public GumpCheck(int x, int y, int inactiveID, int activeID, bool initialState, int switchID) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("checkbox"); + X = x; + Y = y; + InactiveID = inactiveID; + ActiveID = activeID; + InitialState = initialState; + SwitchID = switchID; + } - public GumpCheck(int x, int y, int inactiveID, int activeID, bool initialState, int switchID) - { - X = x; - Y = y; - InactiveID = inactiveID; - ActiveID = activeID; - InitialState = initialState; - SwitchID = switchID; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int InactiveID { get; set; } - public int InactiveID { get; set; } + public int ActiveID { get; set; } - public int ActiveID { get; set; } + public bool InitialState { get; set; } - public bool InitialState { get; set; } + public int SwitchID { get; set; } - public int SwitchID { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ checkbox {X} {Y} {InactiveID} {ActiveID} {(InitialState ? 1 : 0)} {SwitchID} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(InactiveID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(ActiveID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(InitialState ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(SwitchID.ToString()); - writer.Write((ushort)0x207D); // " }" - - switches++; - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var initialState = InitialState ? "1" : "0"; + writer.WriteAscii($"{{ checkbox {X} {Y} {InactiveID} {ActiveID} {initialState} {SwitchID} }}"); + switches++; } } diff --git a/Projects/Server/Gumps/GumpECHandleInput.cs b/Projects/Server/Gumps/GumpECHandleInput.cs index 3c3f36ea2..2bd494a8e 100644 --- a/Projects/Server/Gumps/GumpECHandleInput.cs +++ b/Projects/Server/Gumps/GumpECHandleInput.cs @@ -16,16 +16,14 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps -{ - public class GumpECHandleInput : GumpEntry - { - public static readonly byte[] LayoutName = Gump.StringToBuffer("echandleinput"); - public override string Compile(OrderedHashSet strings) => "{ echandleinput }"; +namespace Server.Gumps; - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.WriteAscii("{ echandleinput }"); - } +public class GumpECHandleInput : GumpEntry +{ + private static byte[] _layout = Gump.StringToBuffer("{ echandleinput }"); + + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.Write(_layout); } } diff --git a/Projects/Server/Gumps/GumpEntry.cs b/Projects/Server/Gumps/GumpEntry.cs index fe669f390..175f6f813 100644 --- a/Projects/Server/Gumps/GumpEntry.cs +++ b/Projects/Server/Gumps/GumpEntry.cs @@ -1,32 +1,29 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public abstract class GumpEntry { - public abstract class GumpEntry + private Gump m_Parent; + + public Gump Parent { - private Gump m_Parent; - - public Gump Parent + get => m_Parent; + set { - get => m_Parent; - set + if (m_Parent != value) { - if (m_Parent != value) - { - m_Parent?.Remove(this); + m_Parent?.Remove(this); - m_Parent = value; + m_Parent = value; - m_Parent?.Add(this); - } + m_Parent?.Add(this); } } - - public abstract string Compile(OrderedHashSet strings); - - // TODO: Replace OrderedHashSet with InsertOnlyHashSet, a copy of HashSet that is ReadOnly compatible, but includes - // a public AddIfNotPresent function that returns the index of the element - public abstract void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches); } + + // TODO: Replace OrderedHashSet with InsertOnlyHashSet, a copy of HashSet that is ReadOnly compatible, but includes + // a public AddIfNotPresent function that returns the index of the element + public abstract void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches); } diff --git a/Projects/Server/Gumps/GumpGroup.cs b/Projects/Server/Gumps/GumpGroup.cs index e9d75f3f0..a24c0debc 100644 --- a/Projects/Server/Gumps/GumpGroup.cs +++ b/Projects/Server/Gumps/GumpGroup.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpGroup.cs * * * @@ -16,24 +16,25 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpGroup : GumpEntry { - public class GumpGroup : GumpEntry + private static byte[] _group1 = Gump.StringToBuffer("{ group 1 }"); + + public GumpGroup(int group) => Group = group; + + public int Group { get; set; } + + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("group"); - - public GumpGroup(int group) => Group = group; - - public int Group { get; set; } - public override string Compile(OrderedHashSet strings) => $"{{ group {Group} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + if (Group == 1) { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(Group.ToString()); - writer.Write((ushort)0x207D); // " }" + writer.Write(_group1); + } + else + { + writer.WriteAscii($"{{ group {Group} }}"); } } } diff --git a/Projects/Server/Gumps/GumpHtml.cs b/Projects/Server/Gumps/GumpHtml.cs index cb0759960..12b77ed05 100644 --- a/Projects/Server/Gumps/GumpHtml.cs +++ b/Projects/Server/Gumps/GumpHtml.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpHtml.cs * * * @@ -16,59 +16,40 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpHtml : GumpEntry { - public class GumpHtml : GumpEntry + public GumpHtml(int x, int y, int width, int height, string text, bool background, bool scrollbar) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("htmlgump"); + X = x; + Y = y; + Width = width; + Height = height; + Text = text; + Background = background; + Scrollbar = scrollbar; + } - public GumpHtml(int x, int y, int width, int height, string text, bool background, bool scrollbar) - { - X = x; - Y = y; - Width = width; - Height = height; - Text = text; - Background = background; - Scrollbar = scrollbar; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public string Text { get; set; } - public string Text { get; set; } + public bool Background { get; set; } - public bool Background { get; set; } + public bool Scrollbar { get; set; } - public bool Scrollbar { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ htmlgump {X} {Y} {Width} {Height} {strings.GetOrAdd(Text ?? "")} {(Background ? 1 : 0)} {(Scrollbar ? 1 : 0)} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(strings.GetOrAdd(Text ?? "").ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Background ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(Scrollbar ? '1' : '0'); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var textIndex = strings.GetOrAdd(Text ?? ""); + var background = Background ? "1" : "0"; + var scrollbar = Scrollbar ? "1" : "0"; + writer.WriteAscii($"{{ htmlgump {X} {Y} {Width} {Height} {textIndex} {background} {scrollbar} }}"); } } diff --git a/Projects/Server/Gumps/GumpHtmlLocalized.cs b/Projects/Server/Gumps/GumpHtmlLocalized.cs index 39fe020b4..a4241dfbb 100644 --- a/Projects/Server/Gumps/GumpHtmlLocalized.cs +++ b/Projects/Server/Gumps/GumpHtmlLocalized.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpHtmlLocalized.cs * * * @@ -16,182 +16,105 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public enum GumpHtmlLocalizedType { - public enum GumpHtmlLocalizedType + Plain, + Color, + Args +} + +public class GumpHtmlLocalized : GumpEntry +{ + public GumpHtmlLocalized( + int x, int y, int width, int height, int number, + bool background = false, bool scrollbar = false + ) { - Plain, - Color, - Args + X = x; + Y = y; + Width = width; + Height = height; + Number = number; + Background = background; + Scrollbar = scrollbar; + + Type = GumpHtmlLocalizedType.Plain; } - public class GumpHtmlLocalized : GumpEntry + public GumpHtmlLocalized( + int x, int y, int width, int height, int number, int color, + bool background = false, bool scrollbar = false + ) { - public static readonly byte[] LayoutNamePlain = Gump.StringToBuffer("xmfhtmlgump"); - public static readonly byte[] LayoutNameColor = Gump.StringToBuffer("xmfhtmlgumpcolor"); - public static readonly byte[] LayoutNameArgs = Gump.StringToBuffer("xmfhtmltok"); + X = x; + Y = y; + Width = width; + Height = height; + Number = number; + Color = color; + Background = background; + Scrollbar = scrollbar; - public GumpHtmlLocalized( - int x, int y, int width, int height, int number, - bool background = false, bool scrollbar = false - ) - { - X = x; - Y = y; - Width = width; - Height = height; - Number = number; - Background = background; - Scrollbar = scrollbar; + Type = GumpHtmlLocalizedType.Color; + } - Type = GumpHtmlLocalizedType.Plain; - } + public GumpHtmlLocalized( + int x, int y, int width, int height, int number, string args, int color, + bool background = false, bool scrollbar = false + ) + { + // Are multiple arguments unsupported? And what about non ASCII arguments? - public GumpHtmlLocalized( - int x, int y, int width, int height, int number, int color, - bool background = false, bool scrollbar = false - ) - { - X = x; - Y = y; - Width = width; - Height = height; - Number = number; - Color = color; - Background = background; - Scrollbar = scrollbar; + X = x; + Y = y; + Width = width; + Height = height; + Number = number; + Args = args; + Color = color; + Background = background; + Scrollbar = scrollbar; - Type = GumpHtmlLocalizedType.Color; - } + Type = GumpHtmlLocalizedType.Args; + } - public GumpHtmlLocalized( - int x, int y, int width, int height, int number, string args, int color, - bool background = false, bool scrollbar = false - ) - { - // Are multiple arguments unsupported? And what about non ASCII arguments? + public int X { get; set; } - X = x; - Y = y; - Width = width; - Height = height; - Number = number; - Args = args; - Color = color; - Background = background; - Scrollbar = scrollbar; + public int Y { get; set; } - Type = GumpHtmlLocalizedType.Args; - } + public int Width { get; set; } - public int X { get; set; } + public int Height { get; set; } - public int Y { get; set; } + public int Number { get; set; } - public int Width { get; set; } + public string Args { get; set; } - public int Height { get; set; } + public int Color { get; set; } - public int Number { get; set; } + public bool Background { get; set; } - public string Args { get; set; } + public bool Scrollbar { get; set; } - public int Color { get; set; } + public GumpHtmlLocalizedType Type { get; set; } - public bool Background { get; set; } - public bool Scrollbar { get; set; } - - public GumpHtmlLocalizedType Type { get; set; } - - public override string Compile(OrderedHashSet strings) => + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var background = Background ? "1" : "0"; + var scrollbar = Scrollbar ? "1" : "0"; + writer.WriteAscii( Type switch { GumpHtmlLocalizedType.Plain => - $"{{ xmfhtmlgump {X} {Y} {Width} {Height} {Number} {(Background ? 1 : 0)} {(Scrollbar ? 1 : 0)} }}", + $"{{ xmfhtmlgump {X} {Y} {Width} {Height} {Number} {background} {scrollbar} }}", GumpHtmlLocalizedType.Color => - $"{{ xmfhtmlgumpcolor {X} {Y} {Width} {Height} {Number} {(Background ? 1 : 0)} {(Scrollbar ? 1 : 0)} {Color} }}", + $"{{ xmfhtmlgumpcolor {X} {Y} {Width} {Height} {Number} {background} {scrollbar} {Color} }}", _ => - $"{{ xmfhtmltok {X} {Y} {Width} {Height} {(Background ? 1 : 0)} {(Scrollbar ? 1 : 0)} {Color} {Number} @{Args}@ }}" - }; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - - switch (Type) - { - case GumpHtmlLocalizedType.Plain: - { - writer.Write(LayoutNamePlain); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Number.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Background ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(Scrollbar ? '1' : '0'); - - break; - } - case GumpHtmlLocalizedType.Color: - { - writer.Write(LayoutNameColor); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Number.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Background ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(Scrollbar ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(Color.ToString()); - - break; - } - case GumpHtmlLocalizedType.Args: - { - writer.Write(LayoutNameArgs); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Background ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(Scrollbar ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(Color.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Number.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii('@'); - writer.WriteAscii(Args ?? ""); - writer.WriteAscii('@'); - - break; - } + $"{{ xmfhtmltok {X} {Y} {Width} {Height} {background} {scrollbar} {Color} {Number} @{Args}@ }}" } - - writer.Write((ushort)0x207D); // " }" - } + ); } } diff --git a/Projects/Server/Gumps/GumpImage.cs b/Projects/Server/Gumps/GumpImage.cs index 6122fecff..ba3d17969 100644 --- a/Projects/Server/Gumps/GumpImage.cs +++ b/Projects/Server/Gumps/GumpImage.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpImage.cs * * * @@ -16,60 +16,41 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpImage : GumpEntry { - public class GumpImage : GumpEntry + public GumpImage(int x, int y, int gumpID, int hue = 0, string cls = null) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("gumppic"); - public static readonly byte[] HueEquals = Gump.StringToBuffer(" hue="); - public static readonly byte[] ClassEquals = Gump.StringToBuffer(" class="); + X = x; + Y = y; + GumpID = gumpID; + Hue = hue; + Class = cls; + } - public GumpImage(int x, int y, int gumpID, int hue = 0, string cls = null) - { - X = x; - Y = y; - GumpID = gumpID; - Hue = hue; - Class = cls; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int GumpID { get; set; } - public int GumpID { get; set; } + public int Hue { get; set; } - public int Hue { get; set; } + public string Class { get; set; } - public string Class { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ gumppic {X} {Y} {GumpID}{(Hue == 0 ? "" : $"hue={Hue}")}{(string.IsNullOrEmpty(Class) ? "" : $"class={Class}")} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(GumpID.ToString()); - - if (Hue != 0) + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var hasHue = Hue != 0; + var hasClass = !string.IsNullOrEmpty(Class); + writer.WriteAscii( + hasHue switch { - writer.Write(HueEquals); - writer.WriteAscii(Hue.ToString()); + true when hasClass => $"{{ gumppic {X} {Y} {GumpID} hue={Hue} class={Class} }}", + true => $"{{ gumppic {X} {Y} {GumpID} hue={Hue} }}", + false when hasClass => $"{{ gumppic {X} {Y} {GumpID} class={Class} }}", + false => $"{{ gumppic {X} {Y} {GumpID} }}", } - - if (!string.IsNullOrWhiteSpace(Class)) - { - writer.Write(ClassEquals); - writer.WriteAscii(Class); - } - - writer.Write((ushort)0x207D); // " }" - } + ); } } diff --git a/Projects/Server/Gumps/GumpImageTileButton.cs b/Projects/Server/Gumps/GumpImageTileButton.cs index 383a42649..4bb862af9 100644 --- a/Projects/Server/Gumps/GumpImageTileButton.cs +++ b/Projects/Server/Gumps/GumpImageTileButton.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpImageTileButton.cs * * * @@ -16,100 +16,55 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpImageTileButton : GumpEntry { - public class GumpImageTileButton : GumpEntry + public GumpImageTileButton( + int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param, + int itemID, int hue, int width, int height + ) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("buttontileart"); - public static readonly byte[] LayoutTooltip = Gump.StringToBuffer(" }{ tooltip"); + X = x; + Y = y; + NormalID = normalID; + PressedID = pressedID; + ButtonID = buttonID; + Type = type; + Param = param; - // 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) + ItemID = itemID; + Hue = hue; + Width = width; + Height = height; + } - 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 = -1 - ) - { - X = x; - Y = y; - NormalID = normalID; - PressedID = pressedID; - ButtonID = buttonID; - Type = type; - Param = param; + public int X { get; set; } - ItemID = itemID; - Hue = hue; - Width = width; - Height = height; + public int Y { get; set; } - LocalizedTooltip = localizedTooltip; - } + public int NormalID { get; set; } - public int X { get; set; } + public int PressedID { get; set; } - public int Y { get; set; } + public int ButtonID { get; set; } - public int NormalID { get; set; } + public GumpButtonType Type { get; set; } - public int PressedID { get; set; } + public int Param { get; set; } - public int ButtonID { get; set; } + public int ItemID { get; set; } - public GumpButtonType Type { get; set; } + public int Hue { get; set; } - public int Param { get; set; } + public int Width { get; set; } - public int ItemID { get; set; } + public int Height { get; set; } - public int Hue { get; set; } - - public int Width { get; set; } - - public int Height { get; set; } - - public int LocalizedTooltip { get; set; } - - public override string Compile(OrderedHashSet strings) => - LocalizedTooltip > 0 ? - $"{{ buttontileart {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} {ItemID} {Hue} {Width} {Height} }}{{ tooltip {LocalizedTooltip} }}" : - $"{{ buttontileart {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} {ItemID} {Hue} {Width} {Height} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(NormalID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(PressedID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(((int)Type).ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Param.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(ButtonID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(ItemID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Hue.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - - if (LocalizedTooltip > 0) - { - writer.Write(LayoutTooltip); - writer.WriteAscii(LocalizedTooltip.ToString()); - } - - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii( + $"{{ buttontileart {X} {Y} {NormalID} {PressedID} {(int)Type} {Param} {ButtonID} {ItemID} {Hue} {Width} {Height} }}" + ); } } diff --git a/Projects/Server/Gumps/GumpImageTiled.cs b/Projects/Server/Gumps/GumpImageTiled.cs index 30c0c7800..ab62bcc55 100644 --- a/Projects/Server/Gumps/GumpImageTiled.cs +++ b/Projects/Server/Gumps/GumpImageTiled.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpImageTiled.cs * * * @@ -16,47 +16,31 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpImageTiled : GumpEntry { - public class GumpImageTiled : GumpEntry + public GumpImageTiled(int x, int y, int width, int height, int gumpID) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("gumppictiled"); + X = x; + Y = y; + Width = width; + Height = height; + GumpID = gumpID; + } - public GumpImageTiled(int x, int y, int width, int height, int gumpID) - { - X = x; - Y = y; - Width = width; - Height = height; - GumpID = gumpID; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public int GumpID { get; set; } - public int GumpID { get; set; } - public override string Compile(OrderedHashSet strings) => $"{{ gumppictiled {X} {Y} {Width} {Height} {GumpID} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(GumpID.ToString()); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii($"{{ gumppictiled {X} {Y} {Width} {Height} {GumpID} }}"); } } diff --git a/Projects/Server/Gumps/GumpItem.cs b/Projects/Server/Gumps/GumpItem.cs index 4562f6e43..7ca97af42 100644 --- a/Projects/Server/Gumps/GumpItem.cs +++ b/Projects/Server/Gumps/GumpItem.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpItem.cs * * * @@ -16,50 +16,28 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpItem : GumpEntry { - public class GumpItem : GumpEntry + public GumpItem(int x, int y, int itemID, int hue = 0) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("tilepic"); - public static readonly byte[] LayoutNameHue = Gump.StringToBuffer("tilepichue"); + X = x; + Y = y; + ItemID = itemID; + Hue = hue; + } - public GumpItem(int x, int y, int itemID, int hue = 0) - { - X = x; - Y = y; - ItemID = itemID; - Hue = hue; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int ItemID { get; set; } - public int ItemID { get; set; } + public int Hue { get; set; } - public int Hue { get; set; } - - public override string Compile(OrderedHashSet strings) => - Hue == 0 ? $"{{ tilepic {X} {Y} {ItemID} }}" : $"{{ tilepichue {X} {Y} {ItemID} {Hue} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(Hue == 0 ? LayoutName : LayoutNameHue); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(ItemID.ToString()); - - if (Hue != 0) - { - writer.WriteAscii(' '); - writer.WriteAscii(Hue.ToString()); - } - - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii(Hue == 0 ? $"{{ tilepic {X} {Y} {ItemID} }}" : $"{{ tilepichue {X} {Y} {ItemID} {Hue} }}"); } } diff --git a/Projects/Server/Gumps/GumpItemProperty.cs b/Projects/Server/Gumps/GumpItemProperty.cs index fb248fd50..fc5c8fa94 100644 --- a/Projects/Server/Gumps/GumpItemProperty.cs +++ b/Projects/Server/Gumps/GumpItemProperty.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpItemProperty.cs * * * @@ -16,25 +16,16 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpItemProperty : GumpEntry { - public class GumpItemProperty : GumpEntry + public GumpItemProperty(Serial serial) => Serial = serial; + + public Serial Serial { get; set; } + + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("itemproperty"); - - public GumpItemProperty(Serial serial) => Serial = serial; - - public Serial Serial { get; set; } - - public override string Compile(OrderedHashSet strings) => $"{{ itemproperty {Serial.Value} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(Serial.Value.ToString()); - writer.Write((ushort)0x207D); // " }" - } + writer.WriteAscii($"{{ itemproperty {Serial.Value} }}"); } } diff --git a/Projects/Server/Gumps/GumpLabel.cs b/Projects/Server/Gumps/GumpLabel.cs index 7f3b40e94..8299ec21b 100644 --- a/Projects/Server/Gumps/GumpLabel.cs +++ b/Projects/Server/Gumps/GumpLabel.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpLabel.cs * * * @@ -16,42 +16,29 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpLabel : GumpEntry { - public class GumpLabel : GumpEntry + public GumpLabel(int x, int y, int hue, string text) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("text"); + X = x; + Y = y; + Hue = hue; + Text = text; + } - public GumpLabel(int x, int y, int hue, string text) - { - X = x; - Y = y; - Hue = hue; - Text = text; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Hue { get; set; } - public int Hue { get; set; } + public string Text { get; set; } - public string Text { get; set; } - public override string Compile(OrderedHashSet strings) => $"{{ text {X} {Y} {Hue} {strings.GetOrAdd(Text ?? "")} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Hue.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(strings.GetOrAdd(Text ?? "").ToString()); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var textIndex = strings.GetOrAdd(Text ?? ""); + writer.WriteAscii($"{{ text {X} {Y} {Hue} {textIndex} }}"); } } diff --git a/Projects/Server/Gumps/GumpLabelCropped.cs b/Projects/Server/Gumps/GumpLabelCropped.cs index 18e7d67be..ec1961f5d 100644 --- a/Projects/Server/Gumps/GumpLabelCropped.cs +++ b/Projects/Server/Gumps/GumpLabelCropped.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpLabelCropped.cs * * * @@ -16,54 +16,35 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpLabelCropped : GumpEntry { - public class GumpLabelCropped : GumpEntry + public GumpLabelCropped(int x, int y, int width, int height, int hue, string text) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("croppedtext"); + X = x; + Y = y; + Width = width; + Height = height; + Hue = hue; + Text = text; + } - public GumpLabelCropped(int x, int y, int width, int height, int hue, string text) - { - X = x; - Y = y; - Width = width; - Height = height; - Hue = hue; - Text = text; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public int Hue { get; set; } - public int Hue { get; set; } + public string Text { get; set; } - public string Text { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ croppedtext {X} {Y} {Width} {Height} {Hue} {strings.GetOrAdd(Text ?? "")} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Hue.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(strings.GetOrAdd(Text ?? "").ToString()); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var textIndex = strings.GetOrAdd(Text ?? ""); + writer.WriteAscii($"{{ croppedtext {X} {Y} {Width} {Height} {Hue} {textIndex} }}"); } } diff --git a/Projects/Server/Gumps/GumpMasterGump.cs b/Projects/Server/Gumps/GumpMasterGump.cs index 035b1ec9b..b37ed6aa4 100644 --- a/Projects/Server/Gumps/GumpMasterGump.cs +++ b/Projects/Server/Gumps/GumpMasterGump.cs @@ -16,25 +16,16 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpMasterGump : GumpEntry { - public class GumpMasterGump : GumpEntry + public GumpMasterGump(int gumpID) => GumpID = gumpID; + + public int GumpID { get; set; } + + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("mastergump"); - - public GumpMasterGump(int gumpID) => GumpID = gumpID; - - public int GumpID { get; set; } - - public override string Compile(OrderedHashSet strings) => $"{{ mastergump {GumpID} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(GumpID.ToString()); - writer.Write((ushort)0x207D); // " }" - } + writer.WriteAscii($"{{ mastergump {GumpID} }}"); } } diff --git a/Projects/Server/Gumps/GumpPage.cs b/Projects/Server/Gumps/GumpPage.cs index 16aa448d7..1c8ae540a 100644 --- a/Projects/Server/Gumps/GumpPage.cs +++ b/Projects/Server/Gumps/GumpPage.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpPage.cs * * * @@ -16,24 +16,25 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpPage : GumpEntry { - public class GumpPage : GumpEntry + private static byte[] _page0 = Gump.StringToBuffer("{ page 0 }"); + + public GumpPage(int page) => Page = page; + + public int Page { get; set; } + + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("page"); - - public GumpPage(int page) => Page = page; - - public int Page { get; set; } - public override string Compile(OrderedHashSet strings) => $"{{ page {Page} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + if (Page == 0) { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(Page.ToString()); - writer.Write((ushort)0x207D); // " }" + writer.Write(_page0); + } + else + { + writer.WriteAscii($"{{ page {Page} }}"); } } } diff --git a/Projects/Server/Gumps/GumpRadio.cs b/Projects/Server/Gumps/GumpRadio.cs index ff7d6d32c..b5ad2506f 100644 --- a/Projects/Server/Gumps/GumpRadio.cs +++ b/Projects/Server/Gumps/GumpRadio.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpRadio.cs * * * @@ -16,56 +16,36 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpRadio : GumpEntry { - public class GumpRadio : GumpEntry + public GumpRadio(int x, int y, int inactiveID, int activeID, bool initialState, int switchID) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("radio"); + X = x; + Y = y; + InactiveID = inactiveID; + ActiveID = activeID; + InitialState = initialState; + SwitchID = switchID; + } - public GumpRadio(int x, int y, int inactiveID, int activeID, bool initialState, int switchID) - { - X = x; - Y = y; - InactiveID = inactiveID; - ActiveID = activeID; - InitialState = initialState; - SwitchID = switchID; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int InactiveID { get; set; } - public int InactiveID { get; set; } + public int ActiveID { get; set; } - public int ActiveID { get; set; } + public bool InitialState { get; set; } - public bool InitialState { get; set; } + public int SwitchID { get; set; } - public int SwitchID { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ radio {X} {Y} {InactiveID} {ActiveID} {(InitialState ? 1 : 0)} {SwitchID} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(InactiveID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(ActiveID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(InitialState ? '1' : '0'); - writer.WriteAscii(' '); - writer.WriteAscii(SwitchID.ToString()); - writer.Write((ushort)0x207D); // " }" - - switches++; - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var initialState = InitialState ? "1" : "0"; + writer.WriteAscii($"{{ radio {X} {Y} {InactiveID} {ActiveID} {initialState} {SwitchID} }}"); + switches++; } } diff --git a/Projects/Server/Gumps/GumpSpriteImage.cs b/Projects/Server/Gumps/GumpSpriteImage.cs index 59c8e8492..953e3c29f 100644 --- a/Projects/Server/Gumps/GumpSpriteImage.cs +++ b/Projects/Server/Gumps/GumpSpriteImage.cs @@ -16,58 +16,37 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpSpriteImage : GumpEntry { - public class GumpSpriteImage : GumpEntry + public GumpSpriteImage(int x, int y, int gumpID, int width, int height, int sx, int sy) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("picinpic"); + X = x; + Y = y; + GumpID = gumpID; + Width = width; + Height = height; + SX = sx; + SY = sy; + } - public GumpSpriteImage(int x, int y, int gumpID, int width, int height, int sx, int sy) - { - X = x; - Y = y; - GumpID = gumpID; - Width = width; - Height = height; - SX = sx; - SY = sy; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public int GumpID { get; set; } - public int GumpID { get; set; } + public int SX { get; set; } - public int SX { get; set; } + public int SY { get; set; } - public int SY { get; set; } - public override string Compile(OrderedHashSet strings) => - $"{{ picinpic {X} {Y} {GumpID} {Width} {Height} {SX} {SY} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(GumpID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(SX.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(SY.ToString()); - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii($"{{ picinpic {X} {Y} {GumpID} {Width} {Height} {SX} {SY} }}"); } } diff --git a/Projects/Server/Gumps/GumpTextEntry.cs b/Projects/Server/Gumps/GumpTextEntry.cs index 1eb0aa84c..f56ef77a4 100644 --- a/Projects/Server/Gumps/GumpTextEntry.cs +++ b/Projects/Server/Gumps/GumpTextEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpTextEntry.cs * * * @@ -16,61 +16,39 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpTextEntry : GumpEntry { - public class GumpTextEntry : GumpEntry + public GumpTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("textentry"); + X = x; + Y = y; + Width = width; + Height = height; + Hue = hue; + EntryID = entryID; + InitialText = initialText; + } - public GumpTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText) - { - X = x; - Y = y; - Width = width; - Height = height; - Hue = hue; - EntryID = entryID; - InitialText = initialText; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public int Hue { get; set; } - public int Hue { get; set; } + public int EntryID { get; set; } - public int EntryID { get; set; } + public string InitialText { get; set; } - public string InitialText { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ textentry {X} {Y} {Width} {Height} {Hue} {EntryID} {strings.GetOrAdd(InitialText ?? "")} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Hue.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(EntryID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(strings.GetOrAdd(InitialText ?? "").ToString()); - writer.Write((ushort)0x207D); // " }" - - entries++; - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var textIndex = strings.GetOrAdd(InitialText ?? ""); + writer.WriteAscii($"{{ textentry {X} {Y} {Width} {Height} {Hue} {EntryID} {textIndex} }}"); + entries++; } } diff --git a/Projects/Server/Gumps/GumpTextEntryLimited.cs b/Projects/Server/Gumps/GumpTextEntryLimited.cs index 029a2d127..aebe6c08c 100644 --- a/Projects/Server/Gumps/GumpTextEntryLimited.cs +++ b/Projects/Server/Gumps/GumpTextEntryLimited.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpTextEntryLimited.cs * * * @@ -16,68 +16,44 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +public class GumpTextEntryLimited : GumpEntry { - public class GumpTextEntryLimited : GumpEntry + public GumpTextEntryLimited( + int x, int y, int width, int height, int hue, int entryID, string initialText, int size = 0 + ) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("textentrylimited"); + X = x; + Y = y; + Width = width; + Height = height; + Hue = hue; + EntryID = entryID; + InitialText = initialText; + Size = size; + } - public GumpTextEntryLimited( - int x, int y, int width, int height, int hue, int entryID, string initialText, int size = 0 - ) - { - X = x; - Y = y; - Width = width; - Height = height; - Hue = hue; - EntryID = entryID; - InitialText = initialText; - Size = size; - } + public int X { get; set; } - public int X { get; set; } + public int Y { get; set; } - public int Y { get; set; } + public int Width { get; set; } - public int Width { get; set; } + public int Height { get; set; } - public int Height { get; set; } + public int Hue { get; set; } - public int Hue { get; set; } + public int EntryID { get; set; } - public int EntryID { get; set; } + public string InitialText { get; set; } - public string InitialText { get; set; } + public int Size { get; set; } - public int Size { get; set; } - - public override string Compile(OrderedHashSet strings) => - $"{{ textentrylimited {X} {Y} {Width} {Height} {Hue} {EntryID} {strings.GetOrAdd(InitialText ?? "")} {Size} }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(X.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Y.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Width.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Height.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Hue.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(EntryID.ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(strings.GetOrAdd(InitialText ?? "").ToString()); - writer.WriteAscii(' '); - writer.WriteAscii(Size.ToString()); - writer.Write((ushort)0x207D); // " }" - - entries++; - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + var textIndex = strings.GetOrAdd(InitialText ?? ""); + writer.WriteAscii($"{{ textentrylimited {X} {Y} {Width} {Height} {Hue} {EntryID} {textIndex} {Size} }}"); + entries++; } } diff --git a/Projects/Server/Gumps/GumpTooltip.cs b/Projects/Server/Gumps/GumpTooltip.cs index 6898e2b78..4332c1d8d 100644 --- a/Projects/Server/Gumps/GumpTooltip.cs +++ b/Projects/Server/Gumps/GumpTooltip.cs @@ -16,41 +16,24 @@ using System.Buffers; using Server.Collections; -namespace Server.Gumps +namespace Server.Gumps; + +// 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) +public class GumpTooltip : GumpEntry { - public class GumpTooltip : GumpEntry + public GumpTooltip(int number, string args) { - public static readonly byte[] LayoutName = Gump.StringToBuffer("tooltip"); + Number = number; + Args = args; + } - public GumpTooltip(int number, string args) - { - Number = number; - Args = args; - } + public int Number { get; set; } - public int Number { get; set; } + public string Args { get; set; } - public string Args { get; set; } - - public override string Compile(OrderedHashSet strings) => - string.IsNullOrEmpty(Args) ? $"{{ tooltip {Number} }}" : $"{{ tooltip {Number} @{Args}@ }}"; - - public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) - { - writer.Write((ushort)0x7B20); // "{ " - writer.Write(LayoutName); - writer.WriteAscii(' '); - writer.WriteAscii(Number.ToString()); - - if (!string.IsNullOrEmpty(Args)) - { - writer.WriteAscii(' '); - writer.WriteAscii('@'); - writer.WriteAscii(Args); - writer.WriteAscii('@'); - } - - writer.Write((ushort)0x207D); // " }" - } + public override void AppendTo(ref SpanWriter writer, OrderedHashSet strings, ref int entries, ref int switches) + { + writer.WriteAscii(string.IsNullOrEmpty(Args) ? $"{{ tooltip {Number} }}" : $"{{ tooltip {Number} @{Args}@ }}"); } } diff --git a/Projects/Server/Gumps/InvalidGumpResponseException.cs b/Projects/Server/Gumps/InvalidGumpResponseException.cs index af2c11c6c..df4b06d69 100644 --- a/Projects/Server/Gumps/InvalidGumpResponseException.cs +++ b/Projects/Server/Gumps/InvalidGumpResponseException.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright (C) 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: InvalidGumpResponseException.cs * * * @@ -15,12 +15,11 @@ using System; -namespace Server.Gumps +namespace Server.Gumps; + +public class InvalidGumpResponseException : Exception { - public class InvalidGumpResponseException : Exception + public InvalidGumpResponseException(string reason) : base(reason) { - public InvalidGumpResponseException(string reason) : base(reason) - { - } } } diff --git a/Projects/Server/Gumps/RelayInfo.cs b/Projects/Server/Gumps/RelayInfo.cs index 7831f2174..b0736e368 100644 --- a/Projects/Server/Gumps/RelayInfo.cs +++ b/Projects/Server/Gumps/RelayInfo.cs @@ -1,57 +1,56 @@ -namespace Server.Gumps +namespace Server.Gumps; + +public class TextRelay { - public class TextRelay + public TextRelay(int entryID, string text) { - public TextRelay(int entryID, string text) - { - EntryID = entryID; - Text = text; - } - - public int EntryID { get; } - - public string Text { get; } + EntryID = entryID; + Text = text; } - public class RelayInfo - { - public RelayInfo(int buttonID, int[] switches, TextRelay[] textEntries) - { - ButtonID = buttonID; - Switches = switches; - TextEntries = textEntries; - } + public int EntryID { get; } - public int ButtonID { get; } - - public int[] Switches { get; } - - public TextRelay[] TextEntries { get; } - - public bool IsSwitched(int switchID) - { - for (var i = 0; i < Switches.Length; ++i) - { - if (Switches[i] == switchID) - { - return true; - } - } - - return false; - } - - public TextRelay GetTextEntry(int entryID) - { - for (var i = 0; i < TextEntries.Length; ++i) - { - if (TextEntries[i].EntryID == entryID) - { - return TextEntries[i]; - } - } - - return null; - } - } + public string Text { get; } } + +public class RelayInfo +{ + public RelayInfo(int buttonID, int[] switches, TextRelay[] textEntries) + { + ButtonID = buttonID; + Switches = switches; + TextEntries = textEntries; + } + + public int ButtonID { get; } + + public int[] Switches { get; } + + public TextRelay[] TextEntries { get; } + + public bool IsSwitched(int switchID) + { + for (var i = 0; i < Switches.Length; ++i) + { + if (Switches[i] == switchID) + { + return true; + } + } + + return false; + } + + public TextRelay GetTextEntry(int entryID) + { + for (var i = 0; i < TextEntries.Length; ++i) + { + if (TextEntries[i].EntryID == entryID) + { + return TextEntries[i]; + } + } + + return null; + } +} \ No newline at end of file diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index 1b8375553..1d06752cc 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -755,7 +755,7 @@ namespace Server.Items public virtual void SendContentTo(NetState state) => state.SendContainerContent(state.Mobile, this); - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -767,21 +767,14 @@ namespace Server.Items { list.Add( 1073841, // Contents: ~1_COUNT~/~2_MAXCOUNT~ items, ~3_WEIGHT~ stones - "{0}\t{1}\t{2}", - TotalItems, - MaxItems, - TotalWeight + $"{TotalItems}\t{MaxItems}\t{TotalWeight}" ); } else { list.Add( 1072241, // Contents: ~1_COUNT~/~2_MAXCOUNT~ items, ~3_WEIGHT~/~4_MAXWEIGHT~ stones - "{0}\t{1}\t{2}\t{3}", - TotalItems, - MaxItems, - TotalWeight, - MaxWeight + $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}" ); } @@ -790,7 +783,7 @@ namespace Server.Items else { // ~1_COUNT~ items, ~2_WEIGHT~ stones - list.Add(1050044, "{0}\t{1}", TotalItems, TotalWeight); + list.Add(1050044, $"{TotalItems}\t{TotalWeight}"); } } } diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 8531c5a86..6515aeca4 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using Server.ContextMenus; using Server.Items; +using Server.Logging; using Server.Network; using Server.Targeting; @@ -175,8 +176,10 @@ namespace Server Spawner = 0x100 } - public class Item : IHued, IComparable, ISpawnable, IPropertyListObject + public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity { + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Item)); + public const int QuestItemHue = 0x4EA; // Hmmmm... "for EA"? public static readonly List EmptyItems = new(); private static readonly Queue m_DeltaQueue = new(); @@ -684,9 +687,9 @@ namespace Server if (!Stackable && m_Amount > 1) { - Console.WriteLine( - "Warning: 0x{0:X}: Amount changed for non-stackable item '{2}'. ({1})", - Serial.Value, + logger.Warning( + "{Serial}: Amount changed for non-stackable item '{Name}'. ({Amount})", + Serial, m_Amount, GetType().Name ); @@ -766,7 +769,7 @@ namespace Server /// custom /// properties. /// - public virtual void GetProperties(ObjectPropertyList list) + public virtual void GetProperties(IPropertyList list) { AddNameProperties(list); } @@ -1120,7 +1123,6 @@ namespace Server Span oldWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); Span saWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); Span hsWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); - Span opl = ObjectPropertyList.Enabled ? stackalloc byte[OutgoingEntityPackets.OPLPacketLength].InitializePacket() : null; var eable = m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange()); @@ -1138,7 +1140,7 @@ namespace Server hsWorldItem = hsWorldItem[..length]; } - SendInfoTo(state, hsWorldItem, opl); + SendInfoTo(state, hsWorldItem); } else if (state.StygianAbyss) { @@ -1148,7 +1150,7 @@ namespace Server saWorldItem = saWorldItem[..length]; } - SendInfoTo(state, saWorldItem, opl); + SendInfoTo(state, saWorldItem); } else { @@ -1158,7 +1160,7 @@ namespace Server oldWorldItem = oldWorldItem[..length]; } - SendInfoTo(state, oldWorldItem, opl); + SendInfoTo(state, oldWorldItem); } } } @@ -1468,8 +1470,7 @@ namespace Server World.RemoveEntity(this); OnAfterDelete(); - - m_PropertyList = null; + ClearProperties(); } public ISpawner Spawner @@ -1805,16 +1806,16 @@ namespace Server /// /// Overridable. Sends the object property list to . /// - public virtual void SendPropertiesTo(Mobile from) + public virtual void SendPropertiesTo(NetState ns) { - from.NetState?.Send(PropertyList.Buffer); + ns?.Send(PropertyList.Buffer); } /// /// Overridable. Adds the name of this item to the given . This method should be overridden /// if the item requires a complex naming format. /// - public virtual void AddNameProperty(ObjectPropertyList list) + public virtual void AddNameProperty(IPropertyList list) { var name = Name; @@ -1826,7 +1827,7 @@ namespace Server } else { - list.Add(1050039, "{0}\t#{1}", m_Amount, LabelNumber); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1050039, $"{m_Amount}\t{LabelNumber:#}"); // ~1_NUMBER~ ~2_ITEMNAME~ } } else @@ -1837,7 +1838,7 @@ namespace Server } else { - list.Add(1050039, "{0}\t{1}", m_Amount, Name); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1050039, $"{m_Amount}\t{Name}"); // ~1_NUMBER~ ~2_ITEMNAME~ } } } @@ -1846,7 +1847,7 @@ namespace Server /// 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) + public virtual void AddLootTypeProperty(IPropertyList list) { if (m_LootType == LootType.Blessed) { @@ -1865,59 +1866,51 @@ namespace Server /// /// Overridable. Adds any elemental resistances of this item to the given . /// - public virtual void AddResistanceProperties(ObjectPropertyList list) + public virtual void AddResistanceProperties(IPropertyList list) { var v = PhysicalResistance; if (v != 0) { - list.Add(1060448, v.ToString()); // physical resist ~1_val~% + list.Add(1060448, v); // physical resist ~1_val~% } v = FireResistance; if (v != 0) { - list.Add(1060447, v.ToString()); // fire resist ~1_val~% + list.Add(1060447, v); // fire resist ~1_val~% } v = ColdResistance; if (v != 0) { - list.Add(1060445, v.ToString()); // cold resist ~1_val~% + list.Add(1060445, v); // cold resist ~1_val~% } v = PoisonResistance; if (v != 0) { - list.Add(1060449, v.ToString()); // poison resist ~1_val~% + list.Add(1060449, v); // poison resist ~1_val~% } v = EnergyResistance; if (v != 0) { - list.Add(1060446, v.ToString()); // energy resist ~1_val~% + list.Add(1060446, v); // energy resist ~1_val~% } } /// /// Overridable. Displays cliloc 1072788-1072789. /// - public virtual void AddWeightProperty(ObjectPropertyList list) + public virtual void AddWeightProperty(IPropertyList list) { var weight = PileWeight + TotalWeight; - - if (weight == 1) - { - list.Add(1072788, weight.ToString()); // Weight: ~1_WEIGHT~ stone - } - else - { - list.Add(1072789, weight.ToString()); // Weight: ~1_WEIGHT~ stones - } + list.Add(weight == 1 ? 1072788 : 1072789, $"{weight}"); } /// @@ -1925,7 +1918,7 @@ namespace Server /// (if applicable), and (if /// ). /// - public virtual void AddNameProperties(ObjectPropertyList list) + public virtual void AddNameProperties(IPropertyList list) { AddNameProperty(list); @@ -1966,7 +1959,7 @@ namespace Server /// /// Overridable. Adds the "Quest Item" property to the given . /// - public virtual void AddQuestItemProperty(ObjectPropertyList list) + public virtual void AddQuestItemProperty(IPropertyList list) { list.Add(1072351); // Quest Item } @@ -1974,7 +1967,7 @@ namespace Server /// /// Overridable. Adds the "Locked Down & Secure" property to the given . /// - public virtual void AddSecureProperty(ObjectPropertyList list) + public virtual void AddSecureProperty(IPropertyList list) { list.Add(501644); // locked down & secure } @@ -1982,7 +1975,7 @@ namespace Server /// /// Overridable. Adds the "Locked Down" property to the given . /// - public virtual void AddLockedDownProperty(ObjectPropertyList list) + public virtual void AddLockedDownProperty(IPropertyList list) { list.Add(501643); // locked down } @@ -1990,9 +1983,9 @@ namespace Server /// /// Overridable. Adds the "Blessed for ~1_NAME~" property to the given . /// - public virtual void AddBlessedForProperty(ObjectPropertyList list, Mobile m) + public virtual void AddBlessedForProperty(IPropertyList list, Mobile m) { - list.Add(1062203, "{0}", m.Name); // Blessed for ~1_NAME~ + list.Add(1062203, m.Name); // Blessed for ~1_NAME~ } /// @@ -2000,7 +1993,7 @@ namespace Server /// Recursively calls Item.GetChildProperties or /// Mobile.GetChildProperties. /// - public virtual void GetChildProperties(ObjectPropertyList list, Item item) + public virtual void GetChildProperties(IPropertyList list, Item item) { if (m_Parent is Item parentItem) { @@ -2018,7 +2011,7 @@ namespace Server /// . Recursively calls Item.GetChildNameProperties or /// Mobile.GetChildNameProperties. /// - public virtual void GetChildNameProperties(ObjectPropertyList list, Item item) + public virtual void GetChildNameProperties(IPropertyList list, Item item) { if (m_Parent is Item parentItem) { @@ -2071,9 +2064,12 @@ namespace Server MoveToWorld(from.Location, from.Map); } } - else if ((parent as Mobile)?.EquipItem(this) == false) + else if (parent is Mobile mobile) { - MoveToWorld(bounce.WorldLoc, bounce.Map); + if (!mobile.EquipItem(this)) + { + MoveToWorld(bounce.WorldLoc, bounce.Map); + } } else { @@ -2371,7 +2367,7 @@ namespace Server return bounds; } - public virtual void AppendChildProperties(ObjectPropertyList list) + public virtual void AppendChildProperties(IPropertyList list) { if (m_Parent is Item item) { @@ -2383,7 +2379,7 @@ namespace Server } } - public virtual void AppendChildNameProperties(ObjectPropertyList list) + public virtual void AppendChildNameProperties(IPropertyList list) { if (m_Parent is Item item) { @@ -2403,7 +2399,7 @@ namespace Server return list; } - public void ClearProperties() + public virtual void ClearProperties() { m_PropertyList = null; } @@ -3076,27 +3072,18 @@ namespace Server public virtual int GetUpdateRange(Mobile m) => 18; - public virtual void SendInfoTo(NetState ns, ReadOnlySpan world = default, Span opl = default) + public virtual void SendInfoTo(NetState ns, ReadOnlySpan world = default) { SendWorldPacketTo(ns, world); - SendOPLPacketTo(ns, opl); + SendOPLPacketTo(ns); } - public void SendOPLPacketTo(NetState ns, Span opl = default) + public virtual void SendOPLPacketTo(NetState ns) { - if (!ObjectPropertyList.Enabled) - { - return; - } - - if (opl == null) + if (ObjectPropertyList.Enabled) { ns.SendOPLInfo(this); - return; } - - OutgoingEntityPackets.CreateOPLInfo(opl, this); - ns.Send(opl); } public virtual void SendWorldPacketTo(NetState ns, ReadOnlySpan world = default) @@ -3177,27 +3164,29 @@ namespace Server if (item == this) { - Console.WriteLine( - "Warning: Adding item to itself: [0x{0:X} {1}].AddItem( [0x{2:X} {3}] )", - Serial.Value, + var customException = new InvalidOperationException("Adding item to itself"); + logger.Warning( + customException, + "Adding item to itself: ({Serial1} {Item1}).AddItem({Serial2} {Item2})", + Serial, GetType().Name, - item.Serial.Value, + item.Serial, item.GetType().Name ); - Console.WriteLine(new StackTrace()); return; } if (IsChildOf(item)) { - Console.WriteLine( - "Warning: Adding parent item to child: [0x{0:X} {1}].AddItem( [0x{2:X} {3}] )", - Serial.Value, + var customException = new InvalidOperationException("Adding parent item to child"); + logger.Warning( + customException, + "Adding parent item to child: [{Serial1} {Item1}].AddItem( [{Serial2} {Item2}] )", + Serial, GetType().Name, - item.Serial.Value, + item.Serial, item.GetType().Name ); - Console.WriteLine(new StackTrace()); return; } @@ -3277,17 +3266,16 @@ namespace Server } catch (Exception ex) { -#if DEBUG - Console.WriteLine("Process Delta Queue for {0} failed: {1}", item, ex); -#endif + logger.Debug(ex, "Process Delta Queue for {Item} failed", item); } } if (m_DeltaQueue.Count > 0) { - Utility.PushColor(ConsoleColor.DarkYellow); - Console.WriteLine("Warning: {0} items left in delta queue after processing.", m_DeltaQueue.Count); - Utility.PopColor(); + logger.Warning( + "{Count} items left in delta queue after processing.", + m_DeltaQueue.Count + ); } } @@ -4139,7 +4127,7 @@ namespace Server var ns = from.NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -4260,7 +4248,7 @@ namespace Server public virtual bool IsStandardLoot() => (!Mobile.InsuranceEnabled || !Insured) && BlessedFor == null && m_LootType == LootType.Regular; - public override string ToString() => $"0x{Serial.Value:X} \"{GetType().Name}\""; + public override string ToString() => $"{Serial} \"{GetType().Name}\""; public virtual void OnSectorActivate() { diff --git a/Projects/Server/Items/ItemBounds.cs b/Projects/Server/Items/ItemBounds.cs index 4fea6ae67..d2333395b 100644 --- a/Projects/Server/Items/ItemBounds.cs +++ b/Projects/Server/Items/ItemBounds.cs @@ -1,44 +1,60 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ItemBounds.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.IO; +using Server.Logging; -namespace Server +namespace Server; + +public static class ItemBounds { - public static class ItemBounds + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ItemBounds)); + + static ItemBounds() { - static ItemBounds() + Table = new Rectangle2D[TileData.ItemTable.Length]; + + if (!File.Exists("Data/Binary/Bounds.bin")) { - Table = new Rectangle2D[TileData.ItemTable.Length]; - - if (File.Exists("Data/Binary/Bounds.bin")) - { - using var fs = new FileStream( - "Data/Binary/Bounds.bin", - FileMode.Open, - FileAccess.Read, - FileShare.Read - ); - var bin = new BinaryReader(fs); - - var count = Math.Min(Table.Length, (int)(fs.Length / 8)); - - for (var i = 0; i < count; ++i) - { - int xMin = bin.ReadInt16(); - int yMin = bin.ReadInt16(); - int xMax = bin.ReadInt16(); - int yMax = bin.ReadInt16(); - - Table[i].Set(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); - } - - bin.Close(); - } - else - { - Console.WriteLine("Warning: Data/Binary/Bounds.bin does not exist"); - } + logger.Error("Data/Binary/Bounds.bin does not exist"); + return; } - public static Rectangle2D[] Table { get; } + using var fs = new FileStream( + "Data/Binary/Bounds.bin", + FileMode.Open, + FileAccess.Read, + FileShare.Read + ); + var bin = new BinaryReader(fs); + + var count = Math.Min(Table.Length, (int)(fs.Length / 8)); + + for (var i = 0; i < count; ++i) + { + int xMin = bin.ReadInt16(); + int yMin = bin.ReadInt16(); + int xMax = bin.ReadInt16(); + int yMax = bin.ReadInt16(); + + Table[i].Set(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); + } + + bin.Close(); } + + public static Rectangle2D[] Table { get; } } diff --git a/Projects/Server/Items/VirtualCheck.cs b/Projects/Server/Items/VirtualCheck.cs index da952aaef..c72866f20 100644 --- a/Projects/Server/Items/VirtualCheck.cs +++ b/Projects/Server/Items/VirtualCheck.cs @@ -120,7 +120,7 @@ namespace Server.Items LabelTo(from, "Offer: {0:#,0} platinum, {1:#,0} gold", Plat, Gold); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/Server/Items/VirtualHair.cs b/Projects/Server/Items/VirtualHair.cs index ba09bedf1..91debb93b 100644 --- a/Projects/Server/Items/VirtualHair.cs +++ b/Projects/Server/Items/VirtualHair.cs @@ -12,7 +12,7 @@ namespace Server public static void SendHairEquipUpdatePacket(this NetState ns, Mobile m, uint hairSerial, int itemId, int hue, Layer layer) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -42,7 +42,7 @@ namespace Server public static void SendRemoveHairPacket(this NetState ns, uint hairSerial) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/Server/Json/Converters/ClientVersionConverter.cs b/Projects/Server/Json/Converters/ClientVersionConverter.cs index c1dc8ecb9..571881ce3 100644 --- a/Projects/Server/Json/Converters/ClientVersionConverter.cs +++ b/Projects/Server/Json/Converters/ClientVersionConverter.cs @@ -17,21 +17,20 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class ClientVersionConverter : JsonConverter - { - public override ClientVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - return new ClientVersion(reader.GetString()); - } +namespace Server.Json; - throw new JsonException($"Value must be a string"); +public class ClientVersionConverter : JsonConverter +{ + public override ClientVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + return new ClientVersion(reader.GetString()); } - public override void Write(Utf8JsonWriter writer, ClientVersion value, JsonSerializerOptions options) => - writer.WriteStringValue(value.ToString()); + throw new JsonException("Value must be a string"); } -} + + public override void Write(Utf8JsonWriter writer, ClientVersion value, JsonSerializerOptions options) => + writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs index 3e778d591..86e8f503e 100644 --- a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs +++ b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs @@ -14,17 +14,15 @@ *************************************************************************/ using System; -using System.Net; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class ClientVersionConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(ClientVersion); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new ClientVersionConverter(); - } -} +public class ClientVersionConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(ClientVersion); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new ClientVersionConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/FlagsConverter.cs b/Projects/Server/Json/Converters/FlagsConverter.cs index 38584d05a..0eea803b9 100644 --- a/Projects/Server/Json/Converters/FlagsConverter.cs +++ b/Projects/Server/Json/Converters/FlagsConverter.cs @@ -18,132 +18,132 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class FlagsConverter : JsonConverter where T : struct, Enum { - public class FlagsConverter : JsonConverter where T : struct, Enum + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + var flags = 0ul; + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + + while (true) { - var flags = 0ul; - var underlyingType = Enum.GetUnderlyingType(typeof(T)); - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid Json structure for Flag object"); - } - - var key = reader.GetString(); - - reader.Read(); - - if (!reader.GetBoolean() || !Enum.TryParse(key, out var val)) - { - continue; - } - - flags |= ConvertToUInt64(underlyingType, val); + break; } - switch (Type.GetTypeCode(underlyingType)) + if (reader.TokenType != JsonTokenType.PropertyName) { - case TypeCode.SByte: - { - var num = (sbyte)flags; - return Unsafe.As(ref num); - } - case TypeCode.Byte: - { - var num = (byte)flags; - return Unsafe.As(ref num); - } - case TypeCode.Int16: - { - var num = (short)flags; - return Unsafe.As(ref num); - } - case TypeCode.UInt16: - { - var num = (ushort)flags; - return Unsafe.As(ref num); - } - case TypeCode.UInt32: - { - var num = (uint)flags; - return Unsafe.As(ref num); - } - case TypeCode.Int64: - { - var num = (long)flags; - return Unsafe.As(ref num); - } - case TypeCode.UInt64: - { - return Unsafe.As(ref flags); - } - default: - { - var num = (int)flags; - return Unsafe.As(ref num); - } + throw new JsonException("Invalid Json structure for Flag object"); } + + var key = reader.GetString(); + + reader.Read(); + + if (!reader.GetBoolean() || !Enum.TryParse(key, out var val)) + { + continue; + } + + flags |= ConvertToUInt64(underlyingType, val); } - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + switch (Type.GetTypeCode(underlyingType)) { - writer.WriteStartObject(); - var underlyingType = Enum.GetUnderlyingType(typeof(T)); - var size = GetUnderlyingTypeLength(Type.GetTypeCode(underlyingType)) - 1; - var intValue = ConvertToUInt64(underlyingType, value); - - foreach (var flagName in Enum.GetNames(typeof(T))) - { - var flagValue = Enum.Parse(flagName, false); - var flag = ConvertToUInt64(underlyingType, flagValue); - if (flag == 0 || (flag & size) == 0) + case TypeCode.SByte: { - writer.WriteBoolean(flagName, (intValue & flag) != 0); + var num = (sbyte)flags; + return Unsafe.As(ref num); + } + case TypeCode.Byte: + { + var num = (byte)flags; + return Unsafe.As(ref num); + } + case TypeCode.Int16: + { + var num = (short)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt16: + { + var num = (ushort)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt32: + { + var num = (uint)flags; + return Unsafe.As(ref num); + } + case TypeCode.Int64: + { + var num = (long)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt64: + { + return Unsafe.As(ref flags); + } + default: + { + var num = (int)flags; + return Unsafe.As(ref num); } - } - - writer.WriteEndObject(); } - - private static ulong ConvertToUInt64(Type underlyingType, object value) => - Type.GetTypeCode(underlyingType) switch - { - TypeCode.SByte => (ulong)(sbyte)value, - TypeCode.Byte => (byte)value, - TypeCode.Int16 => (ulong)(short)value, - TypeCode.UInt16 => (ushort)value, - TypeCode.Int32 => (ulong)(int)value, - TypeCode.UInt32 => (uint)value, - TypeCode.Int64 => (ulong)(long)value, - TypeCode.UInt64 => (ulong)value, - _ => throw new InvalidOperationException() - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong GetUnderlyingTypeLength(TypeCode typeCode) => - typeCode switch - { - TypeCode.Byte => 8, - TypeCode.SByte => 8, - TypeCode.Int16 => 16, - TypeCode.UInt16 => 16, - TypeCode.Char => 16, - TypeCode.Int32 => 32, - TypeCode.UInt32 => 32, - TypeCode.Int64 => 64, - TypeCode.UInt64 => 64, - _ => 64 - }; } -} + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + var intValue = ConvertToUInt64(underlyingType, value); + + foreach (var flagName in Enum.GetNames(typeof(T))) + { + var flagValue = Enum.Parse(flagName, false); + var flag = ConvertToUInt64(underlyingType, flagValue); + + // Do not write out multi-bit values. This is a custom behavior + if (flag > 0 && (flag & (flag - 1)) == 0) + { + writer.WriteBoolean(flagName, (intValue & flag) == flag); + } + } + + writer.WriteEndObject(); + } + + private static ulong ConvertToUInt64(Type underlyingType, object value) => + Type.GetTypeCode(underlyingType) switch + { + TypeCode.SByte => (ulong)(sbyte)value, + TypeCode.Byte => (byte)value, + TypeCode.Int16 => (ulong)(short)value, + TypeCode.UInt16 => (ushort)value, + TypeCode.Int32 => (ulong)(int)value, + TypeCode.UInt32 => (uint)value, + TypeCode.Int64 => (ulong)(long)value, + TypeCode.UInt64 => (ulong)value, + _ => throw new InvalidOperationException() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong GetUnderlyingTypeLength(TypeCode typeCode) => + typeCode switch + { + TypeCode.Byte => 8, + TypeCode.SByte => 8, + TypeCode.Int16 => 16, + TypeCode.UInt16 => 16, + TypeCode.Char => 16, + TypeCode.Int32 => 32, + TypeCode.UInt32 => 32, + TypeCode.Int64 => 64, + TypeCode.UInt64 => 64, + _ => 64 + }; +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/GuidConverter.cs b/Projects/Server/Json/Converters/GuidConverter.cs index b6c373094..53034d2d4 100644 --- a/Projects/Server/Json/Converters/GuidConverter.cs +++ b/Projects/Server/Json/Converters/GuidConverter.cs @@ -17,21 +17,20 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class GuidConverter : JsonConverter - { - public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (Guid.TryParse(reader.GetString()!, out var guid)) - { - return guid; - } +namespace Server.Json; - throw new JsonException("Guid must be in the correct format"); +public class GuidConverter : JsonConverter +{ + public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (Guid.TryParse(reader.GetString()!, out var guid)) + { + return guid; } - public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); + throw new JsonException("Guid must be in the correct format"); } -} + + public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/GuidConverterFactory.cs b/Projects/Server/Json/Converters/GuidConverterFactory.cs index e0ba7c4ce..8ec9a5444 100644 --- a/Projects/Server/Json/Converters/GuidConverterFactory.cs +++ b/Projects/Server/Json/Converters/GuidConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class GuidConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Guid); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new GuidConverter(); - } -} +public class GuidConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Guid); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new GuidConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/IPEndPointConverter.cs b/Projects/Server/Json/Converters/IPEndPointConverter.cs index d6c74d0da..41b4bab66 100644 --- a/Projects/Server/Json/Converters/IPEndPointConverter.cs +++ b/Projects/Server/Json/Converters/IPEndPointConverter.cs @@ -18,21 +18,20 @@ using System.Net; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class IPEndPointConverter : JsonConverter - { - public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (IPEndPoint.TryParse(reader.GetString()!, out var ipep)) - { - return ipep; - } +namespace Server.Json; - throw new JsonException("IPEndPoint must be in the correct format"); +public class IPEndPointConverter : JsonConverter +{ + public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (IPEndPoint.TryParse(reader.GetString()!, out var ipep)) + { + return ipep; } - public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); + throw new JsonException("IPEndPoint must be in the correct format"); } -} + + public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs b/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs index 3a05cb364..3906cbb4d 100644 --- a/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs +++ b/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs @@ -18,13 +18,12 @@ using System.Net; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class IPEndPointConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(IPEndPoint); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new IPEndPointConverter(); - } -} +public class IPEndPointConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(IPEndPoint); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new IPEndPointConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/MapConverter.cs b/Projects/Server/Json/Converters/MapConverter.cs index 71109ef88..a220e5ba0 100644 --- a/Projects/Server/Json/Converters/MapConverter.cs +++ b/Projects/Server/Json/Converters/MapConverter.cs @@ -17,19 +17,18 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class MapConverter : JsonConverter - { - public override Map Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch - { - JsonTokenType.String => Map.Parse(reader.GetString()), - JsonTokenType.Number => Map.Maps[reader.GetInt32()], - _ => throw new JsonException($"Value must be a number or string") - }; +namespace Server.Json; - public override void Write(Utf8JsonWriter writer, Map value, JsonSerializerOptions options) => - writer.WriteStringValue(value.Name); - } -} +public class MapConverter : JsonConverter +{ + public override Map Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.String => Map.Parse(reader.GetString()), + JsonTokenType.Number => Map.Maps[reader.GetInt32()], + _ => throw new JsonException("Value must be a number or string") + }; + + public override void Write(Utf8JsonWriter writer, Map value, JsonSerializerOptions options) => + writer.WriteStringValue(value.Name); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/MapConverterFactory.cs b/Projects/Server/Json/Converters/MapConverterFactory.cs index 9e00e463e..a6d312c52 100644 --- a/Projects/Server/Json/Converters/MapConverterFactory.cs +++ b/Projects/Server/Json/Converters/MapConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class MapConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Map); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new MapConverter(); - } -} +public class MapConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Map); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new MapConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point2DConverter.cs b/Projects/Server/Json/Converters/Point2DConverter.cs index 633aeb075..33bc8719b 100644 --- a/Projects/Server/Json/Converters/Point2DConverter.cs +++ b/Projects/Server/Json/Converters/Point2DConverter.cs @@ -17,95 +17,94 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class Point2DConverter : JsonConverter { - public class Point2DConverter : JsonConverter + private Point2D DeserializeArray(ref Utf8JsonReader reader) { - private Point2D DeserializeArray(ref Utf8JsonReader reader) + Span data = stackalloc int[2]; + var count = 0; + + while (true) { - Span data = stackalloc int[2]; - var count = 0; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType == JsonTokenType.Number) - { - if (count < 2) - { - data[count] = reader.GetInt32(); - } - - count++; - } + break; } - if (count > 2) + if (reader.TokenType == JsonTokenType.Number) { - throw new JsonException("Point2D must be an array of x, y"); - } + if (count < 2) + { + data[count] = reader.GetInt32(); + } - return new Point2D(data[0], data[1]); + count++; + } } - private Point2D DeserializeObj(ref Utf8JsonReader reader) + if (count > 2) { - Span data = stackalloc int[2]; - - while (true) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid json structure for Point2D object"); - } - - var key = reader.GetString(); - - var i = key switch - { - "x" => 0, - "y" => 1, - _ => throw new JsonException($"Invalid property {key} for Point2D") - }; - - reader.Read(); - - if (reader.TokenType != JsonTokenType.Number) - { - throw new JsonException($"Value for {key} must be a number"); - } - - data[i] = reader.GetInt32(); - } - - return new Point2D(data[0], data[1]); + throw new JsonException("Point2D must be an array of x, y"); } - public override Point2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch + return new Point2D(data[0], data[1]); + } + + private Point2D DeserializeObj(ref Utf8JsonReader reader) + { + Span data = stackalloc int[2]; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader), - _ => throw new JsonException("Invalid Json for Point3D") + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid json structure for Point2D object"); + } + + var key = reader.GetString(); + + var i = key switch + { + "x" => 0, + "y" => 1, + _ => throw new JsonException($"Invalid property {key} for Point2D") }; - public override void Write(Utf8JsonWriter writer, Point2D value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteEndArray(); + reader.Read(); + + if (reader.TokenType != JsonTokenType.Number) + { + throw new JsonException($"Value for {key} must be a number"); + } + + data[i] = reader.GetInt32(); } + + return new Point2D(data[0], data[1]); } -} + + public override Point2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, Point2D value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.X); + writer.WriteNumberValue(value.Y); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point2DConverterFactory.cs b/Projects/Server/Json/Converters/Point2DConverterFactory.cs index 7243f3854..3e064bd9d 100644 --- a/Projects/Server/Json/Converters/Point2DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Point2DConverterFactory.cs @@ -17,14 +17,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class Point2DConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => - typeToConvert == typeof(Point2D) || typeToConvert == typeof(IPoint2D); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new Point2DConverter(); - } -} +public class Point2DConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => + typeToConvert == typeof(Point2D) || typeToConvert == typeof(IPoint2D); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new Point2DConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point3DConverter.cs b/Projects/Server/Json/Converters/Point3DConverter.cs index 3c9281065..d508dcd66 100644 --- a/Projects/Server/Json/Converters/Point3DConverter.cs +++ b/Projects/Server/Json/Converters/Point3DConverter.cs @@ -17,97 +17,96 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class Point3DConverter : JsonConverter { - public class Point3DConverter : JsonConverter + private Point3D DeserializeArray(ref Utf8JsonReader reader) { - private Point3D DeserializeArray(ref Utf8JsonReader reader) + Span data = stackalloc int[3]; + var count = 0; + + while (true) { - Span data = stackalloc int[3]; - var count = 0; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType == JsonTokenType.Number) - { - if (count < 3) - { - data[count] = reader.GetInt32(); - } - - count++; - } + break; } - if (count > 3) + if (reader.TokenType == JsonTokenType.Number) { - throw new JsonException("Point3D must be an array of x, y, z"); - } + if (count < 3) + { + data[count] = reader.GetInt32(); + } - return new Point3D(data[0], data[1], data[2]); + count++; + } } - private Point3D DeserializeObj(ref Utf8JsonReader reader) + if (count > 3) { - Span data = stackalloc int[3]; - - while (true) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid json structure for Point3D object"); - } - - var key = reader.GetString(); - - var i = key switch - { - "x" => 0, - "y" => 1, - "z" => 2, - _ => throw new JsonException($"Invalid property {key} for Point3D") - }; - - reader.Read(); - - if (reader.TokenType != JsonTokenType.Number) - { - throw new JsonException($"Value for {key} must be a number"); - } - - data[i] = reader.GetInt32(); - } - - return new Point3D(data[0], data[1], data[2]); + throw new JsonException("Point3D must be an array of x, y, z"); } - public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch + return new Point3D(data[0], data[1], data[2]); + } + + private Point3D DeserializeObj(ref Utf8JsonReader reader) + { + Span data = stackalloc int[3]; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader), - _ => throw new JsonException("Invalid Json for Point3D") + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid json structure for Point3D object"); + } + + var key = reader.GetString(); + + var i = key switch + { + "x" => 0, + "y" => 1, + "z" => 2, + _ => throw new JsonException($"Invalid property {key} for Point3D") }; - public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteNumberValue(value.Z); - writer.WriteEndArray(); + reader.Read(); + + if (reader.TokenType != JsonTokenType.Number) + { + throw new JsonException($"Value for {key} must be a number"); + } + + data[i] = reader.GetInt32(); } + + return new Point3D(data[0], data[1], data[2]); } -} + + public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.X); + writer.WriteNumberValue(value.Y); + writer.WriteNumberValue(value.Z); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point3DConverterFactory.cs b/Projects/Server/Json/Converters/Point3DConverterFactory.cs index 1b8097cfa..f7dd0835f 100644 --- a/Projects/Server/Json/Converters/Point3DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Point3DConverterFactory.cs @@ -17,14 +17,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class Point3DConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => - typeToConvert == typeof(Point3D) || typeToConvert == typeof(IPoint3D); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new Point3DConverter(); - } -} +public class Point3DConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => + typeToConvert == typeof(Point3D) || typeToConvert == typeof(IPoint3D); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new Point3DConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Rectangle3DConverter.cs b/Projects/Server/Json/Converters/Rectangle3DConverter.cs index f957337e5..b614c8196 100644 --- a/Projects/Server/Json/Converters/Rectangle3DConverter.cs +++ b/Projects/Server/Json/Converters/Rectangle3DConverter.cs @@ -17,168 +17,167 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class Rectangle3DConverter : JsonConverter { - public class Rectangle3DConverter : JsonConverter + private Rectangle3D DeserializeArray(ref Utf8JsonReader reader) { - private Rectangle3D DeserializeArray(ref Utf8JsonReader reader) + Span data = stackalloc int[6]; + var count = 0; + + while (true) { - Span data = stackalloc int[6]; - var count = 0; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType == JsonTokenType.Number) - { - if (count < 6) - { - data[count] = reader.GetInt32(); - } - - count++; - } + break; } - if (count > 6) + if (reader.TokenType == JsonTokenType.Number) { - throw new JsonException("Rectangle3D must be an array of x, y, z, h, w, d"); - } + if (count < 6) + { + data[count] = reader.GetInt32(); + } - return new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]); + count++; + } } - private Rectangle3D DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) + if (count > 6) { - Span data = stackalloc int[6]; + throw new JsonException("Rectangle3D must be an array of x, y, z, h, w, d"); + } - // 0 - xyzwhd, 1 - x1y1z1x2y2z2, 2 - start/end - var objType = -1; - var hasZ = false; + return new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]); + } - while (true) + private Rectangle3D DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + Span data = stackalloc int[6]; + + // 0 - xyzwhd, 1 - x1y1z1x2y2z2, 2 - start/end + var objType = -1; + var hasZ = false; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } + break; + } - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid json structure for Rectangle3D object"); - } + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid json structure for Rectangle3D object"); + } - var key = reader.GetString(); + var key = reader.GetString(); - reader.Read(); + reader.Read(); - if (key is "start" or "end") - { - if (objType > -1 && objType != 2) - { - throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); - } - - objType = 2; - - var point3D = reader.ToObject(options); - var offset = key == "end" ? 3 : 0; - data[0 + offset] = point3D.X; - data[1 + offset] = point3D.Y; - // We can't do implicit z-level. Abandon using Point3D deserialization? - data[2 + offset] = point3D.Z; - continue; - } - - var i = key switch - { - "x" => 0, - "y" => 1, - "z" => 2, - "w" => 3, - "width" => 3, - "h" => 4, - "height" => 4, - "d" => 5, - "depth" => 5, - "x1" => 10, - "y1" => 11, - "z1" => 12, - "x2" => 13, - "y2" => 14, - "z2" => 15, - _ => throw new JsonException($"Invalid property {key} for Rectangle3D") - }; - - if (i < 10) - { - if (objType > -1 && objType != 0) - { - throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); - } - - objType = 0; - data[i] = reader.GetInt32(); - if (i == 2) - { - hasZ = true; - } - - continue; - } - - if (objType > -1 && objType != 1) + if (key is "start" or "end") + { + if (objType > -1 && objType != 2) { throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); } - objType = 1; - data[i - 10] = reader.GetInt32(); - if (i is 12 or 15) + objType = 2; + + var point3D = reader.ToObject(options); + var offset = key == "end" ? 3 : 0; + data[0 + offset] = point3D.X; + data[1 + offset] = point3D.Y; + // We can't do implicit z-level. Abandon using Point3D deserialization? + data[2 + offset] = point3D.Z; + continue; + } + + var i = key switch + { + "x" => 0, + "y" => 1, + "z" => 2, + "w" => 3, + "width" => 3, + "h" => 4, + "height" => 4, + "d" => 5, + "depth" => 5, + "x1" => 10, + "y1" => 11, + "z1" => 12, + "x2" => 13, + "y2" => 14, + "z2" => 15, + _ => throw new JsonException($"Invalid property {key} for Rectangle3D") + }; + + if (i < 10) + { + if (objType > -1 && objType != 0) + { + throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); + } + + objType = 0; + data[i] = reader.GetInt32(); + if (i == 2) { hasZ = true; } + + continue; } - if (!hasZ) + if (objType > -1 && objType != 1) { - // Bottom to top? - data[2] = -128; - data[5] = 127; + throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); } - return objType == 0 - ? new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]) - : new Rectangle3D( - new Point3D(data[0], data[1], data[2]), - new Point3D(data[3], data[4], data[5]) - ); + objType = 1; + data[i - 10] = reader.GetInt32(); + if (i is 12 or 15) + { + hasZ = true; + } } - public override Rectangle3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch - { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader, options), - _ => throw new JsonException("Invalid Json for Point3D") - }; - - public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options) + if (!hasZ) { - writer.WriteStartArray(); - writer.WriteNumberValue(value.Start.X); - writer.WriteNumberValue(value.Start.Y); - writer.WriteNumberValue(value.Start.Z); - writer.WriteNumberValue(value.Width); - writer.WriteNumberValue(value.Height); - writer.WriteNumberValue(value.Depth); - writer.WriteEndArray(); + // Bottom to top? + data[2] = -128; + data[5] = 127; } + + return objType == 0 + ? new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]) + : new Rectangle3D( + new Point3D(data[0], data[1], data[2]), + new Point3D(data[3], data[4], data[5]) + ); } -} + + public override Rectangle3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader, options), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.Start.X); + writer.WriteNumberValue(value.Start.Y); + writer.WriteNumberValue(value.Start.Z); + writer.WriteNumberValue(value.Width); + writer.WriteNumberValue(value.Height); + writer.WriteNumberValue(value.Depth); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs b/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs index 9a2f6093f..ca5c44fd8 100644 --- a/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class Rectangle3DConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Rectangle3D); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new Rectangle3DConverter(); - } -} +public class Rectangle3DConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Rectangle3D); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new Rectangle3DConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/TimeSpanConverter.cs b/Projects/Server/Json/Converters/TimeSpanConverter.cs index 3115e2440..17ccb2583 100644 --- a/Projects/Server/Json/Converters/TimeSpanConverter.cs +++ b/Projects/Server/Json/Converters/TimeSpanConverter.cs @@ -17,14 +17,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class TimeSpanConverter : JsonConverter - { - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => TimeSpan.Parse(reader.GetString()!); +namespace Server.Json; - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); - } -} +public class TimeSpanConverter : JsonConverter +{ + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => TimeSpan.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs b/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs index f329b5708..9b6164665 100644 --- a/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs +++ b/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class TimeSpanConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TimeSpan); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new TimeSpanConverter(); - } -} +public class TimeSpanConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TimeSpan); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new TimeSpanConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/TypeConverter.cs b/Projects/Server/Json/Converters/TypeConverter.cs index d21531317..3b59c6024 100644 --- a/Projects/Server/Json/Converters/TypeConverter.cs +++ b/Projects/Server/Json/Converters/TypeConverter.cs @@ -16,29 +16,31 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Server.Logging; -namespace Server.Json +namespace Server.Json; + +public class TypeConverter : JsonConverter { - public class TypeConverter : JsonConverter + private static readonly ILogger logger = LogFactory.GetLogger(typeof(TypeConverter)); + + public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + if (reader.TokenType != JsonTokenType.String) { - if (reader.TokenType != JsonTokenType.String) - { - throw new JsonException("The JSON value could not be converted to System.Type"); - } - - var typeName = reader.GetString(); - var type = AssemblyHandler.FindTypeByName(typeName); - if (type == null) - { - Console.WriteLine("Invalid type {0} deserialized", typeName); - } - - return type; + throw new JsonException("The JSON value could not be converted to System.Type"); } - public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) => - writer.WriteStringValue(value.FullName); + var typeName = reader.GetString(); + var type = AssemblyHandler.FindTypeByName(typeName); + if (type == null) + { + logger.Warning("Attempted to deserialize type {Type} which does not exist.", typeName); + } + + return type; } + + public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) => + writer.WriteStringValue(value.FullName); } diff --git a/Projects/Server/Json/Converters/TypeConverterFactory.cs b/Projects/Server/Json/Converters/TypeConverterFactory.cs index ea4b41504..4392bba1e 100644 --- a/Projects/Server/Json/Converters/TypeConverterFactory.cs +++ b/Projects/Server/Json/Converters/TypeConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class TypeConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Type); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new TypeConverter(); - } +public class TypeConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Type); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new TypeConverter(); } diff --git a/Projects/Server/Json/Converters/WorldLocationConverter.cs b/Projects/Server/Json/Converters/WorldLocationConverter.cs index 5cdfc909a..9e957f993 100644 --- a/Projects/Server/Json/Converters/WorldLocationConverter.cs +++ b/Projects/Server/Json/Converters/WorldLocationConverter.cs @@ -17,173 +17,172 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class WorldLocationConverter : JsonConverter { - public class WorldLocationConverter : JsonConverter + private static Point3DConverter _point3DConverter; + private static MapConverter _mapConverter; + + private WorldLocation DeserializeArray(ref Utf8JsonReader reader) { - private static Point3DConverter _point3DConverter; - private static MapConverter _mapConverter; + Span data = stackalloc int[3]; + var count = 0; + var hasMap = false; + Map map = null; - private WorldLocation DeserializeArray(ref Utf8JsonReader reader) + while (true) { - Span data = stackalloc int[3]; - var count = 0; - var hasMap = false; - Map map = null; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) + break; + } + + if (reader.TokenType == JsonTokenType.Number) + { + if (count < 3) { - break; + data[count] = reader.GetInt32(); } - - if (reader.TokenType == JsonTokenType.Number) + else if (count == 3) { - if (count < 3) - { - data[count] = reader.GetInt32(); - } - else if (count == 3) - { - map = Map.Maps[reader.GetInt32()]; - hasMap = true; - } - - count++; - } - - if (reader.TokenType == JsonTokenType.String) - { - var key = reader.GetString(); - - if (count != 3 || hasMap) - { - throw new JsonException($"Value {key} is not valid for this element."); - } - - map = Map.Parse(key); + map = Map.Maps[reader.GetInt32()]; hasMap = true; - break; } + + count++; } - if (!hasMap || count != 3) + if (reader.TokenType == JsonTokenType.String) { - throw new JsonException("WorldLocation must be an array of x, y, z, and map"); - } - - return new WorldLocation(data[0], data[1], data[2], map); - } - - private WorldLocation DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) - { - Span data = stackalloc int[3]; - var count = 0; - var hasLoc = false; - var hasXYZ = false; - var hasMap = false; - Map map = null; - - while (true) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid Json structure for WorldLocation object"); - } - var key = reader.GetString(); - var i = key switch + if (count != 3 || hasMap) { - "x" => 0, - "y" => 1, - "z" => 2, - "loc" => 3, - "map" => 4, - _ => 5 - }; - - if (i == 5) - { - continue; + throw new JsonException($"Value {key} is not valid for this element."); } - reader.Read(); - - if (i < 3) - { - if (hasLoc) - { - throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); - } - - if (reader.TokenType != JsonTokenType.Number) - { - throw new JsonException($"Value for {key} must be a number"); - } - - hasXYZ = true; - data[i] = reader.GetInt32(); - continue; - } - - if (i == 3) - { - if (hasXYZ) - { - throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); - } - - hasLoc = true; - - _point3DConverter ??= new Point3DConverter(); - - var loc = _point3DConverter.Read(ref reader, typeof(Point3D), options); - data[0] = loc.X; - data[1] = loc.Y; - data[2] = loc.Z; - count = 3; - continue; - } - - _mapConverter ??= new MapConverter(); - map = _mapConverter.Read(ref reader, typeof(Map), options); - + map = Map.Parse(key); hasMap = true; + break; } - - if (!hasMap || count != 3) - { - throw new JsonException("WorldLocation must have an x, y, z, and map properties"); - } - - return new WorldLocation(data[0], data[1], data[2], map); } - public override WorldLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch + if (!hasMap || count != 3) + { + throw new JsonException("WorldLocation must be an array of x, y, z, and map"); + } + + return new WorldLocation(data[0], data[1], data[2], map); + } + + private WorldLocation DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + Span data = stackalloc int[3]; + var count = 0; + var hasLoc = false; + var hasXYZ = false; + var hasMap = false; + Map map = null; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader, options), - _ => throw new JsonException("Invalid Json for Point3D") + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid Json structure for WorldLocation object"); + } + + var key = reader.GetString(); + + var i = key switch + { + "x" => 0, + "y" => 1, + "z" => 2, + "loc" => 3, + "map" => 4, + _ => 5 }; - public override void Write(Utf8JsonWriter writer, WorldLocation value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteNumberValue(value.Z); - writer.WriteStringValue(value.Map.ToString()); - writer.WriteEndArray(); + if (i == 5) + { + continue; + } + + reader.Read(); + + if (i < 3) + { + if (hasLoc) + { + throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); + } + + if (reader.TokenType != JsonTokenType.Number) + { + throw new JsonException($"Value for {key} must be a number"); + } + + hasXYZ = true; + data[i] = reader.GetInt32(); + continue; + } + + if (i == 3) + { + if (hasXYZ) + { + throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); + } + + hasLoc = true; + + _point3DConverter ??= new Point3DConverter(); + + var loc = _point3DConverter.Read(ref reader, typeof(Point3D), options); + data[0] = loc.X; + data[1] = loc.Y; + data[2] = loc.Z; + count = 3; + continue; + } + + _mapConverter ??= new MapConverter(); + map = _mapConverter.Read(ref reader, typeof(Map), options); + + hasMap = true; } + + if (!hasMap || count != 3) + { + throw new JsonException("WorldLocation must have an x, y, z, and map properties"); + } + + return new WorldLocation(data[0], data[1], data[2], map); } -} + + public override WorldLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader, options), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, WorldLocation value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.X); + writer.WriteNumberValue(value.Y); + writer.WriteNumberValue(value.Z); + writer.WriteStringValue(value.Map.ToString()); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs b/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs index a370e9b9b..961684e90 100644 --- a/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs +++ b/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class WorldLocationConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(WorldLocation); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new WorldLocationConverter(); - } -} +public class WorldLocationConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(WorldLocation); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new WorldLocationConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Localization/Localization.cs b/Projects/Server/Localization/Localization.cs new file mode 100644 index 000000000..ca92c06c2 --- /dev/null +++ b/Projects/Server/Localization/Localization.cs @@ -0,0 +1,205 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Localization.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using Server.Buffers; + +namespace Server; + +public static class Localization +{ + private const bool _loadLocalizationOnStartup = false; + public const string FallbackLanguage = "enu"; + + private static Dictionary _fallbackEntries; + private static Dictionary> _localizations = new(); + + public static void Configure() + { + if (_loadLocalizationOnStartup) + { + foreach (var file in Core.FindDataFileByPattern("cliloc.*")) + { + var fi = new FileInfo(file); + LoadClilocs(fi.Extension.ToLowerInvariant(), file); + } + } + } + + public static void Add(string lang, int number, string text) + { + var entry = new LocalizationEntry(lang, number, text); + if (!_localizations.TryGetValue(lang, out var entries)) + { + entries = new Dictionary(); + _localizations[lang] = entries; + if (lang == FallbackLanguage) + { + _fallbackEntries ??= entries; + } + } + + entries.Add(number, entry); + } + + public static bool Remove(string lang, int number) + { + if (!_localizations.TryGetValue(lang, out var entries) || !entries.Remove(number)) + { + return false; + } + + if (entries.Count == 0) + { + _localizations.Remove(lang); + } + + return true; + } + + public static Dictionary LoadClilocs(string lang) => + LoadClilocs(lang, Core.FindDataFile($"cliloc.{lang}", false)); + + private static Dictionary LoadClilocs(string lang, string file) + { + Dictionary entries = _localizations[lang] = new Dictionary(); + if (lang == FallbackLanguage) + { + _fallbackEntries = entries; + } + + if (File.Exists(file)) + { + using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read); + using var bin = new BinaryReader(fs); + + bin.ReadInt32(); + bin.ReadInt16(); + + byte[] buffer = null; + while (bin.BaseStream.Length != bin.BaseStream.Position) + { + var number = bin.ReadInt32(); + var flag = bin.ReadByte(); // Original, Custom, Modified + var length = bin.ReadInt16(); + + if (buffer == null || buffer.Length < length) + { + buffer = GC.AllocateUninitializedArray(length); + } + + var bytesRead = bin.Read(buffer, 0, length); + if (bytesRead != length) + { + throw new Exception($"Could not read enough bytes from {file}"); + } + + var text = Encoding.UTF8.GetString(buffer.AsSpan(0, length)); + entries[number] = new LocalizationEntry(lang, number, text); + } + } + + return entries; + } + + /// + /// Returns the original text for a localization entry. + /// + /// Localization number + /// Language in ISO 639‑2 format + /// Original text for the localizaton entry + public static string GetText(int number, string lang = FallbackLanguage) => + TryGetLocalization(lang, number, out var entry) ? entry.Text : null; + + /// + /// Gets a localization entry using the . + /// + /// Localization number + /// Localization entry retrieved + /// True if the entry exists, otherwise false. + public static bool TryGetLocalization(int number, out LocalizationEntry entry) => + TryGetLocalization(FallbackLanguage, number, out entry); + + /// + /// Gets a localization entry. + /// + /// Language in ISO 639-2 format + /// Localization number + /// Localization entry retrieved + /// True if the entry exists, otherwise false. + public static bool TryGetLocalization(string lang, int number, out LocalizationEntry entry) + { + if (lang != FallbackLanguage) + { + if (!_localizations.TryGetValue(lang, out var entries)) + { + entries = LoadClilocs(lang); + } + + if (entries.TryGetValue(number, out entry)) + { + return true; + } + } + + _fallbackEntries ??= LoadClilocs(FallbackLanguage); + return _fallbackEntries.TryGetValue(number, out entry); + } + + /// + /// Creates a formatted string of the localization entry using the specified language. + /// Uses string interpolation under the hood. This method is preferably relative to the object array method signature. + /// Example: + /// Localization.Format(1073841, "jpn", $"{totalItems}{maxItems}{totalWeight}"); + /// + /// Language in ISO 639-2 format + /// Localization number + /// interpolated string handler used by the compiler as a string builder during compilation + /// A copy of the localization text where the placeholder arguments have been replaced with string representations of the provided interpolation arguments + public static PooledArraySpanFormattable Format( + int number, string lang, + [InterpolatedStringHandlerArgument("number", "lang")] + ref LocalizationEntry.LocalizationInterpolationHandler handler + ) + { + var chars = handler.ToPooledArray(out var length); + handler = default; // Defensive clear + return new PooledArraySpanFormattable(chars, length); + } + + /// + /// Creates a formatted string of the localization entry using the . + /// Uses string interpolation under the hood. This method is preferably relative to the object array method signature. + /// Example: + /// Localization.Format(1073841, $"{totalItems}{maxItems}{totalWeight}"); + /// + /// Localization number + /// interpolated string handler used by the compiler as a string builder during compilation + /// A copy of the localization text where the placeholder arguments have been replaced with string representations of the provided interpolation arguments + public static PooledArraySpanFormattable Format( + int number, + [InterpolatedStringHandlerArgument("number")] + ref LocalizationEntry.LocalizationInterpolationHandler handler + ) + { + var chars = handler.ToPooledArray(out var length); + handler = default; // Defensive clear + return new PooledArraySpanFormattable(chars, length); + } +} diff --git a/Projects/Server/Localization/LocalizationEntry.cs b/Projects/Server/Localization/LocalizationEntry.cs new file mode 100644 index 000000000..ae4747569 --- /dev/null +++ b/Projects/Server/Localization/LocalizationEntry.cs @@ -0,0 +1,714 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: LocalizationEntry.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Server.Buffers; +using Server.Collections; + +namespace Server; + +public class LocalizationEntry +{ + private static readonly Regex _textRegex = new( + @"~(\d+)[_\w]+~", + RegexOptions.Compiled | + RegexOptions.IgnoreCase | + RegexOptions.Singleline | + RegexOptions.CultureInvariant + ); + + public string Language { get; } + public int Number { get; } + public string Text { get; } + public string[] TextSlices { get; } + public string StringFormatter { get; } + + public LocalizationEntry(string lang, int number, string text) + { + Language = lang; + Number = number; + Text = text; + + ParseText(text, out var textSlices, out var stringFormatter); + TextSlices = textSlices; + StringFormatter = stringFormatter; + } + + private static void ParseText(string text, out string[] textSlices, out string stringFormatter) + { + bool hasMatch = false; + var prevIndex = 0; + var builder = new ValueStringBuilder(stackalloc char[256]); + using var queue = PooledRefQueue.Create(); + foreach (Match match in _textRegex.Matches(text)) + { + if (prevIndex < match.Index) + { + var substr = text[prevIndex..match.Index]; + builder.Append(substr); + + queue.Enqueue(substr); + } + + queue.Enqueue(null); + hasMatch = true; + builder.Append($"{{{int.Parse(match.Groups[1].Value) - 1}}}"); + prevIndex = match.Index + match.Length; + } + + if (prevIndex < text.Length - 1) + { + var substr = prevIndex == 0 ? text : text[prevIndex..]; + builder.Append(substr); + queue.Enqueue(substr); + } + + textSlices = queue.ToArray(); + stringFormatter = hasMatch ? builder.ToString() : null; + + builder.Dispose(); + } + + /// + /// Creates a formatted string of the localization entry. + /// Uses string interpolation under the hood. This method is preferably relative to the object array method signature. + /// Example: + /// Format($"{totalItems}{maxItems}{totalWeight}"); + /// + /// interpolated string handler used by the compiler as a string builder during compilation + /// A copy of the localization text where the placeholder arguments have been replaced with string representations of the provided interpolation arguments + public PooledArraySpanFormattable Format( + [InterpolatedStringHandlerArgument("")] + ref LocalizationInterpolationHandler handler + ) + { + var chars = handler.ToPooledArray(out var length); + handler = default; // Defensive clear + return new PooledArraySpanFormattable(chars, length); + } + + [InterpolatedStringHandler] + public ref struct LocalizationInterpolationHandler + { + private static string[] _empty = Array.Empty(); + + private char[]? _arrayToReturnToPool; + private Span _chars; + private int _pos; + + private int _index; + private string?[] _slices; + private string? _current; + private string _lang; + + public LocalizationInterpolationHandler(int literalLength, int formattedCount, LocalizationEntry entry, out bool isValid) + { + _slices = entry.TextSlices; + _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(256); + isValid = true; + + _pos = 0; + _index = 0; + _current = null; + _lang = entry.Language; + } + + public LocalizationInterpolationHandler( + int literalLength, int formattedCount, int number, out bool isValid + ) : this(literalLength, formattedCount, number, Localization.FallbackLanguage, out isValid) + { + } + + public LocalizationInterpolationHandler( + int literalLength, int formattedCount, int number, string lang, out bool isValid + ) + { + if (Localization.TryGetLocalization(lang, number, out var entry)) + { + _slices = entry.TextSlices; + _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(256); + isValid = true; + } + else + { + _slices = _empty; + _chars = _arrayToReturnToPool = default; + isValid = false; + } + + _pos = 0; + _index = 0; + _current = null; + _lang = lang; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool MoveNext() + { + if ((uint)_index >= (uint)_slices.Length) + { + return false; + } + + _current = _slices[_index++]; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool ReadyToAppend() + { + if (!MoveNext()) + { + return false; + } + + if (_current == null) + { + return true; + } + + AppendStringDirect(_current); + return MoveNext(); + } + + public void AppendLiteral(string value) + { + } + + private void AppendStringDirect(string value) + { + if (value.TryCopyTo(_chars[_pos..])) + { + _pos += value.Length; + } + else + { + GrowThenCopyString(value); + } + } + + public void AppendFormatted(T value) + { + if (!ReadyToAppend()) + { + return; + } + + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, default, default)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format: null, default); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + + // Each numeric needs its own override + public void AppendFormatted(int value, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + if (!TryAppendCliloc(value, format)) + { + AppendFormattedDirect(value, format); + } + } + + public void AppendFormatted(uint value, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + if (!TryAppendCliloc((int)value, format)) + { + AppendFormattedDirect(value, format); + } + } + + public void AppendFormatted(long value, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + if (!TryAppendCliloc((int)value, format)) + { + AppendFormattedDirect(value, format); + } + } + + public void AppendFormatted(ulong value, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + if (!TryAppendCliloc((int)value, format)) + { + AppendFormattedDirect(value, format); + } + } + + public void AppendFormatted(T value, string? format) + { + if (ReadyToAppend()) + { + AppendFormattedDirect(value, format); + } + } + + private void AppendFormattedDirect(T value, string? format) + { + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, format, default)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format, default); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + + public void AppendFormatted(T value, int alignment) + { + if (!ReadyToAppend()) + { + return; + } + + var startingPos = _pos; + AppendFormatted(value); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + + // Each numeric needs its own override + public void AppendFormatted(int value, int alignment, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + var startingPos = _pos; + if (TryAppendCliloc(value, format)) + { + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + else + { + AppendFormattedDirect(value, alignment, format); + } + } + + public void AppendFormatted(uint value, int alignment, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + var startingPos = _pos; + if (TryAppendCliloc((int)value, format)) + { + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + else + { + AppendFormattedDirect(value, alignment, format); + } + } + + public void AppendFormatted(long value, int alignment, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + var startingPos = _pos; + if (TryAppendCliloc((int)value, format)) + { + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + else + { + AppendFormattedDirect(value, alignment, format); + } + } + + public void AppendFormatted(ulong value, int alignment, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + var startingPos = _pos; + if (TryAppendCliloc((int)value, format)) + { + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + else + { + AppendFormattedDirect(value, alignment, format); + } + } + + public void AppendFormatted(T value, int alignment, string? format) + { + if (!ReadyToAppend()) + { + return; + } + + AppendFormattedDirect(value, alignment, format); + } + + private void AppendFormattedDirect(T value, int alignment, string? format) + { + var startingPos = _pos; + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + + public void AppendFormatted(ReadOnlySpan value) + { + if (!ReadyToAppend() || TryAppendClilocByNumericString(value)) + { + return; + } + + if (value.TryCopyTo(_chars[_pos..])) + { + _pos += value.Length; + } + else + { + GrowThenCopySpan(value); + } + } + + public void AppendFormatted(ReadOnlySpan value, int alignment, string? format = null) + { + if (!ReadyToAppend()) + { + return; + } + + var leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + var paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + // The value is as large or larger than the required amount of padding, + // so just write the value. + AppendFormatted(value); + return; + } + + // Write the value along with the appropriate padding. + EnsureCapacityForAdditionalChars(value.Length + paddingRequired); + if (leftAlign) + { + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + _chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + } + else + { + _chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + } + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) + { + if (value is int i) + { + AppendFormatted(i, alignment, format); + } + else if (value is uint ui) + { + AppendFormatted(ui, alignment, format); + } + else if (value is long l) + { + AppendFormatted(l, alignment, format); + } + else if (value is ulong ul) + { + AppendFormatted(ul, alignment, format); + } + else + { + AppendFormatted(value, alignment, format); + } + } + + public void AppendFormatted(string? value) + { + if (!ReadyToAppend() || TryAppendClilocByNumericString(value)) + { + return; + } + + if (value?.TryCopyTo(_chars[_pos..]) == true) + { + _pos += value.Length; + } + else + { + AppendFormattedSlow(value); + } + } + + public void AppendFormatted(string? value, int alignment, string? format = null) => + AppendFormatted(value, alignment, format); + + public bool TryAppendCliloc(int number, string? format) + { + if (format != "#" || !Localization.TryGetLocalization(_lang, number, out var entry)) + { + return false; + } + + var text = entry.Text; + + if (text.TryCopyTo(_chars[_pos..])) + { + _pos += text.Length; + } + else + { + AppendFormattedSlow(text); + } + + return true; + } + + private bool TryAppendClilocByNumericString(ReadOnlySpan value) => + value[0] == '#' && long.TryParse(value[1..], out var number) && TryAppendCliloc((int)number, "#"); + + private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) + { + var charsWritten = _pos - startingPos; + + var leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + var paddingNeeded = alignment - charsWritten; + if (paddingNeeded > 0) + { + EnsureCapacityForAdditionalChars(paddingNeeded); + + if (leftAlign) + { + _chars.Slice(_pos, paddingNeeded).Fill(' '); + } + else + { + _chars.Slice(startingPos, charsWritten).CopyTo(_chars[(startingPos + paddingNeeded)..]); + _chars.Slice(startingPos, paddingNeeded).Fill(' '); + } + + _pos += paddingNeeded; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendFormattedSlow(string? value) + { + if (value is not null) + { + EnsureCapacityForAdditionalChars(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureCapacityForAdditionalChars(int additionalChars) + { + if (_chars.Length - _pos < additionalChars) + { + Grow(additionalChars); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopyString(string value) + { + Grow(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopySpan(ReadOnlySpan value) + { + Grow(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + + [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible + private void Grow(int additionalChars) + { + GrowCore((uint)_pos + (uint)additionalChars); + } + + [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible + private void Grow() + { + GrowCore((uint)_chars.Length + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void GrowCore(uint requiredMinCapacity) + { + var newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_chars.Length * 2, 0x3FFFFFDF)); + var arraySize = (int)Math.Clamp(newCapacity, 256, int.MaxValue); + + var newArray = STArrayPool.Shared.Rent(arraySize); + _chars[.._pos].CopyTo(newArray); + + var toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = newArray; + + if (toReturn is not null) + { + STArrayPool.Shared.Return(toReturn); + } + } + + internal ReadOnlySpan Text => _chars[.._pos]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Clear() + { + var toReturn = _arrayToReturnToPool; + this = default; // defensive clear + if (toReturn is not null) + { + STArrayPool.Shared.Return(toReturn); + } + } + + public string ToStringAndClear() + { + if (MoveNext() && _current != null) + { + AppendStringDirect(_current); + } + + var result = new string(Text); + Clear(); + return result; + } + + public char[] ToPooledArray(out int length) + { + if (MoveNext() && _current != null) + { + AppendStringDirect(_current); + } + + length = _pos; + return _arrayToReturnToPool; + } + } +} diff --git a/Projects/Server/Logging/LogFactory.cs b/Projects/Server/Logging/LogFactory.cs index 2bf93a57d..aee8bb927 100644 --- a/Projects/Server/Logging/LogFactory.cs +++ b/Projects/Server/Logging/LogFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: LogFactory.cs * * * @@ -16,16 +16,18 @@ using System; using Serilog; -namespace Server.Logging -{ - public static class LogFactory - { - private static readonly Serilog.ILogger serilogLogger = new LoggerConfiguration() - .WriteTo.Async(a => a.Console( - outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" - )) - .CreateLogger(); +namespace Server.Logging; - public static ILogger GetLogger(Type declaringType) => new SerilogLogger(serilogLogger.ForContext(declaringType)); - } +public static class LogFactory +{ + private static readonly Serilog.ILogger serilogLogger = new LoggerConfiguration() + .WriteTo.Async(a => a.Console( + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" + )) +#if DEBUG + .MinimumLevel.Debug() +#endif + .CreateLogger(); + + public static ILogger GetLogger(Type declaringType) => new SerilogLogger(serilogLogger.ForContext(declaringType)); } diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 1eac5d8e8..f964a49b0 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -249,15 +249,16 @@ namespace Server { fullPath = Path.Combine(p, path); - if (IsLinux) + if (IsLinux && !File.Exists(fullPath)) { var fi = new FileInfo(fullPath); - fullPath = fi.Directory!.EnumerateFiles( - fi.Name, - new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive } - ).FirstOrDefault()?.FullName; - - break; + if (fi.Directory != null && Directory.Exists(fi.Directory.FullName)) + { + fullPath = fi.Directory.EnumerateFiles( + fi.Name, + new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive } + ).FirstOrDefault()?.FullName; + } } if (File.Exists(fullPath)) @@ -276,6 +277,21 @@ namespace Server return fullPath; } + public static IEnumerable FindDataFileByPattern(string pattern) + { + var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }; + foreach (var p in ServerConfiguration.DataDirectories) + { + if (Directory.Exists(p)) + { + foreach (var file in Directory.EnumerateFiles(p, pattern, options)) + { + yield return file; + } + } + } + } + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Console.WriteLine(e.IsTerminating ? "Error:" : "Warning:"); @@ -594,14 +610,14 @@ namespace Server Interlocked.Increment(ref _mobileCount); } - ValueStringBuilder errors = new ValueStringBuilder(); + using var errors = ValueStringBuilder.CreateMT(); try { if (World.DirtyTrackingEnabled) { var manualDirtyCheckingAttribute = type.GetCustomAttribute(false); - var codeGennedAttribute = type.GetCustomAttribute(false); + var codeGennedAttribute = type.GetCustomAttribute(false); if (manualDirtyCheckingAttribute == null && codeGennedAttribute == null) { diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index 7f5e87d1f..88ef42d10 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -1,750 +1,777 @@ using System; -using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; +using Server.Buffers; using Server.Items; using Server.Logging; using Server.Network; using Server.Targeting; -namespace Server +namespace Server; + +[Flags] +public enum MapRules { - [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 IPooledEnumerable : IPooledEnumerable, IEnumerable +{ +} + +public static class PooledEnumeration +{ + public delegate IEnumerable Selector(Sector sector, Rectangle2D bounds); + + static PooledEnumeration() { - 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 + ClientSelector = SelectClients; + EntitySelector = SelectEntities; + MobileSelector = SelectMobiles; + ItemSelector = SelectItems; + MultiSelector = SelectMultis; + MultiTileSelector = SelectMultiTiles; } - public interface IPooledEnumerable : IEnumerable + public static Selector ClientSelector { get; set; } + public static Selector EntitySelector { get; set; } + public static Selector MobileSelector { get; set; } + public static Selector ItemSelector { get; set; } + public static Selector MultiSelector { get; set; } + public static Selector MultiTileSelector { get; set; } + + public static IEnumerable SelectClients(Sector s, Rectangle2D bounds) { - void Free(); + var clients = new List(s.Clients.Count); + foreach (var client in s.Clients) + { + var m = client.Mobile; + + if (m?.Deleted == false && bounds.Contains(m.Location)) + { + clients.Add(client); + } + } + + return clients; } - public interface IPooledEnumerable : IPooledEnumerable, IEnumerable + public static IEnumerable SelectEntities(Sector s, Rectangle2D bounds) { - } - - public static class PooledEnumeration - { - public delegate IEnumerable Selector(Sector sector, Rectangle2D bounds); - - static PooledEnumeration() + var entities = new List(s.Mobiles.Count + s.Items.Count); + for (int i = s.Mobiles.Count - 1, j = s.Items.Count - 1; i >= 0 || j >= 0; --i, --j) { - ClientSelector = SelectClients; - EntitySelector = SelectEntities; - MobileSelector = SelectMobiles; - ItemSelector = SelectItems; - MultiSelector = SelectMultis; - MultiTileSelector = SelectMultiTiles; - } - - public static Selector ClientSelector { get; set; } - public static Selector EntitySelector { get; set; } - public static Selector MobileSelector { get; set; } - public static Selector ItemSelector { get; set; } - public static Selector MultiSelector { get; set; } - public static Selector MultiTileSelector { get; set; } - - public static IEnumerable SelectClients(Sector s, Rectangle2D bounds) - { - var clients = new List(s.Clients.Count); - foreach (var client in s.Clients) + if (j >= 0) { - var m = client.Mobile; - - if (m?.Deleted == false && bounds.Contains(m.Location)) - { - clients.Add(client); - } - } - - return clients; - } - - public static IEnumerable SelectEntities(Sector s, Rectangle2D bounds) - { - var entities = new List(s.Mobiles.Count + s.Items.Count); - for (int i = s.Mobiles.Count - 1, j = s.Items.Count - 1; i >= 0 || j >= 0; --i, --j) - { - if (j >= 0) - { - Item item = s.Items[j]; - if (item is { Deleted: false, Parent: null } && bounds.Contains(item.Location)) - { - entities.Add(item); - } - } - - if (i >= 0) - { - Mobile mob = s.Mobiles[i]; - if (mob is { Deleted: false } && bounds.Contains(mob.Location)) - { - entities.Add(mob); - } - } - } - return entities; - } - - public static IEnumerable SelectMobiles(Sector s, Rectangle2D bounds) where T : Mobile - { - var entities = new List(s.Mobiles.Count); - for (int i = s.Mobiles.Count - 1; i >= 0; --i) - { - if (s.Mobiles[i] is T { Deleted: false } mob && bounds.Contains(mob.Location)) - { - entities.Add(mob); - } - } - return entities; - } - - public static IEnumerable SelectItems(Sector s, Rectangle2D bounds) where T : Item - { - var entities = new List(s.Items.Count); - for (int i = s.Items.Count - 1; i >= 0; --i) - { - if (s.Items[i] is T { Deleted: false, Parent: null } item && bounds.Contains(item.Location)) + Item item = s.Items[j]; + if (item is { Deleted: false, Parent: null } && bounds.Contains(item.Location)) { entities.Add(item); } } - return entities; - } - public static IEnumerable SelectMultis(Sector s, Rectangle2D bounds) - { - var entities = new List(s.Multis.Count); - for (int i = s.Multis.Count - 1; i >= 0; --i) + if (i >= 0) { - BaseMulti multi = s.Multis[i]; - if (multi is { Deleted: false } && bounds.Contains(multi.Location)) + Mobile mob = s.Mobiles[i]; + if (mob is { Deleted: false } && bounds.Contains(mob.Location)) { - entities.Add(multi); + entities.Add(mob); } } - return entities; } + return entities; + } - public static IEnumerable SelectMultiTiles(Sector s, Rectangle2D bounds) + public static IEnumerable SelectMobiles(Sector s, Rectangle2D bounds) where T : Mobile + { + var entities = new List(s.Mobiles.Count); + for (int i = s.Mobiles.Count - 1; i >= 0; --i) { - for (int l = s.Multis.Count - 1; l >= 0; --l) + if (s.Mobiles[i] is T { Deleted: false } mob && bounds.Contains(mob.Location)) { - BaseMulti o = s.Multis[l]; - if (o?.Deleted != false) + entities.Add(mob); + } + } + return entities; + } + + public static IEnumerable SelectItems(Sector s, Rectangle2D bounds) where T : Item + { + var entities = new List(s.Items.Count); + for (int i = s.Items.Count - 1; i >= 0; --i) + { + if (s.Items[i] is T { Deleted: false, Parent: null } item && bounds.Contains(item.Location)) + { + entities.Add(item); + } + } + return entities; + } + + public static IEnumerable SelectMultis(Sector s, Rectangle2D bounds) + { + var entities = new List(s.Multis.Count); + for (int i = s.Multis.Count - 1; i >= 0; --i) + { + BaseMulti multi = s.Multis[i]; + if (multi is { Deleted: false } && bounds.Contains(multi.Location)) + { + entities.Add(multi); + } + } + return entities; + } + + public static IEnumerable SelectMultiTiles(Sector s, Rectangle2D bounds) + { + for (int l = s.Multis.Count - 1; l >= 0; --l) + { + BaseMulti o = s.Multis[l]; + if (o?.Deleted != false) + { + continue; + } + + MultiComponentList c = o.Components; + + int x, y, xo, yo; + StaticTile[] t, r; + + for (x = bounds.Start.X; x < bounds.End.X; x++) + { + xo = x - (o.X + c.Min.X); + + if (xo < 0 || xo >= c.Width) { continue; } - MultiComponentList c = o.Components; - - int x, y, xo, yo; - StaticTile[] t, r; - - for (x = bounds.Start.X; x < bounds.End.X; x++) + for (y = bounds.Start.Y; y < bounds.End.Y; y++) { - xo = x - (o.X + c.Min.X); + yo = y - (o.Y + c.Min.Y); - if (xo < 0 || xo >= c.Width) + if (yo < 0 || yo >= c.Height) { continue; } - for (y = bounds.Start.Y; y < bounds.End.Y; y++) + t = c.Tiles[xo][yo]; + + if (t.Length <= 0) { - yo = y - (o.Y + c.Min.Y); - - if (yo < 0 || yo >= c.Height) - { - continue; - } - - t = c.Tiles[xo][yo]; - - if (t.Length <= 0) - { - continue; - } - - r = new StaticTile[t.Length]; - - for (var i = 0; i < t.Length; i++) - { - r[i] = t[i]; - r[i].Z += o.Z; - } - - yield return r; + continue; } + + r = new StaticTile[t.Length]; + + for (var i = 0; i < t.Length; i++) + { + r[i] = t[i]; + r[i].Z += o.Z; + } + + yield return r; } } } - - public static Map.PooledEnumerable GetClients(Map map, Rectangle2D bounds) => - Map.PooledEnumerable.Instantiate(map, bounds, ClientSelector ?? SelectClients); - - public static Map.PooledEnumerable GetEntities(Map map, Rectangle2D bounds) => - Map.PooledEnumerable.Instantiate(map, bounds, EntitySelector ?? SelectEntities); - - public static Map.PooledEnumerable GetMobiles(Map map, Rectangle2D bounds) => - GetMobiles(map, bounds); - - public static Map.PooledEnumerable GetMobiles(Map map, Rectangle2D bounds) where T : Mobile => - Map.PooledEnumerable.Instantiate(map, bounds, SelectMobiles); - - public static Map.PooledEnumerable GetItems(Map map, Rectangle2D bounds) where T : Item => - Map.PooledEnumerable.Instantiate(map, bounds, SelectItems); - - public static Map.PooledEnumerable GetMultis(Map map, Rectangle2D bounds) => - Map.PooledEnumerable.Instantiate(map, bounds, MultiSelector ?? SelectMultis); - - public static Map.PooledEnumerable GetMultiTiles(Map map, Rectangle2D bounds) => - Map.PooledEnumerable.Instantiate(map, bounds, MultiTileSelector ?? SelectMultiTiles); - - public static IEnumerable EnumerateSectors(Map map, Rectangle2D bounds) - { - if (map == null || map == Map.Internal) - { - yield break; - } - - var x1 = bounds.Start.X; - var y1 = bounds.Start.Y; - var x2 = bounds.End.X; - var y2 = bounds.End.Y; - - if (!Bound(map, ref x1, ref y1, ref x2, ref y2, out var xSector, out var ySector)) - { - yield break; - } - - var index = 0; - - while (NextSector(map, x1, y1, x2, y2, ref index, ref xSector, ref ySector, out var s)) - { - yield return s; - } - } - - public static bool Bound( - Map map, - ref int x1, - ref int y1, - ref int x2, - ref int y2, - out int xSector, - out int ySector - ) - { - if (map == null || map == Map.Internal) - { - xSector = ySector = 0; - return false; - } - - map.Bound(x1, y1, out x1, out y1); - map.Bound(x2 - 1, y2 - 1, out x2, out y2); - - x1 >>= Map.SectorShift; - y1 >>= Map.SectorShift; - x2 >>= Map.SectorShift; - y2 >>= Map.SectorShift; - - xSector = x1; - ySector = y1; - - return true; - } - - private static bool NextSector( - Map map, - int x1, - int y1, - int x2, - int y2, - ref int index, - ref int xSector, - ref int ySector, - out Sector s - ) - { - if (map == null) - { - s = null; - xSector = ySector = 0; - return false; - } - - if (map == Map.Internal) - { - s = map.InvalidSector; - xSector = ySector = 0; - return false; - } - - if (index++ > 0) - { - if (++ySector > y2) - { - ySector = y1; - - if (++xSector > x2) - { - xSector = x1; - - s = map.InvalidSector; - return false; - } - } - } - - s = map.GetRealSector(xSector, ySector); - return true; - } } - [Parsable] - public sealed class Map : IComparable + public static Map.PooledEnumerable GetClients(Map map, Rectangle2D bounds) => + Map.PooledEnumerable.Instantiate(map, bounds, ClientSelector ?? SelectClients); + + public static Map.PooledEnumerable GetEntities(Map map, Rectangle2D bounds) => + Map.PooledEnumerable.Instantiate(map, bounds, EntitySelector ?? SelectEntities); + + public static Map.PooledEnumerable GetMobiles(Map map, Rectangle2D bounds) => + GetMobiles(map, bounds); + + public static Map.PooledEnumerable GetMobiles(Map map, Rectangle2D bounds) where T : Mobile => + Map.PooledEnumerable.Instantiate(map, bounds, SelectMobiles); + + public static Map.PooledEnumerable GetItems(Map map, Rectangle2D bounds) where T : Item => + Map.PooledEnumerable.Instantiate(map, bounds, SelectItems); + + public static Map.PooledEnumerable GetMultis(Map map, Rectangle2D bounds) => + Map.PooledEnumerable.Instantiate(map, bounds, MultiSelector ?? SelectMultis); + + public static Map.PooledEnumerable GetMultiTiles(Map map, Rectangle2D bounds) => + Map.PooledEnumerable.Instantiate(map, bounds, MultiTileSelector ?? SelectMultiTiles); + + public static IEnumerable EnumerateSectors(Map map, Rectangle2D bounds) { - public const int SectorSize = 16; - public const int SectorShift = 4; - public const int SectorActiveRange = 2; - - private static ILogger _logger; - private static ILogger Logger => _logger ??= LogFactory.GetLogger(typeof(Map)); - - private readonly int m_FileIndex; - private readonly Sector[][] m_Sectors; - private readonly int m_SectorsHeight; - - private readonly int m_SectorsWidth; - - private readonly object tileLock = new(); - private Region m_DefaultRegion; - - private string m_Name; - - private TileMatrix m_Tiles; - - public Map(int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules) + if (map == null || map == Map.Internal) { - MapID = mapID; - MapIndex = mapIndex; - m_FileIndex = fileIndex; - Width = width; - Height = height; - Season = season; - m_Name = name; - Rules = rules; - Regions = new Dictionary(StringComparer.OrdinalIgnoreCase); - InvalidSector = new Sector(0, 0, this); - m_SectorsWidth = width >> SectorShift; - m_SectorsHeight = height >> SectorShift; - m_Sectors = new Sector[m_SectorsWidth][]; + yield break; } - public static Map[] Maps { get; } = new Map[0x100]; + var x1 = bounds.Start.X; + var y1 = bounds.Start.Y; + var x2 = bounds.End.X; + var y2 = bounds.End.Y; - public static Map Felucca => Maps[0]; - public static Map Trammel => Maps[1]; - public static Map Ilshenar => Maps[2]; - public static Map Malas => Maps[3]; - public static Map Tokuno => Maps[4]; - public static Map TerMur => Maps[5]; - public static Map Internal => Maps[0x7F]; - - public static List AllMaps { get; } = new(); - - public int Season { get; set; } - - public TileMatrix Tiles + if (!Bound(map, ref x1, ref y1, ref x2, ref y2, out var xSector, out var ySector)) { - get - { - if (m_Tiles == null) - { - lock (tileLock) - { - m_Tiles = new TileMatrix(this, m_FileIndex, MapID, Width, Height); - } - } + yield break; + } - return m_Tiles; + var index = 0; + + while (NextSector(map, x1, y1, x2, y2, ref index, ref xSector, ref ySector, out var s)) + { + yield return s; + } + } + + public static bool Bound( + Map map, + ref int x1, + ref int y1, + ref int x2, + ref int y2, + out int xSector, + out int ySector + ) + { + if (map == null || map == Map.Internal) + { + xSector = ySector = 0; + return false; + } + + map.Bound(x1, y1, out x1, out y1); + map.Bound(x2 - 1, y2 - 1, out x2, out y2); + + x1 >>= Map.SectorShift; + y1 >>= Map.SectorShift; + x2 >>= Map.SectorShift; + y2 >>= Map.SectorShift; + + xSector = x1; + ySector = y1; + + return true; + } + + private static bool NextSector( + Map map, + int x1, + int y1, + int x2, + int y2, + ref int index, + ref int xSector, + ref int ySector, + out Sector s + ) + { + if (map == null) + { + s = null; + xSector = ySector = 0; + return false; + } + + if (map == Map.Internal) + { + s = map.InvalidSector; + xSector = ySector = 0; + return false; + } + + if (index++ > 0) + { + if (++ySector > y2) + { + ySector = y1; + + if (++xSector > x2) + { + xSector = x1; + + s = map.InvalidSector; + return false; + } } } - public int MapID { get; } + s = map.GetRealSector(xSector, ySector); + return true; + } +} - public int MapIndex { get; } +[Parsable] +public sealed class Map : IComparable +{ + public const int SectorSize = 16; + public const int SectorShift = 4; + public const int SectorActiveRange = 2; - public int Width { get; } + private static ILogger logger = LogFactory.GetLogger(typeof(Map)); - public int Height { get; } + private readonly int m_FileIndex; + private readonly Sector[][] m_Sectors; + private readonly int m_SectorsHeight; - public Dictionary Regions { get; } + private readonly int m_SectorsWidth; - public Region DefaultRegion + private readonly object tileLock = new(); + private Region m_DefaultRegion; + + private string m_Name; + + private TileMatrix m_Tiles; + + public Map(int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules) + { + MapID = mapID; + MapIndex = mapIndex; + m_FileIndex = fileIndex; + Width = width; + Height = height; + Season = season; + m_Name = name; + Rules = rules; + Regions = new Dictionary(StringComparer.OrdinalIgnoreCase); + InvalidSector = new Sector(0, 0, this); + m_SectorsWidth = width >> SectorShift; + m_SectorsHeight = height >> SectorShift; + m_Sectors = new Sector[m_SectorsWidth][]; + } + + public static Map[] Maps { get; } = new Map[0x100]; + + public static Map Felucca => Maps[0]; + public static Map Trammel => Maps[1]; + public static Map Ilshenar => Maps[2]; + public static Map Malas => Maps[3]; + public static Map Tokuno => Maps[4]; + public static Map TerMur => Maps[5]; + public static Map Internal => Maps[0x7F]; + + public static List AllMaps { get; } = new(); + + public int Season { get; set; } + + public TileMatrix Tiles + { + get { - get => m_DefaultRegion ??= new Region(null, this, 0, Array.Empty()); - set => m_DefaultRegion = value; - } - - public MapRules Rules { get; set; } - - public Sector InvalidSector { get; } - - public string Name - { - get + if (m_Tiles == null) { - if (this == Internal && m_Name != "Internal") + lock (tileLock) { - Logger.Warning($"Internal map name was '{m_Name}'\n{new StackTrace()}"); - m_Name = "Internal"; + m_Tiles = new TileMatrix(this, m_FileIndex, MapID, Width, Height); } - - return m_Name; } - set + + return m_Tiles; + } + } + + public int MapID { get; } + + public int MapIndex { get; } + + public int Width { get; } + + public int Height { get; } + + public Dictionary Regions { get; } + + public Region DefaultRegion + { + get => m_DefaultRegion ??= new Region(null, this, 0, Array.Empty()); + set => m_DefaultRegion = value; + } + + public MapRules Rules { get; set; } + + public Sector InvalidSector { get; } + + public string Name + { + get + { + if (this == Internal && m_Name != "Internal") { - if (this == Internal && value != "Internal") - { - Logger.Warning($"Attempted to set internal map name to '{value}'\n{new StackTrace()}"); + logger.Warning("Internal map name was '{Name}'\n{StackTrace}", m_Name, new StackTrace()); + m_Name = "Internal"; + } - value = "Internal"; - } + return m_Name; + } + set + { + if (this == Internal && value != "Internal") + { + logger.Warning("Attempted to set internal map name to '{Value}'\n{StackTrace}", value, new StackTrace()); + value = "Internal"; + } - m_Name = value; + m_Name = value; + } + } + + public static int[] InvalidLandTiles { get; set; } = { 0x244 }; + + public static int MaxLOSDistance { get; set; } = 25; + + public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID); + + public static string[] GetMapNames() + { + var mapCount = 0; + for (var i = 0; i < Maps.Length; i++) + { + var map = Maps[i]; + if (map != null) + { + mapCount++; } } - public static int[] InvalidLandTiles { get; set; } = { 0x244 }; - - public static int MaxLOSDistance { get; set; } = 25; - - public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID); - - public static string[] GetMapNames() + var mapNames = new string[mapCount]; + for (int i = 0, mIndex = 0; i < Maps.Length; i++) { - var mapCount = 0; - for (var i = 0; i < Maps.Length; i++) + var map = Maps[i]; + if (map != null) { - var map = Maps[i]; - if (map != null) - { - mapCount++; - } + mapNames[mIndex++] = map.Name; } - - var mapNames = new string[mapCount]; - for (int i = 0, mIndex = 0; i < Maps.Length; i++) - { - var map = Maps[i]; - if (map != null) - { - mapNames[mIndex++] = map.Name; - } - } - - return mapNames; } - public static Map[] GetMapValues() + return mapNames; + } + + public static Map[] GetMapValues() + { + var mapCount = 0; + for (var i = 0; i < Maps.Length; i++) { - var mapCount = 0; - for (var i = 0; i < Maps.Length; i++) + var map = Maps[i]; + if (map != null) { - var map = Maps[i]; - if (map != null) - { - mapCount++; - } + mapCount++; } - - var mapValues = new Map[mapCount]; - for (int i = 0, mIndex = 0; i < Maps.Length; i++) - { - var map = Maps[i]; - if (map != null) - { - mapValues[mIndex++] = map; - } - } - - return mapValues; } - public static Map Parse(string value) + var mapValues = new Map[mapCount]; + for (int i = 0, mIndex = 0; i < Maps.Length; i++) { - if (string.IsNullOrWhiteSpace(value)) + var map = Maps[i]; + if (map != null) { - return null; + mapValues[mIndex++] = map; } + } - if (value.InsensitiveEquals("Internal")) - { - return Internal; - } + return mapValues; + } - if (!int.TryParse(value, out var index)) - { - index = -1; - } - else if (index == 127) - { - return Internal; - } + // Handles null checks + public static Map Parse(string value) => Parse(value ?? ReadOnlySpan.Empty); - for (int i = 0; i < Maps.Length; i++) - { - var map = Maps[i]; - if (map == null) - { - continue; - } - - if (index >= 0 && map.MapIndex == index || map.Name.InsensitiveEquals(value)) - { - return map; - } - } + public static Map Parse(ReadOnlySpan value) + { + value = value.Trim(); + if (value.Length == 0) + { return null; } - public override string ToString() => Name; - - public int GetAverageZ(int x, int y) + if (value.InsensitiveEquals("Internal")) { - GetAverageZ(x, y, out _, out var avg, out _); - return avg; + return Internal; } - public void GetAverageZ(int x, int y, out int z, out int avg, out int top) + if (!int.TryParse(value, out var index)) { - var zTop = Tiles.GetLandTile(x, y).Z; - var zLeft = Tiles.GetLandTile(x, y + 1).Z; - var zRight = Tiles.GetLandTile(x + 1, y).Z; - var 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; - } - - avg = (zTop - zBottom).Abs() > (zLeft - zRight).Abs() - ? FloorAverage(zLeft, zRight) - : FloorAverage(zTop, zBottom); + index = -1; + } + else if (index == 127) + { + return Internal; } - private static int FloorAverage(int a, int b) + for (int i = 0; i < Maps.Length; i++) { - var v = a + b; - - if (v < 0) + var map = Maps[i]; + if (map == null) { - --v; + continue; } - return v / 2; + if (index >= 0 && map.MapIndex == index || value.InsensitiveEquals(map.Name)) + { + return map; + } } - public IPooledEnumerable GetMultiTilesAt(int x, int y) => - PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1)); + return null; + } - private static void AcquireFixItems(Map map, int x, int y, Item[] pool, out int length) + public override string ToString() => Name; + + public int GetAverageZ(int x, int y) + { + GetAverageZ(x, y, out _, out var avg, out _); + return avg; + } + + public void GetAverageZ(int x, int y, out int z, out int avg, out int top) + { + var zTop = Tiles.GetLandTile(x, y).Z; + var zLeft = Tiles.GetLandTile(x, y + 1).Z; + var zRight = Tiles.GetLandTile(x + 1, y).Z; + var zBottom = Tiles.GetLandTile(x + 1, y + 1).Z; + + z = zTop; + if (zLeft < z) { - length = 0; - if (map == null || map == Internal || x < 0 || x > map.Width || y < 0 || y > map.Height) - { - return; - } + z = zLeft; + } - var eable = map.GetItemsInRange(new Point3D(x, y, 0), 0); - foreach (var item in eable) + 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; + } + + avg = (zTop - zBottom).Abs() > (zLeft - zRight).Abs() + ? FloorAverage(zLeft, zRight) + : FloorAverage(zTop, zBottom); + } + + private static int FloorAverage(int a, int b) + { + var v = a + b; + + if (v < 0) + { + --v; + } + + return v / 2; + } + + public IPooledEnumerable GetMultiTilesAt(int x, int y) => + PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1)); + + private static void AcquireFixItems(Map map, int x, int y, Item[] pool, out int length) + { + length = 0; + if (map == null || map == Internal || x < 0 || x > map.Width || y < 0 || y > map.Height) + { + return; + } + + var eable = map.GetItemsInRange(new Point3D(x, y, 0), 0); + foreach (var item in eable) + { + if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue) { - if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue) + if (length == 128) { - if (length == 128) - { - break; - } + break; + } - pool[length++] = item; + pool[length++] = item; + } + } + + eable.Free(); + + Array.Sort(pool, 0, length, ZComparer.Default); + } + + public void FixColumn(int x, int y) + { + var landTile = Tiles.GetLandTile(x, y); + var tiles = Tiles.GetStaticTiles(x, y, true); + + GetAverageZ(x, y, out _, out var landAvg, out _); + + var items = STArrayPool.Shared.Rent(128); + AcquireFixItems(this, x, y, items, out var length); + + for (var i = 0; i < length; i++) + { + var toFix = items[i]; + + if (!toFix.Movable) + { + continue; + } + + var z = int.MinValue; + var currentZ = toFix.Z; + + if (!landTile.Ignored && landAvg <= currentZ) + { + z = landAvg; + } + + foreach (var tile in tiles) + { + var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; + + var checkZ = tile.Z; + var checkTop = checkZ + id.CalcHeight; + + if (checkTop == checkZ && !id.Surface) + { + ++checkTop; + } + + if (checkTop > z && checkTop <= currentZ) + { + z = checkTop; } } - eable.Free(); - - Array.Sort(pool, 0, length, ZComparer.Default); - } - - public void FixColumn(int x, int y) - { - var landTile = Tiles.GetLandTile(x, y); - var tiles = Tiles.GetStaticTiles(x, y, true); - - GetAverageZ(x, y, out _, out var landAvg, out _); - - var items = ArrayPool.Shared.Rent(128); - AcquireFixItems(this, x, y, items, out var length); - - for (var i = 0; i < length; i++) + for (var j = 0; j < length; ++j) { - var toFix = items[i]; - - if (!toFix.Movable) + if (j == i) { continue; } - var z = int.MinValue; - var currentZ = toFix.Z; + var item = items[j]; + var id = item.ItemData; - if (!landTile.Ignored && landAvg <= currentZ) + var checkZ = item.Z; + var checkTop = checkZ + id.CalcHeight; + + if (checkTop == checkZ && !id.Surface) { - z = landAvg; + ++checkTop; } - foreach (var tile in tiles) + if (checkTop > z && checkTop <= currentZ) { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - - var checkZ = tile.Z; - var checkTop = checkZ + id.CalcHeight; - - if (checkTop == checkZ && !id.Surface) - { - ++checkTop; - } - - if (checkTop > z && checkTop <= currentZ) - { - z = checkTop; - } - } - - for (var j = 0; j < length; ++j) - { - if (j == i) - { - continue; - } - - var item = items[j]; - var id = item.ItemData; - - var checkZ = item.Z; - var 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); + z = checkTop; } } - ArrayPool.Shared.Return(items); - } - - /* This could probably be re-implemented if necessary (perhaps via an ITile interface?). - public List GetTilesAt( Point2D p, bool items, bool land, bool statics ) - { - List list = new List(); - - if (this == 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 StaticTile( (ushort)item.ItemID, (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 == Internal) + if (z != int.MinValue) { - return null; + toFix.Location = new Point3D(toFix.X, toFix.Y, z); } + } - object surface = null; - var surfaceZ = int.MinValue; + STArrayPool.Shared.Return(items, true); + } - var lt = Tiles.GetLandTile(p.X, p.Y); + /* This could probably be re-implemented if necessary (perhaps via an ITile interface?). + public List GetTilesAt( Point2D p, bool items, bool land, bool statics ) + { + List list = new List(); - if (!lt.Ignored) + if (this == 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 StaticTile( (ushort)item.ItemID, (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 == Internal) + { + return null; + } + + object surface = null; + var surfaceZ = int.MinValue; + + var lt = Tiles.GetLandTile(p.X, p.Y); + + if (!lt.Ignored) + { + var avgZ = GetAverageZ(p.X, p.Y); + + if (avgZ <= p.Z) { - var avgZ = GetAverageZ(p.X, p.Y); + surface = lt; + surfaceZ = avgZ; - if (avgZ <= p.Z) + if (surfaceZ == p.Z) { - surface = lt; - surfaceZ = avgZ; + return surface; + } + } + } + + var staticTiles = Tiles.GetStaticTiles(p.X, p.Y, true); + + for (var i = 0; i < staticTiles.Length; i++) + { + var tile = staticTiles[i]; + var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; + + if (id.Surface || id.Wet) + { + var tileZ = tile.Z + id.CalcHeight; + + if (tileZ > surfaceZ && tileZ <= p.Z) + { + surface = tile; + surfaceZ = tileZ; if (surfaceZ == p.Z) { @@ -752,22 +779,27 @@ namespace Server } } } + } - var staticTiles = Tiles.GetStaticTiles(p.X, p.Y, true); + var sector = GetSector(p.X, p.Y); - for (var i = 0; i < staticTiles.Length; i++) + for (var i = 0; i < sector.Items.Count; i++) + { + var item = sector.Items[i]; + + if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(p.X, p.Y) && + !item.Movable) { - var tile = staticTiles[i]; - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; + var id = item.ItemData; if (id.Surface || id.Wet) { - var tileZ = tile.Z + id.CalcHeight; + var itemZ = item.Z + id.CalcHeight; - if (tileZ > surfaceZ && tileZ <= p.Z) + if (itemZ > surfaceZ && itemZ <= p.Z) { - surface = tile; - surfaceZ = tileZ; + surface = item; + surfaceZ = itemZ; if (surfaceZ == p.Z) { @@ -776,908 +808,884 @@ namespace Server } } } + } - var sector = GetSector(p.X, p.Y); + return surface; + } - for (var i = 0; i < sector.Items.Count; i++) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Bound(int x, int y, out int newX, out int newY) + { + newX = Math.Clamp(x, 0, Width - 1); + newY = Math.Clamp(y, 0, Height - 1); + } + + public Point2D Bound(Point3D p) + { + Bound(p.m_X, p.m_Y, out var x, out var y); + return new Point2D(x, y); + } + + public Point2D Bound(Point2D p) + { + Bound(p.m_X, p.m_Y, out var x, out var y); + return new Point2D(x, y); + } + + public void ActivateSectors(int cx, int cy) + { + for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x) + { + for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y) { - var item = sector.Items[i]; - - if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(p.X, p.Y) && - !item.Movable) + var sect = GetRealSector(x, y); + if (sect != InvalidSector) { - var id = item.ItemData; - - if (id.Surface || id.Wet) - { - var itemZ = item.Z + id.CalcHeight; - - if (itemZ > surfaceZ && itemZ <= p.Z) - { - surface = item; - surfaceZ = itemZ; - - if (surfaceZ == p.Z) - { - return surface; - } - } - } + sect.Activate(); } } - - return surface; } + } - public void Bound(int x, int y, out int newX, out int newY) + public void DeactivateSectors(int cx, int cy) + { + for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x) { - newX = Math.Clamp(x, 0, Width - 1); - newY = Math.Clamp(y, 0, Height - 1); - } - - public Point2D Bound(Point2D p) - { - var x = Math.Clamp(p.m_X, 0, Width - 1); - var y = Math.Clamp(p.m_Y, 0, Height - 1); - - return new Point2D(x, y); - } - - public void ActivateSectors(int cx, int cy) - { - for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x) + for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y) { - for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y) + var sect = GetRealSector(x, y); + if (sect != InvalidSector && !PlayersInRange(sect, SectorActiveRange)) { - var sect = GetRealSector(x, y); - if (sect != InvalidSector) - { - sect.Activate(); - } + sect.Deactivate(); + } + } + } + } + + private bool PlayersInRange(Sector sect, int range) + { + for (var x = sect.X - range; x <= sect.X + range; ++x) + { + for (var y = sect.Y - range; y <= sect.Y + range; ++y) + { + var check = GetRealSector(x, y); + if (check != InvalidSector && check.Clients.Count > 0) + { + return true; } } } - public void DeactivateSectors(int cx, int cy) + return false; + } + + public void OnClientChange(NetState oldState, NetState newState, Mobile m) + { + if (this != Internal) { - for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x) - { - for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y) - { - var sect = GetRealSector(x, y); - if (sect != InvalidSector && !PlayersInRange(sect, SectorActiveRange)) - { - sect.Deactivate(); - } - } - } + GetSector(m.Location).OnClientChange(oldState, newState); + } + } + + public void OnEnter(Mobile m) + { + if (this != Internal) + { + GetSector(m.Location).OnEnter(m); + } + } + + public void OnEnter(Item item) + { + if (this == Internal) + { + return; } - private bool PlayersInRange(Sector sect, int range) - { - for (var x = sect.X - range; x <= sect.X + range; ++x) - { - for (var y = sect.Y - range; y <= sect.Y + range; ++y) - { - var check = GetRealSector(x, y); - if (check != InvalidSector && check.Clients.Count > 0) - { - return true; - } - } - } + GetSector(item.Location).OnEnter(item); - return false; + if (item is BaseMulti m) + { + var mcl = m.Components; + + var start = GetMultiMinSector(m.Location, mcl); + var end = GetMultiMaxSector(m.Location, mcl); + + AddMulti(m, start, end); + } + } + + public void OnLeave(Mobile m) + { + if (this != Internal) + { + GetSector(m.Location).OnLeave(m); + } + } + + public void OnLeave(Item item) + { + if (this == Internal) + { + return; } - public void OnClientChange(NetState oldState, NetState newState, Mobile m) + GetSector(item.Location).OnLeave(item); + + if (item is BaseMulti m) { - if (this != Internal) - { - GetSector(m.Location).OnClientChange(oldState, newState); - } + var mcl = m.Components; + + var start = GetMultiMinSector(m.Location, mcl); + var end = GetMultiMaxSector(m.Location, mcl); + + RemoveMulti(m, start, end); + } + } + + public void RemoveMulti(BaseMulti m, Sector start, Sector end) + { + if (this == Internal) + { + return; } - public void OnEnter(Mobile m) + for (var x = start.X; x <= end.X; ++x) { - if (this != Internal) + for (var y = start.Y; y <= end.Y; ++y) { - GetSector(m.Location).OnEnter(m); + InternalGetSector(x, y).OnMultiLeave(m); } } + } - public void OnEnter(Item item) + public void AddMulti(BaseMulti m, Sector start, Sector end) + { + if (this == Internal) { - if (this == Internal) + return; + } + + for (var x = start.X; x <= end.X; ++x) + { + for (var y = start.Y; y <= end.Y; ++y) { - return; + InternalGetSector(x, y).OnMultiEnter(m); } + } + } - GetSector(item.Location).OnEnter(item); + public Sector GetMultiMinSector(Point3D loc, MultiComponentList mcl) => + GetSector(Bound(new Point2D(loc.m_X + mcl.Min.m_X, loc.m_Y + mcl.Min.m_Y))); - if (item is BaseMulti m) + public Sector GetMultiMaxSector(Point3D loc, MultiComponentList mcl) => + 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 == Internal) + { + return; + } + + var oldSector = GetSector(oldLocation); + var newSector = GetSector(m.Location); + + if (oldSector != newSector) + { + oldSector.OnLeave(m); + newSector.OnEnter(m); + } + } + + public void OnMove(Point3D oldLocation, Item item) + { + if (this == Internal) + { + return; + } + + var oldSector = GetSector(oldLocation); + var newSector = GetSector(item.Location); + + if (oldSector != newSector) + { + oldSector.OnLeave(item); + newSector.OnEnter(item); + } + + if (item is BaseMulti m) + { + var mcl = m.Components; + + var start = GetMultiMinSector(m.Location, mcl); + var end = GetMultiMaxSector(m.Location, mcl); + + var oldStart = GetMultiMinSector(oldLocation, mcl); + var oldEnd = GetMultiMaxSector(oldLocation, mcl); + + if (oldStart != start || oldEnd != end) { - var mcl = m.Components; - - var start = GetMultiMinSector(m.Location, mcl); - var end = GetMultiMaxSector(m.Location, mcl); - + RemoveMulti(m, oldStart, oldEnd); AddMulti(m, start, end); } } + } - public void OnLeave(Mobile m) + public void RegisterRegion(Region reg) + { + var regName = reg.Name; + + if (regName == null) { - if (this != Internal) - { - GetSector(m.Location).OnLeave(m); - } + return; } - public void OnLeave(Item item) + if (Regions.ContainsKey(regName)) { - if (this == Internal) - { - return; - } + logger.Warning("Duplicate region name '{RegionName}' for map '{MapName}'", regName, Name); + } + else + { + Regions[regName] = reg; + } + } - GetSector(item.Location).OnLeave(item); + public void UnregisterRegion(Region reg) + { + var regName = reg.Name; - if (item is BaseMulti m) - { - var mcl = m.Components; + if (regName != null) + { + Regions.Remove(regName); + } + } - var start = GetMultiMinSector(m.Location, mcl); - var end = GetMultiMaxSector(m.Location, mcl); + public Point3D GetPoint(object o, bool eye) + { + Point3D p; - RemoveMulti(m, start, end); - } + if (o is Mobile mobile) + { + p = mobile.Location; + p.Z += 14; // eye ? 15 : 10; + } + else if (o is Item item) + { + p = item.GetWorldLocation(); + p.Z += item.ItemData.Height / 2 + 1; + } + else if (o is Point3D point3D) + { + p = point3D; + } + else if (o is LandTarget target) + { + p = target.Location; + + GetAverageZ(p.X, p.Y, out _, out _, out var top); + + p.Z = top + 1; + } + else if (o is StaticTarget st) + { + var id = TileData.ItemTable[st.ItemID & TileData.MaxItemValue]; + + p = new Point3D(st.X, st.Y, st.Z - id.CalcHeight + id.Height / 2 + 1); + } + else if (o is IPoint3D d) + { + p = new Point3D(d.X, d.Y, d.Z); + } + else + { + logger.Warning("Warning: Invalid object ({Object}) in line of sight", o); + p = Point3D.Zero; } - public void RemoveMulti(BaseMulti m, Sector start, Sector end) - { - if (this == Internal) - { - return; - } + return p; + } - for (var x = start.X; x <= end.X; ++x) - { - for (var y = start.Y; y <= end.Y; ++y) - { - InternalGetSector(x, y).OnMultiLeave(m); - } - } + public IPooledEnumerable GetObjectsInRange(Point3D p) => GetObjectsInRange(p, Core.GlobalMaxUpdateRange); + + public IPooledEnumerable GetObjectsInRange(Point3D p, int range) => + GetObjectsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); + + public IPooledEnumerable GetObjectsInBounds(Rectangle2D bounds) => + PooledEnumeration.GetEntities(this, bounds); + + public IPooledEnumerable GetClientsInRange(Point3D p) => GetClientsInRange(p, Core.GlobalMaxUpdateRange); + + public IPooledEnumerable GetClientsInRange(Point3D p, int range) => + GetClientsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); + + public IPooledEnumerable GetClientsInBounds(Rectangle2D bounds) => + PooledEnumeration.GetClients(this, bounds); + + public IPooledEnumerable GetItemsInRange(Point3D p) => GetItemsInRange(p, Core.GlobalMaxUpdateRange); + + public IPooledEnumerable GetItemsInRange(Point3D p, int range) => GetItemsInRange(p, range); + + public IPooledEnumerable GetItemsInRange(Point3D p, int range) where T : Item => + GetItemsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); + + public IPooledEnumerable GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds(bounds); + + public IPooledEnumerable GetItemsInBounds(Rectangle2D bounds) where T : Item => + PooledEnumeration.GetItems(this, bounds); + + public IPooledEnumerable GetMobilesInRange(Point3D p) => GetMobilesInRange(p, Core.GlobalMaxUpdateRange); + + public IPooledEnumerable GetMobilesInRange(Point3D p, int range) => GetMobilesInRange(p, range); + + public IPooledEnumerable GetMobilesInRange(Point3D p, int range) where T : Mobile => + GetMobilesInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); + + public IPooledEnumerable GetMobilesInBounds(Rectangle2D bounds) => GetMobilesInBounds(bounds); + + public IPooledEnumerable GetMobilesInBounds(Rectangle2D bounds) where T : Mobile => + PooledEnumeration.GetMobiles(this, bounds); + + public bool CanFit( + Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, + bool requireSurface = true + ) => + CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface); + + public bool CanFit( + Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, + bool requireSurface = true + ) => + CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface); + + public bool CanFit( + int x, int y, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, + bool requireSurface = true + ) + { + if (this == Internal) + { + return false; } - public void AddMulti(BaseMulti m, Sector start, Sector end) + if (x < 0 || y < 0 || x >= Width || y >= Height) { - if (this == Internal) - { - return; - } - - for (var x = start.X; x <= end.X; ++x) - { - for (var y = start.Y; y <= end.Y; ++y) - { - InternalGetSector(x, y).OnMultiEnter(m); - } - } + return false; } - public Sector GetMultiMinSector(Point3D loc, MultiComponentList mcl) => - GetSector(Bound(new Point2D(loc.m_X + mcl.Min.m_X, loc.m_Y + mcl.Min.m_Y))); + var hasSurface = false; - public Sector GetMultiMaxSector(Point3D loc, MultiComponentList mcl) => - GetSector(Bound(new Point2D(loc.m_X + mcl.Max.m_X, loc.m_Y + mcl.Max.m_Y))); + var lt = Tiles.GetLandTile(x, y); + GetAverageZ(x, y, out var lowZ, out var avgZ, out _); + var landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags; - public void OnMove(Point3D oldLocation, Mobile m) + if ((landFlags & TileFlag.Impassable) != 0 && avgZ > z && z + height > lowZ) { - if (this == Internal) - { - return; - } - - var oldSector = GetSector(oldLocation); - var newSector = GetSector(m.Location); - - if (oldSector != newSector) - { - oldSector.OnLeave(m); - newSector.OnEnter(m); - } + return false; } - public void OnMove(Point3D oldLocation, Item item) + if ((landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored) { - if (this == Internal) - { - return; - } - - var oldSector = GetSector(oldLocation); - var newSector = GetSector(item.Location); - - if (oldSector != newSector) - { - oldSector.OnLeave(item); - newSector.OnEnter(item); - } - - if (item is BaseMulti m) - { - var mcl = m.Components; - - var start = GetMultiMinSector(m.Location, mcl); - var end = GetMultiMaxSector(m.Location, mcl); - - var oldStart = GetMultiMinSector(oldLocation, mcl); - var oldEnd = GetMultiMaxSector(oldLocation, mcl); - - if (oldStart != start || oldEnd != end) - { - RemoveMulti(m, oldStart, oldEnd); - AddMulti(m, start, end); - } - } + hasSurface = true; } - public void RegisterRegion(Region reg) + var staticTiles = Tiles.GetStaticTiles(x, y, true); + + bool surface, impassable; + + for (var i = 0; i < staticTiles.Length; ++i) { - var regName = reg.Name; + var id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue]; + surface = id.Surface; + impassable = id.Impassable; - if (regName == null) - { - return; - } - - if (Regions.ContainsKey(regName)) - { - Logger.Warning($"Duplicate region name '{regName}' for map '{Name}'"); - } - else - { - Regions[regName] = reg; - } - } - - public void UnregisterRegion(Region reg) - { - var regName = reg.Name; - - if (regName != null) - { - Regions.Remove(regName); - } - } - - public Point3D GetPoint(object o, bool eye) - { - Point3D p; - - if (o is Mobile mobile) - { - p = mobile.Location; - p.Z += 14; // eye ? 15 : 10; - } - else if (o is Item item) - { - p = item.GetWorldLocation(); - p.Z += item.ItemData.Height / 2 + 1; - } - else if (o is Point3D point3D) - { - p = point3D; - } - else if (o is LandTarget target) - { - p = target.Location; - - GetAverageZ(p.X, p.Y, out _, out _, out var top); - - p.Z = top + 1; - } - else if (o is StaticTarget st) - { - var id = TileData.ItemTable[st.ItemID & TileData.MaxItemValue]; - - p = new Point3D(st.X, st.Y, st.Z - id.CalcHeight + id.Height / 2 + 1); - } - else if (o is IPoint3D d) - { - p = new Point3D(d); - } - else - { - Logger.Warning($"Warning: Invalid object ({o}) in line of sight"); - p = Point3D.Zero; - } - - return p; - } - - public IPooledEnumerable GetObjectsInRange(Point3D p) => GetObjectsInRange(p, Core.GlobalMaxUpdateRange); - - public IPooledEnumerable GetObjectsInRange(Point3D p, int range) => - GetObjectsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); - - public IPooledEnumerable GetObjectsInBounds(Rectangle2D bounds) => - PooledEnumeration.GetEntities(this, bounds); - - public IPooledEnumerable GetClientsInRange(Point3D p) => GetClientsInRange(p, Core.GlobalMaxUpdateRange); - - public IPooledEnumerable GetClientsInRange(Point3D p, int range) => - GetClientsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); - - public IPooledEnumerable GetClientsInBounds(Rectangle2D bounds) => - PooledEnumeration.GetClients(this, bounds); - - public IPooledEnumerable GetItemsInRange(Point3D p) => GetItemsInRange(p, Core.GlobalMaxUpdateRange); - - public IPooledEnumerable GetItemsInRange(Point3D p, int range) => GetItemsInRange(p, range); - - public IPooledEnumerable GetItemsInRange(Point3D p, int range) where T : Item => - GetItemsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); - - public IPooledEnumerable GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds(bounds); - - public IPooledEnumerable GetItemsInBounds(Rectangle2D bounds) where T : Item => - PooledEnumeration.GetItems(this, bounds); - - public IPooledEnumerable GetMobilesInRange(Point3D p) => GetMobilesInRange(p, Core.GlobalMaxUpdateRange); - - public IPooledEnumerable GetMobilesInRange(Point3D p, int range) => GetMobilesInRange(p, range); - - public IPooledEnumerable GetMobilesInRange(Point3D p, int range) where T : Mobile => - GetMobilesInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); - - public IPooledEnumerable GetMobilesInBounds(Rectangle2D bounds) => GetMobilesInBounds(bounds); - - public IPooledEnumerable GetMobilesInBounds(Rectangle2D bounds) where T : Mobile => - PooledEnumeration.GetMobiles(this, bounds); - - public bool CanFit( - Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, - bool requireSurface = true - ) => - CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface); - - public bool CanFit( - Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, - bool requireSurface = true - ) => - CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface); - - public bool CanFit( - int x, int y, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, - bool requireSurface = true - ) - { - if (this == Internal) + if ((surface || impassable) && staticTiles[i].Z + id.CalcHeight > z && z + height > staticTiles[i].Z) { return false; } - if (x < 0 || y < 0 || x >= Width || y >= Height) - { - return false; - } - - var hasSurface = false; - - var lt = Tiles.GetLandTile(x, y); - GetAverageZ(x, y, out var lowZ, out var avgZ, out _); - var landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags; - - if ((landFlags & TileFlag.Impassable) != 0 && avgZ > z && z + height > lowZ) - { - return false; - } - - if ((landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored) + if (surface && !impassable && z == staticTiles[i].Z + id.CalcHeight) { hasSurface = true; } + } - var staticTiles = Tiles.GetStaticTiles(x, y, true); + var sector = GetSector(x, y); + var items = sector.Items; + var mobs = sector.Mobiles; - bool surface, impassable; + for (var i = 0; i < items.Count; ++i) + { + var item = items[i]; - for (var i = 0; i < staticTiles.Length; ++i) + if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y)) { - var id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue]; + var id = item.ItemData; surface = id.Surface; impassable = id.Impassable; - if ((surface || impassable) && staticTiles[i].Z + id.CalcHeight > z && z + height > staticTiles[i].Z) + if ((surface || impassable || checkBlocksFit && item.BlocksFit) && item.Z + id.CalcHeight > z && + z + height > item.Z) { return false; } - if (surface && !impassable && z == staticTiles[i].Z + id.CalcHeight) + if (surface && !impassable && !item.Movable && z == item.Z + id.CalcHeight) { hasSurface = true; } } - - var sector = GetSector(x, y); - var items = sector.Items; - var mobs = sector.Mobiles; - - for (var i = 0; i < items.Count; ++i) - { - var item = items[i]; - - if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y)) - { - var 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; - } - - if (surface && !impassable && !item.Movable && z == item.Z + id.CalcHeight) - { - hasSurface = true; - } - } - } - - if (checkMobiles) - { - for (var i = 0; i < mobs.Count; ++i) - { - var m = mobs[i]; - - if (m.Location.m_X == x && m.Location.m_Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden) && - m.Z + 16 > z && z + height > m.Z) - { - return false; - } - } - } - - return !requireSurface || hasSurface; } - public bool CanSpawnMobile(Point3D p) => CanSpawnMobile(p.m_X, p.m_Y, p.m_Z); - - public bool CanSpawnMobile(Point2D p, int z) => CanSpawnMobile(p.m_X, p.m_Y, z); - - public bool CanSpawnMobile(int x, int y, int z) => - Region.Find(new Point3D(x, y, z), this).AllowSpawn() && CanFit(x, y, z, 16); - - private class ZComparer : IComparer + if (checkMobiles) { - public static readonly ZComparer Default = new(); - - public int Compare(Item x, Item y) => x!.Z.CompareTo(y!.Z); - } - - public Sector GetSector(Point3D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift); - - public Sector GetSector(Point2D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift); - - // public Sector GetSector(IPoint2D p) => InternalGetSector(p.X >> SectorShift, p.Y >> SectorShift); - - public Sector GetSector(int x, int y) => InternalGetSector(x >> SectorShift, y >> SectorShift); - - public Sector GetRealSector(int x, int y) => InternalGetSector(x, y); - - private Sector InternalGetSector(int x, int y) - { - if (x >= 0 && x < m_SectorsWidth && y >= 0 && y < m_SectorsHeight) + for (var i = 0; i < mobs.Count; ++i) { - var xSectors = m_Sectors[x]; + var m = mobs[i]; - if (xSectors == null) - { - m_Sectors[x] = xSectors = new Sector[m_SectorsHeight]; - } - - var sec = xSectors[y]; - - if (sec == null) - { - xSectors[y] = sec = new Sector(x, y, this); - } - - return sec; - } - - return InvalidSector; - } - - public bool LineOfSight(Point3D org, Point3D dest) - { - if (this == Internal) - { - return false; - } - - if (!Utility.InRange(org, dest, MaxLOSDistance)) - { - return false; - } - - var 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) - { - (org, dest) = (dest, org); - } - - int height; - Point3D p; - var path = new Point3DList(); - TileFlag flags; - - if (org == dest) - { - return true; - } - - if (path.Count > 0) - { - path.Clear(); - } - - var xd = dest.m_X - org.m_X; - var yd = dest.m_Y - org.m_Y; - var zd = dest.m_Z - org.m_Z; - var zslp = Math.Sqrt(xd * xd + yd * yd); - var sq3d = zd != 0 ? Math.Sqrt(zslp * zslp + zd * zd) : zslp; - - var rise = yd / sq3d; - var run = xd / sq3d; - zslp = zd / sq3d; - - double y = org.m_Y; - double z = org.m_Z; - double 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)) - { - var ix = (int)Math.Round(x); - var iy = (int)Math.Round(y); - var 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); - - var pathCount = path.Count; - var endTop = end.m_Z + 1; - - for (var i = 0; i < pathCount; ++i) - { - var point = path[i]; - var pointTop = point.m_Z + 1; - - var landTile = Tiles.GetLandTile(point.X, point.Y); - GetAverageZ(point.m_X, point.m_Y, out var landZ, out _, out var landTop); - - if (landZ <= pointTop && landTop >= point.m_Z && - (point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > endTop || landTop < end.m_Z) && - !landTile.Ignored) + if (m.Location.m_X == x && m.Location.m_Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden) && + m.Z + 16 > z && z + height > m.Z) { return false; } - - /* --Do land tiles need to be checked? There is never land between two people, always statics.-- - LandTile landTile = Tiles.GetLandTile( point.X, point.Y ); - if (landTile.Z-1 >= point.Z && landTile.Z+1 <= point.Z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Impassable) != 0) - return false; - */ - - var statics = Tiles.GetStaticTiles(point.m_X, point.m_Y, true); - - var contains = false; - var ltID = landTile.ID; - - for (var j = 0; !contains && j < InvalidLandTiles.Length; ++j) - { - contains = ltID == InvalidLandTiles[j]; - } - - if (contains && statics.Length == 0) - { - var eable = GetItemsInRange(point, 0); - - foreach (Item item in eable) - { - if (item.Visible) - { - contains = false; - break; - } - } - - eable.Free(); - - if (contains) - { - return false; - } - } - - for (var j = 0; j < statics.Length; ++j) - { - var t = statics[j]; - - var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - - flags = id.Flags; - height = id.CalcHeight; - - if (t.Z <= pointTop && 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 <= endTop && t.Z + height >= end.m_Z) - { - continue; - } - - return false; - } - } } + } - var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1); + return !requireSurface || hasSurface; + } - var area = GetItemsInBounds(rect); + public bool CanSpawnMobile(Point3D p) => CanSpawnMobile(p.m_X, p.m_Y, p.m_Z); - foreach (var i in area) + public bool CanSpawnMobile(Point2D p, int z) => CanSpawnMobile(p.m_X, p.m_Y, z); + + public bool CanSpawnMobile(int x, int y, int z) => + Region.Find(new Point3D(x, y, z), this).AllowSpawn() && CanFit(x, y, z, 16); + + private class ZComparer : IComparer + { + public static readonly ZComparer Default = new(); + + public int Compare(Item x, Item y) => x!.Z.CompareTo(y!.Z); + } + + public Sector GetSector(Point3D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift); + + public Sector GetSector(Point2D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift); + + // public Sector GetSector(IPoint2D p) => InternalGetSector(p.X >> SectorShift, p.Y >> SectorShift); + + public Sector GetSector(int x, int y) => InternalGetSector(x >> SectorShift, y >> SectorShift); + + public Sector GetRealSector(int x, int y) => InternalGetSector(x, y); + + private Sector InternalGetSector(int x, int y) + { + if (x >= 0 && x < m_SectorsWidth && y >= 0 && y < m_SectorsHeight) + { + var xSectors = m_Sectors[x]; + + if (xSectors == null) { - if (!i.Visible) - { - continue; - } - - if (i is BaseMulti || i.ItemID > TileData.MaxItemValue) - { - continue; - } - - var id = i.ItemData; - flags = id.Flags; - - if ((flags & (TileFlag.Window | TileFlag.NoShoot)) == 0) - { - continue; - } - - height = id.CalcHeight; - - var found = false; - - var count = path.Count; - - for (var j = 0; j < count; ++j) - { - var point = path[j]; - var pointTop = point.m_Z + 1; - var 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 <= pointTop && loc.m_Z + height >= point.m_Z) - { - if (loc.m_X != end.m_X || loc.m_Y != end.m_Y || loc.m_Z > endTop || loc.m_Z + height < end.m_Z) - { - found = true; - break; - } - } - } - - if (!found) - { - continue; - } - - area.Free(); - return false; + m_Sectors[x] = xSectors = new Sector[m_SectorsHeight]; } - area.Free(); + var sec = xSectors[y]; + + if (sec == null) + { + xSectors[y] = sec = new Sector(x, y, this); + } + + return sec; + } + + return InvalidSector; + } + + public bool LineOfSight(Point3D org, Point3D dest) + { + if (this == Internal) + { + return false; + } + + if (!Utility.InRange(org, dest, MaxLOSDistance)) + { + return false; + } + + var 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) + { + (org, dest) = (dest, org); + } + + int height; + Point3D p; + var path = new Point3DList(); + TileFlag flags; + + if (org == dest) + { return true; } - public bool LineOfSight(object from, object dest) => - from == dest || (from as Mobile)?.AccessLevel > AccessLevel.Player || - (dest as Item)?.RootParent == from || LineOfSight(GetPoint(from, true), GetPoint(dest, false)); - - public bool LineOfSight(Mobile from, Point3D target) + if (path.Count > 0) { - if (from.AccessLevel > AccessLevel.Player) - { - return true; - } - - var eye = from.Location; - - eye.Z += 14; - - return LineOfSight(eye, target); + path.Clear(); } - public bool LineOfSight(Mobile from, Mobile to) + var xd = dest.m_X - org.m_X; + var yd = dest.m_Y - org.m_Y; + var zd = dest.m_Z - org.m_Z; + var zslp = Math.Sqrt(xd * xd + yd * yd); + var sq3d = zd != 0 ? Math.Sqrt(zslp * zslp + zd * zd) : zslp; + + var rise = yd / sq3d; + var run = xd / sq3d; + zslp = zd / sq3d; + + double y = org.m_Y; + double z = org.m_Z; + double 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)) { - if (from == to || from.AccessLevel > AccessLevel.Player) + var ix = (int)Math.Round(x); + var iy = (int)Math.Round(y); + var iz = (int)Math.Round(z); + if (path.Count > 0) { - return true; + 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); } - var eye = from.Location; - var target = to.Location; - - eye.Z += 14; - target.Z += 14; // 10; - - return LineOfSight(eye, target); + x += run; + y += rise; + z += zslp; } - public Point3D GetRandomNearbyLocation( - Point3D loc, int maxRange = 2, int minRange = 0, int retryCount = 10, - int height = 16, bool checkBlocksFit = false, - bool checkMobiles = false + 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); + + var pathCount = path.Count; + var endTop = end.m_Z + 1; + + for (var i = 0; i < pathCount; ++i) + { + var point = path[i]; + var pointTop = point.m_Z + 1; + + var landTile = Tiles.GetLandTile(point.X, point.Y); + GetAverageZ(point.m_X, point.m_Y, out var landZ, out _, out var landTop); + + if (landZ <= pointTop && landTop >= point.m_Z && + (point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > endTop || landTop < end.m_Z) && + !landTile.Ignored) + { + return false; + } + + /* --Do land tiles need to be checked? There is never land between two people, always statics.-- + LandTile landTile = Tiles.GetLandTile( point.X, point.Y ); + if (landTile.Z-1 >= point.Z && landTile.Z+1 <= point.Z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Impassable) != 0) + return false; + */ + + var statics = Tiles.GetStaticTiles(point.m_X, point.m_Y, true); + + var contains = false; + var ltID = landTile.ID; + + for (var j = 0; !contains && j < InvalidLandTiles.Length; ++j) + { + contains = ltID == InvalidLandTiles[j]; + } + + if (contains && statics.Length == 0) + { + var eable = GetItemsInRange(point, 0); + + foreach (Item item in eable) + { + if (item.Visible) + { + contains = false; + break; + } + } + + eable.Free(); + + if (contains) + { + return false; + } + } + + for (var j = 0; j < statics.Length; ++j) + { + var t = statics[j]; + + var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; + + flags = id.Flags; + height = id.CalcHeight; + + if (t.Z <= pointTop && 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 <= endTop && t.Z + height >= end.m_Z) + { + continue; + } + + return false; + } + } + } + + var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1); + + var area = GetItemsInBounds(rect); + + foreach (var i in area) + { + if (!i.Visible) + { + continue; + } + + if (i is BaseMulti || i.ItemID > TileData.MaxItemValue) + { + continue; + } + + var id = i.ItemData; + flags = id.Flags; + + if ((flags & (TileFlag.Window | TileFlag.NoShoot)) == 0) + { + continue; + } + + height = id.CalcHeight; + + var found = false; + + var count = path.Count; + + for (var j = 0; j < count; ++j) + { + var point = path[j]; + var pointTop = point.m_Z + 1; + var 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 <= pointTop && loc.m_Z + height >= point.m_Z) + { + if (loc.m_X != end.m_X || loc.m_Y != end.m_Y || loc.m_Z > endTop || loc.m_Z + height < end.m_Z) + { + found = true; + break; + } + } + } + + if (!found) + { + continue; + } + + area.Free(); + return false; + } + + area.Free(); + return true; + } + + public bool LineOfSight(object from, object dest) => + from == dest || (from as Mobile)?.AccessLevel > AccessLevel.Player || + (dest as Item)?.RootParent == from || LineOfSight(GetPoint(from, true), GetPoint(dest, false)); + + public bool LineOfSight(Mobile from, Point3D target) + { + if (from.AccessLevel > AccessLevel.Player) + { + return true; + } + + var 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; + } + + var eye = from.Location; + var target = to.Location; + + eye.Z += 14; + target.Z += 14; // 10; + + return LineOfSight(eye, target); + } + + public Point3D GetRandomNearbyLocation( + Point3D loc, int maxRange = 2, int minRange = 0, int retryCount = 10, + int height = 16, bool checkBlocksFit = false, + bool checkMobiles = false + ) + { + var j = 0; + var range = maxRange - minRange; + var locs = range <= 10 ? new bool[range + 1, range + 1] : null; + + do + { + var xRand = Utility.Random(range); + var yRand = Utility.Random(range); + + if (locs?[xRand, yRand] != true) + { + var x = loc.X + xRand + minRange; + var y = loc.Y + yRand + minRange; + + if (CanFit(x, y, loc.Z, height, checkBlocksFit, checkMobiles)) + { + loc = new Point3D(x, y, loc.Z); + break; + } + + var z = GetAverageZ(x, y); + + if (CanFit(x, y, z, height, checkBlocksFit, checkMobiles)) + { + loc = new Point3D(x, y, z); + break; + } + + if (locs != null) + { + locs[xRand, yRand] = true; + } + } + + j++; + } while (j < retryCount); + + return loc; + } + + public class NullEnumerable : IPooledEnumerable + { + public static readonly NullEnumerable Instance = new(); + + private readonly IEnumerable m_Empty = Enumerable.Empty(); + + IEnumerator IEnumerable.GetEnumerator() => m_Empty.GetEnumerator(); + + public IEnumerator GetEnumerator() => m_Empty.GetEnumerator(); + + public void Free() + { + } + } + + public sealed class PooledEnumerable : IPooledEnumerable, IDisposable + { + private static readonly Queue> _Buffer = new(0x400); + + private bool m_IsDisposed; + + private List m_Pool = new(0x40); + + public PooledEnumerable(IEnumerable pool) + { + m_Pool.AddRange(pool); + } + + public void Dispose() + { + m_IsDisposed = true; + + m_Pool.Clear(); + m_Pool.TrimExcess(); + m_Pool = null; + } + + IEnumerator IEnumerable.GetEnumerator() => m_Pool.GetEnumerator(); + + public IEnumerator GetEnumerator() => m_Pool.GetEnumerator(); + + public void Free() + { + if (m_IsDisposed) + { + return; + } + + m_Pool.Clear(); + m_Pool.Capacity = Math.Max(m_Pool.Capacity, 0x100); + + lock (((ICollection)_Buffer).SyncRoot) + { + _Buffer.Enqueue(this); + } + } +#pragma warning disable CA1000 // Do not declare static members on generic types + public static PooledEnumerable Instantiate( + Map map, Rectangle2D bounds, PooledEnumeration.Selector selector ) { - var j = 0; - var range = maxRange - minRange; - var locs = range <= 10 ? new bool[range + 1, range + 1] : null; + PooledEnumerable e = null; - do + lock (((ICollection)_Buffer).SyncRoot) { - var xRand = Utility.Random(range); - var yRand = Utility.Random(range); - - if (locs?[xRand, yRand] != true) + if (_Buffer.Count > 0) { - var x = loc.X + xRand + minRange; - var y = loc.Y + yRand + minRange; - - if (CanFit(x, y, loc.Z, height, checkBlocksFit, checkMobiles)) - { - loc = new Point3D(x, y, loc.Z); - break; - } - - var z = GetAverageZ(x, y); - - if (CanFit(x, y, z, height, checkBlocksFit, checkMobiles)) - { - loc = new Point3D(x, y, z); - break; - } - - if (locs != null) - { - locs[xRand, yRand] = true; - } + e = _Buffer.Dequeue(); } + } - j++; - } while (j < retryCount); + var pool = PooledEnumeration.EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds)); - return loc; + if (e == null) + { + return new PooledEnumerable(pool); + } + + e.m_Pool.AddRange(pool); + return e; } - - public class NullEnumerable : IPooledEnumerable - { - public static readonly NullEnumerable Instance = new(); - - private readonly IEnumerable m_Empty = Enumerable.Empty(); - - IEnumerator IEnumerable.GetEnumerator() => m_Empty.GetEnumerator(); - - public IEnumerator GetEnumerator() => m_Empty.GetEnumerator(); - - public void Free() - { - } - } - - public sealed class PooledEnumerable : IPooledEnumerable, IDisposable - { - private static readonly Queue> _Buffer = new(0x400); - - private bool m_IsDisposed; - - private List m_Pool = new(0x40); - - public PooledEnumerable(IEnumerable pool) - { - m_Pool.AddRange(pool); - } - - public void Dispose() - { - m_IsDisposed = true; - - m_Pool.Clear(); - m_Pool.TrimExcess(); - m_Pool = null; - } - - IEnumerator IEnumerable.GetEnumerator() => m_Pool.GetEnumerator(); - - public IEnumerator GetEnumerator() => m_Pool.GetEnumerator(); - - public void Free() - { - if (m_IsDisposed) - { - return; - } - - m_Pool.Clear(); - m_Pool.Capacity = Math.Max(m_Pool.Capacity, 0x100); - - lock (((ICollection)_Buffer).SyncRoot) - { - _Buffer.Enqueue(this); - } - } -#pragma warning disable CA1000 // Do not declare static members on generic types - public static PooledEnumerable Instantiate( - Map map, Rectangle2D bounds, PooledEnumeration.Selector selector - ) - { - PooledEnumerable e = null; - - lock (((ICollection)_Buffer).SyncRoot) - { - if (_Buffer.Count > 0) - { - e = _Buffer.Dequeue(); - } - } - - var pool = PooledEnumeration.EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds)); - - if (e == null) - { - return new PooledEnumerable(pool); - } - - e.m_Pool.AddRange(pool); - return e; - } - } -#pragma warning restore CA1000 // Do not declare static members on generic types } +#pragma warning restore CA1000 // Do not declare static members on generic types } diff --git a/Projects/Server/Maps/MapLoader.cs b/Projects/Server/Maps/MapLoader.cs index 80294537d..a335d5799 100644 --- a/Projects/Server/Maps/MapLoader.cs +++ b/Projects/Server/Maps/MapLoader.cs @@ -70,9 +70,7 @@ namespace Server } catch (Exception ex) { -#if DEBUG - Console.WriteLine(ex); -#endif + logger.Debug(ex, "Failed to load map definition {MapDefName} ({MapDefId})", def.Name, def.Id); failures.Add($"\tInvalid map definition {def.Name} ({def.Id})"); } } @@ -82,18 +80,18 @@ namespace Server if (failures.Count > 0) { logger.Warning( - "Map Definitions loaded with failures ({0} maps, {1} failures) ({2:F2} seconds)", + "Map Definitions loaded with failures ({Count} maps, {FailureCount} failures) ({Duration:F2} seconds)", count, failures.Count, stopwatch.Elapsed.TotalSeconds ); - logger.Warning(string.Join(Environment.NewLine, failures)); + logger.Warning("Map load failures: {Failure}", failures); } else { logger.Information( - "Map Definitions loaded successfully ({0} maps, {1} failures) ({2:F2} seconds)", + "Map Definitions loaded successfully ({Count} maps, {FailureCount} failures) ({Duration:F2} seconds)", count, failures.Count, stopwatch.Elapsed.TotalSeconds diff --git a/Projects/Server/Migrations/Server.DefaultSkillMod.v0.json b/Projects/Server/Migrations/Server.DefaultSkillMod.v0.json new file mode 100644 index 000000000..846a99051 --- /dev/null +++ b/Projects/Server/Migrations/Server.DefaultSkillMod.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.DefaultSkillMod" +} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.EquippedSkillMod.v0.json b/Projects/Server/Migrations/Server.EquippedSkillMod.v0.json new file mode 100644 index 000000000..e1761685f --- /dev/null +++ b/Projects/Server/Migrations/Server.EquippedSkillMod.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.EquippedSkillMod", + "properties": [ + { + "name": "Item", + "type": "Server.Item", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.MobileMod.v0.json b/Projects/Server/Migrations/Server.MobileMod.v0.json new file mode 100644 index 000000000..5e9c66631 --- /dev/null +++ b/Projects/Server/Migrations/Server.MobileMod.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.MobileMod" +} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.ResistanceMod.v0.json b/Projects/Server/Migrations/Server.ResistanceMod.v0.json new file mode 100644 index 000000000..b2d027d5f --- /dev/null +++ b/Projects/Server/Migrations/Server.ResistanceMod.v0.json @@ -0,0 +1,19 @@ +{ + "version": 0, + "type": "Server.ResistanceMod", + "properties": [ + { + "name": "Type", + "type": "Server.ResistanceType", + "rule": "EnumMigrationRule" + }, + { + "name": "Offset", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.SkillMod.v0.json b/Projects/Server/Migrations/Server.SkillMod.v0.json new file mode 100644 index 000000000..174c434c2 --- /dev/null +++ b/Projects/Server/Migrations/Server.SkillMod.v0.json @@ -0,0 +1,43 @@ +{ + "version": 0, + "type": "Server.SkillMod", + "properties": [ + { + "name": "ObeyCap", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Skill", + "type": "Server.SkillName", + "rule": "EnumMigrationRule" + }, + { + "name": "Relative", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Absolute", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Value", + "type": "double", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.StatMod.v0.json b/Projects/Server/Migrations/Server.StatMod.v0.json new file mode 100644 index 000000000..5b3b28174 --- /dev/null +++ b/Projects/Server/Migrations/Server.StatMod.v0.json @@ -0,0 +1,40 @@ +{ + "version": 0, + "type": "Server.StatMod", + "properties": [ + { + "name": "Added", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Duration", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "Type", + "type": "Server.StatType", + "rule": "EnumMigrationRule" + }, + { + "name": "Name", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Offset", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.TimedSkillMod.v0.json b/Projects/Server/Migrations/Server.TimedSkillMod.v0.json new file mode 100644 index 000000000..d1c4b0296 --- /dev/null +++ b/Projects/Server/Migrations/Server.TimedSkillMod.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.TimedSkillMod", + "properties": [ + { + "name": "Expire", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/Server/Mobiles/Body.cs b/Projects/Server/Mobiles/Body.cs index 1b89f9e2a..41e1861ec 100644 --- a/Projects/Server/Mobiles/Body.cs +++ b/Projects/Server/Mobiles/Body.cs @@ -1,137 +1,152 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Body.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.IO; using System.Runtime.CompilerServices; +using Server.Logging; -namespace Server +namespace Server; + +public enum BodyType : byte { - public enum BodyType : byte - { - Empty, - Monster, - Sea, - Animal, - Human, - Equipment - } + Empty, + Monster, + Sea, + Animal, + Human, + Equipment +} - [Parsable] - public readonly struct Body : IEquatable, IEquatable, IEquatable, IComparable, IComparable - { - private static readonly BodyType[] m_Types = Array.Empty(); +[Parsable] +public readonly struct Body : IEquatable, IEquatable, IEquatable, IComparable, IComparable +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Body)); - static Body() + private static readonly BodyType[] m_Types = Array.Empty(); + + static Body() + { + if (!File.Exists("Data/bodyTable.cfg")) { - if (File.Exists("Data/bodyTable.cfg")) + logger.Error("Data/bodyTable.cfg does not exist."); + return; + } + + using var ip = new StreamReader("Data/bodyTable.cfg"); + m_Types = new BodyType[0x1000]; + + string line; + + while ((line = ip.ReadLine()) != null) + { + if (line.Length == 0 || line.StartsWithOrdinal("#")) { - using var ip = new StreamReader("Data/bodyTable.cfg"); - m_Types = new BodyType[0x1000]; + continue; + } - string line; + var split = line.Split('\t'); - while ((line = ip.ReadLine()) != null) - { - if (line.Length == 0 || line.StartsWithOrdinal("#")) - { - continue; - } - - var split = line.Split('\t'); - - if (int.TryParse(split[0], out var bodyID) && Enum.TryParse(split[1], true, out BodyType type) && - bodyID >= 0 && - bodyID < m_Types.Length) - { - m_Types[bodyID] = type; - } - else - { - Console.WriteLine("Warning: Invalid bodyTable entry:"); - Console.WriteLine(line); - } - } + if (int.TryParse(split[0], out var bodyID) && Enum.TryParse(split[1], true, out BodyType type) && + bodyID >= 0 && + bodyID < m_Types.Length) + { + m_Types[bodyID] = type; } else { - Console.WriteLine("Warning: Data/bodyTable.cfg does not exist"); + logger.Warning("Invalid bodyTable entry: {Entry}", line); } } - - public Body(int bodyID) => BodyID = bodyID; - - public BodyType Type => BodyID >= 0 && BodyID < m_Types.Length ? m_Types[BodyID] : BodyType.Empty; - - public bool IsHuman => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Human - && BodyID != 402 - && BodyID != 403 - && BodyID != 607 - && BodyID != 608 - && BodyID != 694 - && BodyID != 695 - && BodyID != 970; - - public bool IsGargoyle => BodyID is 666 or 667 or 694 or 695; - - public bool IsMale => BodyID is 183 or 185 or 400 or 402 or 605 or 607 or 666 or 694 or 750; - - public bool IsFemale => BodyID is 184 or 186 or 401 or 403 or 606 or 608 or 667 or 695 or 751; - - public bool IsGhost => BodyID is 402 or 403 or 607 or 608 or 694 or 695 or 970; - - public bool IsMonster => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Monster; - - public bool IsAnimal => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Animal; - - public bool IsEmpty => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Empty; - - public bool IsSea => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Sea; - - public bool IsEquipment => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Equipment; - - public int BodyID { get; } - - public static implicit operator int(Body a) => a.BodyID; - - public static implicit operator Body(int a) => new(a); - - public override string ToString() => $"0x{BodyID:X}"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => BodyID.GetHashCode(); - - public override bool Equals(object o) => o is Body b && b.BodyID == BodyID; - - public bool Equals(Body b) => b.BodyID == BodyID; - - public bool Equals(int number) => number == BodyID; - - public static bool operator ==(Body l, Body r) => l.BodyID == r.BodyID; - - public static bool operator !=(Body l, Body r) => l.BodyID != r.BodyID; - - public static bool operator >(Body l, Body r) => l.BodyID > r.BodyID; - - public static bool operator >=(Body l, Body r) => l.BodyID >= r.BodyID; - - public static bool operator <(Body l, Body r) => l.BodyID < r.BodyID; - - public static bool operator <=(Body l, Body r) => l.BodyID <= r.BodyID; - - public int CompareTo(Body other) => BodyID.CompareTo(other.BodyID); - - public int CompareTo(int other) => BodyID.CompareTo(other); - - public static Body Parse(string value) => Utility.ToInt32(value); } + + public Body(int bodyID) => BodyID = bodyID; + + public BodyType Type => BodyID >= 0 && BodyID < m_Types.Length ? m_Types[BodyID] : BodyType.Empty; + + public bool IsHuman => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Human + && BodyID != 402 + && BodyID != 403 + && BodyID != 607 + && BodyID != 608 + && BodyID != 694 + && BodyID != 695 + && BodyID != 970; + + public bool IsGargoyle => BodyID is 666 or 667 or 694 or 695; + + public bool IsMale => BodyID is 183 or 185 or 400 or 402 or 605 or 607 or 666 or 694 or 750; + + public bool IsFemale => BodyID is 184 or 186 or 401 or 403 or 606 or 608 or 667 or 695 or 751; + + public bool IsGhost => BodyID is 402 or 403 or 607 or 608 or 694 or 695 or 970; + + public bool IsMonster => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Monster; + + public bool IsAnimal => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Animal; + + public bool IsEmpty => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Empty; + + public bool IsSea => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Sea; + + public bool IsEquipment => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Equipment; + + public int BodyID { get; } + + public static implicit operator int(Body a) => a.BodyID; + + public static implicit operator Body(int a) => new(a); + + public override string ToString() => $"0x{BodyID:X}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => BodyID.GetHashCode(); + + public override bool Equals(object o) => o is Body b && b.BodyID == BodyID; + + public bool Equals(Body b) => b.BodyID == BodyID; + + public bool Equals(int number) => number == BodyID; + + public static bool operator ==(Body l, Body r) => l.BodyID == r.BodyID; + + public static bool operator !=(Body l, Body r) => l.BodyID != r.BodyID; + + public static bool operator >(Body l, Body r) => l.BodyID > r.BodyID; + + public static bool operator >=(Body l, Body r) => l.BodyID >= r.BodyID; + + public static bool operator <(Body l, Body r) => l.BodyID < r.BodyID; + + public static bool operator <=(Body l, Body r) => l.BodyID <= r.BodyID; + + public int CompareTo(Body other) => BodyID.CompareTo(other.BodyID); + + public int CompareTo(int other) => BodyID.CompareTo(other); + + public static Body Parse(string value) => Utility.ToInt32(value); } diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 0e1f21fd4..84a041743 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -1,14 +1,15 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance; using Server.Accounting; using Server.Buffers; +using Server.Collections; using Server.ContextMenus; using Server.Guilds; using Server.Gumps; using Server.HuePickers; using Server.Items; +using Server.Logging; using Server.Menus; using Server.Mobiles; using Server.Network; @@ -27,229 +28,6 @@ namespace Server public delegate void PromptStateCallback(Mobile from, string text, T state); - public class TimedSkillMod : SkillMod - { - private readonly DateTime m_Expire; - - public TimedSkillMod(SkillName skill, bool relative, double value, TimeSpan delay) - : this(skill, relative, value, Core.Now + delay) - { - } - - public TimedSkillMod(SkillName skill, bool relative, double value, DateTime expire) - : base(skill, relative, value) => - m_Expire = expire; - - public override bool CheckCondition() => Core.Now < m_Expire; - } - - public class EquippedSkillMod : SkillMod - { - private readonly Item m_Item; - private readonly Mobile m_Mobile; - - public EquippedSkillMod(SkillName skill, bool relative, double value, Item item, Mobile mobile) - : base(skill, relative, value) - { - m_Item = item; - m_Mobile = mobile; - } - - public override bool CheckCondition() => !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() => true; - } - - public abstract class SkillMod - { - private bool m_ObeyCap; - private Mobile m_Owner; - private bool m_Relative; - private SkillName m_Skill; - private double m_Value; - - protected SkillMod(SkillName skill, bool relative, double value) - { - m_Skill = skill; - m_Relative = relative; - m_Value = value; - } - - public bool ObeyCap - { - get => m_ObeyCap; - set - { - m_ObeyCap = value; - - var sk = m_Owner?.Skills[m_Skill]; - sk?.Update(); - } - } - - public Mobile Owner - { - get => m_Owner; - set - { - if (m_Owner != value) - { - m_Owner?.RemoveSkillMod(this); - - m_Owner = value; - - if (m_Owner != value) - { - m_Owner.AddSkillMod(this); - } - } - } - } - - public SkillName Skill - { - get => m_Skill; - set - { - if (m_Skill != value) - { - var oldUpdate = m_Owner?.Skills[m_Skill]; - - m_Skill = value; - - var sk = m_Owner?.Skills[m_Skill]; - sk?.Update(); - oldUpdate?.Update(); - } - } - } - - public bool Relative - { - get => m_Relative; - set - { - if (m_Relative != value) - { - m_Relative = value; - - var sk = m_Owner?.Skills[m_Skill]; - sk?.Update(); - } - } - } - - public bool Absolute - { - get => !m_Relative; - set - { - if (m_Relative == value) - { - m_Relative = !value; - - var sk = m_Owner?.Skills[m_Skill]; - sk?.Update(); - } - } - } - - public double Value - { - get => m_Value; - set - { - if (m_Value != value) - { - m_Value = value; - - var sk = m_Owner?.Skills[m_Skill]; - sk?.Update(); - } - } - } - - public void Remove() - { - Owner = null; - } - - public abstract bool CheckCondition(); - } - - public class ResistanceMod - { - private int m_Offset; - private ResistanceType m_Type; - - public ResistanceMod(ResistanceType type, int offset) - { - m_Type = type; - m_Offset = offset; - } - - public Mobile Owner { get; set; } - - public ResistanceType Type - { - get => m_Type; - set - { - if (m_Type != value) - { - m_Type = value; - - Owner?.UpdateResistances(); - } - } - } - - public int Offset - { - get => m_Offset; - set - { - if (m_Offset != value) - { - m_Offset = value; - - Owner?.UpdateResistances(); - } - } - } - } - - public class StatMod - { - private readonly DateTime m_Added; - private readonly TimeSpan m_Duration; - - public StatMod(StatType type, string name, int offset, TimeSpan duration) - { - Type = type; - Name = name; - Offset = offset; - m_Duration = duration; - m_Added = Core.Now; - } - - public StatType Type { get; } - - public string Name { get; } - - public int Offset { get; } - - public bool HasElapsed() => m_Duration != TimeSpan.Zero && Core.Now - m_Added >= m_Duration; - } - public class DamageEntry { public DamageEntry(Mobile damager) => Damager = damager; @@ -403,11 +181,13 @@ namespace Server /// /// Base class representing players, npcs, and creatures. /// - public class Mobile : IHued, IComparable, ISpawnable, IPropertyListObject + public class Mobile : IHued, IComparable, ISpawnable, IObjectPropertyListEntity { // Allow four warmode changes in 0.5 seconds, any more will be delay for two seconds private const int WarmodeCatchCount = 4; + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Mobile)); + // TODO: Make these configurations private static readonly TimeSpan WarmodeSpamCatch = TimeSpan.FromSeconds(Core.SE ? 1.0 : 0.5); private static readonly TimeSpan WarmodeSpamDelay = TimeSpan.FromSeconds(Core.SE ? 4.0 : 2.0); @@ -645,7 +425,7 @@ namespace Server public object Party { get; set; } - public List SkillMods { get; private set; } + public HashSet SkillMods { get; private set; } [CommandProperty(AccessLevel.GameMaster)] public int VirtualArmorMod @@ -959,7 +739,7 @@ namespace Server var weapon = Weapon; - if (!InRange(combatant, weapon.MaxRange)) + if (weapon == null || !InRange(combatant, weapon.MaxRange)) { return; } @@ -1081,7 +861,7 @@ namespace Server { if (m_Spell != null && value != null) { - Console.WriteLine("Warning: Spell has been overwritten"); + logger.Warning("Spell has been overwritten."); } m_Spell = value; @@ -1462,6 +1242,9 @@ namespace Server box.Close(); } + OnBeforeDisconnected(); + EventSink.InvokeBeforeDisconnected(this); + m_NetState = value; _logoutTimerToken.Cancel(); @@ -1872,7 +1655,7 @@ namespace Server public static bool DisableDismountInWarmode { get; set; } - public static int BodyWeight { get; set; } = 14; + public static int BodyWeight { get; set; } = 11; // 11 + 3 for the backpack [CommandProperty(AccessLevel.GameMaster)] public IMount Mount @@ -2013,7 +1796,7 @@ namespace Server /// /// Gets a list of all StatMod's currently active for the Mobile. /// - public List StatMods { get; private set; } + public HashSet StatMods { get; private set; } /// /// Gets or sets the base, unmodified, strength of the Mobile. Ranges from 1 to 65000, inclusive. @@ -2471,7 +2254,7 @@ namespace Server public virtual int HuedItemID => m_Female ? 0x2107 : 0x2106; public ObjectPropertyList PropertyList => m_PropertyList ??= InitializePropertyList(new ObjectPropertyList(this)); - public virtual void GetProperties(ObjectPropertyList list) + public virtual void GetProperties(IPropertyList list) { AddNameProperties(list); } @@ -2569,8 +2352,6 @@ namespace Server writer.Write(DisarmReady); writer.Write(StunReady); - // Poison.Serialize( m_Poison, writer ); - writer.Write(m_StatCap); writer.Write(NameHue); @@ -2958,13 +2739,12 @@ namespace Server ? OutgoingVirtualHairPackets.RemovePacketLength : OutgoingVirtualHairPackets.EquipUpdatePacketLength; - Span facialhairPacket = stackalloc byte[facialHairLength].InitializePacket(); + Span facialHairPacket = stackalloc byte[facialHairLength].InitializePacket(); const int cacheLength = OutgoingMobilePackets.MobileMovingPacketCacheByteLength; const int width = OutgoingMobilePackets.MobileMovingPacketLength; - const int height = OutgoingMobilePackets.MobileMovingPacketCacheHeight; - var mobileMovingCache = stackalloc byte[cacheLength].AsSpan2D(height, width).InitializePackets(); + var mobileMovingCache = stackalloc byte[cacheLength].InitializePackets(width); var ourState = m_NetState; @@ -3066,12 +2846,12 @@ namespace Server { if (removeFacialHair) { - OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialhairPacket, facialHairSerial); + OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialHairPacket, facialHairSerial); } else { OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket( - facialhairPacket, + facialHairPacket, this, facialHairSerial, FacialHairItemID, @@ -3079,7 +2859,7 @@ namespace Server Layer.FacialHair ); } - ourState.Send(facialhairPacket); + ourState.Send(facialHairPacket); } if (sendOPLUpdate) @@ -3205,12 +2985,12 @@ namespace Server { if (removeFacialHair) { - OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialhairPacket, facialHairSerial); + OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialHairPacket, facialHairSerial); } else { OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket( - facialhairPacket, + facialHairPacket, this, facialHairSerial, FacialHairItemID, @@ -3218,7 +2998,7 @@ namespace Server Layer.FacialHair ); } - state.Send(facialhairPacket); + state.Send(facialHairPacket); } SendOPLPacketTo(state); @@ -3411,9 +3191,9 @@ namespace Server public int GetAOSStatus(int index) => AOSStatusHandler?.Invoke(this, index) ?? 0; - public virtual void SendPropertiesTo(Mobile from) + public virtual void SendPropertiesTo(NetState ns) { - from.NetState?.Send(PropertyList.Buffer); + ns?.Send(PropertyList.Buffer); } public virtual void OnAosSingleClick(Mobile from) @@ -3443,56 +3223,53 @@ namespace Server public virtual string ApplyNameSuffix(string suffix) => suffix; - public virtual void AddNameProperties(ObjectPropertyList list) + public virtual void AddNameProperties(IPropertyList list) { - var name = Name ?? ""; + var name = Name ?? " "; string prefix; if (ShowFameTitle && (m_Player || m_Body.IsHuman) && m_Fame >= 10000) { - prefix = m_Female ? "Lady" : "Lord"; + prefix = m_Female ? "Lady " : "Lord "; } else { - prefix = ""; + prefix = " "; } - var suffix = ""; - - if (PropertyTitle && !string.IsNullOrEmpty(Title)) - { - suffix = Title; - } + var title = PropertyTitle && !string.IsNullOrEmpty(Title) ? Title : ""; + string suffix; var guild = m_Guild; - if (guild != null && (m_Player || m_DisplayGuildTitle)) { - suffix = suffix.Length > 0 - ? $"{suffix} [{Utility.FixHtml(guild.Abbreviation)}]" + suffix = title.Length > 0 + ? $"{title} [{Utility.FixHtml(guild.Abbreviation)}]" : $"[{Utility.FixHtml(guild.Abbreviation)}]"; } + else + { + suffix = " "; + } - suffix = ApplyNameSuffix(suffix); - - list.Add(1050045, "{0} \t{1}\t {2}", prefix, name, suffix); // ~1_PREFIX~~2_NAME~~3_SUFFIX~ + list.Add(1050045, $"{prefix}\t{name}\t{ApplyNameSuffix(suffix)}"); // ~1_PREFIX~~2_NAME~~3_SUFFIX~ if (guild != null && (m_DisplayGuildTitle || m_Player && guild.Type != GuildType.Regular)) { var type = guild.Type >= 0 && (int)guild.Type < m_GuildTypes.Length ? m_GuildTypes[(int)guild.Type] : ""; - var title = GuildTitle?.Trim() ?? ""; + var guildTitle = GuildTitle?.Trim() ?? ""; - if (title.Length > 0) + if (guildTitle.Length > 0) { if (NewGuildDisplay) { - list.Add("{0}, {1}", Utility.FixHtml(title), Utility.FixHtml(guild.Name)); + list.Add($"{Utility.FixHtml(guildTitle)}, {Utility.FixHtml(guild.Name)}"); } else { - list.Add("{0}, {1} Guild{2}", Utility.FixHtml(title), Utility.FixHtml(guild.Name), type); + list.Add($"{Utility.FixHtml(guildTitle)}, {Utility.FixHtml(guild.Name)} Guild{type}"); } } else @@ -3502,11 +3279,11 @@ namespace Server } } - public virtual void GetChildProperties(ObjectPropertyList list, Item item) + public virtual void GetChildProperties(IPropertyList list, Item item) { } - public virtual void GetChildNameProperties(ObjectPropertyList list, Item item) + public virtual void GetChildNameProperties(IPropertyList list, Item item) { } @@ -3603,9 +3380,8 @@ namespace Server { ValidateSkillMods(); - for (var i = 0; i < SkillMods.Count; ++i) + foreach (var mod in SkillMods) { - var mod = SkillMods[i]; var sk = Skills[mod.Skill]; sk?.Update(); } @@ -3613,18 +3389,18 @@ namespace Server public virtual void ValidateSkillMods() { - for (var i = 0; i < SkillMods.Count;) + using var queue = PooledRefQueue.Create(8); + foreach (var mod in SkillMods) { - var mod = SkillMods[i]; + if (!mod.CheckCondition()) + { + queue.Enqueue(mod); + } + } - if (mod.CheckCondition()) - { - ++i; - } - else - { - InternalRemoveSkillMod(mod); - } + while (queue.Count > 0) + { + InternalRemoveSkillMod(queue.Dequeue()); } } @@ -3637,9 +3413,8 @@ namespace Server ValidateSkillMods(); - if (!SkillMods.Contains(mod)) + if (SkillMods.Add(mod)) { - SkillMods.Add(mod); mod.Owner = this; var sk = Skills[mod.Skill]; @@ -3654,16 +3429,14 @@ namespace Server return; } - ValidateSkillMods(); - InternalRemoveSkillMod(mod); + ValidateSkillMods(); } private void InternalRemoveSkillMod(SkillMod mod) { - if (SkillMods.Contains(mod)) + if (SkillMods.Remove(mod)) { - SkillMods.Remove(mod); mod.Owner = null; var sk = Skills[mod.Skill]; @@ -3796,7 +3569,7 @@ namespace Server } } - public override string ToString() => $"0x{Serial.Value:X} \"{Name}\""; + public override string ToString() => $"{Serial} \"{Name}\""; public virtual void SendSkillMessage() { @@ -4107,19 +3880,25 @@ namespace Server switch (type) { default: - m_TotalGold += delta; - Delta(MobileDelta.Gold); - break; + { + m_TotalGold += delta; + Delta(MobileDelta.Gold); + break; + } case TotalType.Items: - m_TotalItems += delta; - break; + { + m_TotalItems += delta; + break; + } case TotalType.Weight: - m_TotalWeight += delta; - Delta(MobileDelta.Weight); - OnWeightChange(m_TotalWeight - delta); - break; + { + m_TotalWeight += delta; + Delta(MobileDelta.Weight); + OnWeightChange(m_TotalWeight - delta); + break; + } } } @@ -4463,10 +4242,9 @@ namespace Server eable.Free(); const int cacheLength = OutgoingMobilePackets.MobileMovingPacketCacheByteLength; - var width = OutgoingMobilePackets.MobileMovingPacketLength; - var height = OutgoingMobilePackets.MobileMovingPacketCacheHeight; + const int width = OutgoingMobilePackets.MobileMovingPacketLength; - var mobileMovingCache = stackalloc byte[cacheLength].AsSpan2D(height, width).InitializePackets(); + var mobileMovingCache = stackalloc byte[cacheLength].InitializePackets(width); foreach (var m in m_MoveClientList) { @@ -5185,13 +4963,25 @@ namespace Server item.Spawner = null; } - amount = Math.Clamp(amount, 1, item.Amount); - var oldAmount = item.Amount; - if (amount < oldAmount) + if (oldAmount <= 0) { - LiftItemDupe(item, amount); + logger.Error( + "Item {Type} ({Serial}) has amount of {OldAmount}, but must be at least 1", + item.GetType(), + item.Serial, + oldAmount + ); + } + else + { + amount = Math.Clamp(amount, 1, oldAmount); + + if (amount < oldAmount) + { + LiftItemDupe(item, amount); + } } var map = from.Map; @@ -5290,23 +5080,28 @@ namespace Server } } - public static Item LiftItemDupe(Item oldItem, int amount) + public static T LiftItemDupe(T oldItem, int amount) where T : Item { - Item item; + T item; try { - item = oldItem.GetType().CreateInstance(); + item = oldItem.GetType().CreateInstance(); } - catch + catch (Exception e) { - Console.WriteLine( - "Warning: 0x{0:X}: Item must have a zero parameter constructor to be separated from a stack. '{1}'.", - oldItem.Serial.Value, + logger.Warning( + e, + "[{Serial} {Name}]: Item must have a zero parameter constructor to be separated from a stack.", + oldItem.Serial, oldItem.GetType().Name ); + return null; } + var oldAmount = oldItem.Amount; + oldItem.Amount = amount; + item.Visible = oldItem.Visible; item.Movable = oldItem.Movable; item.LootType = oldItem.LootType; @@ -5318,10 +5113,9 @@ namespace Server item.Name = oldItem.Name; item.Weight = oldItem.Weight; - item.Amount = oldItem.Amount - amount; + item.Amount = oldAmount - amount; item.Map = oldItem.Map; - oldItem.Amount = amount; oldItem.OnAfterDuped(item); if (oldItem.Parent is Mobile parentMobile) @@ -6373,7 +6167,7 @@ namespace Server { if (version <= 25) { - Poison.Deserialize(reader); + reader.ReadPoison(); } goto case 3; @@ -6477,8 +6271,8 @@ namespace Server m_DexLock = (StatLockType)reader.ReadByte(); m_IntLock = (StatLockType)reader.ReadByte(); - StatMods = new List(); - SkillMods = new List(); + StatMods = new HashSet(); + SkillMods = new HashSet(); if (version < 32) { @@ -6876,21 +6670,14 @@ namespace Server eable.Free(); } - public void SendOPLPacketTo(NetState state) => SendOPLPacketTo(state, ObjectPropertyList.Enabled); - - protected virtual void SendOPLPacketTo(NetState ns, bool sendOplPacket) + public virtual void SendOPLPacketTo(NetState ns) { - if (sendOplPacket) + if (ObjectPropertyList.Enabled) { ns.SendOPLInfo(this); } } - public virtual void SendOPLPacketTo(NetState ns, ReadOnlySpan opl) - { - ns?.Send(opl); - } - public virtual void OnAccessLevelChanged(AccessLevel oldLevel) { } @@ -7184,6 +6971,10 @@ namespace Server { } + public virtual void OnBeforeDisconnected() + { + } + public virtual void OnDisconnected() { } @@ -7829,8 +7620,8 @@ namespace Server m_FollowersMax = 5; Skills = new Skills(this); Items = new List(); - StatMods = new List(); - SkillMods = new List(); + StatMods = new HashSet(); + SkillMods = new HashSet(); Map = Map.Internal; AutoPageNotify = true; Aggressors = new List(); @@ -7890,17 +7681,13 @@ namespace Server } catch (Exception ex) { -#if DEBUG - Console.WriteLine("Process Delta Queue for {0} failed: {1}", mob, ex); -#endif + logger.Debug(ex, "Process Delta Queue for {Mobile} failed", mob); } } if (m_DeltaQueue.Count > 0) { - Utility.PushColor(ConsoleColor.DarkYellow); - Console.WriteLine("Warning: {0} mobiles left in delta queue after processing.", m_DeltaQueue.Count); - Utility.PopColor(); + logger.Warning("{Count} mobiles left in delta queue after processing.", m_DeltaQueue.Count); } } @@ -8301,7 +8088,7 @@ namespace Server { var ns = m_NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return false; } @@ -8527,19 +8314,16 @@ namespace Server public bool RemoveStatMod(string name) { - StatMods ??= new List(); + StatMods ??= new HashSet(); - for (var i = 0; i < StatMods.Count; ++i) + StatMod mod = GetStatMod(name); + + if (mod != null) { - var check = StatMods[i]; - - if (check.Name == name) - { - StatMods.RemoveAt(i); - CheckStatTimers(); - Delta(MobileDelta.Stat | GetStatDelta(check.Type)); - return true; - } + StatMods.Remove(mod); + CheckStatTimers(); + Delta(MobileDelta.Stat | GetStatDelta(mod.Type)); + return true; } return false; @@ -8547,12 +8331,10 @@ namespace Server public StatMod GetStatMod(string name) { - StatMods ??= new List(); + StatMods ??= new HashSet(); - for (var i = 0; i < StatMods.Count; ++i) + foreach (var check in StatMods) { - var check = StatMods[i]; - if (check.Name == name) { return check; @@ -8564,19 +8346,7 @@ namespace Server public void AddStatMod(StatMod mod) { - StatMods ??= new List(); - - for (var i = 0; i < StatMods.Count; ++i) - { - var check = StatMods[i]; - - if (check.Name == mod.Name) - { - Delta(MobileDelta.Stat | GetStatDelta(check.Type)); - StatMods.RemoveAt(i); - break; - } - } + RemoveStatMod(mod.Name); StatMods.Add(mod); Delta(MobileDelta.Stat | GetStatDelta(mod.Type)); @@ -8612,23 +8382,29 @@ namespace Server { var offset = 0; - StatMods ??= new List(); + StatMods ??= new HashSet(); - for (var i = 0; i < StatMods.Count; ++i) + if (StatMods.Count > 0) { - var mod = StatMods[i]; - - if (mod.HasElapsed()) + using var queue = PooledRefQueue.Create(8); + foreach (var mod in StatMods) { - StatMods.RemoveAt(i); + if (mod.HasElapsed()) + { + queue.Enqueue(mod); + } + else if ((mod.Type & type) != 0) + { + offset += mod.Offset; + } + } + + while (queue.Count > 0) + { + var mod = queue.Dequeue(); + StatMods.Remove(mod); Delta(MobileDelta.Stat | GetStatDelta(mod.Type)); CheckStatTimers(); - - --i; - } - else if ((mod.Type & type) != 0) - { - offset += mod.Offset; } } @@ -9013,7 +8789,10 @@ namespace Server public Direction GetDirectionTo(IPoint2D p, bool run = false) => p == null ? Direction.North | (run ? Direction.Running : 0) : GetDirectionTo(p.X, p.Y, run); - public void PublicOverheadMessage(MessageType type, int hue, bool ascii, string text, bool noLineOfSight = true) + public void PublicOverheadMessage( + MessageType type, int hue, bool ascii, string text, bool noLineOfSight = true, + AccessLevel accessLevel = AccessLevel.Player + ) { if (m_Map == null) { @@ -9026,7 +8805,11 @@ namespace Server foreach (var state in eable) { - if (state.Mobile.CanSee(this) && (noLineOfSight || state.Mobile.InLOS(this))) + if ( + state.Mobile.AccessLevel >= accessLevel && + state.Mobile.CanSee(this) && + (noLineOfSight || state.Mobile.InLOS(this)) + ) { var length = OutgoingMessagePackets.CreateMessage( buffer, Serial, Body, type, hue, 3, ascii, Language, Name, text @@ -9077,7 +8860,8 @@ namespace Server public void PublicOverheadMessage( MessageType type, int hue, int number, AffixType affixType, string affix, - string args = "", bool noLineOfSight = false + string args = "", bool noLineOfSight = false, + AccessLevel accessLevel = AccessLevel.Player ) { if (m_Map == null) @@ -9091,7 +8875,11 @@ namespace Server foreach (var state in eable) { - if (state.Mobile.CanSee(this) && (noLineOfSight || state.Mobile.InLOS(this))) + if ( + state.Mobile.AccessLevel >= accessLevel && + state.Mobile.CanSee(this) && + (noLineOfSight || state.Mobile.InLOS(this)) + ) { var length = OutgoingMessagePackets.CreateMessageLocalizedAffix( buffer, Serial, Body, type, hue, 3, number, Name, affixType, affix, args diff --git a/Projects/Server/Serialization/Attributes/EncodedIntAttribute.cs b/Projects/Server/Mobiles/Mods/DefaultSkillMod.cs similarity index 66% rename from Projects/Server/Serialization/Attributes/EncodedIntAttribute.cs rename to Projects/Server/Mobiles/Mods/DefaultSkillMod.cs index 3cfd5dbaa..578cf2cb5 100644 --- a/Projects/Server/Serialization/Attributes/EncodedIntAttribute.cs +++ b/Projects/Server/Mobiles/Mods/DefaultSkillMod.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: EncodedIntAttribute.cs * + * File: DefaultSkillMod.cs * * * * 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 * @@ -13,15 +13,20 @@ * along with this program. If not, see . * *************************************************************************/ -using System; +using ModernUO.Serialization; -namespace Server +namespace Server; + +[SerializationGenerator(0)] +public partial class DefaultSkillMod : SkillMod { - /// - /// Hints to the source generator that a serializable int field or property should be encoded - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class EncodedIntAttribute : Attribute + public DefaultSkillMod(Mobile owner) : base(owner) { } + + public DefaultSkillMod(SkillName skill, bool relative, double value) : base(skill, relative, value) + { + } + + public override bool CheckCondition() => true; } diff --git a/Projects/Server/Serialization/Attributes/EmbeddedSerializableAttribute.cs b/Projects/Server/Mobiles/Mods/EquippedSkillMod.cs old mode 100755 new mode 100644 similarity index 59% rename from Projects/Server/Serialization/Attributes/EmbeddedSerializableAttribute.cs rename to Projects/Server/Mobiles/Mods/EquippedSkillMod.cs index 8513097ef..101a19681 --- a/Projects/Server/Serialization/Attributes/EmbeddedSerializableAttribute.cs +++ b/Projects/Server/Mobiles/Mods/EquippedSkillMod.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: EmbeddedSerializableAttribute.cs * + * File: EquippedSkillMod.cs * * * * 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 * @@ -13,20 +13,22 @@ * along with this program. If not, see . * *************************************************************************/ -using System; +using ModernUO.Serialization; -namespace Server +namespace Server; + +[SerializationGenerator(0)] +public partial class EquippedSkillMod : SkillMod { - [AttributeUsage(AttributeTargets.Class)] - public sealed class EmbeddedSerializableAttribute : Attribute - { - public int Version { get; } - public bool EncodedVersion { get; } + [SerializableField(0)] + private Item _item; - public EmbeddedSerializableAttribute(int version, bool encodedVersion = true) - { - Version = version; - EncodedVersion = encodedVersion; - } + public EquippedSkillMod(Mobile owner) : base(owner) + { } + + public EquippedSkillMod(SkillName skill, bool relative, double value, Item item, Mobile mobile) + : base(skill, relative, value, mobile) => _item = item; + + public override bool CheckCondition() => !_item.Deleted && Owner?.Deleted == false && _item.Parent == Owner; } diff --git a/Projects/Server/Serialization/Attributes/TidyAttribute.cs b/Projects/Server/Mobiles/Mods/MobileMod.cs similarity index 69% rename from Projects/Server/Serialization/Attributes/TidyAttribute.cs rename to Projects/Server/Mobiles/Mods/MobileMod.cs index 0e2ba92d9..797e0e543 100644 --- a/Projects/Server/Serialization/Attributes/TidyAttribute.cs +++ b/Projects/Server/Mobiles/Mods/MobileMod.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: TidyAttribute.cs * + * File: MobileMod.cs * * * * 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 * @@ -13,15 +13,15 @@ * along with this program. If not, see . * *************************************************************************/ -using System; +using ModernUO.Serialization; -namespace Server +namespace Server; + +[SerializationGenerator(0)] +public partial class MobileMod { - /// - /// Hints to the source generator that a serializable list should be tidied up - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class TidyAttribute : Attribute - { - } + [DirtyTrackingEntity] + public Mobile Owner { get; set; } + + public MobileMod(Mobile owner) => Owner = owner; } diff --git a/Projects/Server/Mobiles/Mods/ResistanceMod.cs b/Projects/Server/Mobiles/Mods/ResistanceMod.cs new file mode 100644 index 000000000..8b14cc1a5 --- /dev/null +++ b/Projects/Server/Mobiles/Mods/ResistanceMod.cs @@ -0,0 +1,70 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ResistanceMod.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using ModernUO.Serialization; + +namespace Server; + +[SerializationGenerator(0)] +public partial class ResistanceMod : MobileMod +{ + // Field 0 + private int _offset; + + // Field 1 + private ResistanceType _type; + + public ResistanceMod(Mobile owner) : base(owner) + { + } + + public ResistanceMod(ResistanceType type, int offset, Mobile owner = null) : base(owner) + { + _type = type; + _offset = offset; + } + + [SerializableField(0)] + public ResistanceType Type + { + get => _type; + set + { + if (_type != value) + { + _type = value; + + Owner?.UpdateResistances(); + MarkDirty(); + } + } + } + + [SerializableField(1)] + public int Offset + { + get => _offset; + set + { + if (_offset != value) + { + _offset = value; + + Owner?.UpdateResistances(); + MarkDirty(); + } + } + } +} diff --git a/Projects/Server/Mobiles/Mods/SkillMod.cs b/Projects/Server/Mobiles/Mods/SkillMod.cs new file mode 100644 index 000000000..680d92b88 --- /dev/null +++ b/Projects/Server/Mobiles/Mods/SkillMod.cs @@ -0,0 +1,129 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: SkillMod.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Runtime.CompilerServices; +using ModernUO.Serialization; + +namespace Server; + +[SerializationGenerator(0)] +public abstract partial class SkillMod : MobileMod +{ + private bool _obeyCap; + private bool _relative; + private SkillName _skill; + private double _value; + + public SkillMod(Mobile owner) : base(owner) + { + } + + public SkillMod(SkillName skill, bool relative, double value, Mobile owner = null) : base(owner) + { + _skill = skill; + _relative = relative; + _value = value; + } + + [SerializableField(0)] + public bool ObeyCap + { + get => _obeyCap; + set + { + _obeyCap = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + + [SerializableField(1)] + public SkillName Skill + { + get => _skill; + set + { + if (_skill != value) + { + var oldUpdate = Owner?.Skills[_skill]; + + _skill = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + oldUpdate?.Update(); + MarkDirty(); + } + } + } + + [SerializableField(2)] + public bool Relative + { + get => _relative; + set + { + if (_relative != value) + { + _relative = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + } + + [SerializableField(3)] + public bool Absolute + { + get => !_relative; + set + { + if (_relative == value) + { + _relative = !value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + } + + [SerializableField(4)] + public double Value + { + get => _value; + set + { + if (_value != value) + { + _value = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Remove() => Owner?.RemoveSkillMod(this); + + public abstract bool CheckCondition(); +} diff --git a/Projects/Server/Mobiles/Mods/StatMod.cs b/Projects/Server/Mobiles/Mods/StatMod.cs new file mode 100644 index 000000000..d92067368 --- /dev/null +++ b/Projects/Server/Mobiles/Mods/StatMod.cs @@ -0,0 +1,53 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: StatMod.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using ModernUO.Serialization; + +namespace Server; + +[SerializationGenerator(0)] +public partial class StatMod : MobileMod +{ + [SerializableField(0, getter: "private", setter: "private")] + private DateTime _added; + + [SerializableField(1, getter: "private", setter: "private")] + private TimeSpan _duration; + + [SerializableField(2, setter: "private")] + private StatType _type; + + [SerializableField(3, setter: "private")] + private string _name; + + [SerializableField(4, setter: "private")] + private int _offset; + + public StatMod(Mobile owner) : base(owner) + { + } + + public StatMod(StatType type, string name, int offset, TimeSpan duration, Mobile owner = null) : base(owner) + { + _type = type; + _name = name; + _offset = offset; + _duration = duration; + _added = Core.Now; + } + + public bool HasElapsed() => _duration != TimeSpan.Zero && Core.Now - _added >= _duration; +} diff --git a/Projects/Server/Serialization/Attributes/DeserializeTimerFieldAttribute.cs b/Projects/Server/Mobiles/Mods/TimedSkillMod.cs similarity index 55% rename from Projects/Server/Serialization/Attributes/DeserializeTimerFieldAttribute.cs rename to Projects/Server/Mobiles/Mods/TimedSkillMod.cs index fb33ce62b..388e79c0e 100644 --- a/Projects/Server/Serialization/Attributes/DeserializeTimerFieldAttribute.cs +++ b/Projects/Server/Mobiles/Mods/TimedSkillMod.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: DeserializeTimerFieldAttribute.cs * + * File: TimedSkillMod.cs * * * * 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 * @@ -14,21 +14,27 @@ *************************************************************************/ using System; +using ModernUO.Serialization; -namespace Server +namespace Server; + +[SerializationGenerator(0)] +public partial class TimedSkillMod : SkillMod { - /// - /// Hints to the source generator that the specified serializable field, which must be a timer, - /// can be deserialized by this method. The method signature should look like this: - /// - /// [DeserializeTimerField(0)] - /// private void DeserializeTimer(TimeSpan delay) - /// - [AttributeUsage(AttributeTargets.Method)] - public sealed class DeserializeTimerFieldAttribute : Attribute - { - public int Order { get; } + [SerializableField(0, setter: "private")] + private DateTime _expire; - public DeserializeTimerFieldAttribute(int order) => Order = order; + public TimedSkillMod(Mobile owner) : base(owner) + { } + + public TimedSkillMod(SkillName skill, bool relative, double value, TimeSpan delay) + : this(skill, relative, value, Core.Now + delay) + { + } + + public TimedSkillMod(SkillName skill, bool relative, double value, DateTime expire) + : base(skill, relative, value) => _expire = expire; + + public override bool CheckCondition() => Core.Now < _expire; } diff --git a/Projects/Server/Mobiles/Movement.cs b/Projects/Server/Mobiles/Movement.cs index 3ea012573..44cfc473d 100644 --- a/Projects/Server/Mobiles/Movement.cs +++ b/Projects/Server/Mobiles/Movement.cs @@ -19,16 +19,16 @@ namespace Server.Movement { // Movement implementation algorithm public static IMovementImpl Impl { get; set; } - public static int WalkFootDelay { get; set; } = 440; - public static int RunFootDelay { get; set; } = 220; - public static int WalkMountDelay { get; set; } = 220; - public static int RunMountDelay { get; set; } = 110; + public static int WalkFootDelay { get; set; } = 400; + public static int RunFootDelay { get; set; } = 200; + public static int WalkMountDelay { get; set; } = 200; + public static int RunMountDelay { get; set; } = 100; public static bool EnableFastwalkPrevention { get; set; } = true; public static AccessLevel FastwalkExemptionLevel { get; set; } = AccessLevel.Counselor; // If this is changed during runtime, then the steps array needs resizing. - public static int MaxSteps { get; private set; } = 4; + public static int MaxSteps { get; private set; } = 3; public static void Configure() { diff --git a/Projects/Server/MultiData.cs b/Projects/Server/MultiData.cs index 6bda94398..b815ab0c9 100644 --- a/Projects/Server/MultiData.cs +++ b/Projects/Server/MultiData.cs @@ -4,991 +4,943 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; -namespace Server +namespace Server; + +public static class MultiData { - public static class MultiData + public static void Configure() { - private static readonly BinaryReader m_IndexReader; - private static readonly BinaryReader m_StreamReader; + var multiUOPPath = Core.FindDataFile("MultiCollection.uop", false); - private static readonly bool UsingUOPFormat; - - static MultiData() + if (File.Exists(multiUOPPath)) { - var multiUOPPath = Core.FindDataFile("MultiCollection.uop", false); - - if (File.Exists(multiUOPPath)) - { - LoadUOP(multiUOPPath); - UsingUOPFormat = true; - return; - } - - var idxPath = Core.FindDataFile("multi.idx"); - var mulPath = Core.FindDataFile("multi.mul"); - - var idx = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read); - m_IndexReader = new BinaryReader(idx); - - var stream = new FileStream(mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); - m_StreamReader = new BinaryReader(stream); - - var vdPath = Core.FindDataFile("verdata.mul", false); - - if (!File.Exists(vdPath)) - { - return; - } - - using var fs = new FileStream(vdPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var bin = new BinaryReader(fs); - - var count = bin.ReadInt32(); - - for (var i = 0; i < count; ++i) - { - var file = bin.ReadInt32(); - var index = bin.ReadInt32(); - var lookup = bin.ReadInt32(); - var length = bin.ReadInt32(); - bin.ReadInt32(); // extra - - if (file == 14 && index >= 0 && lookup >= 0 && length > 0) - { - bin.BaseStream.Seek(lookup, SeekOrigin.Begin); - - Components[index] = new MultiComponentList(bin, length / 12); - - bin.BaseStream.Seek(24 + i * 20, SeekOrigin.Begin); - } - } - - bin.Close(); + LoadUOP(multiUOPPath); + return; } - public static Dictionary Components { get; } = new(); + // OSI Client 7.0.9.0+ uses 64bit tiledata flags + var postHSMulFormat = ServerConfiguration.GetSetting( + "maps.enablePostHSMultiComponentFormat", + UOClient.ServerClientVersion >= ClientVersion.Version7090 + ); - public static MultiComponentList GetComponents(int multiID) - { - MultiComponentList mcl; - - multiID &= 0x3FFF; - - if (Components.ContainsKey(multiID)) - { - mcl = Components[multiID]; - } - else if (!UsingUOPFormat) - { - Components[multiID] = mcl = Load(multiID); - } - else - { - mcl = MultiComponentList.Empty; - } - - return mcl; - } - - public static void LoadUOP(string path) - { - var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - var streamReader = new BinaryReader(stream); - - // Head Information Start - if (streamReader.ReadInt32() != 0x0050594D) // Not a UOP Files - { - return; - } - - if (streamReader.ReadInt32() > 5) // Bad Version - { - return; - } - - // Multi ID List Array Start - UOPHash.BuildChunkIDs(out var chunkIds); - // Multi ID List Array End - - streamReader.ReadUInt32(); // format timestamp? 0xFD23EC43 - var startAddress = streamReader.ReadInt64(); - - streamReader.ReadInt32(); - streamReader.ReadInt32(); - - stream.Seek(startAddress, SeekOrigin.Begin); // Head Information End - - long nextBlock; - - do - { - var blockFileCount = streamReader.ReadInt32(); - nextBlock = streamReader.ReadInt64(); - - var index = 0; - - do - { - var offset = streamReader.ReadInt64(); - - var headerSize = streamReader.ReadInt32(); // header length - var compressedSize = streamReader.ReadInt32(); // compressed size - var decompressedSize = streamReader.ReadInt32(); // decompressed size - - var filehash = streamReader.ReadUInt64(); // filename hash (HashLittle2) - streamReader.ReadUInt32(); - var compressionMethod = streamReader.ReadInt16(); // compression method (0 = none, 1 = zlib) - - index++; - - if (offset == 0 || decompressedSize == 0 || filehash == 0x126D1E99DDEDEE0A) // Exclude housing.bin - { - continue; - } - - chunkIds.TryGetValue(filehash, out var chunkID); - - var position = stream.Position; // save current position - - stream.Seek(offset + headerSize, SeekOrigin.Begin); - - Span sourceData = GC.AllocateUninitializedArray(compressedSize); - - if (stream.Read(sourceData) != compressedSize) - { - continue; - } - - Span data; - - if (compressionMethod == 1) - { - data = GC.AllocateUninitializedArray(decompressedSize); - Zlib.Unpack(data, ref decompressedSize, sourceData, compressedSize); - } - else - { - data = sourceData; - } - - var tileList = new List(); - - var reader = new SpanReader(data); - reader.Seek(4, SeekOrigin.Begin); - var count = reader.ReadUInt32LE(); - - for (uint i = 0; i < count; i++) - { - var itemId = reader.ReadUInt16LE(); - var x = reader.ReadInt16LE(); - var y = reader.ReadInt16LE(); - var z = reader.ReadInt16LE(); - var flagValue = reader.ReadUInt16LE(); - - var tileFlag = flagValue switch - { - 1 => TileFlag.None, - 257 => TileFlag.Generic, - _ => TileFlag.Background // 0 - }; - - var clilocsCount = reader.ReadUInt32LE(); - var skip = (int)Math.Min(clilocsCount, int.MaxValue) * 4; // bypass binary block - reader.Seek(skip, SeekOrigin.Current); - - tileList.Add(new MultiTileEntry(itemId, x, y, z, tileFlag)); - } - - Components[chunkID] = new MultiComponentList(tileList); - - stream.Seek(position, SeekOrigin.Begin); // back to position - } while (index < blockFileCount); - } while (stream.Seek(nextBlock, SeekOrigin.Begin) != 0); - } - - // TODO: Change this to read the file all during load time - public static MultiComponentList Load(int multiID) - { - try - { - m_IndexReader.BaseStream.Seek(multiID * 12, SeekOrigin.Begin); - - var lookup = m_IndexReader.ReadInt32(); - var 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 / (MultiComponentList.PostHSFormat ? 16 : 12)); - } - catch - { - return MultiComponentList.Empty; - } - } + LoadMul(postHSMulFormat); } - public struct MultiTileEntry + private static Dictionary _components = new(); + + public static MultiComponentList GetComponents(int multiID) => + _components.TryGetValue(multiID & 0x3FFF, out var mcl) ? mcl : MultiComponentList.Empty; + + private static void LoadUOP(string path) { - public ushort ItemId { get; set; } - public short OffsetX { get; set; } - public short OffsetY { get; set; } - public short OffsetZ { get; set; } - public TileFlag Flags { get; set; } + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + var streamReader = new BinaryReader(stream); - public MultiTileEntry(ushort itemID, short xOffset, short yOffset, short zOffset, TileFlag flags) + // Head Information Start + if (streamReader.ReadInt32() != 0x0050594D) // Not a UOP File { - ItemId = itemID; - OffsetX = xOffset; - OffsetY = yOffset; - OffsetZ = zOffset; - Flags = flags; - } - } - - public sealed class MultiComponentList - { - public static readonly MultiComponentList Empty = new(); - - private Point2D m_Min, m_Max; - - public MultiComponentList(MultiComponentList toCopy) - { - m_Min = toCopy.m_Min; - m_Max = toCopy.m_Max; - - Center = toCopy.Center; - - Width = toCopy.Width; - Height = toCopy.Height; - - Tiles = new StaticTile[Width][][]; - - for (var x = 0; x < Width; ++x) - { - Tiles[x] = new StaticTile[Height][]; - - for (var y = 0; y < Height; ++y) - { - Tiles[x][y] = new StaticTile[toCopy.Tiles[x][y].Length]; - - for (var i = 0; i < Tiles[x][y].Length; ++i) - { - Tiles[x][y][i] = toCopy.Tiles[x][y][i]; - } - } - } - - List = new MultiTileEntry[toCopy.List.Length]; - - for (var i = 0; i < List.Length; ++i) - { - List[i] = toCopy.List[i]; - } + return; } - public MultiComponentList(IGenericReader reader) + if (streamReader.ReadInt32() > 5) // Bad Version { - var version = reader.ReadInt(); - - m_Min = reader.ReadPoint2D(); - m_Max = reader.ReadPoint2D(); - Center = reader.ReadPoint2D(); - Width = reader.ReadInt(); - Height = reader.ReadInt(); - - var length = reader.ReadInt(); - - var allTiles = List = new MultiTileEntry[length]; - - if (version == 0) - { - for (var i = 0; i < length; ++i) - { - int id = reader.ReadShort(); - if (id >= 0x4000) - { - id -= 0x4000; - } - - allTiles[i].ItemId = (ushort)id; - allTiles[i].OffsetX = reader.ReadShort(); - allTiles[i].OffsetY = reader.ReadShort(); - allTiles[i].OffsetZ = reader.ReadShort(); - allTiles[i].Flags = (TileFlag)reader.ReadInt(); - } - } - else - { - for (var i = 0; i < length; ++i) - { - allTiles[i].ItemId = reader.ReadUShort(); - allTiles[i].OffsetX = reader.ReadShort(); - allTiles[i].OffsetY = reader.ReadShort(); - allTiles[i].OffsetZ = reader.ReadShort(); - allTiles[i].Flags = (TileFlag)reader.ReadInt(); - } - } - - var tiles = new TileList[Width][]; - Tiles = new StaticTile[Width][][]; - - for (var x = 0; x < Width; ++x) - { - tiles[x] = new TileList[Height]; - Tiles[x] = new StaticTile[Height][]; - - for (var y = 0; y < Height; ++y) - { - tiles[x][y] = new TileList(); - } - } - - for (var i = 0; i < allTiles.Length; ++i) - { - if (i == 0 || allTiles[i].Flags != 0) - { - var xOffset = allTiles[i].OffsetX + Center.m_X; - var yOffset = allTiles[i].OffsetY + Center.m_Y; - - tiles[xOffset][yOffset].Add(allTiles[i].ItemId, (sbyte)allTiles[i].OffsetZ); - } - } - - for (var x = 0; x < Width; ++x) - { - for (var y = 0; y < Height; ++y) - { - Tiles[x][y] = tiles[x][y].ToArray(); - } - } + return; } - public MultiComponentList(BinaryReader reader, int count) + UOPHash.BuildChunkIDs(out var chunkIds); + + streamReader.ReadUInt32(); // format timestamp? 0xFD23EC43 + var startAddress = streamReader.ReadInt64(); + + stream.Seek(startAddress, SeekOrigin.Begin); // End of head block + + long nextBlock; + + do { - var allTiles = List = new MultiTileEntry[count]; - - for (var i = 0; i < count; ++i) - { - allTiles[i].ItemId = reader.ReadUInt16(); - allTiles[i].OffsetX = reader.ReadInt16(); - allTiles[i].OffsetY = reader.ReadInt16(); - allTiles[i].OffsetZ = reader.ReadInt16(); - - if (PostHSFormat) - { - allTiles[i].Flags = (TileFlag)reader.ReadUInt64(); - } - else - { - allTiles[i].Flags = (TileFlag)reader.ReadUInt32(); - } - - var e = allTiles[i]; - - if (i == 0 || e.Flags != 0) - { - if (e.OffsetX < m_Min.m_X) - { - m_Min.m_X = e.OffsetX; - } - - if (e.OffsetY < m_Min.m_Y) - { - m_Min.m_Y = e.OffsetY; - } - - if (e.OffsetX > m_Max.m_X) - { - m_Max.m_X = e.OffsetX; - } - - if (e.OffsetY > m_Max.m_Y) - { - m_Max.m_Y = e.OffsetY; - } - } - } - - Center = new Point2D(-m_Min.m_X, -m_Min.m_Y); - Width = m_Max.m_X - m_Min.m_X + 1; - Height = m_Max.m_Y - m_Min.m_Y + 1; - - var tiles = new TileList[Width][]; - Tiles = new StaticTile[Width][][]; - - for (var x = 0; x < Width; ++x) - { - tiles[x] = new TileList[Height]; - Tiles[x] = new StaticTile[Height][]; - - for (var y = 0; y < Height; ++y) - { - tiles[x][y] = new TileList(); - } - } - - for (var i = 0; i < allTiles.Length; ++i) - { - if (i == 0 || allTiles[i].Flags != 0) - { - var xOffset = allTiles[i].OffsetX + Center.m_X; - var yOffset = allTiles[i].OffsetY + Center.m_Y; - - tiles[xOffset][yOffset].Add(allTiles[i].ItemId, (sbyte)allTiles[i].OffsetZ); - } - } - - for (var x = 0; x < Width; ++x) - { - for (var y = 0; y < Height; ++y) - { - Tiles[x][y] = tiles[x][y].ToArray(); - } - } - } - - public MultiComponentList(List list) - { - var allTiles = List = new MultiTileEntry[list.Count]; - - for (var i = 0; i < list.Count; ++i) - { - allTiles[i].ItemId = list[i].ItemId; - allTiles[i].OffsetX = list[i].OffsetX; - allTiles[i].OffsetY = list[i].OffsetY; - allTiles[i].OffsetZ = list[i].OffsetZ; - - allTiles[i].Flags = list[i].Flags; - - var e = allTiles[i]; - - if (i == 0 || e.Flags != 0) - { - if (e.OffsetX < m_Min.m_X) - { - m_Min.m_X = e.OffsetX; - } - - if (e.OffsetY < m_Min.m_Y) - { - m_Min.m_Y = e.OffsetY; - } - - if (e.OffsetX > m_Max.m_X) - { - m_Max.m_X = e.OffsetX; - } - - if (e.OffsetY > m_Max.m_Y) - { - m_Max.m_Y = e.OffsetY; - } - } - } - - Center = new Point2D(-m_Min.m_X, -m_Min.m_Y); - Width = m_Max.m_X - m_Min.m_X + 1; - Height = m_Max.m_Y - m_Min.m_Y + 1; - - var tiles = new TileList[Width][]; - Tiles = new StaticTile[Width][][]; - - for (var x = 0; x < Width; ++x) - { - tiles[x] = new TileList[Height]; - Tiles[x] = new StaticTile[Height][]; - - for (var y = 0; y < Height; ++y) - { - tiles[x][y] = new TileList(); - } - } - - for (var i = 0; i < allTiles.Length; ++i) - { - if (i == 0 || allTiles[i].Flags != 0) - { - var xOffset = allTiles[i].OffsetX + Center.m_X; - var yOffset = allTiles[i].OffsetY + Center.m_Y; - var itemID = (allTiles[i].ItemId & TileData.MaxItemValue) | 0x10000; - - tiles[xOffset][yOffset].Add((ushort)itemID, (sbyte)allTiles[i].OffsetZ); - } - } - - for (var x = 0; x < Width; ++x) - { - for (var y = 0; y < Height; ++y) - { - Tiles[x][y] = tiles[x][y].ToArray(); - } - } - } - - private MultiComponentList() - { - Tiles = Array.Empty(); - List = Array.Empty(); - } - - public static void Configure() - { - // OSI Client Patch 7.0.9.0 - PostHSFormat = ServerConfiguration.GetOrUpdateSetting("maps.enablePostHSMultiComponentFormat", true); - } - - public static bool PostHSFormat { get; set; } - - public Point2D Min => m_Min; - public Point2D Max => m_Max; - - public Point2D Center { get; } - - public int Width { get; private set; } - - public int Height { get; private set; } - - public StaticTile[][][] Tiles { get; private set; } - - public MultiTileEntry[] List { get; private set; } - - public void Add(int itemID, int x, int y, int z) - { - var vx = x + Center.m_X; - var vy = y + Center.m_Y; - - if (vx >= 0 && vx < Width && vy >= 0 && vy < Height) - { - var oldTiles = Tiles[vx][vy]; - - for (var i = oldTiles.Length - 1; i >= 0; --i) - { - var data = TileData.ItemTable[itemID & TileData.MaxItemValue]; - - if (oldTiles[i].Z == z && oldTiles[i].Height > 0 == data.Height > 0) - { - var newIsRoof = (data.Flags & TileFlag.Roof) != 0; - var oldIsRoof = - (TileData.ItemTable[oldTiles[i].ID & TileData.MaxItemValue].Flags & TileFlag.Roof) != 0; - - if (newIsRoof == oldIsRoof) - { - Remove(oldTiles[i].ID, x, y, z); - } - } - } - - oldTiles = Tiles[vx][vy]; - - var newTiles = new StaticTile[oldTiles.Length + 1]; - - for (var i = 0; i < oldTiles.Length; ++i) - { - newTiles[i] = oldTiles[i]; - } - - newTiles[oldTiles.Length] = new StaticTile((ushort)itemID, (sbyte)z); - - Tiles[vx][vy] = newTiles; - - var oldList = List; - var newList = new MultiTileEntry[oldList.Length + 1]; - - for (var i = 0; i < oldList.Length; ++i) - { - newList[i] = oldList[i]; - } - - newList[oldList.Length] = new MultiTileEntry( - (ushort)itemID, - (short)x, - (short)y, - (short)z, - TileFlag.Background - ); - - 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) - { - var vx = x + Center.m_X; - var vy = y + Center.m_Y; - - if (vx >= 0 && vx < Width && vy >= 0 && vy < Height) - { - var oldTiles = Tiles[vx][vy]; - - for (var i = 0; i < oldTiles.Length; ++i) - { - var tile = oldTiles[i]; - - if (tile.Z == z && tile.Height >= minHeight) - { - var newTiles = new StaticTile[oldTiles.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newTiles[j] = oldTiles[j]; - } - - for (var j = i + 1; j < oldTiles.Length; ++j) - { - newTiles[j - 1] = oldTiles[j]; - } - - Tiles[vx][vy] = newTiles; - - break; - } - } - - var oldList = List; - - for (var i = 0; i < oldList.Length; ++i) - { - var tile = oldList[i]; - - if (tile.OffsetX == (short)x && tile.OffsetY == (short)y && tile.OffsetZ == (short)z && - TileData.ItemTable[tile.ItemId & TileData.MaxItemValue].Height >= minHeight) - { - var newList = new MultiTileEntry[oldList.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newList[j] = oldList[j]; - } - - for (var j = i + 1; j < oldList.Length; ++j) - { - newList[j - 1] = oldList[j]; - } - - List = newList; - - break; - } - } - } - } - - public void Remove(int itemID, int x, int y, int z) - { - var vx = x + Center.m_X; - var vy = y + Center.m_Y; - - if (vx >= 0 && vx < Width && vy >= 0 && vy < Height) - { - var oldTiles = Tiles[vx][vy]; - - for (var i = 0; i < oldTiles.Length; ++i) - { - var tile = oldTiles[i]; - - if (tile.ID == itemID && tile.Z == z) - { - var newTiles = new StaticTile[oldTiles.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newTiles[j] = oldTiles[j]; - } - - for (var j = i + 1; j < oldTiles.Length; ++j) - { - newTiles[j - 1] = oldTiles[j]; - } - - Tiles[vx][vy] = newTiles; - - break; - } - } - - var oldList = List; - - for (var i = 0; i < oldList.Length; ++i) - { - var tile = oldList[i]; - - if (tile.ItemId == itemID && tile.OffsetX == (short)x && tile.OffsetY == (short)y && - tile.OffsetZ == (short)z) - { - var newList = new MultiTileEntry[oldList.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newList[j] = oldList[j]; - } - - for (var j = i + 1; j < oldList.Length; ++j) - { - newList[j - 1] = oldList[j]; - } - - List = newList; - - break; - } - } - } - } - - public void Resize(int newWidth, int newHeight) - { - int oldWidth = Width, oldHeight = Height; - var oldTiles = Tiles; - - var totalLength = 0; - - var newTiles = new StaticTile[newWidth][][]; - - for (var x = 0; x < newWidth; ++x) - { - newTiles[x] = new StaticTile[newHeight][]; - - for (var y = 0; y < newHeight; ++y) - { - if (x < oldWidth && y < oldHeight) - { - newTiles[x][y] = oldTiles[x][y]; - } - else - { - newTiles[x][y] = Array.Empty(); - } - - totalLength += newTiles[x][y].Length; - } - } - - Tiles = newTiles; - List = new MultiTileEntry[totalLength]; - Width = newWidth; - Height = newHeight; - - m_Min = Point2D.Zero; - m_Max = Point2D.Zero; + var blockFileCount = streamReader.ReadInt32(); + nextBlock = streamReader.ReadInt64(); var index = 0; - for (var x = 0; x < newWidth; ++x) + do { - for (var y = 0; y < newHeight; ++y) + var offset = streamReader.ReadInt64(); + + var headerSize = streamReader.ReadInt32(); // header length + var compressedSize = streamReader.ReadInt32(); // compressed size + var decompressedSize = streamReader.ReadInt32(); // decompressed size + + var filehash = streamReader.ReadUInt64(); // filename hash (HashLittle2) + streamReader.ReadUInt32(); + var compressionMethod = streamReader.ReadInt16(); // compression method (0 = none, 1 = zlib) + + index++; + + if (offset == 0 || decompressedSize == 0 || filehash == 0x126D1E99DDEDEE0A) // Exclude housing.bin { - var tiles = newTiles[x][y]; + continue; + } - for (var i = 0; i < tiles.Length; ++i) + chunkIds.TryGetValue(filehash, out var chunkID); + + var position = stream.Position; // save current position + + stream.Seek(offset + headerSize, SeekOrigin.Begin); + + Span sourceData = GC.AllocateUninitializedArray(compressedSize); + + if (stream.Read(sourceData) != compressedSize) + { + continue; + } + + Span data; + + if (compressionMethod == 1) + { + data = GC.AllocateUninitializedArray(decompressedSize); + Zlib.Unpack(data, ref decompressedSize, sourceData, compressedSize); + } + else + { + data = sourceData; + } + + var tileList = new List(); + + var reader = new SpanReader(data); + reader.Seek(4, SeekOrigin.Begin); + var count = reader.ReadUInt32LE(); + + for (uint i = 0; i < count; i++) + { + var itemId = reader.ReadUInt16LE(); + var x = reader.ReadInt16LE(); + var y = reader.ReadInt16LE(); + var z = reader.ReadInt16LE(); + var flagValue = reader.ReadUInt16LE(); + + var tileFlag = flagValue switch { - var tile = tiles[i]; + 1 => TileFlag.None, + 257 => TileFlag.Generic, + _ => TileFlag.Background // 0 + }; - var vx = x - Center.X; - var vy = y - Center.Y; + var clilocsCount = reader.ReadUInt32LE(); + var skip = (int)Math.Min(clilocsCount, int.MaxValue) * 4; // bypass binary block + reader.Seek(skip, SeekOrigin.Current); - if (vx < m_Min.m_X) - { - m_Min.m_X = vx; - } + tileList.Add(new MultiTileEntry(itemId, x, y, z, tileFlag)); + } - if (vy < m_Min.m_Y) - { - m_Min.m_Y = vy; - } + _components[chunkID] = new MultiComponentList(tileList); - if (vx > m_Max.m_X) - { - m_Max.m_X = vx; - } + stream.Seek(position, SeekOrigin.Begin); // back to position + } while (index < blockFileCount); + } while (stream.Seek(nextBlock, SeekOrigin.Begin) != 0); - if (vy > m_Max.m_Y) - { - m_Max.m_Y = vy; - } + streamReader.Close(); + } - List[index++] = new MultiTileEntry( - (ushort)tile.ID, - (short)vx, - (short)vy, - (short)tile.Z, - TileFlag.Background - ); + private static void LoadMul(bool postHSMulFormat) + { + var idxPath = Core.FindDataFile("multi.idx"); + var mulPath = Core.FindDataFile("multi.mul"); + + using var idx = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read); + var idxReader = new BinaryReader(idx); + + using var stream = new FileStream(mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); + var bin = new BinaryReader(stream); + + var count = (int)(idx.Length / 12); + for (var i = 0; i < count; i++) + { + var lookup = idxReader.ReadInt32(); + var length = idxReader.ReadInt32(); + idx.Seek(4, SeekOrigin.Current); // Extra + + if (lookup < 0 || length <= 0) + { + continue; + } + + bin.BaseStream.Seek(lookup, SeekOrigin.Begin); + _components[i] = new MultiComponentList(bin, length, postHSMulFormat); + } + + idxReader.Close(); + } +} + +public struct MultiTileEntry +{ + public ushort ItemId { get; set; } + public short OffsetX { get; set; } + public short OffsetY { get; set; } + public short OffsetZ { get; set; } + public TileFlag Flags { get; set; } + + public MultiTileEntry(ushort itemID, short xOffset, short yOffset, short zOffset, TileFlag flags) + { + ItemId = itemID; + OffsetX = xOffset; + OffsetY = yOffset; + OffsetZ = zOffset; + Flags = flags; + } +} + +public sealed class MultiComponentList +{ + public static readonly MultiComponentList Empty = new(); + + private Point2D m_Min, m_Max; + + public MultiComponentList(MultiComponentList toCopy) + { + m_Min = toCopy.m_Min; + m_Max = toCopy.m_Max; + + Center = toCopy.Center; + + Width = toCopy.Width; + Height = toCopy.Height; + + Tiles = new StaticTile[Width][][]; + + for (var x = 0; x < Width; ++x) + { + Tiles[x] = new StaticTile[Height][]; + + for (var y = 0; y < Height; ++y) + { + Tiles[x][y] = new StaticTile[toCopy.Tiles[x][y].Length]; + + for (var i = 0; i < Tiles[x][y].Length; ++i) + { + Tiles[x][y][i] = toCopy.Tiles[x][y][i]; + } + } + } + + List = new MultiTileEntry[toCopy.List.Length]; + + for (var i = 0; i < List.Length; ++i) + { + List[i] = toCopy.List[i]; + } + } + + public MultiComponentList(IGenericReader reader) + { + var version = reader.ReadInt(); + + m_Min = reader.ReadPoint2D(); + m_Max = reader.ReadPoint2D(); + Center = reader.ReadPoint2D(); + Width = reader.ReadInt(); + Height = reader.ReadInt(); + + var length = reader.ReadInt(); + + var allTiles = List = new MultiTileEntry[length]; + + if (version == 0) + { + for (var i = 0; i < length; ++i) + { + int id = reader.ReadShort(); + if (id >= 0x4000) + { + id -= 0x4000; + } + + allTiles[i].ItemId = (ushort)id; + allTiles[i].OffsetX = reader.ReadShort(); + allTiles[i].OffsetY = reader.ReadShort(); + allTiles[i].OffsetZ = reader.ReadShort(); + allTiles[i].Flags = (TileFlag)reader.ReadInt(); + } + } + else + { + for (var i = 0; i < length; ++i) + { + allTiles[i].ItemId = reader.ReadUShort(); + allTiles[i].OffsetX = reader.ReadShort(); + allTiles[i].OffsetY = reader.ReadShort(); + allTiles[i].OffsetZ = reader.ReadShort(); + allTiles[i].Flags = (TileFlag)reader.ReadInt(); + } + } + + var tiles = new TileList[Width][]; + Tiles = new StaticTile[Width][][]; + + for (var x = 0; x < Width; ++x) + { + tiles[x] = new TileList[Height]; + Tiles[x] = new StaticTile[Height][]; + + for (var y = 0; y < Height; ++y) + { + tiles[x][y] = new TileList(); + } + } + + for (var i = 0; i < allTiles.Length; ++i) + { + if (i == 0 || allTiles[i].Flags != 0) + { + var xOffset = allTiles[i].OffsetX + Center.m_X; + var yOffset = allTiles[i].OffsetY + Center.m_Y; + + tiles[xOffset][yOffset].Add(allTiles[i].ItemId, (sbyte)allTiles[i].OffsetZ); + } + } + + for (var x = 0; x < Width; ++x) + { + for (var y = 0; y < Height; ++y) + { + Tiles[x][y] = tiles[x][y].ToArray(); + } + } + } + + public MultiComponentList(BinaryReader reader, int length, bool postHSFormat) + { + var count = length / (postHSFormat ? 16 : 12); + var allTiles = List = new MultiTileEntry[count]; + + for (var i = 0; i < count; ++i) + { + allTiles[i].ItemId = reader.ReadUInt16(); + allTiles[i].OffsetX = reader.ReadInt16(); + allTiles[i].OffsetY = reader.ReadInt16(); + allTiles[i].OffsetZ = reader.ReadInt16(); + allTiles[i].Flags = postHSFormat ? (TileFlag)reader.ReadUInt64() : (TileFlag)reader.ReadUInt32(); + + var e = allTiles[i]; + + if (i == 0 || e.Flags != 0) + { + if (e.OffsetX < m_Min.m_X) + { + m_Min.m_X = e.OffsetX; + } + + if (e.OffsetY < m_Min.m_Y) + { + m_Min.m_Y = e.OffsetY; + } + + if (e.OffsetX > m_Max.m_X) + { + m_Max.m_X = e.OffsetX; + } + + if (e.OffsetY > m_Max.m_Y) + { + m_Max.m_Y = e.OffsetY; + } + } + } + + Center = new Point2D(-m_Min.m_X, -m_Min.m_Y); + Width = m_Max.m_X - m_Min.m_X + 1; + Height = m_Max.m_Y - m_Min.m_Y + 1; + + var tiles = new TileList[Width][]; + Tiles = new StaticTile[Width][][]; + + for (var x = 0; x < Width; ++x) + { + tiles[x] = new TileList[Height]; + Tiles[x] = new StaticTile[Height][]; + + for (var y = 0; y < Height; ++y) + { + tiles[x][y] = new TileList(); + } + } + + for (var i = 0; i < allTiles.Length; ++i) + { + if (i == 0 || allTiles[i].Flags != 0) + { + var xOffset = allTiles[i].OffsetX + Center.m_X; + var yOffset = allTiles[i].OffsetY + Center.m_Y; + + tiles[xOffset][yOffset].Add(allTiles[i].ItemId, (sbyte)allTiles[i].OffsetZ); + } + } + + for (var x = 0; x < Width; ++x) + { + for (var y = 0; y < Height; ++y) + { + Tiles[x][y] = tiles[x][y].ToArray(); + } + } + } + + public MultiComponentList(List list) + { + var allTiles = List = new MultiTileEntry[list.Count]; + + for (var i = 0; i < list.Count; ++i) + { + allTiles[i].ItemId = list[i].ItemId; + allTiles[i].OffsetX = list[i].OffsetX; + allTiles[i].OffsetY = list[i].OffsetY; + allTiles[i].OffsetZ = list[i].OffsetZ; + + allTiles[i].Flags = list[i].Flags; + + var e = allTiles[i]; + + if (i == 0 || e.Flags != 0) + { + if (e.OffsetX < m_Min.m_X) + { + m_Min.m_X = e.OffsetX; + } + + if (e.OffsetY < m_Min.m_Y) + { + m_Min.m_Y = e.OffsetY; + } + + if (e.OffsetX > m_Max.m_X) + { + m_Max.m_X = e.OffsetX; + } + + if (e.OffsetY > m_Max.m_Y) + { + m_Max.m_Y = e.OffsetY; + } + } + } + + Center = new Point2D(-m_Min.m_X, -m_Min.m_Y); + Width = m_Max.m_X - m_Min.m_X + 1; + Height = m_Max.m_Y - m_Min.m_Y + 1; + + var tiles = new TileList[Width][]; + Tiles = new StaticTile[Width][][]; + + for (var x = 0; x < Width; ++x) + { + tiles[x] = new TileList[Height]; + Tiles[x] = new StaticTile[Height][]; + + for (var y = 0; y < Height; ++y) + { + tiles[x][y] = new TileList(); + } + } + + for (var i = 0; i < allTiles.Length; ++i) + { + if (i == 0 || allTiles[i].Flags != 0) + { + var xOffset = allTiles[i].OffsetX + Center.m_X; + var yOffset = allTiles[i].OffsetY + Center.m_Y; + var itemID = (allTiles[i].ItemId & TileData.MaxItemValue) | 0x10000; + + tiles[xOffset][yOffset].Add((ushort)itemID, (sbyte)allTiles[i].OffsetZ); + } + } + + for (var x = 0; x < Width; ++x) + { + for (var y = 0; y < Height; ++y) + { + Tiles[x][y] = tiles[x][y].ToArray(); + } + } + } + + private MultiComponentList() + { + Tiles = Array.Empty(); + List = Array.Empty(); + } + + public Point2D Min => m_Min; + public Point2D Max => m_Max; + + public Point2D Center { get; } + + public int Width { get; private set; } + + public int Height { get; private set; } + + public StaticTile[][][] Tiles { get; private set; } + + public MultiTileEntry[] List { get; private set; } + + public void Add(int itemID, int x, int y, int z) + { + var vx = x + Center.m_X; + var vy = y + Center.m_Y; + + if (vx >= 0 && vx < Width && vy >= 0 && vy < Height) + { + var oldTiles = Tiles[vx][vy]; + + for (var i = oldTiles.Length - 1; i >= 0; --i) + { + var data = TileData.ItemTable[itemID & TileData.MaxItemValue]; + + if (oldTiles[i].Z == z && oldTiles[i].Height > 0 == data.Height > 0) + { + var newIsRoof = (data.Flags & TileFlag.Roof) != 0; + var oldIsRoof = + (TileData.ItemTable[oldTiles[i].ID & TileData.MaxItemValue].Flags & TileFlag.Roof) != 0; + + if (newIsRoof == oldIsRoof) + { + Remove(oldTiles[i].ID, x, y, z); } } } - } - public void Serialize(IGenericWriter writer) - { - writer.Write(1); // version; + oldTiles = Tiles[vx][vy]; - writer.Write(m_Min); - writer.Write(m_Max); - writer.Write(Center); + var newTiles = new StaticTile[oldTiles.Length + 1]; - writer.Write(Width); - writer.Write(Height); - - writer.Write(List.Length); - - for (var i = 0; i < List.Length; ++i) + for (var i = 0; i < oldTiles.Length; ++i) { - var ent = List[i]; + newTiles[i] = oldTiles[i]; + } - writer.Write(ent.ItemId); - writer.Write(ent.OffsetX); - writer.Write(ent.OffsetY); - writer.Write(ent.OffsetZ); - writer.Write((int)ent.Flags); + newTiles[oldTiles.Length] = new StaticTile((ushort)itemID, (sbyte)z); + + Tiles[vx][vy] = newTiles; + + var oldList = List; + var newList = new MultiTileEntry[oldList.Length + 1]; + + for (var i = 0; i < oldList.Length; ++i) + { + newList[i] = oldList[i]; + } + + newList[oldList.Length] = new MultiTileEntry( + (ushort)itemID, + (short)x, + (short)y, + (short)z, + TileFlag.Background + ); + + 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 static class UOPHash + public void RemoveXYZH(int x, int y, int z, int minHeight) { - public static void BuildChunkIDs(out Dictionary chunkIds) + var vx = x + Center.m_X; + var vy = y + Center.m_Y; + + if (vx >= 0 && vx < Width && vy >= 0 && vy < Height) { - const int maxId = 0x10000; + var oldTiles = Tiles[vx][vy]; - chunkIds = new Dictionary(); - - for (var i = 0; i < maxId; ++i) + for (var i = 0; i < oldTiles.Length; ++i) { - chunkIds[HashLittle2($"build/multicollection/{i:000000}.bin")] = i; + var tile = oldTiles[i]; + + if (tile.Z == z && tile.Height >= minHeight) + { + var newTiles = new StaticTile[oldTiles.Length - 1]; + + for (var j = 0; j < i; ++j) + { + newTiles[j] = oldTiles[j]; + } + + for (var j = i + 1; j < oldTiles.Length; ++j) + { + newTiles[j - 1] = oldTiles[j]; + } + + Tiles[vx][vy] = newTiles; + + break; + } + } + + var oldList = List; + + for (var i = 0; i < oldList.Length; ++i) + { + var tile = oldList[i]; + + if (tile.OffsetX == (short)x && tile.OffsetY == (short)y && tile.OffsetZ == (short)z && + TileData.ItemTable[tile.ItemId & TileData.MaxItemValue].Height >= minHeight) + { + var newList = new MultiTileEntry[oldList.Length - 1]; + + for (var j = 0; j < i; ++j) + { + newList[j] = oldList[j]; + } + + for (var j = i + 1; j < oldList.Length; ++j) + { + newList[j - 1] = oldList[j]; + } + + List = newList; + + break; + } + } + } + } + + public void Remove(int itemID, int x, int y, int z) + { + var vx = x + Center.m_X; + var vy = y + Center.m_Y; + + if (vx >= 0 && vx < Width && vy >= 0 && vy < Height) + { + var oldTiles = Tiles[vx][vy]; + + for (var i = 0; i < oldTiles.Length; ++i) + { + var tile = oldTiles[i]; + + if (tile.ID == itemID && tile.Z == z) + { + var newTiles = new StaticTile[oldTiles.Length - 1]; + + for (var j = 0; j < i; ++j) + { + newTiles[j] = oldTiles[j]; + } + + for (var j = i + 1; j < oldTiles.Length; ++j) + { + newTiles[j - 1] = oldTiles[j]; + } + + Tiles[vx][vy] = newTiles; + + break; + } + } + + var oldList = List; + + for (var i = 0; i < oldList.Length; ++i) + { + var tile = oldList[i]; + + if (tile.ItemId == itemID && tile.OffsetX == (short)x && tile.OffsetY == (short)y && + tile.OffsetZ == (short)z) + { + var newList = new MultiTileEntry[oldList.Length - 1]; + + for (var j = 0; j < i; ++j) + { + newList[j] = oldList[j]; + } + + for (var j = i + 1; j < oldList.Length; ++j) + { + newList[j - 1] = oldList[j]; + } + + List = newList; + + break; + } + } + } + } + + public void Resize(int newWidth, int newHeight) + { + int oldWidth = Width, oldHeight = Height; + var oldTiles = Tiles; + + var totalLength = 0; + + var newTiles = new StaticTile[newWidth][][]; + + for (var x = 0; x < newWidth; ++x) + { + newTiles[x] = new StaticTile[newHeight][]; + + for (var y = 0; y < newHeight; ++y) + { + if (x < oldWidth && y < oldHeight) + { + newTiles[x][y] = oldTiles[x][y]; + } + else + { + newTiles[x][y] = Array.Empty(); + } + + totalLength += newTiles[x][y].Length; } } - private static ulong HashLittle2(string s) + Tiles = newTiles; + List = new MultiTileEntry[totalLength]; + Width = newWidth; + Height = newHeight; + + m_Min = Point2D.Zero; + m_Max = Point2D.Zero; + + var index = 0; + + for (var x = 0; x < newWidth; ++x) { - var length = s.Length; - - uint b, c; - var a = b = c = 0xDEADBEEF + (uint)length; - - var k = 0; - - while (length > 12) + for (var y = 0; y < newHeight; ++y) { - a += s[k]; - a += (uint)s[k + 1] << 8; - a += (uint)s[k + 2] << 16; - a += (uint)s[k + 3] << 24; - b += s[k + 4]; - b += (uint)s[k + 5] << 8; - b += (uint)s[k + 6] << 16; - b += (uint)s[k + 7] << 24; - c += s[k + 8]; - c += (uint)s[k + 9] << 8; - c += (uint)s[k + 10] << 16; - c += (uint)s[k + 11] << 24; + var tiles = newTiles[x][y]; - a -= c; - a ^= (c << 4) | (c >> 28); - c += b; - b -= a; - b ^= (a << 6) | (a >> 26); - a += c; - c -= b; - c ^= (b << 8) | (b >> 24); - b += a; - a -= c; - a ^= (c << 16) | (c >> 16); - c += b; - b -= a; - b ^= (a << 19) | (a >> 13); - a += c; - c -= b; - c ^= (b << 4) | (b >> 28); - b += a; - - length -= 12; - k += 12; - } - - if (length != 0) - { - switch (length) + for (var i = 0; i < tiles.Length; ++i) { - case 12: - c += (uint)s[k + 11] << 24; - goto case 11; - case 11: - c += (uint)s[k + 10] << 16; - goto case 10; - case 10: - c += (uint)s[k + 9] << 8; - goto case 9; - case 9: - c += s[k + 8]; - goto case 8; - case 8: - b += (uint)s[k + 7] << 24; - goto case 7; - case 7: - b += (uint)s[k + 6] << 16; - goto case 6; - case 6: - b += (uint)s[k + 5] << 8; - goto case 5; - case 5: - b += s[k + 4]; - goto case 4; - case 4: - a += (uint)s[k + 3] << 24; - goto case 3; - case 3: - a += (uint)s[k + 2] << 16; - goto case 2; - case 2: - a += (uint)s[k + 1] << 8; - goto case 1; - case 1: - a += s[k]; - break; + var tile = tiles[i]; + + var vx = x - Center.X; + var vy = y - 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; + } + + List[index++] = new MultiTileEntry( + (ushort)tile.ID, + (short)vx, + (short)vy, + (short)tile.Z, + TileFlag.Background + ); } - - c ^= b; - c -= (b << 14) | (b >> 18); - a ^= c; - a -= (c << 11) | (c >> 21); - b ^= a; - b -= (a << 25) | (a >> 7); - c ^= b; - c -= (b << 16) | (b >> 16); - a ^= c; - a -= (c << 4) | (c >> 28); - b ^= a; - b -= (a << 14) | (a >> 18); - c ^= b; - c -= (b << 24) | (b >> 8); } + } + } - return ((ulong)b << 32) | c; + public void Serialize(IGenericWriter writer) + { + writer.Write(1); // version; + + writer.Write(m_Min); + writer.Write(m_Max); + writer.Write(Center); + + writer.Write(Width); + writer.Write(Height); + + writer.Write(List.Length); + + for (var i = 0; i < List.Length; ++i) + { + var ent = List[i]; + + writer.Write(ent.ItemId); + writer.Write(ent.OffsetX); + writer.Write(ent.OffsetY); + writer.Write(ent.OffsetZ); + writer.Write((int)ent.Flags); } } } + +public static class UOPHash +{ + public static void BuildChunkIDs(out Dictionary chunkIds) + { + const int maxId = 0x10000; + + chunkIds = new Dictionary(); + + for (var i = 0; i < maxId; ++i) + { + chunkIds[HashLittle2($"build/multicollection/{i:000000}.bin")] = i; + } + } + + public static ulong HashLittle2(ReadOnlySpan s) + { + var length = s.Length; + + uint b, c; + var a = b = c = 0xDEADBEEF + (uint)length; + + var k = 0; + + while (length > 12) + { + a += s[k]; + a += (uint)s[k + 1] << 8; + a += (uint)s[k + 2] << 16; + a += (uint)s[k + 3] << 24; + b += s[k + 4]; + b += (uint)s[k + 5] << 8; + b += (uint)s[k + 6] << 16; + b += (uint)s[k + 7] << 24; + c += s[k + 8]; + c += (uint)s[k + 9] << 8; + c += (uint)s[k + 10] << 16; + c += (uint)s[k + 11] << 24; + + a -= c; + a ^= (c << 4) | (c >> 28); + c += b; + b -= a; + b ^= (a << 6) | (a >> 26); + a += c; + c -= b; + c ^= (b << 8) | (b >> 24); + b += a; + a -= c; + a ^= (c << 16) | (c >> 16); + c += b; + b -= a; + b ^= (a << 19) | (a >> 13); + a += c; + c -= b; + c ^= (b << 4) | (b >> 28); + b += a; + + length -= 12; + k += 12; + } + + if (length != 0) + { + switch (length) + { + case 12: + { + c += (uint)s[k + 11] << 24; + goto case 11; + } + case 11: + { + c += (uint)s[k + 10] << 16; + goto case 10; + } + case 10: + { + c += (uint)s[k + 9] << 8; + goto case 9; + } + case 9: + { + c += s[k + 8]; + goto case 8; + } + case 8: + { + b += (uint)s[k + 7] << 24; + goto case 7; + } + case 7: + { + b += (uint)s[k + 6] << 16; + goto case 6; + } + case 6: + { + b += (uint)s[k + 5] << 8; + goto case 5; + } + case 5: + { + b += s[k + 4]; + goto case 4; + } + case 4: + { + a += (uint)s[k + 3] << 24; + goto case 3; + } + case 3: + { + a += (uint)s[k + 2] << 16; + goto case 2; + } + case 2: + { + a += (uint)s[k + 1] << 8; + goto case 1; + } + case 1: + { + a += s[k]; + break; + } + } + + c ^= b; + c -= (b << 14) | (b >> 18); + a ^= c; + a -= (c << 11) | (c >> 21); + b ^= a; + b -= (a << 25) | (a >> 7); + c ^= b; + c -= (b << 16) | (b >> 16); + a ^= c; + a -= (c << 4) | (c >> 28); + b ^= a; + b -= (a << 14) | (a >> 18); + c ^= b; + c -= (b << 24) | (b >> 8); + } + + return ((ulong)b << 32) | c; + } +} diff --git a/Projects/Server/NativeReader.cs b/Projects/Server/NativeReader.cs deleted file mode 100644 index 0b6dc1d41..000000000 --- a/Projects/Server/NativeReader.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Server -{ - public static class NativeReader - { - private static readonly INativeReader m_NativeReader; - - static NativeReader() => m_NativeReader = Core.Unix ? new NativeReaderUnix() : new NativeReaderWin32(); - - public static unsafe int Read(FileStream source, void* buffer, int length) => Read(source, buffer, 0, length); - - public static unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length) => - m_NativeReader.Read(source, buffer, bufferIndex, length); - } - - public interface INativeReader - { - unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length); - } - - public sealed class NativeReaderWin32 : INativeReader - { - internal class UnsafeNativeMethods - { - [DllImport("kernel32")] - internal static extern unsafe bool ReadFile(IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped); - } - - public unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length) => InternalRead(source, buffer, bufferIndex, length); - - internal static unsafe int InternalRead(FileStream source, void* buffer, int bufferIndex, int length) - { - var byteCount = 0U; - - if (UnsafeNativeMethods.ReadFile(source.SafeFileHandle!.DangerousGetHandle(), (byte*)buffer + bufferIndex, (uint)length, ref byteCount, null)) - { - return (int)byteCount; - } - - return -1; - } - } - - public sealed class NativeReaderUnix : INativeReader - { - internal class UnsafeNativeMethods - { - [DllImport("libc")] - internal static extern unsafe int read(IntPtr ptr, void* buffer, int length); - } - - public unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length) => - InternalRead(source, buffer, bufferIndex, length); - - internal unsafe int InternalRead(FileStream source, void* buffer, int bufferIndex, int length) => - UnsafeNativeMethods.read(source.SafeFileHandle!.DangerousGetHandle(), (byte*)buffer + bufferIndex, length); - } -} diff --git a/Projects/Server/Serialization/Attributes/SerializableAttribute.cs b/Projects/Server/Network/ContainerGridPacketHandler.cs old mode 100755 new mode 100644 similarity index 64% rename from Projects/Server/Serialization/Attributes/SerializableAttribute.cs rename to Projects/Server/Network/ContainerGridPacketHandler.cs index 052d81d11..0907a3d43 --- a/Projects/Server/Serialization/Attributes/SerializableAttribute.cs +++ b/Projects/Server/Network/ContainerGridPacketHandler.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: SerializableEntityAttribute.cs * + * File: ContainerGridPacketHandler.cs * * * * 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 * @@ -13,20 +13,15 @@ * along with this program. If not, see . * *************************************************************************/ -using System; +namespace Server.Network; -namespace Server +public unsafe class ContainerGridPacketHandler : PacketHandler { - [AttributeUsage(AttributeTargets.Class)] - public sealed class SerializableAttribute : Attribute + public ContainerGridPacketHandler(int packetID, int length, bool ingame, + delegate* onReceive) + : base(packetID, length, ingame, onReceive) { - public int Version { get; } - public bool EncodedVersion { get; } - - public SerializableAttribute(int version, bool encodedVersion = true) - { - Version = version; - EncodedVersion = encodedVersion; - } } + + public override int GetLength(NetState ns) => base.GetLength(ns) + (ns.ContainerGridLines ? 1 : 0); } diff --git a/Projects/Server/Network/EncodedPacketHandler.cs b/Projects/Server/Network/EncodedPacketHandler.cs index 3b417f9f4..33499beaf 100644 --- a/Projects/Server/Network/EncodedPacketHandler.cs +++ b/Projects/Server/Network/EncodedPacketHandler.cs @@ -1,20 +1,32 @@ -namespace Server.Network +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: EncodedPacketHandler.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network; + +public unsafe class EncodedPacketHandler { - public delegate void OnEncodedPacketReceive(NetState state, IEntity ent, EncodedReader reader); - - public class EncodedPacketHandler + public EncodedPacketHandler(int packetID, bool ingame, delegate* onReceive) { - public EncodedPacketHandler(int packetID, bool ingame, OnEncodedPacketReceive onReceive) - { - PacketID = packetID; - Ingame = ingame; - OnReceive = onReceive; - } - - public int PacketID { get; } - - public OnEncodedPacketReceive OnReceive { get; } - - public bool Ingame { get; } + PacketID = packetID; + Ingame = ingame; + OnReceive = onReceive; } + + public int PacketID { get; } + + public delegate* OnReceive { get; } + + public bool Ingame { get; } } diff --git a/Projects/Server/Network/EncodedReader.cs b/Projects/Server/Network/EncodedReader.cs index 9c2ecf5cb..c7b6ce7d0 100644 --- a/Projects/Server/Network/EncodedReader.cs +++ b/Projects/Server/Network/EncodedReader.cs @@ -1,26 +1,37 @@ -namespace Server.Network +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: EncodedReader.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network; + +public ref struct EncodedReader { - public ref struct EncodedReader - { - private CircularBufferReader m_Reader; + private CircularBufferReader _reader; - public EncodedReader(CircularBufferReader reader) => m_Reader = reader; + public EncodedReader(CircularBufferReader reader) => _reader = reader; - public void Trace(NetState state) - { - m_Reader.Trace(state); - } + public void Trace(NetState state) => _reader.Trace(state); - public int ReadInt32() => m_Reader.ReadByte() != 0 ? 0 : m_Reader.ReadInt32(); + public int ReadInt32() => _reader.ReadByte() != 0 ? 0 : _reader.ReadInt32(); - public Point3D ReadPoint3D() => m_Reader.ReadByte() != 3 - ? Point3D.Zero - : new Point3D(m_Reader.ReadInt16(), m_Reader.ReadInt16(), m_Reader.ReadByte()); + public Point3D ReadPoint3D() => _reader.ReadByte() != 3 + ? Point3D.Zero + : new Point3D(_reader.ReadInt16(), _reader.ReadInt16(), _reader.ReadByte()); - public string ReadUnicodeStringSafe() => - m_Reader.ReadByte() != 2 ? string.Empty : m_Reader.ReadBigUniSafe(m_Reader.ReadUInt16()); + public string ReadUnicodeStringSafe() => + _reader.ReadByte() != 2 ? string.Empty : _reader.ReadBigUniSafe(_reader.ReadUInt16()); - public string ReadUnicodeString() => - m_Reader.ReadByte() != 2 ? string.Empty : m_Reader.ReadBigUni(m_Reader.ReadUInt16()); - } + public string ReadUnicodeString() => + _reader.ReadByte() != 2 ? string.Empty : _reader.ReadBigUni(_reader.ReadUInt16()); } diff --git a/Projects/Server/Network/ISocket.cs b/Projects/Server/Network/ISocket.cs deleted file mode 100644 index 75d3d8475..000000000 --- a/Projects/Server/Network/ISocket.cs +++ /dev/null @@ -1,44 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ISocket.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace Server.Network -{ - public interface ISocket - { - public IntPtr Handle { get; } - - public EndPoint LocalEndPoint { get; } - - public EndPoint RemoteEndPoint { get; } - - public Task SendAsync(IList> buffer, SocketFlags flags); - - public int Send(IList> buffer, SocketFlags flags); - - public Task ReceiveAsync(IList> buffer, SocketFlags flags); - - public int Receive(IList> buffers, SocketFlags flags); - - public void Shutdown(SocketShutdown how); - - public void Close(); - } -} diff --git a/Projects/Server/Network/NetState/NetState.ClientVersion.cs b/Projects/Server/Network/NetState/NetState.ClientVersion.cs index 4302ec0b1..844c9fb21 100644 --- a/Projects/Server/Network/NetState/NetState.ClientVersion.cs +++ b/Projects/Server/Network/NetState/NetState.ClientVersion.cs @@ -19,23 +19,6 @@ namespace Server.Network { public partial class NetState { - private static readonly ClientVersion m_Version400a = new("4.0.0a"); - private static readonly ClientVersion m_Version407a = new("4.0.7a"); - private static readonly ClientVersion m_Version500a = new("5.0.0a"); - private static readonly ClientVersion m_Version502b = new("5.0.2b"); - private static readonly ClientVersion m_Version6000 = new("6.0.0.0"); - private static readonly ClientVersion m_Version6017 = new("6.0.1.7"); - private static readonly ClientVersion m_Version60142 = new("6.0.14.2"); - private static readonly ClientVersion m_Version7000 = new("7.0.0.0"); - private static readonly ClientVersion m_Version7090 = new("7.0.9.0"); - private static readonly ClientVersion m_Version70130 = new("7.0.13.0"); - private static readonly ClientVersion m_Version70160 = new("7.0.16.0"); - private static readonly ClientVersion m_Version70300 = new("7.0.30.0"); - private static readonly ClientVersion m_Version70331 = new("7.0.33.1"); - private static readonly ClientVersion m_Version704565 = new("7.0.45.65"); - private static readonly ClientVersion m_Version70500 = new("7.0.50.0"); - private static readonly ClientVersion m_Version70610 = new("7.0.61.0"); - public ProtocolChanges ProtocolChanges { get; set; } public ClientFlags Flags { get; set; } @@ -52,23 +35,23 @@ namespace Server.Network public static ProtocolChanges ProtocolChangesByVersion(ClientVersion version) => version switch { - var v when v >= m_Version70610 => ProtocolChanges.Version70610, - var v when v >= m_Version70500 => ProtocolChanges.Version70500, - var v when v >= m_Version704565 => ProtocolChanges.Version704565, - var v when v >= m_Version70331 => ProtocolChanges.Version70331, - var v when v >= m_Version70300 => ProtocolChanges.Version70300, - var v when v >= m_Version70160 => ProtocolChanges.Version70160, - var v when v >= m_Version70130 => ProtocolChanges.Version70130, - var v when v >= m_Version7090 => ProtocolChanges.Version7090, - var v when v >= m_Version7000 => ProtocolChanges.Version7000, - var v when v >= m_Version60142 => ProtocolChanges.Version60142, - var v when v >= m_Version6017 => ProtocolChanges.Version6017, - var v when v >= m_Version6000 => ProtocolChanges.Version6000, - var v when v >= m_Version502b => ProtocolChanges.Version502b, - var v when v >= m_Version500a => ProtocolChanges.Version500a, - var v when v >= m_Version407a => ProtocolChanges.Version407a, - var v when v >= m_Version400a => ProtocolChanges.Version400a, - _ => ProtocolChanges.None + var v when v >= ClientVersion.Version70610 => ProtocolChanges.Version70610, + var v when v >= ClientVersion.Version70500 => ProtocolChanges.Version70500, + var v when v >= ClientVersion.Version704565 => ProtocolChanges.Version704565, + var v when v >= ClientVersion.Version70331 => ProtocolChanges.Version70331, + var v when v >= ClientVersion.Version70300 => ProtocolChanges.Version70300, + var v when v >= ClientVersion.Version70160 => ProtocolChanges.Version70160, + var v when v >= ClientVersion.Version70130 => ProtocolChanges.Version70130, + var v when v >= ClientVersion.Version7090 => ProtocolChanges.Version7090, + var v when v >= ClientVersion.Version7000 => ProtocolChanges.Version7000, + var v when v >= ClientVersion.Version60142 => ProtocolChanges.Version60142, + var v when v >= ClientVersion.Version6017 => ProtocolChanges.Version6017, + var v when v >= ClientVersion.Version6000 => ProtocolChanges.Version6000, + var v when v >= ClientVersion.Version502b => ProtocolChanges.Version502b, + var v when v >= ClientVersion.Version500a => ProtocolChanges.Version500a, + var v when v >= ClientVersion.Version407a => ProtocolChanges.Version407a, + var v when v >= ClientVersion.Version400a => ProtocolChanges.Version400a, + _ => ProtocolChanges.None }; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Network/NetState/NetState.Fastwalk.cs b/Projects/Server/Network/NetState/NetState.Fastwalk.cs index eb6934aaf..3b1036254 100644 --- a/Projects/Server/Network/NetState/NetState.Fastwalk.cs +++ b/Projects/Server/Network/NetState/NetState.Fastwalk.cs @@ -13,80 +13,109 @@ * along with this program. If not, see . * *************************************************************************/ +using System.Runtime.CompilerServices; using CalcMoves = Server.Movement.Movement; -namespace Server.Network +namespace Server.Network; + +public partial class NetState { - public partial class NetState + // The next step + private int _stepIndex; + // The last index to expire + private int _expiredIndex; + + private long[] _steps; + + public bool AddStep(Direction d) { - private int _stepIndex; - private int _stepCount; - private long _startDelay; - private long[] _stepDelays; - - public bool AddStep(Direction d) + if (Mobile == null) { - if (Mobile == null) - { - return false; - } - - var maxSteps = CalcMoves.MaxSteps; - - _stepDelays ??= new long[maxSteps]; - var length = _stepDelays.Length; - - var index = _stepIndex - _stepCount; - if (index < 0) - { - index += length; - } - - var now = Core.TickCount; - var last = _startDelay; - - // Discard old steps by decrementing the step counter - while (index != _stepIndex || _stepCount >= maxSteps) - { - var step = _stepDelays[index++]; - if (now - last < step) - { - break; - } - - last += step; - _stepCount--; - if (index >= length) - { - index = 0; - } - } - - _startDelay = last; - - // If we are out of steps, fail - if (_stepCount >= maxSteps) - { - return false; - } - - var delay = Mobile.ComputeMovementSpeed(d); - - // Add the delay - _stepDelays[_stepIndex++] = delay; - - if (_stepIndex >= length) - { - _stepIndex = 0; - } - - if (_stepCount == 0) - { - _startDelay = now; - } - _stepCount++; - - return true; + return false; } + + _steps ??= new long[CalcMoves.MaxSteps + 1]; // Extra index as a sentinel + var stepsLength = _steps.Length; + + var now = Core.TickCount; + + var lastIndex = -1; + + // Expire old steps + while (_expiredIndex != _stepIndex) + { + var step = _steps[_expiredIndex]; + + // Is the step ahead of us, or the next step rolled over and we didn't yet + if (step > now || lastIndex > -1 && _steps[lastIndex] > step) + { + break; + } + + lastIndex = _expiredIndex++; + + if (_expiredIndex == stepsLength) + { + _expiredIndex -= stepsLength; + } + } + + var stepsTaken = (_stepIndex < _expiredIndex ? _stepIndex + stepsLength : _stepIndex) - _expiredIndex; + var maxSteps = _steps.Length - 1; + + // Can we take a step? + if (stepsTaken >= maxSteps) + { + return false; + } + + var delay = Mobile.ComputeMovementSpeed(d); + + var prev = _stepIndex - 1; + if (prev < 0) + { + prev += stepsLength; + } + + // Give a 5% buffer on the first step + _steps[_stepIndex++] = stepsTaken > 0 ? _steps[prev] + delay : now + delay * 950 / 1000; + + if (_stepIndex == stepsLength) + { + _stepIndex -= stepsLength; + } + + // If CalcMoves.MaxSteps is modified, we need to adjust accordingly + AdjustSteps(CalcMoves.MaxSteps); + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AdjustSteps(int maxSteps) + { + var stepsLength = maxSteps + 1; + + if (_steps.Length == stepsLength) + { + return; + } + + var oldSteps = _steps; + _steps = new long[stepsLength]; + + var expiredIndex = _expiredIndex; + var newStepIndex = 0; + while (newStepIndex < maxSteps && expiredIndex != _stepIndex) + { + _steps[newStepIndex++] = oldSteps[expiredIndex++]; + if (expiredIndex >= oldSteps.Length) + { + expiredIndex -= oldSteps.Length; + } + } + + _expiredIndex = 0; + _stepIndex = newStepIndex; } } diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index b1916bf7b..0282c0504 100755 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -15,7 +15,6 @@ using System; using System.Buffers; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Net; @@ -31,438 +30,424 @@ using Server.Items; using Server.Logging; using Server.Menus; -namespace Server.Network +namespace Server.Network; + +public delegate void NetStateCreatedCallback(NetState ns); + +public delegate void DecodePacket(CircularBuffer buffer, ref int length); +public delegate void EncodePacket(ReadOnlySpan inputBuffer, CircularBuffer outputBuffer, out int length); + +public partial class NetState : IComparable { - public delegate void NetStateCreatedCallback(NetState ns); + private static readonly ILogger logger = LogFactory.GetLogger(typeof(NetState)); - public delegate void DecodePacket(CircularBuffer buffer, ref int length); - public delegate void EncodePacket(ReadOnlySpan inputBuffer, CircularBuffer outputBuffer, out int length); + private const int RecvPipeSize = 1024 * 64; + private const int SendPipeSize = 1024 * 256; + private const int GumpCap = 512; + private const int HuePickerCap = 512; + private const int MenuCap = 512; + private const int PacketPerSecondThreshold = 3000; - public partial class NetState : IComparable + private static GCHandle[] _polledStates = new GCHandle[2048]; + private static readonly IPollGroup _pollGroup = PollGroup.Create(); + private static readonly Queue _flushPending = new(2048); + private static readonly Queue _flushedPartials = new(256); + private static readonly Queue _disposed = new(256); + + public static NetStateCreatedCallback CreatedCallback { get; set; } + + private readonly string _toString; + private ClientVersion _version; + private long _nextActivityCheck; + private bool _running = true; + private volatile DecodePacket _packetDecoder; + private volatile EncodePacket _packetEncoder; + private bool _flushQueued; + private readonly long[] _packetThrottles = new long[0x100]; + private readonly long[] _packetCounts = new long[0x100]; + private string _disconnectReason = string.Empty; + + internal int _authId; + internal int _seed; + internal ParserState _parserState = ParserState.AwaitingNextPacket; + internal ProtocolState _protocolState = ProtocolState.AwaitingSeed; + internal GCHandle _handle; + private bool _packetLogging; + + public GCHandle Handle => _handle; + + internal enum ParserState { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(NetState)); + AwaitingNextPacket, + AwaitingPartialPacket, + ProcessingPacket, + Throttled, + Error + } - private const int RecvPipeSize = 1024 * 64; - private const int SendPipeSize = 1024 * 256; - private const int GumpCap = 512; - private const int HuePickerCap = 512; - private const int MenuCap = 512; - private const int PacketPerSecondThreshold = 3000; + internal enum ProtocolState + { + AwaitingSeed, // Based on the way the seed arrives, we know if this is a login server or a game server connection - private static GCHandle[] _polledStates = new GCHandle[2048]; - private static readonly IPollGroup _pollGroup = PollGroup.Create(); - private static readonly Queue FlushPending = new(2048); - private static readonly Queue FlushedPartials = new(2048); - private static readonly ConcurrentQueue Disposed = new(); + LoginServer_AwaitingLogin, + LoginServer_AwaitingServerSelect, + LoginServer_ServerSelectAck, - public static NetStateCreatedCallback CreatedCallback { get; set; } + GameServer_AwaitingGameServerLogin, + GameServer_LoggedIn, - private readonly string _toString; - private ClientVersion _version; - private long _nextActivityCheck; - private bool _running = true; - private volatile DecodePacket _packetDecoder; - private volatile EncodePacket _packetEncoder; - private bool _flushQueued; - private readonly long[] _packetThrottles = new long[0x100]; - private readonly long[] _packetCounts = new long[0x100]; - private string _disconnectReason = string.Empty; + Error + } - internal int _authId; - internal int _seed; - internal ParserState _parserState = ParserState.AwaitingNextPacket; - internal ProtocolState _protocolState = ProtocolState.AwaitingSeed; - internal GCHandle _handle; - private bool _packetLogging; + private static string _packetLoggingPath; - internal enum ParserState + public static void Configure() + { + _packetLoggingPath = ServerConfiguration.GetSetting("netstate.packetLoggingPath", Path.Combine(Core.BaseDirectory, "Packets")); + } + + public static void Initialize() + { + Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); + } + + public NetState(Socket connection) + { + Connection = connection; + Seeded = false; + Gumps = new List(); + HuePickers = new List(); + Menus = new List(); + Trades = new List(); + RecvPipe = new Pipe(GC.AllocateUninitializedArray(RecvPipeSize)); + SendPipe = new Pipe(GC.AllocateUninitializedArray(SendPipeSize)); + _nextActivityCheck = Core.TickCount + 30000; + ConnectedOn = Core.Now; + + try { - AwaitingNextPacket, - AwaitingPartialPacket, - ProcessingPacket, - Throttled, - Error + Address = Utility.Intern((Connection?.RemoteEndPoint as IPEndPoint)?.Address); + _toString = Address?.ToString() ?? "(error)"; + } + catch (Exception ex) + { + TraceException(ex); + Address = IPAddress.None; + _toString = "(error)"; } - internal enum ProtocolState + _handle = GCHandle.Alloc(this); + + try { - AwaitingSeed, // Based on the way the seed arrives, we know if this is a login server or a game server connection - - LoginServer_AwaitingLogin, - LoginServer_AwaitingServerSelect, - LoginServer_ServerSelectAck, - - GameServer_AwaitingGameServerLogin, - GameServer_LoggedIn, - - Error + _pollGroup.Add(connection, _handle); + } + catch (Exception ex) + { + TraceException(ex); + Disconnect("Unable to add socket to poll group"); } - private static string _packetLoggingPath; + CreatedCallback?.Invoke(this); + } - public static void Configure() + // Only use this for debugging. This will make your server very slow! + public bool PacketLogging + { + get => _packetLogging; + set { - _packetLoggingPath = ServerConfiguration.GetSetting("netstate.packetLoggingPath", Path.Combine(Core.BaseDirectory, "Packets")); - } + _packetLogging = value; - public static void Initialize() - { - Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); - } - - public NetState(Socket connection) - { - Connection = connection; - Seeded = false; - Gumps = new List(); - HuePickers = new List(); - Menus = new List(); - Trades = new List(); - RecvPipe = new Pipe(GC.AllocateUninitializedArray(RecvPipeSize)); - SendPipe = new Pipe(GC.AllocateUninitializedArray(SendPipeSize)); - _nextActivityCheck = Core.TickCount + 30000; - ConnectedOn = Core.Now; - - try + if (_packetLogging) { - Address = Utility.Intern((Connection?.RemoteEndPoint as IPEndPoint)?.Address); - _toString = Address?.ToString() ?? "(error)"; - } - catch (Exception ex) - { - TraceException(ex); - Address = IPAddress.None; - _toString = "(error)"; - } - - _handle = GCHandle.Alloc(this); - - try - { - _pollGroup.Add(connection, _handle); - } - catch (Exception ex) - { - TraceException(ex); - Disconnect("Unable to add socket to poll group"); - } - - CreatedCallback?.Invoke(this); - } - - // Only use this for debugging. This will make your server very slow! - public bool PacketLogging - { - get => _packetLogging; - set - { - _packetLogging = value; - - if (_packetLogging) - { - StartPacketLog(); - } + StartPacketLog(); } } + } - public DateTime ConnectedOn { get; } + public DateTime ConnectedOn { get; } - public TimeSpan ConnectedFor => Core.Now - ConnectedOn; + public TimeSpan ConnectedFor => Core.Now - ConnectedOn; - public IPAddress Address { get; } + public IPAddress Address { get; } - public DecodePacket PacketDecoder + public DecodePacket PacketDecoder + { + get => _packetDecoder; + set => _packetDecoder = value; + } + + public EncodePacket PacketEncoder + { + get => _packetEncoder; + set => _packetEncoder = value; + } + + public int CurrentPacket { get; internal set; } + + public bool SentFirstPacket { get; set; } + + public bool BlockAllPackets { get; set; } + + public List Trades { get; } + + public bool Seeded { get; set; } + + public Pipe RecvPipe { get; } + + public Pipe SendPipe { get; } + + public bool Running => _running; + + public Socket Connection { get; private set; } + + public bool CompressionEnabled { get; set; } + + public int Sequence { get; set; } + + public List Gumps { get; private set; } + + public List HuePickers { get; private set; } + + public List Menus { get; private set; } + + public CityInfo[] CityInfo { get; set; } + + public Mobile Mobile { get; set; } + + public ServerInfo[] ServerInfo { get; set; } + + public IAccount Account { get; set; } + + public string Assistant { get; set; } + + public int CompareTo(NetState other) => string.CompareOrdinal(_toString, other?._toString); + + private void SetPacketTime(int packetID) + { + if (packetID is >= 0 and < 0x100) { - get => _packetDecoder; - set => _packetDecoder = value; - } - - public EncodePacket PacketEncoder - { - get => _packetEncoder; - set => _packetEncoder = value; - } - - public int CurrentPacket { get; internal set; } - - public bool SentFirstPacket { get; set; } - - public bool BlockAllPackets { get; set; } - - public List Trades { get; } - - public bool Seeded { get; set; } - - public Pipe RecvPipe { get; } - - public Pipe SendPipe { get; } - - public bool Running => _running; - - public Socket Connection { get; private set; } - - public bool CompressionEnabled { get; set; } - - public int Sequence { get; set; } - - public List Gumps { get; private set; } - - public List HuePickers { get; private set; } - - public List Menus { get; private set; } - - public CityInfo[] CityInfo { get; set; } - - public Mobile Mobile { get; set; } - - public ServerInfo[] ServerInfo { get; set; } - - public IAccount Account { get; set; } - - public int CompareTo(NetState other) => string.CompareOrdinal(_toString, other?._toString); - - private void SetPacketTime(int packetID) - { - if (packetID is < 0 or >= 0x100) - { - return; - } - _packetThrottles[packetID] = Core.TickCount; } + } - public long GetPacketDelay(int packetID) + public long GetPacketTime(int packetID) => packetID is >= 0 and < 0x100 ? _packetThrottles[packetID] : 0; + + private void UpdatePacketCount(int packetID) + { + if (packetID is >= 0 and < 0x100) { - if (packetID is < 0 or >= 0x100) - { - return 0; - } - - return _packetThrottles[packetID]; - } - - private void UpdatePacketCount(int packetID) - { - if (packetID is < 0 or >= 0x100) - { - return; - } - _packetCounts[packetID]++; } + } - public int CheckPacketCounts() + public int CheckPacketCounts() + { + for (int i = 0; i < _packetCounts.Length; i++) { - for (int i = 0; i < _packetCounts.Length; i++) + long count = _packetCounts[i]; + _packetCounts[i] = 0; + + if (count > PacketPerSecondThreshold) { - long count = _packetCounts[i]; - _packetCounts[i] = 0; - - if (count > PacketPerSecondThreshold) - { - return i; - } - } - - return 0; - } - - public void ValidateAllTrades() - { - for (var i = Trades.Count - 1; i >= 0; --i) - { - if (i >= Trades.Count) - { - continue; - } - - var trade = 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(); - } + return i; } } - public void CancelAllTrades() + return 0; + } + + public void ValidateAllTrades() + { + for (var i = Trades.Count - 1; i >= 0; --i) { - for (var i = Trades.Count - 1; i >= 0; --i) + if (i >= Trades.Count) { - if (i < Trades.Count) - { - Trades[i].Cancel(); - } + continue; + } + + var trade = 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 (var i = Trades.Count - 1; i >= 0; --i) + { + if (i < Trades.Count) + { + Trades[i].Cancel(); + } + } + } + + public void RemoveTrade(SecureTrade trade) + { + Trades.Remove(trade); + } + + public SecureTrade FindTrade(Mobile m) + { + for (var i = 0; i < Trades.Count; ++i) + { + var trade = Trades[i]; + + if (trade.From.Mobile == m || trade.To.Mobile == m) + { + return trade; } } - public void RemoveTrade(SecureTrade trade) - { - Trades.Remove(trade); - } + return null; + } - public SecureTrade FindTrade(Mobile m) + public SecureTradeContainer FindTradeContainer(Mobile m) + { + for (var i = 0; i < Trades.Count; ++i) { - for (var i = 0; i < Trades.Count; ++i) + var trade = Trades[i]; + + var from = trade.From; + var to = trade.To; + + if (from.Mobile == Mobile && to.Mobile == m) { - var trade = Trades[i]; - - if (trade.From.Mobile == m || trade.To.Mobile == m) - { - return trade; - } + return from.Container; } - return null; - } - - public SecureTradeContainer FindTradeContainer(Mobile m) - { - for (var i = 0; i < Trades.Count; ++i) + if (from.Mobile == m && to.Mobile == Mobile) { - var trade = Trades[i]; - - var from = trade.From; - var to = trade.To; - - if (from.Mobile == Mobile && to.Mobile == m) - { - return from.Container; - } - - if (from.Mobile == m && to.Mobile == Mobile) - { - return to.Container; - } - } - - return null; - } - - public SecureTradeContainer AddTrade(NetState state) - { - var newTrade = new SecureTrade(Mobile, state.Mobile); - - Trades.Add(newTrade); - state.Trades.Add(newTrade); - - return newTrade.From.Container; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void LogInfo(string text) - { - logger.Information("Client: {0}: {1}", this, text); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void LogInfo(string format, params object[] args) - { - LogInfo(string.Format(format, args)); - } - - public void AddMenu(IMenu menu) - { - Menus ??= new List(); - - if (Menus.Count < MenuCap) - { - Menus.Add(menu); - } - else - { - LogInfo("Exceeded menu cap, disconnecting..."); - Disconnect("Exceeded menu cap."); + return to.Container; } } - public void RemoveMenu(IMenu menu) + return null; + } + + public SecureTradeContainer AddTrade(NetState state) + { + var newTrade = new SecureTrade(Mobile, state.Mobile); + + Trades.Add(newTrade); + state.Trades.Add(newTrade); + + return newTrade.From.Container; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void LogInfo(string text) + { + logger.Information("Client: {NetState}: {Message}", this, text); + } + + public void AddMenu(IMenu menu) + { + Menus ??= new List(); + + if (Menus.Count < MenuCap) { - Menus?.Remove(menu); + Menus.Add(menu); } - - public void RemoveMenu(int index) + else { - Menus?.RemoveAt(index); + LogInfo("Exceeded menu cap, disconnecting..."); + Disconnect("Exceeded menu cap."); } + } - public void ClearMenus() + public void RemoveMenu(IMenu menu) + { + Menus?.Remove(menu); + } + + public void RemoveMenu(int index) + { + Menus?.RemoveAt(index); + } + + public void ClearMenus() + { + Menus?.Clear(); + } + + public void AddHuePicker(HuePicker huePicker) + { + HuePickers ??= new List(); + + if (HuePickers.Count < HuePickerCap) { - Menus?.Clear(); + HuePickers.Add(huePicker); } - - public void AddHuePicker(HuePicker huePicker) + else { - HuePickers ??= new List(); - - if (HuePickers.Count < HuePickerCap) - { - HuePickers.Add(huePicker); - } - else - { - LogInfo("Exceeded hue picker cap, disconnecting..."); - Disconnect("Exceeded hue picker cap."); - } + LogInfo("Exceeded hue picker cap, disconnecting..."); + Disconnect("Exceeded hue picker cap."); } + } - public void RemoveHuePicker(HuePicker huePicker) + public void RemoveHuePicker(HuePicker huePicker) + { + HuePickers?.Remove(huePicker); + } + + public void RemoveHuePicker(int index) + { + HuePickers?.RemoveAt(index); + } + + public void ClearHuePickers() + { + HuePickers?.Clear(); + } + + public void AddGump(Gump gump) + { + Gumps ??= new List(); + + if (Gumps.Count < GumpCap) { - HuePickers?.Remove(huePicker); + Gumps.Add(gump); } - - public void RemoveHuePicker(int index) + else { - HuePickers?.RemoveAt(index); + LogInfo("Exceeded gump cap, disconnecting..."); + Disconnect("Exceeded gump cap."); } + } - public void ClearHuePickers() - { - HuePickers?.Clear(); - } + public void RemoveGump(Gump gump) + { + Gumps?.Remove(gump); + } - public void AddGump(Gump gump) - { - Gumps ??= new List(); + public void RemoveGump(int index) + { + Gumps?.RemoveAt(index); + } - if (Gumps.Count < GumpCap) - { - Gumps.Add(gump); - } - else - { - LogInfo("Exceeded gump cap, disconnecting..."); - Disconnect("Exceeded gump cap."); - } - } + public void ClearGumps() + { + Gumps?.Clear(); + } - public void RemoveGump(Gump gump) - { - Gumps?.Remove(gump); - } + public void LaunchBrowser(string url) + { + this.SendMessageLocalized(Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231); + this.SendLaunchBrowser(url); + } - public void RemoveGump(int index) - { - Gumps?.RemoveAt(index); - } + public override string ToString() => _toString; - public void ClearGumps() - { - Gumps?.Clear(); - } - - public void LaunchBrowser(string url) - { - this.SendMessageLocalized(Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231); - this.SendLaunchBrowser(url); - } - - public override string ToString() => _toString; - - public bool GetSendBuffer(out CircularBuffer cBuffer) - { + public bool GetSendBuffer(out CircularBuffer cBuffer) + { #if THREADGUARD if (Thread.CurrentThread != Core.Thread) { @@ -473,630 +458,627 @@ namespace Server.Network return; } #endif - var result = SendPipe.Writer.TryGetMemory(); - cBuffer = new CircularBuffer(result.Buffer); + var result = SendPipe.Writer.TryGetMemory(); + cBuffer = new CircularBuffer(result.Buffer); - return !(result.IsClosed || result.Length <= 0); + return !(result.IsClosed || result.Length <= 0); + } + + public void Send(ReadOnlySpan span) + { + if (span == null || this.CannotSendPackets()) + { + return; } - public void Send(ReadOnlySpan span) + var length = span.Length; + if (length <= 0 || !GetSendBuffer(out var buffer)) { - if (span == null || Connection == null || BlockAllPackets) + return; + } + + try + { + PacketSendProfile prof = null; + + if (Core.Profiling) { - return; + prof = PacketSendProfile.Acquire(span[0]); + prof.Start(); } - var length = span.Length; - if (length <= 0 || !GetSendBuffer(out var buffer)) + if (_packetEncoder != null) { - return; + _packetEncoder(span, buffer, out length); + } + else + { + buffer.CopyFrom(span); } - try + if (PacketLogging) { - PacketSendProfile prof = null; + LogPacket(span, ReadOnlySpan.Empty, span.Length, false); + } - if (Core.Profiling) + SendPipe.Writer.Advance((uint)length); + + if (!_flushQueued) + { + _flushPending.Enqueue(this); + _flushQueued = true; + } + + prof?.Finish(); + } + catch (Exception ex) + { + TraceException(ex); + Disconnect("Exception while sending."); + } + } + + private void StartPacketLog() + { + try + { + var logDir = Path.Combine(_packetLoggingPath, _toString); + PathUtility.EnsureDirectory(logDir); + var logPath = Path.Combine(logDir, "packets.log"); + using var op = new StreamWriter(logPath, true); + + op.WriteLine(">>>>>>>>>> Logging started {0:yyyy/MM/dd HH:mm::ss} <<<<<<<<<<", Core.Now); + op.WriteLine(); + op.WriteLine(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + private void LogPacket(ReadOnlySpan first, ReadOnlySpan second, int totalLength, bool incoming) + { + try + { + var logDir = Path.Combine(_packetLoggingPath, _toString); + PathUtility.EnsureDirectory(logDir); + var logPath = Path.Combine(logDir, "packets.log"); + + const string incomingStr = "Client -> Server"; + const string outgoingStr = "Server -> Client"; + + using var sw = new StreamWriter(logPath, true); + sw.WriteLine($"{Core.Now:HH:mm:ss.ffff}: {(incoming ? incomingStr : outgoingStr)} 0x{first[0]:X2} (Length: {totalLength})"); + sw.FormatBuffer(first, second, totalLength); + sw.WriteLine(); + sw.WriteLine(); + } + catch + { + // ignored + } + } + + public void HandleReceive() + { + if (!_running) + { + return; + } + + ReceiveData(); + + var reader = RecvPipe.Reader; + + try + { + // Process as many packets as we can synchronously + while (_running && _parserState != ParserState.Error && _protocolState != ProtocolState.Error) + { + var result = reader.TryRead(); + var length = result.Length; + + if (length <= 0) { - prof = PacketSendProfile.Acquire(span[0]); - prof.Start(); + break; } - if (_packetEncoder != null) + var packetReader = new CircularBufferReader(result.Buffer); + var packetId = packetReader.ReadByte(); + int packetLength = length; + + // These can arrive at any time and are only informational + if (_protocolState != ProtocolState.AwaitingSeed && IncomingPackets.IsInfoPacket(packetId)) { - _packetEncoder(span, buffer, out length); + _parserState = ParserState.ProcessingPacket; + _parserState = HandlePacket(packetReader, packetId, out packetLength); } else { - buffer.CopyFrom(span); - } - - if (PacketLogging) - { - LogPacket(span, ReadOnlySpan.Empty, span.Length, false); - } - - SendPipe.Writer.Advance((uint)length); - - if (!_flushQueued) - { - FlushPending.Enqueue(this); - _flushQueued = true; - } - - prof?.Finish(); - } - catch (Exception ex) - { -#if DEBUG - Console.WriteLine(ex); -#endif - TraceException(ex); - Disconnect("Exception while sending."); - } - } - - private void StartPacketLog() - { - try - { - var logDir = Path.Combine(_packetLoggingPath, _toString); - PathUtility.EnsureDirectory(logDir); - var logPath = Path.Combine(logDir, "packets.log"); - using var op = new StreamWriter(logPath, true); - - op.WriteLine(">>>>>>>>>> Logging started {0:yyyy/MM/dd HH:mm::ss} <<<<<<<<<<", Core.Now); - op.WriteLine(); - op.WriteLine(); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - private void LogPacket(ReadOnlySpan first, ReadOnlySpan second, int totalLength, bool incoming) - { - try - { - var logDir = Path.Combine(_packetLoggingPath, _toString); - PathUtility.EnsureDirectory(logDir); - var logPath = Path.Combine(logDir, "packets.log"); - - const string incomingStr = "Client -> Server"; - const string outgoingStr = "Server -> Client"; - - using var sw = new StreamWriter(logPath, true); - sw.WriteLine($"{Core.Now:HH:mm:ss.ffff}: {(incoming ? incomingStr : outgoingStr)} 0x{first[0]:X2} (Length: {totalLength})"); - sw.FormatBuffer(first, second, totalLength); - sw.WriteLine(); - sw.WriteLine(); - } - catch - { - // ignored - } - } - - public void HandleReceive() - { - if (!_running) - { - return; - } - - ReceiveData(); - - var reader = RecvPipe.Reader; - - try - { - // Process as many packets as we can synchronously - while (_running && _parserState != ParserState.Error && _protocolState != ProtocolState.Error) - { - var result = reader.TryRead(); - var length = result.Length; - - if (length <= 0) + switch (_protocolState) { - break; - } - - var packetReader = new CircularBufferReader(result.Buffer); - var packetId = packetReader.ReadByte(); - int packetLength = length; - - // These can arrive at any time and are only informational - if (_protocolState != ProtocolState.AwaitingSeed && IncomingPackets.IsInfoPacket(packetId)) - { - _parserState = ParserState.ProcessingPacket; - _parserState = HandlePacket(packetReader, packetId, length, out packetLength); - } - else - { - switch (_protocolState) - { - case ProtocolState.AwaitingSeed: + case ProtocolState.AwaitingSeed: + { + if (packetId == 0xEF) { - if (packetId == 0xEF) + _parserState = ParserState.ProcessingPacket; + _parserState = HandlePacket(packetReader, packetId, out packetLength); + if (_parserState == ParserState.AwaitingNextPacket) { - _parserState = ParserState.ProcessingPacket; - _parserState = HandlePacket(packetReader, packetId, length, out packetLength); - if (_parserState == ParserState.AwaitingNextPacket) - { - _protocolState = ProtocolState.LoginServer_AwaitingLogin; - } + _protocolState = ProtocolState.LoginServer_AwaitingLogin; } - else if (length >= 4) - { - int seed = (packetId << 24) | (packetReader.ReadByte() << 16) | (packetReader.ReadByte() << 8) | packetReader.ReadByte(); - - if (seed == 0) - { - HandleError(0, 0); - return; - } - - _seed = seed; - packetLength = 4; - - _parserState = ParserState.AwaitingNextPacket; - _protocolState = ProtocolState.GameServer_AwaitingGameServerLogin; - } - else - { - _parserState = ParserState.AwaitingPartialPacket; - } - break; } - - case ProtocolState.LoginServer_AwaitingLogin: + else if (length >= 4) { - if (packetId != 0xCF && packetId != 0x80) + int seed = (packetId << 24) | (packetReader.ReadByte() << 16) | (packetReader.ReadByte() << 8) | packetReader.ReadByte(); + + if (seed == 0) { - LogInfo("Possible encrypted client detected, disconnecting..."); - HandleError(packetId, packetLength); + HandleError(0, 0); return; } - _parserState = ParserState.ProcessingPacket; - _parserState = HandlePacket(packetReader, packetId, length, out packetLength); - if (_parserState == ParserState.AwaitingNextPacket) - { - _protocolState = ProtocolState.LoginServer_AwaitingServerSelect; - } - break; - } + _seed = seed; + packetLength = 4; - case ProtocolState.LoginServer_AwaitingServerSelect: - { - if (packetId != 0xA0) - { - HandleError(packetId, packetLength); - return; - } - - _parserState = ParserState.ProcessingPacket; - _parserState = HandlePacket(packetReader, packetId, length, out packetLength); - if (_parserState == ParserState.AwaitingNextPacket) - { - _protocolState = ProtocolState.LoginServer_ServerSelectAck; - Disconnect(string.Empty); - } - break; - } - - case ProtocolState.LoginServer_ServerSelectAck: - { -#if STRICT_UO_PROTOCOL - HandleError(packetId, packetLength); -#else - // Reset the state because CUO/Orion do not reconnect _parserState = ParserState.AwaitingNextPacket; - _protocolState = ProtocolState.AwaitingSeed; -#endif + _protocolState = ProtocolState.GameServer_AwaitingGameServerLogin; + } + else + { + _parserState = ParserState.AwaitingPartialPacket; + } + break; + } + + case ProtocolState.LoginServer_AwaitingLogin: + { + if (packetId != 0xCF && packetId != 0x80) + { + LogInfo("Possible encrypted client detected, disconnecting..."); + HandleError(packetId, packetLength); return; } - case ProtocolState.GameServer_AwaitingGameServerLogin: + _parserState = ParserState.ProcessingPacket; + _parserState = HandlePacket(packetReader, packetId, out packetLength); + if (_parserState == ParserState.AwaitingNextPacket) { - if (packetId != 0x91 && packetId != 0x80) - { - HandleError(packetId, packetLength); - return; - } + _protocolState = ProtocolState.LoginServer_AwaitingServerSelect; + } + break; + } - _parserState = ParserState.ProcessingPacket; - _parserState = HandlePacket(packetReader, packetId, length, out packetLength); - if (_parserState == ParserState.AwaitingNextPacket) - { - _protocolState = ProtocolState.GameServer_LoggedIn; - } - break; + case ProtocolState.LoginServer_AwaitingServerSelect: + { + if (packetId != 0xA0) + { + HandleError(packetId, packetLength); + return; } - case ProtocolState.GameServer_LoggedIn: + _parserState = ParserState.ProcessingPacket; + _parserState = HandlePacket(packetReader, packetId, out packetLength); + if (_parserState == ParserState.AwaitingNextPacket) { - _parserState = ParserState.ProcessingPacket; - _parserState = HandlePacket(packetReader, packetId, length, out packetLength); - break; + _protocolState = ProtocolState.LoginServer_ServerSelectAck; + Disconnect(string.Empty); } - } - } + break; + } - if (_parserState == ParserState.AwaitingNextPacket) - { - reader.Advance((uint)packetLength); - } - else if (_parserState is ParserState.AwaitingPartialPacket or ParserState.Throttled) - { - break; - } - else - { - HandleError(packetId, packetLength); - break; - } - } - - reader.Commit(); - } - catch (Exception ex) - { -#if DEBUG - Console.WriteLine(ex); + case ProtocolState.LoginServer_ServerSelectAck: + { +#if STRICT_UO_PROTOCOL + HandleError(packetId, packetLength); +#else + // Reset the state because CUO/Orion do not reconnect + _parserState = ParserState.AwaitingNextPacket; + _protocolState = ProtocolState.AwaitingSeed; #endif - TraceException(ex); - Disconnect("Exception during HandleReceive"); - } - } + return; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void HandleError(byte packetId, int packetLength) - { - var msg = - $"{this} entered bad state on packet 0x{packetId:X2} with length {packetLength} while in protocol state {_protocolState} and parser state {_parserState}"; - Disconnect(msg); - _parserState = ParserState.Error; - _protocolState = ProtocolState.Error; - } + case ProtocolState.GameServer_AwaitingGameServerLogin: + { + if (packetId != 0x91 && packetId != 0x80) + { + HandleError(packetId, packetLength); + return; + } - /* - * length is the total buffer length. We might be able to use packetReader.Capacity() instead. - * packetLength is the length of the packet that this function actually found. - */ - private ParserState HandlePacket(CircularBufferReader packetReader, byte packetId, int length, out int packetLength) - { - PacketHandler handler = GetHandler(packetId); - if (handler == null) - { - LogInfo($"received unknown packet 0x{packetId:X2} while in state {_protocolState}"); - packetLength = length; - return ParserState.Error; - } + _parserState = ParserState.ProcessingPacket; + _parserState = HandlePacket(packetReader, packetId, out packetLength); + if (_parserState == ParserState.AwaitingNextPacket) + { + _protocolState = ProtocolState.GameServer_LoggedIn; + } + break; + } - packetLength = handler.Length; - if (packetLength <= 0) - { - // Variable length packet. See if we have pulled in the length. - if (length < 3) - { - return ParserState.AwaitingPartialPacket; + case ProtocolState.GameServer_LoggedIn: + { + _parserState = ParserState.ProcessingPacket; + _parserState = HandlePacket(packetReader, packetId, out packetLength); + break; + } + } } - packetLength = packetReader.ReadUInt16(); - if (packetLength < 3) + if (_parserState is ParserState.AwaitingNextPacket or ParserState.Throttled) { - return ParserState.Error; + reader.Advance((uint)packetLength); + } + else if (_parserState is ParserState.AwaitingPartialPacket) + { + break; + } + else if (_parserState is ParserState.Error) + { + HandleError(packetId, packetLength); + break; } } - // Not enough data, let's wait for more to come in - if (length < packetLength) + reader.Commit(); + } + catch (Exception ex) + { +#if DEBUG + Console.WriteLine(ex); +#endif + TraceException(ex); + Disconnect("Exception during HandleReceive"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void HandleError(byte packetId, int packetLength) + { + var msg = + $"{this} entered bad state on packet 0x{packetId:X2} with length {packetLength} while in protocol state {_protocolState} and parser state {_parserState}"; + Disconnect(msg); + _parserState = ParserState.Error; + _protocolState = ProtocolState.Error; + } + + /* + * length is the total buffer length. We might be able to use packetReader.Capacity() instead. + * packetLength is the length of the packet that this function actually found. + */ + private unsafe ParserState HandlePacket(CircularBufferReader packetReader, byte packetId, out int packetLength) + { + PacketHandler handler = IncomingPackets.GetHandler(packetId); + int length = packetReader.Length; + + if (handler == null) + { + LogInfo($"Received unknown packet 0x{packetId:X2} while in state {_protocolState}"); + packetLength = 1; + return ParserState.Error; + } + + packetLength = handler.GetLength(this); + if (packetLength <= 0) + { + // Variable length packet. See if we have pulled in the length. + if (length < 3) { return ParserState.AwaitingPartialPacket; } - if (handler.Ingame) + packetLength = packetReader.ReadUInt16(); + if (packetLength < 3) { - if (Mobile == null) - { - LogInfo($"received packet 0x{packetId:X2} before having been attached to a mobile"); - return ParserState.Error; - } - - if (Mobile.Deleted) - { - return ParserState.Error; - } + return ParserState.Error; } - - ThrottlePacketCallback throttler = handler.ThrottleCallback; - if (throttler != null) - { - if (!throttler(packetId, this, out bool drop)) - { - return drop ? ParserState.AwaitingNextPacket : ParserState.Throttled; - } - - SetPacketTime(packetId); - } - - PacketReceiveProfile prof = null; - - if (Core.Profiling) - { - prof = PacketReceiveProfile.Acquire(packetId); - prof?.Start(); - } - - UpdatePacketCount(packetId); - - if (PacketLogging) - { - LogPacket(packetReader.First, packetReader.Second, packetLength, true); - } - - handler.OnReceive(this, packetReader, ref packetLength); - - prof?.Finish(packetLength); - - return ParserState.AwaitingNextPacket; } - private bool Flush() + // Not enough data, let's wait for more to come in + if (length < packetLength) { - _flushQueued = false; - - if (Connection == null) - { - return true; - } - - SendPipe.Writer.Flush(); - - var reader = SendPipe.Reader; - var result = reader.TryRead(); - - if (result.IsClosed || result.Length == 0) - { - return true; - } - - var bytesWritten = 0; - - try - { - bytesWritten = Connection.Send(result.Buffer, SocketFlags.None); - } - catch (SocketException ex) - { - // Socket exceptions are generally ok, just spammy -#if DEBUG - Console.WriteLine(ex); -#endif - - Disconnect(string.Empty); - } - catch (Exception ex) - { -#if DEBUG - Console.WriteLine(ex); -#endif - Disconnect($"Disconnected with error: {ex}"); - TraceException(ex); - } - - if (bytesWritten > 0) - { - _nextActivityCheck = Core.TickCount + 90000; - reader.Advance((uint)bytesWritten); - } - - return bytesWritten == result.Length; + return ParserState.AwaitingPartialPacket; } - private void DecodePacket(ArraySegment[] buffer, ref int length) + if (handler.Ingame) { - CircularBuffer cBuffer = new CircularBuffer(buffer); - _packetDecoder?.Invoke(cBuffer, ref length); + if (Mobile == null) + { + LogInfo($"received packet 0x{packetId:X2} before having been attached to a mobile"); + return ParserState.Error; + } + + if (Mobile.Deleted) + { + return ParserState.Error; + } } - private void ReceiveData() + var throttler = handler.ThrottleCallback; + if (throttler != null) { - var writer = RecvPipe.Writer; - var result = writer.TryGetMemory(); - - if (result.IsClosed || result.Length == 0) + if (!throttler(packetId, this, out bool drop)) { - return; + return drop ? ParserState.Throttled : ParserState.AwaitingNextPacket; } - var bytesWritten = 0; + SetPacketTime(packetId); + } - try + PacketReceiveProfile prof = null; + + if (Core.Profiling) + { + prof = PacketReceiveProfile.Acquire(packetId); + prof?.Start(); + } + + UpdatePacketCount(packetId); + + if (PacketLogging) + { + LogPacket(packetReader.First, packetReader.Second, packetLength, true); + } + + handler.OnReceive(this, packetReader, packetLength); + + prof?.Finish(packetLength); + + return ParserState.AwaitingNextPacket; + } + + private bool Flush() + { + _flushQueued = false; + + if (Connection == null) + { + return true; + } + + SendPipe.Writer.Flush(); + + var reader = SendPipe.Reader; + var result = reader.TryRead(); + + if (result.IsClosed || result.Length == 0) + { + return true; + } + + var bytesWritten = 0; + + try + { + bytesWritten = Connection.Send(result.Buffer, SocketFlags.None); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode != SocketError.WouldBlock) { - bytesWritten = Connection.Receive(result.Buffer, SocketFlags.None); - } - catch (SocketException ex) - { -#if DEBUG - if (ex.ErrorCode != 54 && ex.ErrorCode != 89 && ex.ErrorCode != 995) - { - Console.WriteLine(ex); - } -#endif + logger.Debug(ex, "Disconnected due to a socket exception"); Disconnect(string.Empty); } - catch (Exception ex) - { -#if DEBUG - Console.WriteLine(ex); -#endif - Disconnect($"Disconnected with error: {ex}"); - TraceException(ex); - } + } + catch (Exception ex) + { + Disconnect($"Disconnected with error: {ex}"); + TraceException(ex); + } - if (bytesWritten <= 0) - { - Disconnect(string.Empty); - return; - } - - DecodePacket(result.Buffer, ref bytesWritten); - - writer.Advance((uint)bytesWritten); + if (bytesWritten > 0) + { _nextActivityCheck = Core.TickCount + 90000; + reader.Advance((uint)bytesWritten); } - public static void FlushAll() + return bytesWritten == result.Length; + } + + private void DecodePacket(ArraySegment[] buffer, ref int length) + { + CircularBuffer cBuffer = new CircularBuffer(buffer); + _packetDecoder?.Invoke(cBuffer, ref length); + } + + private void ReceiveData() + { + var writer = RecvPipe.Writer; + var result = writer.TryGetMemory(); + + if (result.IsClosed || result.Length == 0) { - while (FlushPending.Count != 0) + return; + } + + var bytesWritten = 0; + + try + { + bytesWritten = Connection.Receive(result.Buffer, SocketFlags.None); + } + catch (SocketException ex) + { + if (ex.ErrorCode is not 54 and not 89 and not 995) { - FlushPending.Dequeue()?.Flush(); + logger.Debug(ex, "Disconnected due to a socket exception"); + } + + Disconnect(string.Empty); + } + catch (Exception ex) + { + Disconnect($"Disconnected with error: {ex}"); + TraceException(ex); + } + + if (bytesWritten <= 0) + { + Disconnect(string.Empty); + return; + } + + DecodePacket(result.Buffer, ref bytesWritten); + + writer.Advance((uint)bytesWritten); + _nextActivityCheck = Core.TickCount + 90000; + } + + public static void FlushAll() + { + while (_flushPending.Count != 0) + { + _flushPending.Dequeue()?.Flush(); + } + } + + public static void Slice() + { + int count = _pollGroup.Poll(_polledStates); + + if (count > 0) + { + for (int i = 0; i < count; i++) + { + (_polledStates[i].Target as NetState)?.HandleReceive(); + _polledStates[i] = default; } } - public static void Slice() + while (_flushPending.TryDequeue(out var ns)) { - int count = _pollGroup.Poll(ref _polledStates); - - if (count > 0) + if (!ns.Flush()) { - for (int i = 0; i < count; i++) - { - (_polledStates[i].Target as NetState)?.HandleReceive(); - _polledStates[i] = default; - } - } - - while (FlushPending.TryDequeue(out var ns)) - { - if (!ns.Flush()) - { - // Incomplete data, so we need to requeue - FlushedPartials.Enqueue(ns); - } - } - - var hasDisposes = !Disposed.IsEmpty; - while (Disposed.TryDequeue(out var ns)) - { - ns.Dispose(); - } - - if (hasDisposes) - { - _pollGroup.Poll(ref _polledStates); + // Incomplete data, so we need to requeue + _flushedPartials.Enqueue(ns); } } - public void CheckAlive(long curTicks) + var hasDisposes = false; + while (_disposed.TryDequeue(out var ns)) { - if (Connection != null && _nextActivityCheck - curTicks < 0) + hasDisposes = true; + ns.Dispose(); + } + + // If they weren't disconnected, requeue them + while (_flushedPartials.TryDequeue(out var ns)) + { + if (ns.Running) { - LogInfo("Disconnecting due to inactivity..."); - Disconnect("Disconnecting due to inactivity."); + _flushPending.Enqueue(ns); } } - public static void CheckAllAlive() + if (hasDisposes) { - try - { - long curTicks = Core.TickCount; + _pollGroup.Poll(_polledStates.Length); + } + } - foreach (var ns in TcpServer.Instances) - { - ns.CheckAlive(curTicks); - } - } - catch (Exception ex) + public void CheckAlive(long curTicks) + { + if (Connection != null && _nextActivityCheck - curTicks < 0) + { + LogInfo("Disconnecting due to inactivity..."); + Disconnect("Disconnecting due to inactivity."); + } + } + + public static void CheckAllAlive() + { + try + { + long curTicks = Core.TickCount; + + foreach (var ns in TcpServer.Instances) { - TraceException(ex); + ns.CheckAlive(curTicks); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PacketHandler GetHandler(int packetID) => IncomingPackets.GetHandler(packetID); - - public static void TraceException(Exception ex) + catch (Exception ex) { - try - { - using var op = new StreamWriter("network-errors.log", true); - op.WriteLine("# {0}", Core.Now); + TraceException(ex); + } + } - op.WriteLine(ex); + public static void TraceException(Exception ex) + { + try + { + using var op = new StreamWriter("network-errors.log", true); + op.WriteLine("# {0}", Core.Now); - op.WriteLine(); - op.WriteLine(); - } - catch - { - // ignored - } + op.WriteLine(ex); - Console.WriteLine(ex); + op.WriteLine(); + op.WriteLine(); + } + catch + { + // ignored } - public void Disconnect(string reason) + Console.WriteLine(ex); + } + + public void Disconnect(string reason) + { + if (!_running) { - if (!_running) + return; + } + + _running = false; + +#if THREADGUARD + if (Thread.CurrentThread != Core.Thread) { + Utility.PushColor(ConsoleColor.Red); + Console.WriteLine("Attempting to disconnect a netstate from an invalid thread!"); + Console.WriteLine(new StackTrace()); + Utility.PopColor(); return; } +#endif - _running = false; + _disconnectReason = reason; + _disposed.Enqueue(this); + } - try - { - if (_disconnectReason != string.Empty) - { - throw new Exception("Attempted to disconnect a netstate twice."); - } - } - catch (Exception ex) - { - TraceException(ex); - } - - _disconnectReason = reason; - Disposed.Enqueue(this); + public static void TraceDisconnect(string reason, string ip) + { + if (reason == string.Empty) + { + return; } - public static void TraceDisconnect(string reason, string ip) + try { - if (reason == string.Empty) - { - return; - } + using StreamWriter op = new StreamWriter("network-disconnects.log", true); + op.WriteLine($"# {Core.Now}"); - try - { - using StreamWriter op = new StreamWriter("network-disconnects.log", true); - op.WriteLine($"# {Core.Now}"); + op.WriteLine($"NetState: {ip}"); + op.WriteLine(reason); - op.WriteLine($"NetState: {ip}"); - op.WriteLine(reason); - - op.WriteLine(); - op.WriteLine(); - } - catch (Exception ex) - { - TraceException(ex); - } + op.WriteLine(); + op.WriteLine(); } - - private void Dispose() + catch (Exception ex) { - TraceDisconnect(_disconnectReason, _toString); + TraceException(ex); + } + } - if (_running) - { - throw new Exception("Disconnected a NetState that is still running."); - } + private void Dispose() + { + TraceDisconnect(_disconnectReason, _toString); + + if (_running) + { + throw new Exception("Disconnected a NetState that is still running."); + } #if THREADGUARD if (Thread.CurrentThread != Core.Thread) @@ -1109,46 +1091,38 @@ namespace Server.Network } #endif - TcpServer.Instances.Remove(this); - try - { - _pollGroup.Remove(Connection); - } - catch (Exception ex) - { - TraceException(ex); - } - - Connection.Close(); - _handle.Free(); - - var m = Mobile; - Mobile = null; - - if (m?.NetState == this) - { - m.NetState = null; - } - - var a = Account; - - Gumps.Clear(); - Menus.Clear(); - HuePickers.Clear(); - Account = null; - ServerInfo = null; - CityInfo = null; - - var count = TcpServer.Instances.Count; - - if (a != null) - { - LogInfo("Disconnected. [{0} Online] [{1}]", count, a); - } - else - { - LogInfo("Disconnected. [{0} Online]", count); - } + TcpServer.Instances.Remove(this); + try + { + _pollGroup.Remove(Connection, _handle); } + catch (Exception ex) + { + TraceException(ex); + } + + Connection.Close(); + _handle.Free(); + + var m = Mobile; + Mobile = null; + + if (m?.NetState == this) + { + m.NetState = null; + } + + var a = Account; + + Gumps.Clear(); + Menus.Clear(); + HuePickers.Clear(); + Account = null; + ServerInfo = null; + CityInfo = null; + + var count = TcpServer.Instances.Count; + + LogInfo(a != null ? $"Disconnected. [{count} Online] [{a}]" : $"Disconnected. [{count} Online]"); } } diff --git a/Projects/Server/Network/PacketHandler.cs b/Projects/Server/Network/PacketHandler.cs index 664119b30..8ee6ff7df 100644 --- a/Projects/Server/Network/PacketHandler.cs +++ b/Projects/Server/Network/PacketHandler.cs @@ -1,27 +1,39 @@ -namespace Server.Network +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PacketHandler.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network; + +public unsafe class PacketHandler { - public delegate void OnPacketReceive(NetState state, CircularBufferReader reader, ref int packetLength); + private readonly int _length; - public delegate bool ThrottlePacketCallback(int packetId, NetState state, out bool drop); - - public class PacketHandler + public PacketHandler(int packetID, int length, bool ingame, delegate* onReceive) { - public PacketHandler(int packetID, int length, bool ingame, OnPacketReceive onReceive) - { - PacketID = packetID; - Length = length; - Ingame = ingame; - OnReceive = onReceive; - } - - public int PacketID { get; } - - public int Length { get; } - - public OnPacketReceive OnReceive { get; } - - public ThrottlePacketCallback ThrottleCallback { get; set; } - - public bool Ingame { get; } + _length = length; + PacketID = packetID; + Ingame = ingame; + OnReceive = onReceive; } + + public int PacketID { get; } + + public virtual int GetLength(NetState ns) => _length; + + public delegate* OnReceive { get; } + + public delegate* ThrottleCallback { get; set; } + + public bool Ingame { get; } } diff --git a/Projects/Server/Network/PacketUtilities.cs b/Projects/Server/Network/PacketUtilities.cs index f6fd7df14..320cda166 100644 --- a/Projects/Server/Network/PacketUtilities.cs +++ b/Projects/Server/Network/PacketUtilities.cs @@ -17,7 +17,6 @@ using System; using System.Buffers; using System.IO; using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance; namespace Server.Network { @@ -55,12 +54,12 @@ namespace Server.Network } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span2D InitializePackets(this Span2D buffer) + public static Span InitializePackets(this Span buffer, int width) { #if NO_LOCAL_INIT - for (var i = 0; i < buffer.Height; i++) + for (var i = 0; i < buffer.Length; i += width) { - buffer.GetRowSpan(i)[0] = 0; + buffer[i] = 0; } #endif return buffer; diff --git a/Projects/Server/Network/Packets/IncomingAccountPackets.cs b/Projects/Server/Network/Packets/IncomingAccountPackets.cs index 4444b95ad..54775eb4d 100644 --- a/Projects/Server/Network/Packets/IncomingAccountPackets.cs +++ b/Projects/Server/Network/Packets/IncomingAccountPackets.cs @@ -18,524 +18,511 @@ using System.Collections.Generic; using System.IO; using CV = Server.ClientVersion; -namespace Server.Network +namespace Server.Network; + +public static class IncomingAccountPackets { - public static class IncomingAccountPackets + private const int m_AuthIDWindowSize = 128; + private static readonly Dictionary m_AuthIDWindow = + new(m_AuthIDWindowSize); + + internal struct AuthIDPersistence { - private const int m_AuthIDWindowSize = 128; - private static readonly Dictionary m_AuthIDWindow = - new(m_AuthIDWindowSize); + public DateTime Age; + public readonly ClientVersion Version; - internal struct AuthIDPersistence + public AuthIDPersistence(ClientVersion v) { - public DateTime Age; - public readonly ClientVersion Version; + Age = Core.Now; + Version = v; + } + } - public AuthIDPersistence(ClientVersion v) - { - Age = Core.Now; - Version = v; - } + public static unsafe void Configure() + { + IncomingPackets.Register(0x00, 104, false, &CreateCharacter); + IncomingPackets.Register(0x5D, 73, false, &PlayCharacter); + IncomingPackets.Register(0x80, 62, false, &AccountLogin); + IncomingPackets.Register(0x83, 39, false, &DeleteCharacter); + IncomingPackets.Register(0x91, 65, false, &GameLogin); + IncomingPackets.Register(0xA0, 3, false, &PlayServer); + IncomingPackets.Register(0xBB, 9, false, &AccountID); + IncomingPackets.Register(0xBD, 0, false, &ClientVersion); + IncomingPackets.Register(0xCF, 0, false, &AccountLogin); + IncomingPackets.Register(0xE1, 0, false, &ClientType); + IncomingPackets.Register(0xEF, 21, false, &LoginServerSeed); + IncomingPackets.Register(0xF8, 106, false, &CreateCharacter); + } + + public static void CreateCharacter(NetState state, CircularBufferReader reader, int packetLength) + { + reader.Seek(9, SeekOrigin.Current); + /* + var unk1 = reader.ReadInt32(); + var unk2 = reader.ReadInt32(); + int unk3 = reader.ReadByte(); + */ + var name = reader.ReadAscii(30); + + reader.Seek(2, SeekOrigin.Current); + var flags = reader.ReadInt32(); + reader.Seek(8, SeekOrigin.Current); + int prof = reader.ReadByte(); + reader.Seek(15, SeekOrigin.Current); + + var genderRace = reader.ReadByte(); + + var stats = new StatNameValue[] + { + new(StatType.Str, reader.ReadByte()), + new(StatType.Dex, reader.ReadByte()), + new(StatType.Int, reader.ReadByte()) + }; + + var skills = new SkillNameValue[state.NewCharacterCreation ? 4 : 3]; + skills[0] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); + skills[1] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); + skills[2] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); + if (state.NewCharacterCreation) + { + skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); } - public static void Configure() + int hue = reader.ReadUInt16(); + int hairVal = reader.ReadInt16(); + int hairHue = reader.ReadInt16(); + int hairValf = reader.ReadInt16(); + int hairHuef = reader.ReadInt16(); + reader.ReadByte(); + int cityIndex = reader.ReadByte(); + reader.Seek(8, SeekOrigin.Current); + /* + var charSlot = reader.ReadInt32(); + var clientIP = reader.ReadInt32(); + */ + int shirtHue = reader.ReadInt16(); + int pantsHue = reader.ReadInt16(); + + /* + Pre-7.0.0.0: + 0x00, 0x01 -> Human Male, Human Female + 0x02, 0x03 -> Elf Male, Elf Female + + Post-7.0.0.0: + 0x00, 0x01 + 0x02, 0x03 -> Human Male, Human Female + 0x04, 0x05 -> Elf Male, Elf Female + 0x05, 0x06 -> Gargoyle Male, Gargoyle Female + */ + + var female = genderRace % 2 != 0; + + var raceID = state.StygianAbyss ? (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1) : (byte)(genderRace / 2); + Race race = Race.Races[raceID] ?? Race.DefaultRace; + + var info = state.CityInfo; + var a = state.Account; + + if (info == null || a == null || cityIndex >= info.Length) { - IncomingPackets.Register(0x00, 104, false, CreateCharacter); - IncomingPackets.Register(0x5D, 73, false, PlayCharacter); - IncomingPackets.Register(0x80, 62, false, AccountLogin); - IncomingPackets.Register(0x83, 39, false, DeleteCharacter); - IncomingPackets.Register(0x91, 65, false, GameLogin); - IncomingPackets.Register(0xA0, 3, false, PlayServer); - IncomingPackets.Register(0xBB, 9, false, AccountID); - IncomingPackets.Register(0xBD, 0, false, ClientVersion); - IncomingPackets.Register(0xBE, 0, true, AssistVersion); - IncomingPackets.Register(0xCF, 0, false, AccountLogin); - IncomingPackets.Register(0xE1, 0, false, ClientType); - IncomingPackets.Register(0xEF, 21, false, LoginServerSeed); - IncomingPackets.Register(0xF8, 106, false, CreateCharacter); + state.Disconnect("Invalid city selected during character creation."); + return; } - public static void CreateCharacter(NetState state, CircularBufferReader reader, ref int packetLength) + // Check if anyone is using this account + for (var i = 0; i < a.Length; ++i) { - reader.Seek(9, SeekOrigin.Current); - /* - var unk1 = reader.ReadInt32(); - var unk2 = reader.ReadInt32(); - int unk3 = reader.ReadByte(); - */ - var name = reader.ReadAscii(30); + var check = a[i]; - reader.Seek(2, SeekOrigin.Current); - var flags = reader.ReadInt32(); - reader.Seek(8, SeekOrigin.Current); - int prof = reader.ReadByte(); - reader.Seek(15, SeekOrigin.Current); - - int genderRace = reader.ReadByte(); - - var stats = new StatNameValue[] + if (check != null && check.Map != Map.Internal) { - new(StatType.Str, reader.ReadByte()), - new(StatType.Dex, reader.ReadByte()), - new(StatType.Int, reader.ReadByte()) - }; - - var skills = new SkillNameValue[state.NewCharacterCreation ? 4 : 3]; - skills[0] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - skills[1] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - skills[2] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - if (state.NewCharacterCreation) - { - skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - } - - int hue = reader.ReadUInt16(); - int hairVal = reader.ReadInt16(); - int hairHue = reader.ReadInt16(); - int hairValf = reader.ReadInt16(); - int hairHuef = reader.ReadInt16(); - reader.ReadByte(); - int cityIndex = reader.ReadByte(); - reader.Seek(8, SeekOrigin.Current); - /* - var charSlot = reader.ReadInt32(); - var clientIP = reader.ReadInt32(); - */ - int shirtHue = reader.ReadInt16(); - int pantsHue = reader.ReadInt16(); - - /* - Pre-7.0.0.0: - 0x00, 0x01 -> Human Male, Human Female - 0x02, 0x03 -> Elf Male, Elf Female - - Post-7.0.0.0: - 0x00, 0x01 - 0x02, 0x03 -> Human Male, Human Female - 0x04, 0x05 -> Elf Male, Elf Female - 0x05, 0x06 -> Gargoyle Male, Gargoyle Female - */ - - var female = genderRace % 2 != 0; - - var raceID = state.StygianAbyss ? (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1) : (byte)(genderRace / 2); - Race race = Race.Races[raceID] ?? Race.DefaultRace; - - var info = state.CityInfo; - var a = state.Account; - - if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length) - { - state.Disconnect("Invalid city selected during character creation."); + state.LogInfo("Account in use"); + state.SendPopupMessage(PMMessage.CharInWorld); return; } - - // Check if anyone is using this account - for (var i = 0; i < a.Length; ++i) - { - var check = a[i]; - - if (check != null && check.Map != Map.Internal) - { - state.LogInfo("Account in use"); - state.SendPopupMessage(PMMessage.CharInWorld); - return; - } - } - - state.Flags = (ClientFlags)flags; - - var args = new CharacterCreatedEventArgs( - state, - a, - name, - female, - hue, - stats, - info[cityIndex], - skills, - shirtHue, - pantsHue, - hairVal, - hairHue, - hairValf, - hairHuef, - prof, - race - ); - - state.SendClientVersionRequest(); - - state.BlockAllPackets = true; - - EventSink.InvokeCharacterCreated(args); - - var m = args.Mobile; - - if (m != null) - { - state.Mobile = m; - m.NetState = state; - new LoginTimer(state, m).Start(); - } - else - { - state.BlockAllPackets = false; - state.Disconnect("Character creation blocked."); - } } - public static void DeleteCharacter(NetState state, CircularBufferReader reader, ref int packetLength) + state.Flags = (ClientFlags)flags; + + var args = new CharacterCreatedEventArgs( + state, + a, + name, + female, + hue, + stats, + info[cityIndex], + skills, + shirtHue, + pantsHue, + hairVal, + hairHue, + hairValf, + hairHuef, + prof, + race + ); + + state.SendClientVersionRequest(); + + state.BlockAllPackets = true; + + EventSink.InvokeCharacterCreated(args); + + var m = args.Mobile; + + if (m != null) { - reader.Seek(30, SeekOrigin.Current); - var index = reader.ReadInt32(); - - EventSink.InvokeDeleteRequest(state, index); - } - - public static void AccountID(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - public static void AssistVersion(NetState state, CircularBufferReader reader, ref int packetLength) - { - var unk = reader.ReadInt32(); - var av = reader.ReadAscii(); - } - - public static void ClientVersion(NetState state, CircularBufferReader reader, ref int packetLength) - { - var version = state.Version = new CV(reader.ReadAscii()); - - EventSink.InvokeClientVersionReceived(state, version); - } - - public static void ClientType(NetState state, CircularBufferReader reader, ref int packetLength) - { - reader.ReadUInt16(); - - int type = reader.ReadUInt16(); - var version = state.Version = new CV(reader.ReadAscii()); - - EventSink.InvokeClientVersionReceived(state, version); - } - - public static void PlayCharacter(NetState state, CircularBufferReader reader, ref int packetLength) - { - reader.Seek(4, SeekOrigin.Current); // 0xEDEDEDED - - reader.Seek(30, SeekOrigin.Current); // var name = reader.ReadAscii(30); - - reader.Seek(2, SeekOrigin.Current); - - var flags = reader.ReadInt32(); - - reader.Seek(24, SeekOrigin.Current); - - var charSlot = reader.ReadInt32(); - reader.Seek(4, SeekOrigin.Current); // var clientIP = reader.ReadInt32(); - - var a = state.Account; - - if (a == null || charSlot < 0 || charSlot >= a.Length) - { - state.Disconnect("Invalid character slot selected."); - return; - } - - var m = a[charSlot]; - - // Check if anyone is using this account - for (var i = 0; i < a.Length; ++i) - { - var check = a[i]; - - if (check != null && check.Map != Map.Internal && check != m) - { - state.LogInfo("Account in use"); - state.SendPopupMessage(PMMessage.CharInWorld); - return; - } - } - - if (m == null) - { - state.Disconnect("Empty character slot selected."); - return; - } - - m.NetState?.Disconnect("Character selected for a player already logged in."); - - state.SendClientVersionRequest(); - - state.BlockAllPackets = true; - - state.Flags = (ClientFlags)flags; - state.Mobile = m; m.NetState = state; - new LoginTimer(state, m).Start(); } - - public static void DoLogin(this NetState state, Mobile m) + else { - state.SendLoginConfirmation(m); + state.BlockAllPackets = false; + state.Disconnect("Character creation blocked."); + } + } - state.SendMapChange(m.Map); + public static void DeleteCharacter(NetState state, CircularBufferReader reader, int packetLength) + { + reader.Seek(30, SeekOrigin.Current); + var index = reader.ReadInt32(); - state.SendMapPatches(); + EventSink.InvokeDeleteRequest(state, index); + } - state.SendSeasonChange((byte)m.GetSeason(), true); + public static void AccountID(NetState state, CircularBufferReader reader, int packetLength) + { + } + + public static void ClientVersion(NetState state, CircularBufferReader reader, int packetLength) + { + var version = state.Version = new CV(reader.ReadAscii()); + + EventSink.InvokeClientVersionReceived(state, version); + } + + public static void ClientType(NetState state, CircularBufferReader reader, int packetLength) + { + reader.ReadUInt16(); + + int type = reader.ReadUInt16(); + var version = state.Version = new CV(reader.ReadAscii()); + + EventSink.InvokeClientVersionReceived(state, version); + } + + public static void PlayCharacter(NetState state, CircularBufferReader reader, int packetLength) + { + reader.Seek(36, SeekOrigin.Current); // 4 = 0xEDEDEDED, 30 = Name, 2 = unknown + var flags = reader.ReadInt32(); + reader.Seek(24, SeekOrigin.Current); + var charSlot = reader.ReadInt32(); + reader.Seek(4, SeekOrigin.Current); // var clientIP = reader.ReadInt32(); + + var a = state.Account; + + if (a == null || charSlot < 0 || charSlot >= a.Length) + { + state.Disconnect("Invalid character slot selected."); + return; + } + + var m = a[charSlot]; + + // Check if anyone is using this account + for (var i = 0; i < a.Length; ++i) + { + var check = a[i]; + + if (check != null && check.Map != Map.Internal && check != m) + { + state.LogInfo("Account in use"); + state.SendPopupMessage(PMMessage.CharInWorld); + return; + } + } + + if (m == null) + { + state.Disconnect("Empty character slot selected."); + return; + } + + m.NetState?.Disconnect("Character selected for a player already logged in."); + + state.SendClientVersionRequest(); + + state.BlockAllPackets = true; + + state.Flags = (ClientFlags)flags; + + state.Mobile = m; + m.NetState = state; + + new LoginTimer(state, m).Start(); + } + + public static void DoLogin(this NetState state, Mobile m) + { + state.SendLoginConfirmation(m); + + state.SendMapChange(m.Map); + + state.SendMapPatches(); + + state.SendSeasonChange((byte)m.GetSeason(), true); + + state.SendSupportedFeature(); + + state.Sequence = 0; + + state.SendMobileUpdate(m); + state.SendMobileUpdate(m); + + m.CheckLightLevels(true); + + state.SendMobileUpdate(m); + + state.SendMobileIncoming(m, m); + + state.SendMobileStatus(m); + state.SendSetWarMode(m.Warmode); + + m.SendEverything(); + + state.SendSupportedFeature(); + state.SendMobileUpdate(m); + + state.SendMobileStatus(m); + state.SendSetWarMode(m.Warmode); + state.SendMobileIncoming(m, m); + + state.SendLoginComplete(); + state.SendCurrentTime(); + state.SendSeasonChange((byte)m.GetSeason(), true); + state.SendMapChange(m.Map); + + state.SendPlayMusic(m.Region.Music); + + EventSink.InvokeLogin(m); + } + + private static int GenerateAuthID(this NetState state) + { + if (m_AuthIDWindow.Count == m_AuthIDWindowSize) + { + var oldestID = 0; + var oldest = DateTime.MaxValue; + + foreach (var (key, authId) in m_AuthIDWindow) + { + if (authId.Age < oldest) + { + oldestID = key; + oldest = authId.Age; + } + } + + m_AuthIDWindow.Remove(oldestID); + } + + int authID; + + do + { + authID = Utility.Random(1, int.MaxValue - 1); + + if (Utility.RandomBool()) + { + authID |= 1 << 31; + } + } while (m_AuthIDWindow.ContainsKey(authID)); + + m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version); + + return authID; + } + + public static void GameLogin(NetState state, CircularBufferReader reader, int packetLength) + { + // TODO: Connection throttling + + if (state.SentFirstPacket) + { + state.Disconnect("Duplicate game login packet received."); + return; + } + + state.SentFirstPacket = true; + + var authID = reader.ReadInt32(); + + if (!m_AuthIDWindow.TryGetValue(authID, out var ap)) + { + state.LogInfo("Invalid client detected, disconnecting..."); + state.Disconnect("Unable to find auth id."); + } + + if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed) + { + state.LogInfo("Invalid client detected, disconnecting..."); + state.Disconnect("Invalid auth id in game login packet."); + return; + } + + m_AuthIDWindow.Remove(authID); + state.Version = ap.Version; + + var username = reader.ReadAscii(30); + var password = reader.ReadAscii(30); + + var e = new GameLoginEventArgs(state, username, password); + + EventSink.InvokeGameLogin(e); + + if (e.Accepted) + { + state.CityInfo = e.CityInfo; + + // Comment out these lines to turn off huffman compression + state.CompressionEnabled = true; + state.PacketEncoder ??= NetworkCompression.Compress; state.SendSupportedFeature(); + state.SendCharacterList(); + } + else + { + state.Disconnect("Login rejected by GameLogin packet handler."); + } + } - state.Sequence = 0; + public static void PlayServer(NetState state, CircularBufferReader reader, int packetLength) + { + int index = reader.ReadInt16(); + var info = state.ServerInfo; + var a = state.Account; - state.SendMobileUpdate(m); - state.SendMobileUpdate(m); + if (info == null || a == null || index < 0 || index >= info.Length) + { + state.Disconnect("Invalid server selected."); + } + else + { + var si = info[index]; - m.CheckLightLevels(true); + state._authId = GenerateAuthID(state); - state.SendMobileUpdate(m); + state.SentFirstPacket = false; + state.SendPlayServerAck(si, state._authId); + } + } - state.SendMobileIncoming(m, m); + public static void LoginServerSeed(NetState state, CircularBufferReader reader, int packetLength) + { + state._seed = reader.ReadInt32(); + state.Seeded = true; - state.SendMobileStatus(m); - state.SendSetWarMode(m.Warmode); - - m.SendEverything(); - - state.SendSupportedFeature(); - state.SendMobileUpdate(m); - - state.SendMobileStatus(m); - state.SendSetWarMode(m.Warmode); - state.SendMobileIncoming(m, m); - - state.SendLoginComplete(); - state.SendCurrentTime(); - state.SendSeasonChange((byte)m.GetSeason(), true); - state.SendMapChange(m.Map); - - EventSink.InvokeLogin(m); + if (state._seed == 0) + { + state.LogInfo("Invalid client detected, disconnecting"); + state.Disconnect("Duplicate seed sent."); + return; } - private static int GenerateAuthID(this NetState state) + var clientMaj = reader.ReadInt32(); + var clientMin = reader.ReadInt32(); + var clientRev = reader.ReadInt32(); + var clientPat = reader.ReadInt32(); + + state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat); + } + + public static void AccountLogin(NetState state, CircularBufferReader reader, int packetLength) + { + // TODO: Throttle Connection + + if (state.SentFirstPacket) { - if (m_AuthIDWindow.Count == m_AuthIDWindowSize) - { - var oldestID = 0; - var oldest = DateTime.MaxValue; - - foreach (var (key, authId) in m_AuthIDWindow) - { - if (authId.Age < oldest) - { - oldestID = key; - oldest = authId.Age; - } - } - - m_AuthIDWindow.Remove(oldestID); - } - - int authID; - - do - { - authID = Utility.Random(1, int.MaxValue - 1); - - if (Utility.RandomBool()) - { - authID |= 1 << 31; - } - } while (m_AuthIDWindow.ContainsKey(authID)); - - m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version); - - return authID; + state.Disconnect("Duplicate account login packet sent."); + return; } - public static void GameLogin(NetState state, CircularBufferReader reader, ref int packetLength) + state.SentFirstPacket = true; + + var username = reader.ReadAscii(30); + var password = reader.ReadAscii(30); + + var accountLoginEventArgs = new AccountLoginEventArgs(state, username, password); + + EventSink.InvokeAccountLogin(accountLoginEventArgs); + + if (accountLoginEventArgs.Accepted) { - // TODO: Connection throttling + var serverListEventArgs = new ServerListEventArgs(state, state.Account); - if (state.SentFirstPacket) + EventSink.InvokeServerList(serverListEventArgs); + + if (serverListEventArgs.Rejected) { - state.Disconnect("Duplicate game login packet received."); - return; - } - - state.SentFirstPacket = true; - - var authID = reader.ReadInt32(); - - if (!m_AuthIDWindow.TryGetValue(authID, out var ap)) - { - state.LogInfo("Invalid client detected, disconnecting..."); - state.Disconnect("Unable to find auth id."); - } - - if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed) - { - state.LogInfo("Invalid client detected, disconnecting..."); - state.Disconnect("Invalid auth id in game login packet."); - return; - } - - m_AuthIDWindow.Remove(authID); - state.Version = ap.Version; - - var username = reader.ReadAscii(30); - var password = reader.ReadAscii(30); - - var e = new GameLoginEventArgs(state, username, password); - - EventSink.InvokeGameLogin(e); - - if (e.Accepted) - { - state.CityInfo = e.CityInfo; - - // Comment out these lines to turn off huffman compression - state.CompressionEnabled = true; - state.PacketEncoder ??= NetworkCompression.Compress; - - state.SendSupportedFeature(); - state.SendCharacterList(); + state.Account = null; + AccountLogin_ReplyRej(state, ALRReason.BadComm); } else { - state.Disconnect("Login rejected by GameLogin packet handler."); + state.ServerInfo = serverListEventArgs.Servers.ToArray(); + state.SendAccountLoginAck(); } } - - public static void PlayServer(NetState state, CircularBufferReader reader, ref int packetLength) + else { - int index = reader.ReadInt16(); - var info = state.ServerInfo; - var a = state.Account; + AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason); + } + } - if (info == null || a == null || index < 0 || index >= info.Length) - { - state.Disconnect("Invalid server selected."); - } - else - { - var si = info[index]; + private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason) + { + state.SendAccountLoginRejected(reason); + state.Disconnect($"Account login rejected due to {reason}"); + } - state._authId = GenerateAuthID(state); + private class LoginTimer : Timer + { + private readonly Mobile _mobile; + private readonly NetState _state; - state.SentFirstPacket = false; - state.SendPlayServerAck(si, state._authId); - } + public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromMilliseconds(64), TimeSpan.FromMilliseconds(64)) + { + _state = state; + _mobile = m; } - public static void LoginServerSeed(NetState state, CircularBufferReader reader, ref int packetLength) + protected override void OnTick() { - state._seed = reader.ReadInt32(); - state.Seeded = true; - - if (state._seed == 0) + if (_state != null) { - state.LogInfo("Invalid client detected, disconnecting"); - state.Disconnect("Duplicate seed sent."); - return; - } - - var clientMaj = reader.ReadInt32(); - var clientMin = reader.ReadInt32(); - var clientRev = reader.ReadInt32(); - var clientPat = reader.ReadInt32(); - - state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat); - } - - public static void AccountLogin(NetState state, CircularBufferReader reader, ref int packetLength) - { - // TODO: Throttle Connection - - if (state.SentFirstPacket) - { - state.Disconnect("Duplicate account login packet sent."); - return; - } - - state.SentFirstPacket = true; - - var username = reader.ReadAscii(30); - var password = reader.ReadAscii(30); - - var accountLoginEventArgs = new AccountLoginEventArgs(state, username, password); - - EventSink.InvokeAccountLogin(accountLoginEventArgs); - - if (accountLoginEventArgs.Accepted) - { - var serverListEventArgs = new ServerListEventArgs(state, state.Account); - - EventSink.InvokeServerList(serverListEventArgs); - - if (serverListEventArgs.Rejected) + if (_state.Account == null) { - state.Account = null; - AccountLogin_ReplyRej(state, ALRReason.BadComm); + _state.Disconnect("Account was deleted during the login process."); } - else + else if (_mobile == null) { - state.ServerInfo = serverListEventArgs.Servers.ToArray(); - state.SendAccountLoginAck(); + _state.Disconnect("Player was deleted during the login process."); + } + else if (_state.Version != null) + { + _state.BlockAllPackets = false; + DoLogin(_state, _mobile); + } + else // Waiting to receive the client version before we continue the login process + { + return; } } - else - { - AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason); - } - } - private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason) - { - state.SendAccountLoginRejected(reason); - state.Disconnect($"Account login rejected due to {reason}"); - } - - private class LoginTimer : Timer - { - private readonly Mobile _mobile; - private readonly NetState _state; - - public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) - { - _state = state; - _mobile = m; - } - - protected override void OnTick() - { - if (_state != null) - { - if (_state.Account == null) - { - _state.Disconnect("Account was deleted during the login process."); - } - else if (_mobile == null) - { - _state.Disconnect("Player was deleted during the login process."); - } - else if (_state.Version != null) - { - _state.BlockAllPackets = false; - DoLogin(_state, _mobile); - } - else // Waiting to receive the client version before we continue the login process - { - return; - } - } - - Stop(); - } + Stop(); } } } diff --git a/Projects/Server/Network/Packets/IncomingEntityPackets.cs b/Projects/Server/Network/Packets/IncomingEntityPackets.cs index ac6c0dd3d..30f56b7ef 100644 --- a/Projects/Server/Network/Packets/IncomingEntityPackets.cs +++ b/Projects/Server/Network/Packets/IncomingEntityPackets.cs @@ -13,97 +13,160 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Network +namespace Server.Network; + +public static class IncomingEntityPackets { - public static class IncomingEntityPackets + public static bool SingleClickProps { get; set; } + + public static unsafe void Configure() { - public static bool SingleClickProps { get; set; } + IncomingPackets.Register(0x06, 5, true, &UseReq); + IncomingPackets.Register(0x09, 5, true, &LookReq); + IncomingPackets.Register(0xB6, 9, true, &ObjectHelpRequest); + IncomingPackets.Register(0xD6, 0, true, &BatchQueryProperties); + } - public static void Configure() + public static void ObjectHelpRequest(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + var serial = (Serial)reader.ReadUInt32(); + int unk = reader.ReadByte(); + var lang = reader.ReadAscii(3); + + if (serial.IsItem) { - IncomingPackets.Register(0x06, 5, true, UseReq); - IncomingPackets.Register(0x09, 5, true, LookReq); - IncomingPackets.Register(0xB6, 9, true, ObjectHelpRequest); - IncomingPackets.Register(0xD6, 0, true, BatchQueryProperties); - } + var item = World.FindItem(serial); - public static void ObjectHelpRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - var serial = (Serial)reader.ReadUInt32(); - int unk = reader.ReadByte(); - var lang = reader.ReadAscii(3); - - if (serial.IsItem) + if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) && + from.CanSee(item)) { - var 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) - { - var m = World.FindMobile(serial); - - if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m)) - { - m.OnHelpRequest(m); - } + item.OnHelpRequest(from); } } - - public static void UseReq(NetState state, CircularBufferReader reader, ref int packetLength) + else if (serial.IsMobile) { - var from = state.Mobile; + var m = World.FindMobile(serial); - if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) + if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m)) { - var value = reader.ReadUInt32(); + m.OnHelpRequest(m); + } + } + } - if ((value & ~0x7FFFFFFF) != 0) - { - from.OnPaperdollRequest(); - } - else - { - Serial s = (Serial)value; + public static void UseReq(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; - if (s.IsMobile) - { - var m = World.FindMobile(s); + if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) + { + var value = reader.ReadUInt32(); - if (m?.Deleted == false) - { - from.Use(m); - } - } - else if (s.IsItem) - { - var item = World.FindItem(s); - - if (item?.Deleted == false) - { - from.Use(item); - } - } - } - - from.NextActionTime = Core.TickCount + Mobile.ActionDelay; + if ((value & ~0x7FFFFFFF) != 0) + { + from.OnPaperdollRequest(); } else { - from.SendActionMessage(); + Serial s = (Serial)value; + + if (s.IsMobile) + { + var m = World.FindMobile(s); + + if (m?.Deleted == false) + { + from.Use(m); + } + } + else if (s.IsItem) + { + var item = World.FindItem(s); + + if (item?.Deleted == false) + { + from.Use(item); + } + } + } + + from.NextActionTime = Core.TickCount + Mobile.ActionDelay; + } + else + { + from.SendActionMessage(); + } + } + + public static void LookReq(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + Serial s = (Serial)reader.ReadUInt32(); + + if (s.IsMobile) + { + var m = World.FindMobile(s); + + if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) + { + if (SingleClickProps) + { + m.OnAosSingleClick(from); + } + else + { + if (from.Region.OnSingleClick(from, m)) + { + m.OnSingleClick(from); + } + } } } - - public static void LookReq(NetState state, CircularBufferReader reader, ref int packetLength) + else if (s.IsItem) { - var from = state.Mobile; + var item = World.FindItem(s); + if (item?.Deleted == false && from.CanSee(item) && + Utility.InUpdateRange(from.Location, item.GetWorldLocation())) + { + if (SingleClickProps) + { + item.OnAosSingleClick(from); + } + else if (from.Region.OnSingleClick(from, item)) + { + if (item.Parent is Item parentItem) + { + parentItem.OnSingleClickContained(from, item); + } + + item.OnSingleClick(from); + } + } + } + } + + public static void BatchQueryProperties(NetState state, CircularBufferReader reader, int packetLength) + { + if (!ObjectPropertyList.Enabled) + { + return; + } + + var from = state.Mobile; + + var length = reader.Remaining; + + if (length % 4 != 0) + { + return; + } + + while (reader.Remaining > 0) + { Serial s = (Serial)reader.ReadUInt32(); if (s.IsMobile) @@ -112,17 +175,7 @@ namespace Server.Network if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) { - if (SingleClickProps) - { - m.OnAosSingleClick(from); - } - else - { - if (from.Region.OnSingleClick(from, m)) - { - m.OnSingleClick(from); - } - } + m.SendPropertiesTo(state); } } else if (s.IsItem) @@ -132,61 +185,7 @@ namespace Server.Network if (item?.Deleted == false && from.CanSee(item) && Utility.InUpdateRange(from.Location, item.GetWorldLocation())) { - if (SingleClickProps) - { - item.OnAosSingleClick(from); - } - else if (from.Region.OnSingleClick(from, item)) - { - if (item.Parent is Item parentItem) - { - parentItem.OnSingleClickContained(from, item); - } - - item.OnSingleClick(from); - } - } - } - } - - public static void BatchQueryProperties(NetState state, CircularBufferReader reader, ref int packetLength) - { - if (!ObjectPropertyList.Enabled) - { - return; - } - - var from = state.Mobile; - - var length = reader.Remaining; - - if (length % 4 != 0) - { - return; - } - - while (reader.Remaining > 0) - { - Serial s = (Serial)reader.ReadUInt32(); - - if (s.IsMobile) - { - var m = World.FindMobile(s); - - if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) - { - m.SendPropertiesTo(from); - } - } - else if (s.IsItem) - { - var item = World.FindItem(s); - - if (item?.Deleted == false && from.CanSee(item) && - Utility.InUpdateRange(from.Location, item.GetWorldLocation())) - { - item.SendPropertiesTo(from); - } + item.SendPropertiesTo(state); } } } diff --git a/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs b/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs index c78da3903..a9b15cfbf 100644 --- a/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs +++ b/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingExtendedCommandPackets.cs * * * @@ -13,505 +13,486 @@ * along with this program. If not, see . * *************************************************************************/ -using System.Collections.Generic; using Server.ContextMenus; -namespace Server.Network +namespace Server.Network; + +public static class IncomingExtendedCommandPackets { - public static class IncomingExtendedCommandPackets + private static readonly PacketHandler[] _extendedHandlers = new PacketHandler[0x100]; + + // TODO: Change to outside configuration + public static int[] ValidAnimations { get; set; } = { - private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100]; - private static readonly Dictionary m_ExtendedHandlersHigh = new(); + 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 + }; - // TODO: Change to outside configuration - public static int[] ValidAnimations { get; set; } = + public static unsafe void Configure() + { + IncomingPackets.Register(0xBF, 0, true, &ExtendedCommand); + + RegisterExtended(0x05, false, &ScreenSize); + RegisterExtended(0x06, true, &PartyMessage); + RegisterExtended(0x09, true, &DisarmRequest); + RegisterExtended(0x0A, true, &StunRequest); + RegisterExtended(0x0B, false, &Language); + RegisterExtended(0x0C, true, &CloseStatus); + RegisterExtended(0x0E, true, &Animate); + RegisterExtended(0x0F, false, &Empty); // What's this? + RegisterExtended(0x10, true, &QueryProperties); + RegisterExtended(0x13, true, &ContextMenuRequest); + RegisterExtended(0x15, true, &ContextMenuResponse); + RegisterExtended(0x1A, true, &StatLockChange); + RegisterExtended(0x1C, true, &CastSpell); + RegisterExtended(0x24, false, &UnhandledBF); + RegisterExtended(0x2C, true, &BandageTarget); + RegisterExtended(0x2D, true, &TargetedSpell); + RegisterExtended(0x2E, true, &TargetedSkillUse); + RegisterExtended(0x30, true, &TargetByResourceMacro); + RegisterExtended(0x32, true, &ToggleFlying); + } + + private static void UnhandledBF(NetState state, CircularBufferReader reader, int packetLength) + { + } + + public static void Empty(NetState state, CircularBufferReader reader, int packetLength) + { + } + + public static unsafe void RegisterExtended(int packetID, bool ingame, + delegate* onReceive) + { + if (packetID is >= 0 and < 0x100) { - 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 - }; + _extendedHandlers[packetID] = new PacketHandler(packetID, 0, ingame, onReceive); + } + } - public static void Configure() + public static PacketHandler GetExtendedHandler(int packetID) => + packetID is >= 0 and < 0x100 ? _extendedHandlers[packetID] : null; + + public static void RemoveExtendedHandler(int packetID) + { + if (packetID is >= 0 and < 0x100) { - IncomingPackets.Register(0xBF, 0, true, ExtendedCommand); + _extendedHandlers[packetID] = null; + } + } - RegisterExtended(0x05, false, ScreenSize); - RegisterExtended(0x06, true, PartyMessage); - RegisterExtended(0x09, true, DisarmRequest); - RegisterExtended(0x0A, true, StunRequest); - RegisterExtended(0x0B, false, Language); - RegisterExtended(0x0C, true, CloseStatus); - RegisterExtended(0x0E, true, Animate); - RegisterExtended(0x0F, false, Empty); // What's this? - RegisterExtended(0x10, true, QueryProperties); - RegisterExtended(0x13, true, ContextMenuRequest); - RegisterExtended(0x15, true, ContextMenuResponse); - RegisterExtended(0x1A, true, StatLockChange); - RegisterExtended(0x1C, true, CastSpell); - RegisterExtended(0x24, false, UnhandledBF); - RegisterExtended(0x2C, true, BandageTarget); - RegisterExtended(0x2D, true, TargetedSpell); - RegisterExtended(0x2E, true, TargetedSkillUse); - RegisterExtended(0x30, true, TargetByResourceMacro); - RegisterExtended(0x32, true, ToggleFlying); + public static unsafe void ExtendedCommand(NetState state, CircularBufferReader reader, int packetLength) + { + int packetId = reader.ReadUInt16(); + + var ph = GetExtendedHandler(packetId); + + if (ph == null) + { + reader.Trace(state); + return; } - private static void UnhandledBF(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - public static void Empty(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - 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]; - } - - m_ExtendedHandlersHigh.TryGetValue(packetID, out var 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 ExtendedCommand(NetState state, CircularBufferReader reader, ref int packetLength) - { - int packetId = reader.ReadUInt16(); - - var ph = GetExtendedHandler(packetId); - - if (ph == null) - { - reader.Trace(state); - return; - } - - if (ph.Ingame && state.Mobile?.Deleted != false) - { - if (state.Mobile == null) - { - state.LogInfo( - "Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile", - packetId - ); - } - - state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted."); - } - else - { - ph.OnReceive(state, reader, ref packetLength); - } - } - - public static void ScreenSize(NetState state, CircularBufferReader reader, ref int packetLength) - { - var width = reader.ReadInt32(); - var unk = reader.ReadInt32(); - } - - // TODO: Move out of the core - public static void PartyMessage(NetState state, CircularBufferReader reader, ref int packetLength) + if (ph.Ingame && state.Mobile?.Deleted != false) { if (state.Mobile == null) { - return; + state.LogInfo( + $"Sent in-game packet (0xBFx{packetId:X2}) before having been attached to a mobile" + ); } - switch (reader.ReadByte()) + state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted."); + } + else + { + ph.OnReceive(state, reader, packetLength); + } + } + + public static void ScreenSize(NetState state, CircularBufferReader reader, int packetLength) + { + var width = reader.ReadInt32(); + var unk = reader.ReadInt32(); + } + + // TODO: Move out of the core + public static void PartyMessage(NetState state, CircularBufferReader reader, int packetLength) + { + if (state.Mobile == null) + { + return; + } + + switch (reader.ReadByte()) + { + case 0x01: + PartyMessage_AddMember(state, reader, packetLength); + break; + case 0x02: + PartyMessage_RemoveMember(state, reader, packetLength); + break; + case 0x03: + PartyMessage_PrivateMessage(state, reader, packetLength); + break; + case 0x04: + PartyMessage_PublicMessage(state, reader, packetLength); + break; + case 0x06: + PartyMessage_SetCanLoot(state, reader, packetLength); + break; + case 0x08: + PartyMessage_Accept(state, reader, packetLength); + break; + case 0x09: + PartyMessage_Decline(state, reader, packetLength); + break; + default: + reader.Trace(state); + break; + } + } + + public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnAdd(state.Mobile); + } + + public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); + } + + public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnPrivateMessage( + state.Mobile, + World.FindMobile((Serial)reader.ReadUInt32()), + reader.ReadBigUniSafe() + ); + } + + public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe()); + } + + public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean()); + } + + public static void PartyMessage_Accept(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); + } + + public static void PartyMessage_Decline(NetState state, CircularBufferReader reader, int packetLength) + { + PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); + } + + public static void Animate(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var action = reader.ReadInt32(); + + var ok = false; + + for (var i = 0; !ok && i < ValidAnimations.Length; ++i) + { + ok = action == ValidAnimations[i]; + } + + if (ok && from.Alive && from.Body.IsHuman && !from.Mounted) + { + from.Animate(action, 7, 1, true, false, 0); + } + } + + public static void CastSpell(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null; + + var spellID = reader.ReadInt16() - 1; + EventSink.InvokeCastSpellRequest(from, spellID, spellbook); + } + + public static void ToggleFlying(NetState state, CircularBufferReader reader, int packetLength) + { + state.Mobile?.ToggleFlying(); + } + + public static void StunRequest(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + EventSink.InvokeStunRequest(from); + } + + public static void DisarmRequest(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + EventSink.InvokeDisarmRequest(from); + } + + public static void StatLockChange(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + int stat = reader.ReadByte(); + int lockValue = reader.ReadByte(); + + if (lockValue > 2) + { + lockValue = 0; + } + + switch (stat) + { + case 0: + from.StrLock = (StatLockType)lockValue; + break; + case 1: + from.DexLock = (StatLockType)lockValue; + break; + case 2: + from.IntLock = (StatLockType)lockValue; + break; + } + } + + public static void CloseStatus(NetState state, CircularBufferReader reader, int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + } + + public static void Language(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + from.Language = reader.ReadAscii(4); + } + + public static void QueryProperties(NetState state, CircularBufferReader reader, int packetLength) + { + if (!ObjectPropertyList.Enabled) + { + return; + } + + var from = state.Mobile; + + Serial s = (Serial)reader.ReadUInt32(); + + if (s.IsMobile) + { + var m = World.FindMobile(s); + + if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) { - case 0x01: - PartyMessage_AddMember(state, reader, ref packetLength); - break; - case 0x02: - PartyMessage_RemoveMember(state, reader, ref packetLength); - break; - case 0x03: - PartyMessage_PrivateMessage(state, reader, ref packetLength); - break; - case 0x04: - PartyMessage_PublicMessage(state, reader, ref packetLength); - break; - case 0x06: - PartyMessage_SetCanLoot(state, reader, ref packetLength); - break; - case 0x08: - PartyMessage_Accept(state, reader, ref packetLength); - break; - case 0x09: - PartyMessage_Decline(state, reader, ref packetLength); - break; - default: - reader.Trace(state); - break; + m.SendPropertiesTo(state); } } - - public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader, ref int packetLength) + else if (s.IsItem) { - PartyCommands.Handler?.OnAdd(state.Mobile); - } + var item = World.FindItem(s); - public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); - } - - public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnPrivateMessage( - state.Mobile, - World.FindMobile((Serial)reader.ReadUInt32()), - reader.ReadBigUniSafe() - ); - } - - public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe()); - } - - public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean()); - } - - public static void PartyMessage_Accept(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); - } - - public static void PartyMessage_Decline(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); - } - - public static void Animate(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) + if (item?.Deleted == false && from.CanSee(item) && + Utility.InUpdateRange(from.Location, item.GetWorldLocation())) { - return; - } - - var action = reader.ReadInt32(); - - var ok = false; - - for (var i = 0; !ok && i < ValidAnimations.Length; ++i) - { - ok = action == ValidAnimations[i]; - } - - if (ok && from.Alive && from.Body.IsHuman && !from.Mounted) - { - from.Animate(action, 7, 1, true, false, 0); - } - } - - public static void CastSpell(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null; - - var spellID = reader.ReadInt16() - 1; - EventSink.InvokeCastSpellRequest(from, spellID, spellbook); - } - - public static void ToggleFlying(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.Mobile?.ToggleFlying(); - } - - public static void StunRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - EventSink.InvokeStunRequest(from); - } - - public static void DisarmRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - EventSink.InvokeDisarmRequest(from); - } - - public static void StatLockChange(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - int stat = reader.ReadByte(); - int lockValue = reader.ReadByte(); - - if (lockValue > 2) - { - lockValue = 0; - } - - switch (stat) - { - case 0: - from.StrLock = (StatLockType)lockValue; - break; - case 1: - from.DexLock = (StatLockType)lockValue; - break; - case 2: - from.IntLock = (StatLockType)lockValue; - break; - } - } - - public static void CloseStatus(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - } - - public static void Language(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - from.Language = reader.ReadAscii(4); - } - - public static void QueryProperties(NetState state, CircularBufferReader reader, ref int packetLength) - { - if (!ObjectPropertyList.Enabled) - { - return; - } - - var from = state.Mobile; - - Serial s = (Serial)reader.ReadUInt32(); - - if (s.IsMobile) - { - var m = World.FindMobile(s); - - if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) - { - m.SendPropertiesTo(from); - } - } - else if (s.IsItem) - { - var item = World.FindItem(s); - - if (item?.Deleted == false && from.CanSee(item) && - Utility.InUpdateRange(from.Location, item.GetWorldLocation())) - { - item.SendPropertiesTo(from); - } - } - } - - public static void ContextMenuResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var menu = from.ContextMenu; - - from.ContextMenu = null; - - if (menu != null && from == menu.From) - { - var entity = World.FindEntity((Serial)reader.ReadUInt32()); - - if (entity != null && entity == menu.Target && from.CanSee(entity)) - { - Point3D p; - - if (entity is Mobile) - { - p = entity.Location; - } - else if (entity is Item item) - { - p = item.GetWorldLocation(); - } - else - { - return; - } - - int index = reader.ReadUInt16(); - - if (index >= 0 && index < menu.Entries.Length) - { - var e = menu.Entries[index]; - - var range = e.Range; - - if (range == -1) - { - range = 18; - } - - if (e.Enabled && from.InRange(p, range)) - { - e.OnClick(); - } - } - } - } - } - - public static void ContextMenuRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - var target = World.FindEntity((Serial)reader.ReadUInt32()); - - if (from != null && target != null && from.Map == target.Map && from.CanSee(target)) - { - var item = target as Item; - - var checkLocation = item?.GetWorldLocation() ?? target.Location; - if (!(Utility.InUpdateRange(from.Location, checkLocation) && from.CheckContextMenuDisplay(target))) - { - return; - } - - var c = new ContextMenu(from, target); - - if (c.Entries.Length > 0) - { - if (item?.RootParent is Mobile mobile && mobile != from && mobile.AccessLevel >= from.AccessLevel) - { - for (var i = 0; i < c.Entries.Length; ++i) - { - if (!c.Entries[i].NonLocalUse) - { - c.Entries[i].Enabled = false; - } - } - } - - from.ContextMenu = c; - } - } - } - - public static void BandageTarget(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) - { - var bandage = World.FindItem((Serial)reader.ReadUInt32()); - - if (bandage == null) - { - return; - } - - var target = World.FindMobile((Serial)reader.ReadUInt32()); - - if (target == null) - { - return; - } - - EventSink.InvokeBandageTargetRequest(from, bandage, target); - - from.NextActionTime = Core.TickCount + Mobile.ActionDelay; - } - else - { - from.SendActionMessage(); - } - } - - public static void TargetedSpell(NetState state, CircularBufferReader reader, ref int packetLength) - { - var spellId = (short)(reader.ReadInt16() - 1); // zero based; - - EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), spellId); - } - - public static void TargetedSkillUse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var skillId = reader.ReadInt16(); - - EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), skillId); - } - - public static void TargetByResourceMacro(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - - if (serial.IsItem) - { - EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16()); + item.SendPropertiesTo(state); } } } + + public static void ContextMenuResponse(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var menu = from.ContextMenu; + + from.ContextMenu = null; + + if (menu != null && from == menu.From) + { + var entity = World.FindEntity((Serial)reader.ReadUInt32()); + + if (entity != null && entity == menu.Target && from.CanSee(entity)) + { + Point3D p; + + if (entity is Mobile) + { + p = entity.Location; + } + else if (entity is Item item) + { + p = item.GetWorldLocation(); + } + else + { + return; + } + + int index = reader.ReadUInt16(); + + if (index >= 0 && index < menu.Entries.Length) + { + var e = menu.Entries[index]; + + var range = e.Range; + + if (range == -1) + { + range = 18; + } + + if (e.Enabled && from.InRange(p, range)) + { + e.OnClick(); + } + } + } + } + } + + public static void ContextMenuRequest(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + var target = World.FindEntity((Serial)reader.ReadUInt32()); + + if (from != null && target != null && from.Map == target.Map && from.CanSee(target)) + { + var item = target as Item; + + var checkLocation = item?.GetWorldLocation() ?? target.Location; + if (!(Utility.InUpdateRange(from.Location, checkLocation) && from.CheckContextMenuDisplay(target))) + { + return; + } + + var c = new ContextMenu(from, target); + + if (c.Entries.Length > 0) + { + if (item?.RootParent is Mobile mobile && mobile != from && mobile.AccessLevel >= from.AccessLevel) + { + for (var i = 0; i < c.Entries.Length; ++i) + { + if (!c.Entries[i].NonLocalUse) + { + c.Entries[i].Enabled = false; + } + } + } + + from.ContextMenu = c; + } + } + } + + public static void BandageTarget(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) + { + var bandage = World.FindItem((Serial)reader.ReadUInt32()); + + if (bandage == null) + { + return; + } + + var target = World.FindMobile((Serial)reader.ReadUInt32()); + + if (target == null) + { + return; + } + + EventSink.InvokeBandageTargetRequest(from, bandage, target); + + from.NextActionTime = Core.TickCount + Mobile.ActionDelay; + } + else + { + from.SendActionMessage(); + } + } + + public static void TargetedSpell(NetState state, CircularBufferReader reader, int packetLength) + { + var spellId = (short)(reader.ReadInt16() - 1); // zero based; + + EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), spellId); + } + + public static void TargetedSkillUse(NetState state, CircularBufferReader reader, int packetLength) + { + var skillId = reader.ReadInt16(); + + EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), skillId); + } + + public static void TargetByResourceMacro(NetState state, CircularBufferReader reader, int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + + if (serial.IsItem) + { + EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16()); + } + } } diff --git a/Projects/Server/Network/Packets/IncomingHousePackets.cs b/Projects/Server/Network/Packets/IncomingHousePackets.cs index 64fd2d4f3..a1ba476ab 100644 --- a/Projects/Server/Network/Packets/IncomingHousePackets.cs +++ b/Projects/Server/Network/Packets/IncomingHousePackets.cs @@ -13,18 +13,17 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Network -{ - public static class IncomingHousePackets - { - public static void Configure() - { - IncomingPackets.Register(0xFB, 2, false, ShowPublicHouseContent); - } +namespace Server.Network; - public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader, ref int packetLength) - { - var showPublicHouseContent = reader.ReadBoolean(); - } +public static class IncomingHousePackets +{ + public static unsafe void Configure() + { + IncomingPackets.Register(0xFB, 2, false, &ShowPublicHouseContent); + } + + public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader, int packetLength) + { + var showPublicHouseContent = reader.ReadBoolean(); } } diff --git a/Projects/Server/Network/Packets/IncomingItemPackets.cs b/Projects/Server/Network/Packets/IncomingItemPackets.cs index 6ad33ef4c..beb58b189 100644 --- a/Projects/Server/Network/Packets/IncomingItemPackets.cs +++ b/Projects/Server/Network/Packets/IncomingItemPackets.cs @@ -17,159 +17,117 @@ using System.Collections.Generic; using System.IO; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class IncomingItemPackets { - public static class IncomingItemPackets + public static unsafe void Configure() { - public static void Configure() + IncomingPackets.Register(0x07, 7, true, &LiftReq); + IncomingPackets.Register(new ContainerGridPacketHandler(0x08, 14, true, &DropReq)); + IncomingPackets.Register(0x13, 10, true, &EquipReq); + IncomingPackets.Register(0xEC, 0, false, &EquipMacro); + IncomingPackets.Register(0xED, 0, false, &UnequipMacro); + } + + public static void LiftReq(NetState state, CircularBufferReader reader, int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + int amount = reader.ReadUInt16(); + var item = World.FindItem(serial); + + state.Mobile.Lift(item, amount, out _, out _); + } + + public static void EquipReq(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + var item = from.Holding; + + var valid = item != null && item.HeldBy == from && item.Map == Map.Internal; + + from.Holding = null; + + if (!valid) { - IncomingPackets.Register(0x07, 7, true, LiftReq); - IncomingPackets.Register(0x08, 15, true, DropReq); - IncomingPackets.Register(0x13, 10, true, EquipReq); - IncomingPackets.Register(0xEC, 0, false, EquipMacro); - IncomingPackets.Register(0xED, 0, false, UnequipMacro); + return; } - public static void LiftReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - int amount = reader.ReadUInt16(); - var item = World.FindItem(serial); + reader.Seek(5, SeekOrigin.Current); + var to = World.FindMobile((Serial)reader.ReadUInt32()) ?? from; - state.Mobile.Lift(item, amount, out _, out _); + if (!to.AllowEquipFrom(from) || !to.EquipItem(item)) + { + item.Bounce(from); } - public static void EquipReq(NetState state, CircularBufferReader reader, ref int packetLength) + item.ClearBounce(); + } + + public static void DropReq(NetState state, CircularBufferReader reader, int packetLength) + { + reader.ReadInt32(); // serial, ignored + int x = reader.ReadInt16(); + int y = reader.ReadInt16(); + int z = reader.ReadSByte(); + + if (state.ContainerGridLines) { - var from = state.Mobile; - var item = from.Holding; - - var valid = item != null && item.HeldBy == from && item.Map == Map.Internal; - - from.Holding = null; - - if (!valid) - { - return; - } - - reader.Seek(5, SeekOrigin.Current); - var to = World.FindMobile((Serial)reader.ReadUInt32()) ?? from; - - if (!to.AllowEquipFrom(from) || !to.EquipItem(item)) - { - item.Bounce(from); - } - - item.ClearBounce(); - } - - public static void DropReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - reader.ReadInt32(); // serial, ignored - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - int z = reader.ReadSByte(); - if (state.ContainerGridLines) - { - reader.ReadByte(); // Grid Location? - } - else - { - packetLength -= 1; - } - - Serial dest = (Serial)reader.ReadUInt32(); - - var loc = new Point3D(x, y, z); - - var from = state.Mobile; - - if (dest.IsMobile) - { - from.Drop(World.FindMobile(dest), loc); - } - else if (dest.IsItem) - { - var item = World.FindItem(dest); - - if (item is BaseMulti multi && multi.AllowsRelativeDrop) - { - loc.m_X += multi.X; - loc.m_Y += multi.Y; - from.Drop(loc); - } - else - { - from.Drop(item, loc); - } - } - else - { - from.Drop(loc); - } - } - - public static void DropReq6017(NetState state, CircularBufferReader reader, ref int packetLength) - { - reader.ReadInt32(); // serial, ignored - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - int z = reader.ReadSByte(); reader.ReadByte(); // Grid Location? - Serial dest = (Serial)reader.ReadUInt32(); + } - var loc = new Point3D(x, y, z); + Serial dest = (Serial)reader.ReadUInt32(); - var from = state.Mobile; + var loc = new Point3D(x, y, z); - if (dest.IsMobile) + var from = state.Mobile; + + if (dest.IsMobile) + { + from.Drop(World.FindMobile(dest), loc); + } + else if (dest.IsItem) + { + var item = World.FindItem(dest); + + if (item is BaseMulti multi && multi.AllowsRelativeDrop) { - from.Drop(World.FindMobile(dest), loc); - } - else if (dest.IsItem) - { - var item = World.FindItem(dest); - - if (item is BaseMulti multi && multi.AllowsRelativeDrop) - { - loc.m_X += multi.X; - loc.m_Y += multi.Y; - from.Drop(loc); - } - else - { - from.Drop(item, loc); - } + loc.m_X += multi.X; + loc.m_Y += multi.Y; + from.Drop(loc); } else { - from.Drop(loc); + from.Drop(item, loc); } } - - public static void EquipMacro(NetState state, CircularBufferReader reader, ref int packetLength) + else { - int count = reader.ReadByte(); - var serialList = new List(count); - for (var i = 0; i < count; ++i) - { - serialList.Add((Serial)reader.ReadUInt32()); - } - - EventSink.InvokeEquipMacro(state.Mobile, serialList); - } - - public static void UnequipMacro(NetState state, CircularBufferReader reader, ref int packetLength) - { - int count = reader.ReadByte(); - var layers = new List(count); - for (var i = 0; i < count; ++i) - { - layers.Add((Layer)reader.ReadUInt16()); - } - - EventSink.InvokeUnequipMacro(state.Mobile, layers); + from.Drop(loc); } } + + public static void EquipMacro(NetState state, CircularBufferReader reader, int packetLength) + { + int count = reader.ReadByte(); + var serialList = new List(count); + for (var i = 0; i < count; ++i) + { + serialList.Add((Serial)reader.ReadUInt32()); + } + + EventSink.InvokeEquipMacro(state.Mobile, serialList); + } + + public static void UnequipMacro(NetState state, CircularBufferReader reader, int packetLength) + { + int count = reader.ReadByte(); + var layers = new List(count); + for (var i = 0; i < count; ++i) + { + layers.Add((Layer)reader.ReadUInt16()); + } + + EventSink.InvokeUnequipMacro(state.Mobile, layers); + } } diff --git a/Projects/Server/Network/Packets/IncomingMessagePackets.cs b/Projects/Server/Network/Packets/IncomingMessagePackets.cs index a40355498..5404bc2ed 100644 --- a/Projects/Server/Network/Packets/IncomingMessagePackets.cs +++ b/Projects/Server/Network/Packets/IncomingMessagePackets.cs @@ -15,146 +15,145 @@ using System; -namespace Server.Network +namespace Server.Network; + +[Flags] +public enum MessageType { - [Flags] - 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 static class IncomingMessagePackets +{ + private static readonly KeywordList m_KeywordList = new(); + + public static unsafe void Configure() { - Regular = 0x00, - System = 0x01, - Emote = 0x02, - Label = 0x06, - Focus = 0x07, - Whisper = 0x08, - Yell = 0x09, - Spell = 0x0A, - - Guild = 0x0D, - Alliance = 0x0E, - Command = 0x0F, - - Encoded = 0xC0 + IncomingPackets.Register(0x03, 0, true, &AsciiSpeech); + IncomingPackets.Register(0xAD, 0, true, &UnicodeSpeech); } - public static class IncomingMessagePackets + public static void AsciiSpeech(NetState state, CircularBufferReader reader, int packetLength) { - private static readonly KeywordList m_KeywordList = new(); + var from = state.Mobile; - public static void Configure() + if (from == null) { - IncomingPackets.Register(0x03, 0, true, AsciiSpeech); - IncomingPackets.Register(0xAD, 0, true, UnicodeSpeech); + return; } - public static void AsciiSpeech(NetState state, CircularBufferReader reader, ref int packetLength) + var type = (MessageType)reader.ReadByte(); + int hue = reader.ReadInt16(); + reader.ReadInt16(); // font + var text = reader.ReadAsciiSafe().Trim(); + + if (text.Length is <= 0 or > 128) { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var type = (MessageType)reader.ReadByte(); - int hue = reader.ReadInt16(); - reader.ReadInt16(); // font - var text = reader.ReadAsciiSafe().Trim(); - - if (text.Length is <= 0 or > 128) - { - return; - } - - if (!Enum.IsDefined(typeof(MessageType), type)) - { - type = MessageType.Regular; - } - - from.DoSpeech(text, Array.Empty(), type, Utility.ClipDyedHue(hue)); + return; } - public static void UnicodeSpeech(NetState state, CircularBufferReader reader, ref int packetLength) + if (!Enum.IsDefined(typeof(MessageType), type)) { - var from = state.Mobile; + type = MessageType.Regular; + } - if (from == null) + from.DoSpeech(text, Array.Empty(), type, Utility.ClipDyedHue(hue)); + } + + public static void UnicodeSpeech(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var type = (MessageType)reader.ReadByte(); + int hue = reader.ReadInt16(); + reader.ReadInt16(); // font + var lang = reader.ReadAscii(4); + string text; + + var isEncoded = (type & MessageType.Encoded) != 0; + int[] keywords; + + if (isEncoded) + { + int value = reader.ReadInt16(); + var count = (value & 0xFFF0) >> 4; + var hold = value & 0xF; + + if (count is < 0 or > 50) { return; } - var type = (MessageType)reader.ReadByte(); - int hue = reader.ReadInt16(); - reader.ReadInt16(); // font - var lang = reader.ReadAscii(4); - string text; + var keyList = m_KeywordList; - var isEncoded = (type & MessageType.Encoded) != 0; - int[] keywords; - - if (isEncoded) + for (var i = 0; i < count; ++i) { - int value = reader.ReadInt16(); - var count = (value & 0xFFF0) >> 4; - var hold = value & 0xF; + int speechID; - if (count is < 0 or > 50) + if ((i & 1) == 0) { - return; + hold <<= 8; + hold |= reader.ReadByte(); + speechID = hold; + hold = 0; + } + else + { + value = reader.ReadInt16(); + speechID = (value & 0xFFF0) >> 4; + hold = value & 0xF; } - var keyList = m_KeywordList; - - for (var i = 0; i < count; ++i) + if (!keyList.Contains(speechID)) { - int speechID; - - if ((i & 1) == 0) - { - hold <<= 8; - hold |= reader.ReadByte(); - speechID = hold; - hold = 0; - } - else - { - value = reader.ReadInt16(); - speechID = (value & 0xFFF0) >> 4; - hold = value & 0xF; - } - - if (!keyList.Contains(speechID)) - { - keyList.Add(speechID); - } + keyList.Add(speechID); } - - text = reader.ReadUTF8Safe(); - - keywords = keyList.ToArray(); - } - else - { - text = reader.ReadBigUniSafe(); - - keywords = Array.Empty(); } - text = text.Trim(); + text = reader.ReadUTF8Safe(); - if (text.Length is <= 0 or > 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)); + keywords = keyList.ToArray(); } + else + { + text = reader.ReadBigUniSafe(); + + keywords = Array.Empty(); + } + + text = text.Trim(); + + if (text.Length is <= 0 or > 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)); } } diff --git a/Projects/Server/Network/Packets/IncomingMobilePackets.cs b/Projects/Server/Network/Packets/IncomingMobilePackets.cs index eed4135ce..1cdcb7c62 100644 --- a/Projects/Server/Network/Packets/IncomingMobilePackets.cs +++ b/Projects/Server/Network/Packets/IncomingMobilePackets.cs @@ -15,152 +15,151 @@ using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class IncomingMobilePackets { - public static class IncomingMobilePackets + public static unsafe void Configure() { - public static void Configure() + IncomingPackets.Register(0x75, 35, true, &RenameRequest); + IncomingPackets.Register(0x98, 0, true, &MobileNameRequest); + IncomingPackets.Register(0xB8, 0, true, &ProfileReq); + IncomingPackets.Register(0x6F, 0, true, &SecureTrade); + } + + public static void RenameRequest(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + var targ = World.FindMobile((Serial)reader.ReadUInt32()); + + if (targ != null) { - IncomingPackets.Register(0x75, 35, true, RenameRequest); - IncomingPackets.Register(0x98, 0, true, MobileNameRequest); - IncomingPackets.Register(0xB8, 0, true, ProfileReq); - IncomingPackets.Register(0x6F, 0, true, SecureTrade); + EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe()); + } + } + + public static void MobileNameRequest(NetState state, CircularBufferReader reader, int packetLength) + { + var m = World.FindMobile((Serial)reader.ReadUInt32()); + + if (m != null && Utility.InUpdateRange(state.Mobile.Location, m.Location) && state.Mobile.CanSee(m)) + { + state.SendMobileName(m); + } + } + + public static void ProfileReq(NetState state, CircularBufferReader reader, int packetLength) + { + int type = reader.ReadByte(); + var serial = (Serial)reader.ReadUInt32(); + + var beholder = state.Mobile; + var beheld = World.FindMobile(serial); + + if (beheld == null) + { + return; } - public static void RenameRequest(NetState state, CircularBufferReader reader, ref int packetLength) + switch (type) { - var from = state.Mobile; - var targ = World.FindMobile((Serial)reader.ReadUInt32()); + case 0x00: // display request + { + EventSink.InvokeProfileRequest(beholder, beheld); - if (targ != null) - { - EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe()); - } - } - - public static void MobileNameRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var m = World.FindMobile((Serial)reader.ReadUInt32()); - - if (m != null && Utility.InUpdateRange(state.Mobile.Location, m.Location) && state.Mobile.CanSee(m)) - { - state.SendMobileName(m); - } - } - - public static void ProfileReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - int type = reader.ReadByte(); - var serial = (Serial)reader.ReadUInt32(); - - var beholder = state.Mobile; - var beheld = World.FindMobile(serial); - - if (beheld == null) - { - return; - } - - switch (type) - { - case 0x00: // display request - { - EventSink.InvokeProfileRequest(beholder, beheld); - - break; - } - case 0x01: // edit request - { - reader.ReadInt16(); // Skip - int length = reader.ReadUInt16(); - - if (length > 511) - { - return; - } - - var text = reader.ReadBigUni(length); - - EventSink.InvokeChangeProfileRequest(beholder, beheld, text); - - break; - } - } - } - - public static void SecureTrade(NetState state, CircularBufferReader reader, ref int packetLength) - { - switch (reader.ReadByte()) - { - case 1: // Cancel - { - var serial = (Serial)reader.ReadUInt32(); - - if (World.FindItem(serial) is SecureTradeContainer cont && cont.Trade != null && - (cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile)) - { - cont.Trade.Cancel(); - } - - break; - } - case 2: // Check - { - var serial = (Serial)reader.ReadUInt32(); - - if (World.FindItem(serial) is SecureTradeContainer cont) - { - var trade = cont.Trade; - - var value = reader.ReadInt32() != 0; - - if (trade != null) - { - if (trade.From.Mobile == state.Mobile) - { - trade.From.Accepted = value; - trade.Update(); - } - else if (trade.To.Mobile == state.Mobile) - { - trade.To.Accepted = value; - trade.Update(); - } - } - } - - break; - } - case 3: // Update Gold - { - var serial = (Serial)reader.ReadUInt32(); - - if (World.FindItem(serial) is SecureTradeContainer cont) - { - var gold = reader.ReadInt32(); - var plat = reader.ReadInt32(); - - var trade = cont.Trade; - - if (trade != null) - { - if (trade.From.Mobile == state.Mobile) - { - trade.From.Gold = gold; - trade.From.Plat = plat; - trade.UpdateFromCurrency(); - } - else if (trade.To.Mobile == state.Mobile) - { - trade.To.Gold = gold; - trade.To.Plat = plat; - trade.UpdateToCurrency(); - } - } - } - } break; - } + } + case 0x01: // edit request + { + reader.ReadInt16(); // Skip + int length = reader.ReadUInt16(); + + if (length > 511) + { + return; + } + + var text = reader.ReadBigUni(length); + + EventSink.InvokeChangeProfileRequest(beholder, beheld, text); + + break; + } + } + } + + public static void SecureTrade(NetState state, CircularBufferReader reader, int packetLength) + { + switch (reader.ReadByte()) + { + case 1: // Cancel + { + var serial = (Serial)reader.ReadUInt32(); + + if (World.FindItem(serial) is SecureTradeContainer cont && cont.Trade != null && + (cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile)) + { + cont.Trade.Cancel(); + } + + break; + } + case 2: // Check + { + var serial = (Serial)reader.ReadUInt32(); + + if (World.FindItem(serial) is SecureTradeContainer cont) + { + var trade = cont.Trade; + + var value = reader.ReadInt32() != 0; + + if (trade != null) + { + if (trade.From.Mobile == state.Mobile) + { + trade.From.Accepted = value; + trade.Update(); + } + else if (trade.To.Mobile == state.Mobile) + { + trade.To.Accepted = value; + trade.Update(); + } + } + } + + break; + } + case 3: // Update Gold + { + var serial = (Serial)reader.ReadUInt32(); + + if (World.FindItem(serial) is SecureTradeContainer cont) + { + var gold = reader.ReadInt32(); + var plat = reader.ReadInt32(); + + var trade = cont.Trade; + + if (trade != null) + { + if (trade.From.Mobile == state.Mobile) + { + trade.From.Gold = gold; + trade.From.Plat = plat; + trade.UpdateFromCurrency(); + } + else if (trade.To.Mobile == state.Mobile) + { + trade.To.Gold = gold; + trade.To.Plat = plat; + trade.UpdateToCurrency(); + } + } + } + } + break; } } } diff --git a/Projects/Server/Network/Packets/IncomingMovementPackets.cs b/Projects/Server/Network/Packets/IncomingMovementPackets.cs index 9dc38d598..9fc1bb0a3 100644 --- a/Projects/Server/Network/Packets/IncomingMovementPackets.cs +++ b/Projects/Server/Network/Packets/IncomingMovementPackets.cs @@ -13,88 +13,49 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Network +namespace Server.Network; + +public static class IncomingMovementPackets { - public static class IncomingMovementPackets + public static unsafe void Configure() { - public static void Configure() + IncomingPackets.Register(0x02, 7, true, &MovementReq); + // Not used by OSI, and interferes with ClassicUO/Razor protocol extensions + // IncomingPackets.Register(0xF0, 0, true, NewMovementReq); + // IncomingPackets.Register(0xF1, 9, true, TimeSyncReq); + } + + public static void NewMovementReq(NetState ns, CircularBufferReader reader) + { + var from = ns.Mobile; + + if (from == null) { - IncomingPackets.Register(0x02, 7, true, MovementReq); - // Not used by OSI, and interferes with ClassicUO/Razor protocol extensions - // IncomingPackets.Register(0xF0, 0, true, NewMovementReq); - // IncomingPackets.Register(0xF1, 9, true, TimeSyncReq); + return; } - public static void NewMovementReq(NetState ns, CircularBufferReader reader) + var steps = reader.ReadByte(); + for (int i = 0; i < steps; i++) { - var from = ns.Mobile; - - if (from == null) - { - return; - } - - var steps = reader.ReadByte(); - for (int i = 0; i < steps; i++) - { - var t1 = reader.ReadUInt64(); // start time? - var t2 = reader.ReadUInt64(); // end time? - int seq = reader.ReadByte(); - var dir = (Direction)reader.ReadByte(); - var mode = reader.ReadInt32(); // 1 = walk, 2 = run - if (mode == 2) - { - dir |= Direction.Running; - } - - // Location - reader.ReadInt32(); // x - reader.ReadInt32(); // y - reader.ReadInt32(); // z - - if (ns.Sequence == 0 && seq != 0 || !from.Move(dir)) - { - ns.SendMovementRej(seq, from); - ns.Sequence = 0; - } - else - { - ++seq; - - if (seq == 256) - { - seq = 1; - } - - ns.Sequence = seq; - } - } - } - - public static void TimeSyncReq(NetState ns, CircularBufferReader reader) - { - reader.ReadUInt64(); // Client Time? - - ns.SendTimeSyncResponse(); - } - - public static void MovementReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var dir = (Direction)reader.ReadByte(); + var t1 = reader.ReadUInt64(); // start time? + var t2 = reader.ReadUInt64(); // end time? int seq = reader.ReadByte(); - var key = reader.ReadUInt32(); - - if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) + var dir = (Direction)reader.ReadByte(); + var mode = reader.ReadInt32(); // 1 = walk, 2 = run + if (mode == 2) { - state.SendMovementRej(seq, from); - state.Sequence = 0; + dir |= Direction.Running; + } + + // Location + reader.ReadInt32(); // x + reader.ReadInt32(); // y + reader.ReadInt32(); // z + + if (ns.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + ns.SendMovementRej(seq, from); + ns.Sequence = 0; } else { @@ -105,8 +66,46 @@ namespace Server.Network seq = 1; } - state.Sequence = seq; + ns.Sequence = seq; } } } + + public static void TimeSyncReq(NetState ns, CircularBufferReader reader) + { + reader.ReadUInt64(); // Client Time? + + ns.SendTimeSyncResponse(); + } + + public static void MovementReq(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var dir = (Direction)reader.ReadByte(); + int seq = reader.ReadByte(); + var key = reader.ReadUInt32(); + + if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + state.SendMovementRej(seq, from); + state.Sequence = 0; + } + else + { + ++seq; + + if (seq == 256) + { + seq = 1; + } + + state.Sequence = seq; + } + } } diff --git a/Projects/Server/Network/Packets/IncomingPackets.cs b/Projects/Server/Network/Packets/IncomingPackets.cs index a99a86053..cb963dde5 100644 --- a/Projects/Server/Network/Packets/IncomingPackets.cs +++ b/Projects/Server/Network/Packets/IncomingPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingPackets.cs * * * @@ -13,94 +13,78 @@ * along with this program. If not, see . * *************************************************************************/ -using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class IncomingPackets { - public static class IncomingPackets + private static readonly EncodedPacketHandler[] _encodedHandlers = new EncodedPacketHandler[0x100]; + + public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Register(int packetID, int length, bool ingame, + delegate* onReceive) => + Register(new PacketHandler(packetID, length, ingame, onReceive)); + + public static void Register(PacketHandler packetHandler) { - private static readonly PacketHandler[] m_6017Handlers = new PacketHandler[0x100]; + Handlers[packetHandler.PacketID] = packetHandler; + } - private static readonly EncodedPacketHandler[] m_EncodedHandlersLow = new EncodedPacketHandler[0x100]; + public static PacketHandler GetHandler(int packetID) => Handlers[packetID]; - private static readonly Dictionary m_EncodedHandlersHigh = - new(); - - public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100]; - - public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive) + public static unsafe void RegisterEncoded( + int packetID, bool ingame, delegate* onReceive + ) + { + if (packetID is >= 0 and < 0x100) { - Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive); - m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive); - } - - public static PacketHandler GetHandler(int packetID) => Handlers[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]; - } - - m_EncodedHandlersHigh.TryGetValue(packetID, out var handler); - return handler; - } - - public static void RemoveEncodedHandler(int packetID) - { - if (packetID >= 0 && packetID < 0x100) - { - m_EncodedHandlersLow[packetID] = null; - } - else - { - m_EncodedHandlersHigh.Remove(packetID); - } - } - - public static void RegisterThrottler(int packetID, ThrottlePacketCallback t) - { - var ph = GetHandler(packetID); - - if (ph != null) - { - ph.ThrottleCallback = t; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsInfoPacket(byte packetId) - { - // These packets can arrive at any time during the login process. They're just informational. - return packetId switch - { - 0x01 => true, // Disconnect - 0x73 => true, // Ping - 0xA4 => true, // SystemInfo - 0xB1 => true, // Gump Response - 0xBB => true, // Account ID - 0xBD => true, // Client Version - 0xBE => true, // Assist Version - 0xD9 => true, // Hardware Info - 0xDD => true, // Gumps (Packed) - 0xE1 => true, // Client Type - 0xF4 => true, // CrashReport - _ => false - }; + _encodedHandlers[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive); } } + + public static EncodedPacketHandler GetEncodedHandler(int packetID) => + packetID is >= 0 and < 0x100 ? _encodedHandlers[packetID] : null; + + public static void RemoveEncodedHandler(int packetID) + { + if (packetID is >= 0 and < 0x100) + { + _encodedHandlers[packetID] = null; + } + } + + public static unsafe void RegisterThrottler(int packetID, delegate* t) + { + var ph = GetHandler(packetID); + + if (ph != null) + { + ph.ThrottleCallback = t; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInfoPacket(byte packetId) + { + // These packets can arrive at any time during the login process. They're just informational. + return packetId switch + { + 0x01 => true, // Disconnect + 0x73 => true, // Ping + 0xA4 => true, // SystemInfo + 0xB1 => true, // Gump Response + 0xBB => true, // Account ID + 0xBD => true, // Client Version + 0xBE => true, // Assist Version + 0xD9 => true, // Hardware Info + 0xDD => true, // Gumps (Packed) + 0xE1 => true, // Client Type + 0xF1 => true, // Freeshard Protocol + 0xF4 => true, // CrashReport + _ => false + }; + } } diff --git a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs index 9fe375898..50c87bce4 100644 --- a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs @@ -19,610 +19,618 @@ using Server.Diagnostics; using Server.Exceptions; using Server.Gumps; -namespace Server.Network +namespace Server.Network; + +public static class IncomingPlayerPackets { - public static class IncomingPlayerPackets + public static unsafe void Configure() { - public static void Configure() - { - IncomingPackets.Register(0x01, 5, false, Disconnect); - IncomingPackets.Register(0x05, 5, true, AttackReq); - IncomingPackets.Register(0x12, 0, true, TextCommand); - IncomingPackets.Register(0x22, 3, true, Resynchronize); - IncomingPackets.Register(0x2C, 2, true, DeathStatusResponse); - IncomingPackets.Register(0x34, 10, true, MobileQuery); - IncomingPackets.Register(0x3A, 0, true, ChangeSkillLock); - IncomingPackets.Register(0x72, 5, true, SetWarMode); - IncomingPackets.Register(0x73, 2, false, PingReq); - IncomingPackets.Register(0x7D, 13, true, MenuResponse); - IncomingPackets.Register(0x95, 9, true, HuePickerResponse); - IncomingPackets.Register(0x9A, 0, true, AsciiPromptResponse); - IncomingPackets.Register(0x9B, 258, true, HelpRequest); - IncomingPackets.Register(0xA4, 149, false, SystemInfo); - IncomingPackets.Register(0xA7, 4, true, RequestScrollWindow); - IncomingPackets.Register(0xB1, 0, true, DisplayGumpResponse); - IncomingPackets.Register(0xC2, 0, true, UnicodePromptResponse); - IncomingPackets.Register(0xC8, 2, true, SetUpdateRange); - IncomingPackets.Register(0xD0, 0, true, ConfigurationFile); - IncomingPackets.Register(0xD1, 2, true, LogoutReq); - IncomingPackets.Register(0xD7, 0, true, EncodedCommand); - IncomingPackets.Register(0xF4, 0, false, CrashReport); + IncomingPackets.Register(0x01, 5, false, &Disconnect); + IncomingPackets.Register(0x05, 5, true, &AttackReq); + IncomingPackets.Register(0x12, 0, true, &TextCommand); + IncomingPackets.Register(0x22, 3, true, &Resynchronize); + IncomingPackets.Register(0x2C, 2, true, &DeathStatusResponse); + IncomingPackets.Register(0x34, 10, true, &MobileQuery); + IncomingPackets.Register(0x3A, 0, true, &ChangeSkillLock); + IncomingPackets.Register(0x72, 5, true, &SetWarMode); + IncomingPackets.Register(0x73, 2, false, &PingReq); + IncomingPackets.Register(0x7D, 13, true, &MenuResponse); + IncomingPackets.Register(0x95, 9, true, &HuePickerResponse); + IncomingPackets.Register(0x9A, 0, true, &AsciiPromptResponse); + IncomingPackets.Register(0x9B, 258, true, &HelpRequest); + IncomingPackets.Register(0xA4, 149, false, &SystemInfo); + IncomingPackets.Register(0xA7, 4, true, &RequestScrollWindow); + IncomingPackets.Register(0xB1, 0, true, &DisplayGumpResponse); + IncomingPackets.Register(0xC2, 0, true, &UnicodePromptResponse); + IncomingPackets.Register(0xC8, 2, true, &SetUpdateRange); + IncomingPackets.Register(0xD0, 0, true, &ConfigurationFile); + IncomingPackets.Register(0xD1, 2, true, &LogoutReq); + IncomingPackets.Register(0xD7, 0, true, &EncodedCommand); + IncomingPackets.Register(0xF4, 0, false, &CrashReport); - IncomingPackets.RegisterEncoded(0x28, true, GuildGumpRequest); - IncomingPackets.RegisterEncoded(0x32, true, QuestGumpRequest); + IncomingPackets.RegisterEncoded(0x28, true, &GuildGumpRequest); + IncomingPackets.RegisterEncoded(0x32, true, &QuestGumpRequest); + } + + public static void DeathStatusResponse(NetState state, CircularBufferReader reader, int packetLength) + { + // Ignored + } + + public static void RequestScrollWindow(NetState state, CircularBufferReader reader, int packetLength) + { + int lastTip = reader.ReadInt16(); + int type = reader.ReadByte(); + } + + public static void AttackReq(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; } - public static void DeathStatusResponse(NetState state, CircularBufferReader reader, ref int packetLength) + var m = World.FindMobile((Serial)reader.ReadUInt32()); + + if (m != null) { - // Ignored + from.Attack(m); } + } - public static void RequestScrollWindow(NetState state, CircularBufferReader reader, ref int packetLength) + public static void HuePickerResponse(NetState state, CircularBufferReader reader, int packetLength) + { + var serial = reader.ReadUInt32(); + _ = reader.ReadInt16(); // Item ID + var hue = Utility.ClipDyedHue(reader.ReadInt16() & 0x3FFF); + + foreach (var huePicker in state.HuePickers) { - int lastTip = reader.ReadInt16(); - int type = reader.ReadByte(); - } - - public static void AttackReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) + if (huePicker.Serial == serial) { - return; - } - - var m = World.FindMobile((Serial)reader.ReadUInt32()); - - if (m != null) - { - from.Attack(m); - } - } - - public static void HuePickerResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = reader.ReadUInt32(); - _ = reader.ReadInt16(); // Item ID - var hue = Utility.ClipDyedHue(reader.ReadInt16() & 0x3FFF); - - foreach (var huePicker in state.HuePickers) - { - if (huePicker.Serial == serial) - { - state.RemoveHuePicker(huePicker); - huePicker.OnResponse(hue); - break; - } - } - } - - public static void SystemInfo(NetState state, CircularBufferReader reader, ref int packetLength) - { - int v1 = reader.ReadByte(); - int v2 = reader.ReadUInt16(); - int v3 = reader.ReadByte(); - var s1 = reader.ReadAscii(32); - var s2 = reader.ReadAscii(32); - var s3 = reader.ReadAscii(32); - var s4 = reader.ReadAscii(32); - int v4 = reader.ReadUInt16(); - int v5 = reader.ReadUInt16(); - var v6 = reader.ReadInt32(); - var v7 = reader.ReadInt32(); - var v8 = reader.ReadInt32(); - } - - public static void TextCommand(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - int type = reader.ReadByte(); - var command = reader.ReadAscii(); - - switch (type) - { - case 0xC7: // Animate - { - EventSink.InvokeAnimateRequest(from, command); - - break; - } - case 0x24: // Use skill - { - var tokenizer = command.Tokenize(' '); - if (!tokenizer.MoveNext() || !int.TryParse(tokenizer.Current, out var skillIndex)) - { - break; - } - - Skills.UseSkill(from, skillIndex); - - break; - } - case 0x43: // Open spellbook - { - if (!int.TryParse(command, out var booktype)) - { - booktype = 1; - } - - EventSink.InvokeOpenSpellbookRequest(from, booktype); - - break; - } - case 0x27: // Cast spell from book - { - var tokenizer = command.Tokenize(' '); - var spellID = (tokenizer.MoveNext() ? Utility.ToInt32(tokenizer.Current) : 0) - 1; - var serial = tokenizer.MoveNext() ? (Serial)Utility.ToUInt32(tokenizer.Current) : Serial.MinusOne; - - EventSink.InvokeCastSpellRequest(from, spellID, World.FindItem(serial)); - - break; - } - case 0x58: // Open door - { - EventSink.InvokeOpenDoorMacroUsed(from); - - break; - } - case 0x56: // Cast spell from macro - { - var spellID = Utility.ToInt32(command) - 1; - - EventSink.InvokeCastSpellRequest(from, spellID, null); - - break; - } - case 0xF4: // Invoke virtues from macro - { - var virtueID = Utility.ToInt32(command) - 1; - - EventSink.InvokeVirtueMacroRequest(from, virtueID); - - break; - } - case 0x2F: // Old scroll double click - { - /* - * This command is still sent for items 0xEF3 - 0xEF9 - * - * Command is one of three, depending on the item ID of the scroll: - * - [scroll serial] - * - [scroll serial] [target serial] - * - [scroll serial] [x] [y] [z] - */ - break; - } - default: - { - state.LogInfo("Unknown text-command type 0x{0:X2}: {1}", state, type, command); - break; - } - } - } - - public static void AsciiPromptResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var serial = reader.ReadUInt32(); - var prompt = reader.ReadInt32(); - var type = reader.ReadInt32(); - var text = reader.ReadAsciiSafe(); - - if (text.Length > 128) - { - return; - } - - var p = from.Prompt; - - if (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, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var serial = reader.ReadUInt32(); - var prompt = reader.ReadInt32(); - var type = reader.ReadInt32(); - var lang = reader.ReadAscii(4); - var text = reader.ReadLittleUniSafe(); - - if (text.Length > 128) - { - return; - } - - var p = from.Prompt; - - if (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, CircularBufferReader reader, ref int packetLength) - { - var serial = reader.ReadUInt32(); - int menuID = reader.ReadInt16(); // unused in our implementation - int index = reader.ReadInt16(); - int itemID = reader.ReadInt16(); - int hue = reader.ReadInt16(); - - index -= 1; // convert from 1-based to 0-based - - foreach (var menu in state.Menus) - { - if (menu.Serial == serial) - { - state.RemoveMenu(menu); - - if (index >= 0 && index < menu.EntryLength) - { - menu.OnResponse(state, index); - } - else - { - menu.OnCancel(state); - } - - break; - } - } - } - - public static void Disconnect(NetState state, CircularBufferReader reader, ref int packetLength) - { - var minusOne = reader.ReadInt32(); - } - - public static void ConfigurationFile(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - public static void LogoutReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.SendLogoutAck(); - } - - public static void ChangeSkillLock(NetState state, CircularBufferReader reader, ref int packetLength) - { - var s = state.Mobile.Skills[reader.ReadInt16()]; - - s?.SetLockNoRelay((SkillLock)reader.ReadByte()); - } - - public static void HelpRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - EventSink.InvokeHelpRequest(state.Mobile); - } - - public static void DisplayGumpResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - var typeID = reader.ReadInt32(); - var buttonID = reader.ReadInt32(); - - foreach (var gump in state.Gumps) - { - if (gump.Serial != serial || gump.TypeID != typeID) - { - continue; - } - var isWebResponse = gump.UseWebRender; - var buttonExists = buttonID == 0; // 0 is always 'close' - - if (!buttonExists) - { - foreach (var e in gump.Entries) - { - if (e is GumpButton button && button.ButtonID == buttonID) - { - buttonExists = true; - break; - } - - if (e is GumpImageTileButton tileButton && tileButton.ButtonID == buttonID) - { - buttonExists = true; - break; - } - } - } - - if (!isWebResponse && !buttonExists) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Button {buttonID} doesn't exist"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var switchCount = reader.ReadInt32(); - - if (!isWebResponse && (switchCount < 0 || switchCount > gump.m_Switches)) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Bad switch count {switchCount}"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var switches = new int[switchCount]; - - for (var i = 0; i < switches.Length; ++i) - { - switches[i] = reader.ReadInt32(); - } - - var textCount = reader.ReadInt32(); - - if (!isWebResponse && (textCount < 0 || textCount > gump.m_TextEntries)) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Bad text entry count {textCount}"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var textEntries = new TextRelay[textCount]; - - for (var i = 0; i < textEntries.Length; ++i) - { - int entryID = reader.ReadUInt16(); - int textLength = reader.ReadUInt16(); - - if (textLength > 239) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Text entry {i} is too long ({textLength})"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var text = reader.ReadBigUniSafe(textLength); - textEntries[i] = new TextRelay(entryID, text); - } - - if (!isWebResponse) - state.RemoveGump(gump); - - var prof = GumpProfile.Acquire(gump.GetType()); - - prof?.Start(); - - gump.OnResponse(state, new RelayInfo(buttonID, switches, textEntries)); - - prof?.Finish(); - - return; - } - - if (typeID == 461) - { - // Virtue gump - var switchCount = reader.Remaining >= 4 ? reader.ReadInt32() : 0; - - if (buttonID == 1 && switchCount > 0) - { - var beheld = World.FindMobile((Serial)reader.ReadUInt32()); - - if (beheld != null) - { - EventSink.InvokeVirtueGumpRequest(state.Mobile, beheld); - } - } - else - { - var beheld = World.FindMobile(serial); - - if (beheld != null) - { - EventSink.InvokeVirtueItemRequest(state.Mobile, beheld, buttonID); - } - } - } - } - - public static void SetWarMode(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.Mobile?.DelayChangeWarmode(reader.ReadBoolean()); - } - - // TODO: Throttle/make this more safe - public static void Resynchronize(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - state.SendMobileUpdate(from); - state.SendMobileIncoming(from, from); - - from.SendEverything(); - - state.Sequence = 0; - } - - public static void PingReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.SendPingAck(reader.ReadByte()); - } - - public static void SetUpdateRange(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.SendChangeUpdateRange(18); - } - - public static void MobileQuery(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - if (from == null) - { - return; - } - - reader.ReadInt32(); // 0xEDEDEDED - int type = reader.ReadByte(); - var m = World.FindMobile((Serial)reader.ReadUInt32()); - - if (m == null) - { - return; - } - - switch (type) - { - case 0x04: // Stats - { - m.OnStatsQuery(from); - break; - } - case 0x05: - { - m.OnSkillsQuery(from); - break; - } - default: - { - reader.Trace(state); - break; - } - } - } - - public static void CrashReport(NetState state, CircularBufferReader reader, ref int packetLength) - { - var clientMaj = reader.ReadByte(); - var clientMin = reader.ReadByte(); - var clientRev = reader.ReadByte(); - var clientPat = reader.ReadByte(); - - var x = reader.ReadUInt16(); - var y = reader.ReadUInt16(); - var z = reader.ReadSByte(); - var map = reader.ReadByte(); - - var account = reader.ReadAscii(32); - var character = reader.ReadAscii(32); - var ip = reader.ReadAscii(15); - - var unk1 = reader.ReadInt32(); - var exception = reader.ReadInt32(); - - var process = reader.ReadAscii(100); - var report = reader.ReadAscii(100); - - reader.ReadByte(); // 0x00 - - var offset = reader.ReadInt32(); - - int count = reader.ReadByte(); - - for (var i = 0; i < count; i++) - { - var address = reader.ReadInt32(); - } - } - - public static void GuildGumpRequest(NetState state, IEntity e, EncodedReader reader) - { - EventSink.InvokeGuildGumpRequest(state.Mobile); - } - - public static void QuestGumpRequest(NetState state, IEntity e, EncodedReader reader) - { - EventSink.InvokeQuestGumpRequest(state.Mobile); - } - - public static void EncodedCommand(NetState state, CircularBufferReader reader, ref int packetLength) - { - var e = World.FindEntity((Serial)reader.ReadUInt32()); - int packetId = reader.ReadUInt16(); - - var ph = IncomingPackets.GetEncodedHandler(packetId); - - if (ph == null) - { - reader.Trace(state); - return; - } - - if (ph.Ingame && state.Mobile == null) - { - state.LogInfo( - "Sent in-game packet (0xD7x{0:X2}) before being attached to a mobile", - packetId - ); - state.Disconnect($"Sent in-game packet (0xD7x{packetId:X2}) before being attached to a mobile."); - } - else if (ph.Ingame && state.Mobile.Deleted) - { - state.Disconnect($"Sent in-game packet(0xD7x{packetId:X2}) but mobile is deleted."); - } - else - { - ph.OnReceive(state, e, new EncodedReader(reader)); + state.RemoveHuePicker(huePicker); + huePicker.OnResponse(hue); + break; } } } + + public static void SystemInfo(NetState state, CircularBufferReader reader, int packetLength) + { + int v1 = reader.ReadByte(); + int v2 = reader.ReadUInt16(); + int v3 = reader.ReadByte(); + var s1 = reader.ReadAscii(32); + var s2 = reader.ReadAscii(32); + var s3 = reader.ReadAscii(32); + var s4 = reader.ReadAscii(32); + int v4 = reader.ReadUInt16(); + int v5 = reader.ReadUInt16(); + var v6 = reader.ReadInt32(); + var v7 = reader.ReadInt32(); + var v8 = reader.ReadInt32(); + } + + public static void TextCommand(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + int type = reader.ReadByte(); + var command = reader.ReadAscii(); + + switch (type) + { + case 0xC7: // Animate + { + EventSink.InvokeAnimateRequest(from, command); + + break; + } + case 0x24: // Use skill + { + var tokenizer = command.Tokenize(' '); + if (!tokenizer.MoveNext() || !int.TryParse(tokenizer.Current, out var skillIndex)) + { + break; + } + + Skills.UseSkill(from, skillIndex); + + break; + } + case 0x43: // Open spellbook + { + if (!int.TryParse(command, out var booktype)) + { + booktype = 1; + } + + EventSink.InvokeOpenSpellbookRequest(from, booktype); + + break; + } + case 0x27: // Cast spell from book + { + var tokenizer = command.Tokenize(' '); + var spellID = (tokenizer.MoveNext() ? Utility.ToInt32(tokenizer.Current) : 0) - 1; + var serial = tokenizer.MoveNext() ? (Serial)Utility.ToUInt32(tokenizer.Current) : Serial.MinusOne; + + EventSink.InvokeCastSpellRequest(from, spellID, World.FindItem(serial)); + + break; + } + case 0x58: // Open door + { + EventSink.InvokeOpenDoorMacroUsed(from); + + break; + } + case 0x56: // Cast spell from macro + { + var spellID = Utility.ToInt32(command) - 1; + + EventSink.InvokeCastSpellRequest(from, spellID, null); + + break; + } + case 0xF4: // Invoke virtues from macro + { + var virtueID = Utility.ToInt32(command) - 1; + + EventSink.InvokeVirtueMacroRequest(from, virtueID); + + break; + } + case 0x2F: // Old scroll double click + { + /* + * This command is still sent for items 0xEF3 - 0xEF9 + * + * Command is one of three, depending on the item ID of the scroll: + * - [scroll serial] + * - [scroll serial] [target serial] + * - [scroll serial] [x] [y] [z] + */ + break; + } + default: + { + state.LogInfo($"Unknown text-command type 0x{state:X2}: {type} ({command})"); + break; + } + } + } + + public static void AsciiPromptResponse(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var serial = reader.ReadUInt32(); + var prompt = reader.ReadInt32(); + var type = reader.ReadInt32(); + var text = reader.ReadAsciiSafe(); + + if (text.Length > 128) + { + return; + } + + var p = from.Prompt; + + if (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, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var serial = reader.ReadUInt32(); + var prompt = reader.ReadInt32(); + var type = reader.ReadInt32(); + var lang = reader.ReadAscii(4); + var text = reader.ReadLittleUniSafe(); + + if (text.Length > 128) + { + return; + } + + var p = from.Prompt; + + if (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, CircularBufferReader reader, int packetLength) + { + var serial = reader.ReadUInt32(); + int menuID = reader.ReadInt16(); // unused in our implementation + int index = reader.ReadInt16(); + int itemID = reader.ReadInt16(); + int hue = reader.ReadInt16(); + + index -= 1; // convert from 1-based to 0-based + + foreach (var menu in state.Menus) + { + if (menu.Serial == serial) + { + state.RemoveMenu(menu); + + if (index >= 0 && index < menu.EntryLength) + { + menu.OnResponse(state, index); + } + else + { + menu.OnCancel(state); + } + + break; + } + } + } + + public static void Disconnect(NetState state, CircularBufferReader reader, int packetLength) + { + var minusOne = reader.ReadInt32(); + } + + public static void ConfigurationFile(NetState state, CircularBufferReader reader, int packetLength) + { + } + + public static void LogoutReq(NetState state, CircularBufferReader reader, int packetLength) + { + state.SendLogoutAck(); + } + + public static void ChangeSkillLock(NetState state, CircularBufferReader reader, int packetLength) + { + var s = state.Mobile.Skills[reader.ReadInt16()]; + + s?.SetLockNoRelay((SkillLock)reader.ReadByte()); + } + + public static void HelpRequest(NetState state, CircularBufferReader reader, int packetLength) + { + EventSink.InvokeHelpRequest(state.Mobile); + } + + public static void DisplayGumpResponse(NetState state, CircularBufferReader reader, int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + var typeID = reader.ReadInt32(); + var buttonID = reader.ReadInt32(); + + foreach (var gump in state.Gumps) + { + if (gump.Serial != serial || gump.TypeID != typeID) + { + continue; + } + + var isWebResponse = gump.UseWebRender; + var buttonExists = buttonID == 0; // 0 is always 'close' + + if (!buttonExists) + { + foreach (var e in gump.Entries) + { + if (e is GumpButton button && button.ButtonID == buttonID) + { + buttonExists = true; + break; + } + + if (e is GumpImageTileButton tileButton && tileButton.ButtonID == buttonID) + { + buttonExists = true; + break; + } + } + } + + if (!isWebResponse && !buttonExists) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Button {buttonID} doesn't exist"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var switchCount = reader.ReadInt32(); + + if (!isWebResponse && (switchCount < 0 || switchCount > gump.m_Switches)) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Bad switch count {switchCount}"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var switches = new int[switchCount]; + + for (var i = 0; i < switches.Length; ++i) + { + switches[i] = reader.ReadInt32(); + } + + var textCount = reader.ReadInt32(); + + if (!isWebResponse && (textCount < 0 || textCount > gump.m_TextEntries)) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Bad text entry count {textCount}"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var textEntries = new TextRelay[textCount]; + + for (var i = 0; i < textEntries.Length; ++i) + { + int entryID = reader.ReadUInt16(); + int textLength = reader.ReadUInt16(); + + if (textLength > 239) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Text entry {i} is too long ({textLength})"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var text = reader.ReadBigUniSafe(textLength); + textEntries[i] = new TextRelay(entryID, text); + } + + if (!isWebResponse) + { + state.RemoveGump(gump); + } + + var prof = GumpProfile.Acquire(gump.GetType()); + + prof?.Start(); + + gump.OnResponse(state, new RelayInfo(buttonID, switches, textEntries)); + + prof?.Finish(); + + return; + } + + if (typeID == 461) + { + // Virtue gump + var switchCount = reader.Remaining >= 4 ? reader.ReadInt32() : 0; + + if (buttonID == 1 && switchCount > 0) + { + var beheld = World.FindMobile((Serial)reader.ReadUInt32()); + + if (beheld != null) + { + EventSink.InvokeVirtueGumpRequest(state.Mobile, beheld); + } + } + else + { + var beheld = World.FindMobile(serial); + + if (beheld != null) + { + EventSink.InvokeVirtueItemRequest(state.Mobile, beheld, buttonID); + } + } + } + } + + public static void SetWarMode(NetState state, CircularBufferReader reader, int packetLength) + { + state.Mobile?.DelayChangeWarmode(reader.ReadBoolean()); + } + + // TODO: Throttle/make this more safe + public static void Resynchronize(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + state.SendMobileUpdate(from); + state.SendMobileIncoming(from, from); + + from.SendEverything(); + + state.Sequence = 0; + } + + public static void PingReq(NetState state, CircularBufferReader reader, int packetLength) + { + state.SendPingAck(reader.ReadByte()); + } + + public static void SetUpdateRange(NetState state, CircularBufferReader reader, int packetLength) + { + state.SendChangeUpdateRange(18); + } + + public static void MobileQuery(NetState state, CircularBufferReader reader, int packetLength) + { + var from = state.Mobile; + if (from == null) + { + return; + } + + reader.ReadInt32(); // 0xEDEDEDED + int type = reader.ReadByte(); + var m = World.FindMobile((Serial)reader.ReadUInt32()); + + if (m == null) + { + return; + } + + switch (type) + { + case 0x04: // Stats + { + m.OnStatsQuery(from); + break; + } + case 0x05: + { + m.OnSkillsQuery(from); + break; + } + default: + { + reader.Trace(state); + break; + } + } + } + + public static void CrashReport(NetState state, CircularBufferReader reader, int packetLength) + { + var clientMaj = reader.ReadByte(); + var clientMin = reader.ReadByte(); + var clientRev = reader.ReadByte(); + var clientPat = reader.ReadByte(); + + var x = reader.ReadUInt16(); + var y = reader.ReadUInt16(); + var z = reader.ReadSByte(); + var map = reader.ReadByte(); + + var account = reader.ReadAscii(32); + var character = reader.ReadAscii(32); + var ip = reader.ReadAscii(15); + + var unk1 = reader.ReadInt32(); + var exception = reader.ReadInt32(); + + var process = reader.ReadAscii(100); + var report = reader.ReadAscii(100); + + reader.ReadByte(); // 0x00 + + var offset = reader.ReadInt32(); + + int count = reader.ReadByte(); + + for (var i = 0; i < count; i++) + { + var address = reader.ReadInt32(); + } + } + + public static void GuildGumpRequest(NetState state, IEntity e, EncodedReader reader) + { + EventSink.InvokeGuildGumpRequest(state.Mobile); + } + + public static void QuestGumpRequest(NetState state, IEntity e, EncodedReader reader) + { + EventSink.InvokeQuestGumpRequest(state.Mobile); + } + + public static unsafe void EncodedCommand(NetState state, CircularBufferReader reader, int packetLength) + { + var e = World.FindEntity((Serial)reader.ReadUInt32()); + int packetId = reader.ReadUInt16(); + + // We will add support if this is ever a real thing. + if (packetId > 0xFF) + { + var reason = $"Sent unsupported encoded packet (0xD7x{packetId:X4}"; + state.LogInfo(reason); + state.Disconnect(reason); + } + + var ph = IncomingPackets.GetEncodedHandler(packetId); + + if (ph == null) + { + reader.Trace(state); + return; + } + + if (ph.Ingame && state.Mobile == null) + { + var reason = $"Sent in-game packet (0xD7x{packetId:X4}) before being attached to a mobile."; + state.LogInfo(reason); + state.Disconnect(reason); + } + else if (ph.Ingame && state.Mobile.Deleted) + { + state.Disconnect($"Sent in-game packet(0xD7x{packetId:X4}) but mobile is deleted."); + } + else + { + ph.OnReceive(state, e, new EncodedReader(reader)); + } + } } diff --git a/Projects/Server/Network/Packets/IncomingTargetingPackets.cs b/Projects/Server/Network/Packets/IncomingTargetingPackets.cs index b87b221de..b4efa9e81 100644 --- a/Projects/Server/Network/Packets/IncomingTargetingPackets.cs +++ b/Projects/Server/Network/Packets/IncomingTargetingPackets.cs @@ -16,130 +16,129 @@ using Server.Diagnostics; using Server.Targeting; -namespace Server.Network +namespace Server.Network; + +public static class IncomingTargetingPackets { - public static class IncomingTargetingPackets + public static unsafe void Configure() { - public static void Configure() + IncomingPackets.Register(0x6C, 19, true, &TargetResponse); + } + + public static void TargetResponse(NetState state, CircularBufferReader reader, int packetLength) + { + int type = reader.ReadByte(); + var targetID = reader.ReadInt32(); + int flags = reader.ReadByte(); + var serial = (Serial)reader.ReadUInt32(); + int x = reader.ReadInt16(); + int y = reader.ReadInt16(); + reader.ReadByte(); + int z = reader.ReadSByte(); + int graphic = reader.ReadUInt16(); + + if (targetID == unchecked((int)0xDEADBEEF)) { - IncomingPackets.Register(0x6C, 19, true, TargetResponse); + return; } - public static void TargetResponse(NetState state, CircularBufferReader reader, ref int packetLength) + var from = state.Mobile; + + var t = from.Target; + + if (t == null) { - int type = reader.ReadByte(); - var targetID = reader.ReadInt32(); - int flags = reader.ReadByte(); - var serial = (Serial)reader.ReadUInt32(); - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - reader.ReadByte(); - int z = reader.ReadSByte(); - int graphic = reader.ReadUInt16(); + return; + } - if (targetID == unchecked((int)0xDEADBEEF)) + var prof = TargetProfile.Acquire(t.GetType()); + prof?.Start(); + + try + { + if (x == -1 && y == -1 && !serial.IsValid) { - return; + // User pressed escape + t.Cancel(from, TargetCancelType.Canceled); } - - var from = state.Mobile; - - var t = from.Target; - - if (t == null) + else if (t.TargetID != targetID) { - return; + // Sanity, prevent fake target } - - var prof = TargetProfile.Acquire(t.GetType()); - prof?.Start(); - - try + else { - if (x == -1 && y == -1 && !serial.IsValid) - { - // User pressed escape - t.Cancel(from, TargetCancelType.Canceled); - } - else if (t.TargetID != targetID) - { - // Sanity, prevent fake target - } - else - { - object toTarget; + object toTarget; - if (type == 1) + if (type == 1) + { + if (graphic == 0) { - if (graphic == 0) + toTarget = new LandTarget(new Point3D(x, y, z), from.Map); + } + else + { + var map = from.Map; + + if (map == null || map == Map.Internal) { - toTarget = new LandTarget(new Point3D(x, y, z), from.Map); + t.Cancel(from, TargetCancelType.Canceled); + return; } else { - var map = from.Map; + var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis); - if (map == null || map == Map.Internal) + var valid = false; + + if (state.HighSeas) + { + var id = TileData.ItemTable[graphic & TileData.MaxItemValue]; + if (id.Surface) + { + z -= id.Height; + } + } + + for (var i = 0; !valid && i < tiles.Length; ++i) + { + if (tiles[i].Z == z && tiles[i].ID == graphic) + { + valid = true; + } + } + + if (!valid) { t.Cancel(from, TargetCancelType.Canceled); return; } else { - var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis); - - var valid = false; - - if (state.HighSeas) - { - var id = TileData.ItemTable[graphic & TileData.MaxItemValue]; - if (id.Surface) - { - z -= id.Height; - } - } - - for (var i = 0; !valid && i < tiles.Length; ++i) - { - if (tiles[i].Z == z && tiles[i].ID == graphic) - { - valid = true; - } - } - - if (!valid) - { - t.Cancel(from, TargetCancelType.Canceled); - return; - } - else - { - toTarget = new StaticTarget(new Point3D(x, y, z), graphic); - } + 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); } + 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); } - finally - { - prof?.Finish(); - } + } + finally + { + prof?.Finish(); } } } diff --git a/Projects/Server/Network/Packets/IncomingVendorPackets.cs b/Projects/Server/Network/Packets/IncomingVendorPackets.cs index 4a9f9cf8a..2f11b20e4 100644 --- a/Projects/Server/Network/Packets/IncomingVendorPackets.cs +++ b/Projects/Server/Network/Packets/IncomingVendorPackets.cs @@ -15,96 +15,95 @@ using System.Collections.Generic; -namespace Server.Network +namespace Server.Network; + +public static class IncomingVendorPackets { - public static class IncomingVendorPackets + public static unsafe void Configure() { - public static void Configure() + IncomingPackets.Register(0x3B, 0, true, &VendorBuyReply); + IncomingPackets.Register(0x9F, 0, true, &VendorSellReply); + } + + public static void VendorBuyReply(NetState state, CircularBufferReader reader, int packetLength) + { + var vendor = World.FindMobile((Serial)reader.ReadUInt32()); + + if (vendor == null) { - IncomingPackets.Register(0x3B, 0, true, VendorBuyReply); - IncomingPackets.Register(0x9F, 0, true, VendorSellReply); + return; } - public static void VendorBuyReply(NetState state, CircularBufferReader reader, ref int packetLength) + var flag = reader.ReadByte(); + + if (!vendor.Deleted && Utility.InRange(vendor.Location, state.Mobile.Location, 10) && flag == 0x02) { - var vendor = World.FindMobile((Serial)reader.ReadUInt32()); + var msgSize = packetLength - 8; // Remaining bytes - if (vendor == null) + if (msgSize / 7 > 100) { return; } - var flag = reader.ReadByte(); - - if (!vendor.Deleted && Utility.InRange(vendor.Location, state.Mobile.Location, 10) && flag == 0x02) + var buyList = new List(msgSize / 7); + while (msgSize > 0) { - var msgSize = packetLength - 8; // Remaining bytes - - if (msgSize / 7 > 100) - { - return; - } - - var buyList = new List(msgSize / 7); - while (msgSize > 0) - { - var layer = reader.ReadByte(); - var serial = (Serial)reader.ReadUInt32(); - int amount = reader.ReadInt16(); - - buyList.Add(new BuyItemResponse(serial, amount)); - msgSize -= 7; - } - - if (buyList.Count <= 0 || (vendor as IVendor)?.OnBuyItems(state.Mobile, buyList) != true) - { - return; - } - } - - state.SendEndVendorBuy(vendor.Serial); - } - - public static void VendorSellReply(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - var vendor = World.FindMobile(serial); - - if (vendor == null) - { - return; - } - - if (vendor.Deleted || !Utility.InRange(vendor.Location, state.Mobile.Location, 10)) - { - state.SendEndVendorSell(vendor.Serial); - return; - } - - int count = reader.ReadUInt16(); - - if (count >= 100 || reader.Remaining != count * 6) - { - return; - } - - var sellList = new List(count); - - for (var i = 0; i < count; i++) - { - var item = World.FindItem((Serial)reader.ReadUInt32()); + var layer = reader.ReadByte(); + var serial = (Serial)reader.ReadUInt32(); int amount = reader.ReadInt16(); - if (item != null && amount > 0) - { - sellList.Add(new SellItemResponse(item, amount)); - } + buyList.Add(new BuyItemResponse(serial, amount)); + msgSize -= 7; } - if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList)) + if (buyList.Count <= 0 || (vendor as IVendor)?.OnBuyItems(state.Mobile, buyList) != true) { - state.SendEndVendorSell(vendor.Serial); + return; } } + + state.SendEndVendorBuy(vendor.Serial); + } + + public static void VendorSellReply(NetState state, CircularBufferReader reader, int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + var vendor = World.FindMobile(serial); + + if (vendor == null) + { + return; + } + + if (vendor.Deleted || !Utility.InRange(vendor.Location, state.Mobile.Location, 10)) + { + state.SendEndVendorSell(vendor.Serial); + return; + } + + int count = reader.ReadUInt16(); + + if (count >= 100 || reader.Remaining != count * 6) + { + return; + } + + var sellList = new List(count); + + for (var i = 0; i < count; i++) + { + var item = World.FindItem((Serial)reader.ReadUInt32()); + int amount = reader.ReadInt16(); + + if (item != null && amount > 0) + { + sellList.Add(new SellItemResponse(item, amount)); + } + } + + if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList)) + { + state.SendEndVendorSell(vendor.Serial); + } } } diff --git a/Projects/Server/Network/Packets/OutgoingAccountPackets.cs b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs index f49793649..1fa8ba71d 100644 --- a/Projects/Server/Network/Packets/OutgoingAccountPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs @@ -19,436 +19,445 @@ using System.Buffers; using System.Runtime.CompilerServices; using Server.Accounting; -namespace Server.Network +namespace Server.Network; + +public enum ALRReason : byte { - public enum ALRReason : byte - { - Invalid = 0, - InUse = 1, - Blocked = 2, - BadPass = 3, - Idle = 254, - BadComm = 255 - } + Invalid = 0, + InUse = 1, + Blocked = 2, + BadPass = 3, + Idle = 254, + BadComm = 255 +} - public enum PMMessage : byte - { - None = 0, - CharNoExist = 1, - CharExists = 2, - CharInWorld = 5, - LoginSyncError = 6, - IdleWarning = 7 - } +public enum PMMessage : byte +{ + None = 0, + CharNoExist = 1, + CharExists = 2, + CharInWorld = 5, + LoginSyncError = 6, + IdleWarning = 7 +} - public enum DeleteResultType - { - PasswordInvalid, - CharNotExist, - CharBeingPlayed, - CharTooYoung, - CharQueued, - BadRequest - } +public enum DeleteResultType +{ + PasswordInvalid, + CharNotExist, + CharBeingPlayed, + CharTooYoung, + CharQueued, + BadRequest +} - public static class OutgoingAccountPackets +public static class OutgoingAccountPackets +{ + /** + * Packet: 0x81 + * Length: Up to 425 bytes + * + * Displays the list of characters during the login process. + * Note: Currently Unused + */ + public static void SendChangeCharacter(this NetState ns, IAccount a) { - /** - * Packet: 0x81 - * Length: Up to 425 bytes - * - * Displays the list of characters during the login process. - * Note: Currently Unused - */ - public static void SendChangeCharacter(this NetState ns, IAccount a) + if (ns == null || a == null) { - if (ns == null || a == null) - { - return; - } - - var length = 5 + a.Length * 60; - var writer = new SpanWriter(stackalloc byte[length]); - - writer.Write((byte)0x81); // Packet ID - writer.Write((ushort)length); - writer.Write((ushort)0); // Count & Placeholder - - int count = 0; - - for (var i = 0; i < a.Length; ++i) - { - var m = a[i]; - - if (m == null) - { - writer.Clear(60); - } - else - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - - count++; - writer.WriteAscii(name, 30); - writer.Clear(30); // Password (empty) - } - } - - var position = writer.Position; - writer.Seek(3, SeekOrigin.Begin); - writer.Write((byte)count); - writer.Seek(position, SeekOrigin.Begin); - - ns.Send(writer.Span); + return; } - /** - * Packet: 0xBD - * Length: 3 bytes - * - * Sends a requests for the client version - */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 }); + var length = 5 + a.Length * 60; + var writer = new SpanWriter(stackalloc byte[length]); - /** - * Packet: 0x85 - * Length: 2 bytes - * - * Sends the result of a deletion request - */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res) => - ns?.Send(stackalloc byte[] { 0x85, (byte)res }); + writer.Write((byte)0x81); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0); // Count & Placeholder - /** - * Packet: 0x53 - * Length: 2 bytes - * - * Sends a PopupMessage with a predetermined message - */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendPopupMessage(this NetState ns, PMMessage msg) => - ns?.Send(stackalloc byte[] { 0x53, (byte)msg }); + int count = 0; - /** - * Packet: 0xB9 - * Length: 3 or 5 bytes - * - * Sends support features based on the client version - */ - public static void SendSupportedFeature(this NetState ns) + for (var i = 0; i < a.Length; ++i) { - if (ns == null) + var m = a[i]; + + if (m == null) { - return; - } - - var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; - - if (ns.Account.Limit >= 6) - { - flags |= FeatureFlags.LiveAccount; - flags &= ~FeatureFlags.UOTD; - - if (ns.Account.Limit > 6) - { - flags |= FeatureFlags.SeventhCharacterSlot; - } - else - { - flags |= FeatureFlags.SixthCharacterSlot; - } - } - - var length = ns.ExtendedSupportedFeatures ? 5 : 3; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xB9); // Packet ID - - if (ns.ExtendedSupportedFeatures) - { - writer.Write((uint)flags); + writer.Clear(60); } else { - writer.Write((ushort)flags); - } + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - ns.Send(writer.Span); + count++; + writer.WriteAscii(name, 30); + writer.Clear(30); // Password (empty) + } } - /** - * Packet: 0x1B - * Length: 37 bytes - * - * Sends login confirmation - */ - public static void SendLoginConfirmation(this NetState ns, Mobile m) + var position = writer.Position; + writer.Seek(3, SeekOrigin.Begin); + writer.Write((byte)count); + writer.Seek(position, SeekOrigin.Begin); + + ns.Send(writer.Span); + } + + /** + * Packet: 0xBD + * Length: 3 bytes + * + * Sends a requests for the client version + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 }); + + /** + * Packet: 0x85 + * Length: 2 bytes + * + * Sends the result of a deletion request + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res) => + ns?.Send(stackalloc byte[] { 0x85, (byte)res }); + + /** + * Packet: 0x53 + * Length: 2 bytes + * + * Sends a PopupMessage with a predetermined message + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendPopupMessage(this NetState ns, PMMessage msg) => + ns?.Send(stackalloc byte[] { 0x53, (byte)msg }); + + /** + * Packet: 0xB9 + * Length: 3 or 5 bytes + * + * Sends support features based on the client version + */ + public static void SendSupportedFeature(this NetState ns) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[37]); - writer.Write((byte)0x1B); // PacketID - writer.Write(m.Serial); - writer.Write(0); - writer.Write((short)m.Body); - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((short)m.Z); - writer.Write((byte)m.Direction); - writer.Write((byte)0); - writer.Write(-1); - - writer.Write(0); - - var map = m.Map; - - if (map == null || map == Map.Internal) - { - map = m.LogoutMap; - } - - writer.Write((short)(map?.Width ?? Map.Felucca.Width)); - writer.Write((short)(map?.Height ?? Map.Felucca.Height)); - writer.Clear(writer.Capacity - writer.Position); // Remaining is zero - - ns.Send(writer.Span); + return; } - /** - * Packet: 0x55 - * Length: 1 byte - * - * Sends login completion - */ - public static void SendLoginComplete(this NetState ns) + var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; + + if (ns.Account.Limit >= 6) { - ns?.Send(stackalloc byte[] { 0x55 }); + flags |= FeatureFlags.LiveAccount; + + if (ns.Account.Limit > 6) + { + flags |= FeatureFlags.SeventhCharacterSlot; + } + else + { + flags |= FeatureFlags.SixthCharacterSlot; + } } - /** - * Packet: 0x86 - * Length: Up to 424 bytes - * - * Sends updated character list - */ - public static void SendCharacterListUpdate(this NetState ns, IAccount a) + if (ExpansionInfo.ForceOldAnimations) { - if (ns == null || a == null) - { - return; - } - - var highSlot = -1; - - for (var i = a.Length - 1; i >= 0; i--) - { - if (a[i] != null) - { - highSlot = i; - break; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - var length = 4 + count * 60; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x86); // Packet ID - writer.Write((ushort)length); - - writer.Write((byte)count); - - for (int i = 0; i < count; i++) - { - var m = a[i]; - - if (m == null) - { - writer.Clear(60); - } - else - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - writer.WriteAscii(name, 30); - writer.Clear(30); // password - } - } - - ns.Send(writer.Span); + flags &= ~FeatureFlags.LBR; } - /** - * Packet: 0xA9 - * Length: 1410 or more bytes - * - * Sends list of characters and starting cities. - */ - public static void SendCharacterList(this NetState ns) + var length = ns.ExtendedSupportedFeatures ? 5 : 3; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB9); // Packet ID + + if (ns.ExtendedSupportedFeatures) { - var acct = ns?.Account; + writer.Write((uint)flags); + } + else + { + writer.Write((ushort)flags); + } - if (acct == null) + ns.Send(writer.Span); + } + + /** + * Packet: 0x1B + * Length: 37 bytes + * + * Sends login confirmation + */ + public static void SendLoginConfirmation(this NetState ns, Mobile m) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[37]); + writer.Write((byte)0x1B); // PacketID + writer.Write(m.Serial); + writer.Write(0); + writer.Write((short)m.Body); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((short)m.Z); + writer.Write((byte)m.Direction); + writer.Write((byte)0); + writer.Write(-1); + + writer.Write(0); + + var map = m.Map; + + if (map == null || map == Map.Internal) + { + map = m.LogoutMap; + } + + writer.Write((short)(map?.Width ?? Map.Felucca.Width)); + writer.Write((short)(map?.Height ?? Map.Felucca.Height)); + writer.Clear(writer.Capacity - writer.Position); // Remaining is zero + + ns.Send(writer.Span); + } + + /** + * Packet: 0x55 + * Length: 1 byte + * + * Sends login completion + */ + public static void SendLoginComplete(this NetState ns) + { + ns?.Send(stackalloc byte[] { 0x55 }); + } + + /** + * Packet: 0x86 + * Length: Up to 424 bytes + * + * Sends updated character list + */ + public static void SendCharacterListUpdate(this NetState ns, IAccount a) + { + if (ns == null || a == null) + { + return; + } + + var highSlot = -1; + + for (var i = a.Length - 1; i >= 0; i--) + { + if (a[i] != null) { - return; + highSlot = i; + break; } + } - var client70130 = ns.NewCharacterList; - var textLength = client70130 ? 32 : 31; + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + var length = 4 + count * 60; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x86); // Packet ID + writer.Write((ushort)length); - var cityInfo = ns.CityInfo; + writer.Write((byte)count); - var highSlot = -1; + for (int i = 0; i < count; i++) + { + var m = a[i]; - for (var i = acct.Length - 1; i >= 0; i--) + if (m == null) { - if (acct[i] != null) - { - highSlot = i; - break; - } + writer.Clear(60); } - - var count = Math.Max(Math.Max(highSlot + 1, acct.Limit), 5); - var length = (client70130 ? - 11 + (textLength * 2 + 25) * cityInfo.Length : - 9 + (textLength * 2 + 1) * cityInfo.Length) + count * 60; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA9); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)count); - - for (int i = 0; i < count; i++) + else { - var m = acct[i]; - - if (m == null) - { - writer.Clear(60); - } - else - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - writer.WriteAscii(name, 30); - writer.Clear(30); // password - } + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + writer.WriteAscii(name, 30); + writer.Clear(30); // password } + } - writer.Write((byte)cityInfo.Length); + ns.Send(writer.Span); + } - for (int i = 0; i < cityInfo.Length; ++i) + /** + * Packet: 0xA9 + * Length: 1410 or more bytes + * + * Sends list of characters and starting cities. + */ + public static void SendCharacterList(this NetState ns) + { + var acct = ns?.Account; + + if (acct == null) + { + return; + } + + var client70130 = ns.NewCharacterList; + var textLength = client70130 ? 32 : 31; + + var cityInfo = ns.CityInfo; + + var highSlot = -1; + + for (var i = acct.Length - 1; i >= 0; i--) + { + if (acct[i] != null) { - var ci = cityInfo[i]; - - writer.Write((byte)i); - writer.WriteAscii(ci.City, textLength); - writer.WriteAscii(ci.Building, textLength); - if (client70130) - { - writer.Write(ci.X); - writer.Write(ci.Y); - writer.Write(ci.Z); - writer.Write(ci.Map?.MapID ?? 0); - writer.Write(ci.Description); - writer.Write(0); - } + highSlot = i; + break; } + } - var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; + // Supported values are 1, 5, 6, or 7 + var count = Math.Max(highSlot + 1, acct.Limit); + if (count is not 1 and < 5) + { + count = 5; + } - if (count > 6) + var length = (client70130 ? + 11 + (textLength * 2 + 25) * cityInfo.Length : + 9 + (textLength * 2 + 1) * cityInfo.Length) + count * 60; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA9); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)count); // TODO: It is probably more proper to use count. + + for (int i = 0; i < count; i++) + { + var m = acct[i]; + + if (m == null) { - flags |= CharacterListFlags.SeventhCharacterSlot | - CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? + writer.Clear(60); } - else if (count == 6) + else { - flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot - } - else if (acct.Limit == 1) - { - flags |= CharacterListFlags.SlotLimit & - CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + writer.WriteAscii(name, 30); + writer.Clear(30); // password } + } - writer.Write((int)flags); + writer.Write((byte)cityInfo.Length); + + for (int i = 0; i < cityInfo.Length; ++i) + { + var ci = cityInfo[i]; + + writer.Write((byte)i); + writer.WriteAscii(ci.City, textLength); + writer.WriteAscii(ci.Building, textLength); if (client70130) { - writer.Write((short)-1); + writer.Write(ci.X); + writer.Write(ci.Y); + writer.Write(ci.Z); + writer.Write(ci.Map?.MapID ?? 0); + writer.Write(ci.Description); + writer.Write(0); } - - ns.Send(writer.Span); } - /** - * Packet: 0x82 - * Length: 2 bytes - * - * Sends a reason for rejecting the login - */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendAccountLoginRejected(this NetState ns, ALRReason reason) => - ns?.Send(stackalloc byte[] { 0x82, (byte)reason }); + var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; - /** - * Packet: 0xA8 - * Length: 6 + 40 bytes per server listing - * - * Sends login acknowledge with server listing - */ - public static void SendAccountLoginAck(this NetState ns) + if (count > 6) { - if (ns == null) - { - return; - } - - var info = ns.ServerInfo; - var length = 6 + 40 * info.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA8); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)0x5D); - writer.Write((ushort)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var si = info[i]; - - writer.Write((ushort)i); - writer.WriteAscii(si.Name, 32); - writer.Write((byte)si.FullPercent); - writer.Write((sbyte)si.TimeZone); - // UO only supports IPv4 - writer.Write(si.RawAddress); - } - - ns.Send(writer.Span); + flags |= CharacterListFlags.SeventhCharacterSlot | + CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? } - - /** - * Packet: 0x8C - * Length: 11 bytes - * - * Sends acknowledge play server - */ - public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId) + else if (count == 6) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[11]); - writer.Write((byte)0x8C); // Packet ID - - writer.WriteLE(si.RawAddress); - writer.Write((short)si.Address.Port); - writer.Write(authId); - - ns.Send(writer.Span); + flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot } + else if (acct.Limit == 1) + { + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + } + + writer.Write((int)flags); + if (client70130) + { + writer.Write((short)-1); + } + + ns.Send(writer.Span); + } + + /** + * Packet: 0x82 + * Length: 2 bytes + * + * Sends a reason for rejecting the login + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendAccountLoginRejected(this NetState ns, ALRReason reason) => + ns?.Send(stackalloc byte[] { 0x82, (byte)reason }); + + /** + * Packet: 0xA8 + * Length: 6 + 40 bytes per server listing + * + * Sends login acknowledge with server listing + */ + public static void SendAccountLoginAck(this NetState ns) + { + if (ns.CannotSendPackets()) + { + return; + } + + var info = ns.ServerInfo; + var length = 6 + 40 * info.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA8); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)0x5D); + writer.Write((ushort)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var si = info[i]; + + writer.Write((ushort)i); + writer.WriteAscii(si.Name, 32); + writer.Write((byte)si.FullPercent); + writer.Write((sbyte)si.TimeZone); + // UO only supports IPv4 + writer.Write(si.RawAddress); + } + + ns.Send(writer.Span); + } + + /** + * Packet: 0x8C + * Length: 11 bytes + * + * Sends acknowledge play server + */ + public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[11]); + writer.Write((byte)0x8C); // Packet ID + + writer.WriteLE(si.RawAddress); + writer.Write((short)si.Address.Port); + writer.Write(authId); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingCombatPackets.cs b/Projects/Server/Network/Packets/OutgoingCombatPackets.cs index e272a79cf..c5b23631b 100644 --- a/Projects/Server/Network/Packets/OutgoingCombatPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingCombatPackets.cs @@ -16,42 +16,41 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingCombatPackets { - public static class OutgoingCombatPackets + public static void SendSwing(this NetState ns, Serial attacker, Serial defender) { - public static void SendSwing(this NetState ns, Serial attacker, Serial defender) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[10]); - writer.Write((byte)0x2F); // Packet ID - writer.Write((byte)0); - writer.Write(attacker); - writer.Write(defender); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void SendSetWarMode(this NetState ns, bool warmode) => - ns?.Send(stackalloc byte[] { 0x72, *(byte*)&warmode, 0x00, 0x32, 0x00 }); + var writer = new SpanWriter(stackalloc byte[10]); + writer.Write((byte)0x2F); // Packet ID + writer.Write((byte)0); + writer.Write(attacker); + writer.Write(defender); - public static void SendChangeCombatant(this NetState ns, Serial combatant) + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void SendSetWarMode(this NetState ns, bool warmode) => + ns?.Send(stackalloc byte[] { 0x72, *(byte*)&warmode, 0x00, 0x32, 0x00 }); + + public static void SendChangeCombatant(this NetState ns, Serial combatant) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[5]); - writer.Write((byte)0xAA); // Packet ID - writer.Write(combatant); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[5]); + writer.Write((byte)0xAA); // Packet ID + writer.Write(combatant); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs index 69d6ffb01..f3071c135 100644 --- a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs @@ -16,195 +16,202 @@ using System; using System.Buffers; using System.IO; +using Server.Logging; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingContainerPackets { - public static class OutgoingContainerPackets + private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingContainerPackets)); + + public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1); + + public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) { - public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1); - - public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - if (ObjectPropertyList.Enabled && ns.NewSpellbook) - { - ns.SendNewSpellbookContent(book, graphic, offset, content); - } - else - { - ns.SendOldSpellbookContent(book, offset, content); - } + return; } - public static void SendNewSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) + if (ObjectPropertyList.Enabled && ns.NewSpellbook) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[23]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)23); // Length - writer.Write((short)0x1B); // Subpacket - writer.Write((short)0x01); // Command - - writer.Write(book); - writer.Write((short)graphic); - writer.Write((short)offset); - - for (var i = 0; i < 8; ++i) - { - writer.Write((byte)(content >> (i * 8))); - } - - ns.Send(writer.Span); + ns.SendNewSpellbookContent(book, graphic, offset, content); } - - public static void SendOldSpellbookContent(this NetState ns, Serial book, int offset, ulong content) + else { - if (ns == null) - { - return; - } - - var count = content.NumberOfSetBits(); - var length = 5 + count * (ns.ContainerGridLines ? 20 : 19); - - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x3C); // Packet ID - writer.Write((ushort)length); - writer.Write((ushort)count); - - ulong mask = 1; - for (var i = 0; i < 64; ++i, mask <<= 1) - { - if ((content & mask) != 0) - { - writer.Write(0x7FFFFFFF - i); - writer.Write((ushort)0); // child ItemID - writer.Write((byte)0); // ItemID offset - writer.Write((ushort)(i + offset)); // Amount - writer.Write(0); // X, Y - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location - } - writer.Write(book); - writer.Write((short)0); // Quest Hue - } - } - - ns.Send(writer.Span); - } - - public static void SendDisplayContainer(this NetState ns, Serial cont, int gumpId) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); - writer.Write((byte)0x24); // Packet ID - writer.Write(cont); - writer.Write((ushort)gumpId); - if (ns.HighSeas) - { - writer.Write((short)0x7D); - } - - ns.Send(writer.Span); - } - - public static void SendContainerContentUpdate(this NetState ns, Item item) - { - if (ns == null) - { - return; - } - - Serial parentSerial; - - if (item.Parent is Item parentItem) - { - parentSerial = parentItem.Serial; - } - else - { - Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); - parentSerial = Serial.Zero; - } - - var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); - writer.Write((byte)0x25); // Packet ID - writer.Write(item.Serial); - writer.Write((ushort)item.ItemID); - writer.Write((byte)0); // signed, itemID offset - writer.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); - writer.Write((short)item.X); - writer.Write((short)item.Y); - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location? - } - writer.Write(parentSerial); - writer.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); - - ns.Send(writer.Span); - } - - public static void SendContainerContent(this NetState ns, Mobile beholder, Item beheld) - { - if (ns == null) - { - return; - } - - var items = beheld.Items; - var count = items.Count; - - var writer = new SpanWriter(stackalloc byte[5 + items.Count * (ns.ContainerGridLines ? 20 : 19)]); - writer.Write((byte)0x3C); // Packet ID - writer.Seek(4, SeekOrigin.Current); // Length & written count - - var written = 0; - - for (var i = 0; i < count; ++i) - { - var child = items[i]; - - if (!child.Deleted && beholder.CanSee(child)) - { - var loc = child.Location; - - writer.Write(child.Serial); - writer.Write((ushort)child.ItemID); - writer.Write((byte)0); // signed, itemID offset - writer.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); - writer.Write((short)loc.X); - writer.Write((short)loc.Y); - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location? - } - writer.Write(beheld.Serial); - writer.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); - - ++written; - } - } - - writer.Seek(1, SeekOrigin.Begin); - writer.Write((ushort)writer.BytesWritten); - writer.Write((ushort)written); - writer.Seek(0, SeekOrigin.End); - - ns.Send(writer.Span); + ns.SendOldSpellbookContent(book, offset, content); } } + + public static void SendNewSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[23]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)23); // Length + writer.Write((short)0x1B); // Subpacket + writer.Write((short)0x01); // Command + + writer.Write(book); + writer.Write((short)graphic); + writer.Write((short)offset); + + for (var i = 0; i < 8; ++i) + { + writer.Write((byte)(content >> (i * 8))); + } + + ns.Send(writer.Span); + } + + public static void SendOldSpellbookContent(this NetState ns, Serial book, int offset, ulong content) + { + if (ns.CannotSendPackets()) + { + return; + } + + var count = content.NumberOfSetBits(); + var length = 5 + count * (ns.ContainerGridLines ? 20 : 19); + + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x3C); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)count); + + ulong mask = 1; + for (var i = 0; i < 64; ++i, mask <<= 1) + { + if ((content & mask) != 0) + { + writer.Write(0x7FFFFFFF - i); + writer.Write((ushort)0); // child ItemID + writer.Write((byte)0); // ItemID offset + writer.Write((ushort)(i + offset)); // Amount + writer.Write(0); // X, Y + if (ns.ContainerGridLines) + { + writer.Write((byte)0); // Grid Location + } + writer.Write(book); + writer.Write((short)0); // Quest Hue + } + } + + ns.Send(writer.Span); + } + + public static void SendDisplayContainer(this NetState ns, Serial cont, int gumpId) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); + writer.Write((byte)0x24); // Packet ID + writer.Write(cont); + writer.Write((ushort)gumpId); + if (ns.HighSeas) + { + writer.Write((short)0x7D); + } + + ns.Send(writer.Span); + } + + public static void SendContainerContentUpdate(this NetState ns, Item item) + { + if (ns.CannotSendPackets()) + { + return; + } + + Serial parentSerial; + + if (item.Parent is Item parentItem) + { + parentSerial = parentItem.Serial; + } + else + { + logger.Warning( + "ContainerContentUpdate on Item {Type} ({Serial}) where parent is not an Item", + item.GetType().Name, + item.Serial + ); + + parentSerial = Serial.Zero; + } + + var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); + writer.Write((byte)0x25); // Packet ID + writer.Write(item.Serial); + writer.Write((ushort)item.ItemID); + writer.Write((byte)0); // signed, itemID offset + writer.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); + writer.Write((short)item.X); + writer.Write((short)item.Y); + if (ns.ContainerGridLines) + { + writer.Write((byte)0); // Grid Location? + } + writer.Write(parentSerial); + writer.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); + + ns.Send(writer.Span); + } + + public static void SendContainerContent(this NetState ns, Mobile beholder, Item beheld) + { + if (ns.CannotSendPackets()) + { + return; + } + + var items = beheld.Items; + var count = items.Count; + + var writer = new SpanWriter(stackalloc byte[5 + items.Count * (ns.ContainerGridLines ? 20 : 19)]); + writer.Write((byte)0x3C); // Packet ID + writer.Seek(4, SeekOrigin.Current); // Length & written count + + var written = 0; + + for (var i = 0; i < count; ++i) + { + var child = items[i]; + + if (!child.Deleted && beholder.CanSee(child)) + { + var loc = child.Location; + + writer.Write(child.Serial); + writer.Write((ushort)child.ItemID); + writer.Write((byte)0); // signed, itemID offset + writer.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); + writer.Write((short)loc.X); + writer.Write((short)loc.Y); + if (ns.ContainerGridLines) + { + writer.Write((byte)0); // Grid Location? + } + writer.Write(beheld.Serial); + writer.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); + + ++written; + } + } + + writer.Seek(1, SeekOrigin.Begin); + writer.Write((ushort)writer.BytesWritten); + writer.Write((ushort)written); + writer.Seek(0, SeekOrigin.End); + + ns.Send(writer.Span); + } } diff --git a/Projects/Server/Network/Packets/OutgoingDamagePackets.cs b/Projects/Server/Network/Packets/OutgoingDamagePackets.cs index 02e3b989b..41d99d0b9 100644 --- a/Projects/Server/Network/Packets/OutgoingDamagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingDamagePackets.cs @@ -16,36 +16,35 @@ using System; using System.Buffers; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingDamagePackets { - public static class OutgoingDamagePackets + public static void SendDamage(this NetState ns, Serial serial, int amount) { - public static void SendDamage(this NetState ns, Serial serial, int amount) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.DamagePacket ? 7 : 11]); - - if (ns.DamagePacket) - { - writer.Write((byte)0x0B); // Packet ID - writer.Write(serial); - writer.Write((ushort)Math.Clamp(amount, 0, 0xFFFF)); - } - else - { - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)11); // Length - writer.Write((ushort)0x22); - writer.Write((byte)1); - writer.Write(serial); - writer.Write((byte)Math.Clamp(amount, 0, 0xFF)); - } - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[ns.DamagePacket ? 7 : 11]); + + if (ns.DamagePacket) + { + writer.Write((byte)0x0B); // Packet ID + writer.Write(serial); + writer.Write((ushort)Math.Clamp(amount, 0, 0xFFFF)); + } + else + { + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)11); // Length + writer.Write((ushort)0x22); + writer.Write((byte)1); + writer.Write(serial); + writer.Write((byte)Math.Clamp(amount, 0, 0xFF)); + } + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingEffectPackets.cs b/Projects/Server/Network/Packets/OutgoingEffectPackets.cs index 1b09c042e..1b3d80c8c 100644 --- a/Projects/Server/Network/Packets/OutgoingEffectPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEffectPackets.cs @@ -16,325 +16,324 @@ using System; using System.Buffers; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingEffectPackets { - public static class OutgoingEffectPackets + public const int SoundPacketLength = 12; + public const int ParticleEffectLength = 49; + public const int HuedEffectLength = 36; + public const int BoltEffectLength = 36; + + public static void SendSoundEffect(this NetState ns, int soundID, IPoint3D target) { - public const int SoundPacketLength = 12; - public const int ParticleEffectLength = 49; - public const int HuedEffectLength = 36; - public const int BoltEffectLength = 36; - - public static void SendSoundEffect(this NetState ns, int soundID, IPoint3D target) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[SoundPacketLength].InitializePacket(); - CreateSoundEffect(buffer, soundID, target); - - ns.Send(buffer); + return; } - public static void CreateSoundEffect(Span buffer, int soundID, IPoint3D target) - { - if (buffer[0] != 0) - { - return; - } + Span buffer = stackalloc byte[SoundPacketLength].InitializePacket(); + CreateSoundEffect(buffer, soundID, target); - var writer = new SpanWriter(buffer); - writer.Write((byte)0x54); // Packet ID - writer.Write((byte)1); // flags - writer.Write((short)soundID); - writer.Write((short)0); // volume - writer.Write((short)target.X); - writer.Write((short)target.Y); - writer.Write((short)target.Z); + ns.Send(buffer); + } + + public static void CreateSoundEffect(Span buffer, int soundID, IPoint3D target) + { + if (buffer[0] != 0) + { + return; } - public static void CreateParticleEffect( - Span buffer, - 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 - ) - { - if (buffer[0] != 0) - { - return; - } + var writer = new SpanWriter(buffer); + writer.Write((byte)0x54); // Packet ID + writer.Write((byte)1); // flags + writer.Write((short)soundID); + writer.Write((short)0); // volume + writer.Write((short)target.X); + writer.Write((short)target.Y); + writer.Write((short)target.Z); + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xC7); // Packet ID - writer.Write((byte)type); - writer.Write(from); - writer.Write(to); - writer.Write((short)itemID); - writer.Write((short)fromPoint.X); - writer.Write((short)fromPoint.Y); - writer.Write((sbyte)fromPoint.Z); - writer.Write((short)toPoint.X); - writer.Write((short)toPoint.Y); - writer.Write((sbyte)toPoint.Z); - writer.Write((byte)speed); - writer.Write((byte)duration); - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write(fixedDirection); - writer.Write(explode); - writer.Write(hue); - writer.Write(renderMode); - writer.Write((short)effect); - writer.Write((short)explodeEffect); - writer.Write((short)explodeSound); - writer.Write(serial); - writer.Write((byte)layer); - writer.Write((short)unknown); + public static void CreateParticleEffect( + Span buffer, + 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 + ) + { + if (buffer[0] != 0) + { + return; } - public static void CreateTargetParticleEffect( - Span buffer, - IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown - ) => CreateParticleEffect( - buffer, - EffectType.FixedFrom, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode, - effect, - 1, - 0, - e.Serial, - layer, - unknown - ); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xC7); // Packet ID + writer.Write((byte)type); + writer.Write(from); + writer.Write(to); + writer.Write((short)itemID); + writer.Write((short)fromPoint.X); + writer.Write((short)fromPoint.Y); + writer.Write((sbyte)fromPoint.Z); + writer.Write((short)toPoint.X); + writer.Write((short)toPoint.Y); + writer.Write((sbyte)toPoint.Z); + writer.Write((byte)speed); + writer.Write((byte)duration); + writer.Write((byte)0); + writer.Write((byte)0); + writer.Write(fixedDirection); + writer.Write(explode); + writer.Write(hue); + writer.Write(renderMode); + writer.Write((short)effect); + writer.Write((short)explodeEffect); + writer.Write((short)explodeSound); + writer.Write(serial); + writer.Write((byte)layer); + writer.Write((short)unknown); + } - public static void CreateLocationParticleEffect( - Span buffer, - IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown - ) => CreateParticleEffect( - buffer, - 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 static void CreateTargetParticleEffect( + Span buffer, + IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown + ) => CreateParticleEffect( + buffer, + 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 static void CreateMovingParticleEffect( - Span buffer, - 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 - ) => CreateParticleEffect( - buffer, - 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 static void CreateLocationParticleEffect( + Span buffer, + IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown + ) => CreateParticleEffect( + buffer, + 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 static void CreateHuedEffect( - Span buffer, - EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint, - int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode - ) + public static void CreateMovingParticleEffect( + Span buffer, + 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 + ) => CreateParticleEffect( + buffer, + 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 static void CreateHuedEffect( + Span buffer, + EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint, + int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode + ) + { + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xC0); // Packet ID - writer.Write((byte)type); - writer.Write(from); - writer.Write(to); - writer.Write((short)itemID); - writer.Write((short)fromPoint.X); - writer.Write((short)fromPoint.Y); - writer.Write((sbyte)fromPoint.Z); - writer.Write((short)toPoint.X); - writer.Write((short)toPoint.Y); - writer.Write((sbyte)toPoint.Z); - writer.Write((byte)speed); - writer.Write((byte)duration); - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write(fixedDirection); - writer.Write(explode); - writer.Write(hue); - writer.Write(renderMode); + return; } - public static void CreateTargetHuedEffect( - Span buffer, - IEntity e, int itemID, int speed, int duration, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.FixedFrom, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode - ); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xC0); // Packet ID + writer.Write((byte)type); + writer.Write(from); + writer.Write(to); + writer.Write((short)itemID); + writer.Write((short)fromPoint.X); + writer.Write((short)fromPoint.Y); + writer.Write((sbyte)fromPoint.Z); + writer.Write((short)toPoint.X); + writer.Write((short)toPoint.Y); + writer.Write((sbyte)toPoint.Z); + writer.Write((byte)speed); + writer.Write((byte)duration); + writer.Write((byte)0); + writer.Write((byte)0); + writer.Write(fixedDirection); + writer.Write(explode); + writer.Write(hue); + writer.Write(renderMode); + } - public static void CreateLocationHuedEffect( - Span buffer, - Point3D p, int itemID, int speed, int duration, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.FixedXYZ, - Serial.Zero, - Serial.Zero, - itemID, - p, - p, - speed, - duration, - true, - false, - hue, - renderMode - ); + public static void CreateTargetHuedEffect( + Span buffer, + IEntity e, int itemID, int speed, int duration, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.FixedFrom, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode + ); - public static void CreateMovingHuedEffect( - Span buffer, - IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, - bool explodes, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.Moving, - from.Serial, - to.Serial, - itemID, - from.Location, - to.Location, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ); + public static void CreateLocationHuedEffect( + Span buffer, + Point3D p, int itemID, int speed, int duration, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.FixedXYZ, + Serial.Zero, + Serial.Zero, + itemID, + p, + p, + speed, + duration, + true, + false, + hue, + renderMode + ); - public static void CreateMovingHuedEffect( - Span buffer, - int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, - bool fixedDirection, bool explodes, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.Moving, - Serial.Zero, - Serial.Zero, - itemID, - fromLocation, - toLocation, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ); + public static void CreateMovingHuedEffect( + Span buffer, + IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, + bool explodes, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.Moving, + from.Serial, + to.Serial, + itemID, + from.Location, + to.Location, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ); - public static void CreateMovingHuedEffect( - Span buffer, - Serial from, Serial to, int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, - bool fixedDirection, bool explodes, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.Moving, - from, - to, - itemID, - fromLocation, - toLocation, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ); + public static void CreateMovingHuedEffect( + Span buffer, + int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, + bool fixedDirection, bool explodes, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.Moving, + Serial.Zero, + Serial.Zero, + itemID, + fromLocation, + toLocation, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ); - public static void CreateBoltEffect(Span buffer, IEntity target, int hue) => CreateHuedEffect( - buffer, - EffectType.Lightning, - target.Serial, - Serial.Zero, - 0, - target.Location, - target.Location, - 0, - 0, - false, - false, - hue, - 0 - ); + public static void CreateMovingHuedEffect( + Span buffer, + Serial from, Serial to, int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, + bool fixedDirection, bool explodes, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.Moving, + from, + to, + itemID, + fromLocation, + toLocation, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ); - public static void SendScreenEffect(this NetState ns, ScreenEffectType type) + public static void CreateBoltEffect(Span buffer, IEntity target, int hue) => CreateHuedEffect( + buffer, + EffectType.Lightning, + target.Serial, + Serial.Zero, + 0, + target.Location, + target.Location, + 0, + 0, + false, + false, + hue, + 0 + ); + + public static void SendScreenEffect(this NetState ns, ScreenEffectType type) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[28]); - - writer.Write((byte)0x70); // Packet ID - writer.Write((byte)0x4); - writer.Clear(8); - writer.Write((ushort)type); - writer.Clear(16); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[28]); + + writer.Write((byte)0x70); // Packet ID + writer.Write((byte)0x4); + writer.Clear(8); + writer.Write((ushort)type); + writer.Clear(16); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs index 5bb608e36..1e546c962 100644 --- a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs @@ -17,135 +17,134 @@ using System; using System.Buffers; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingEntityPackets { - public static class OutgoingEntityPackets + public const int OPLPacketLength = 9; + public const int RemoveEntityLength = 5; + public const int MaxWorldEntityPacketLength = 26; + + public static void CreateOPLInfo(Span buffer, Item item) => + CreateOPLInfo(buffer, item.Serial, item.PropertyList.Hash); + + public static void CreateOPLInfo(Span buffer, Serial serial, int hash) { - public const int OPLPacketLength = 9; - public const int RemoveEntityLength = 5; - public const int MaxWorldEntityPacketLength = 26; - - public static void CreateOPLInfo(Span buffer, Item item) => - CreateOPLInfo(buffer, item.Serial, item.PropertyList.Hash); - - public static void CreateOPLInfo(Span buffer, Serial serial, int hash) + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xDC); // Packet ID - writer.Write(serial); - writer.Write(hash); + return; } - public static void SendOPLInfo(this NetState ns, IPropertyListObject obj) => - ns.SendOPLInfo(obj.Serial, obj.PropertyList.Hash); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xDC); // Packet ID + writer.Write(serial); + writer.Write(hash); + } - public static void SendOPLInfo(this NetState ns, Serial serial, int hash) + public static void SendOPLInfo(this NetState ns, IObjectPropertyListEntity obj) => + ns.SendOPLInfo(obj.Serial, obj.PropertyList.Hash); + + public static void SendOPLInfo(this NetState ns, Serial serial, int hash) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[OPLPacketLength].InitializePacket(); - CreateOPLInfo(buffer, serial, hash); - - ns.Send(buffer); + return; } - public static void CreateRemoveEntity(Span buffer, Serial serial) - { - if (buffer[0] != 0) - { - return; - } + Span buffer = stackalloc byte[OPLPacketLength].InitializePacket(); + CreateOPLInfo(buffer, serial, hash); - var writer = new SpanWriter(buffer); - writer.Write((byte)0x1D); // Packet ID - writer.Write(serial); + ns.Send(buffer); + } + + public static void CreateRemoveEntity(Span buffer, Serial serial) + { + if (buffer[0] != 0) + { + return; } - public static void SendRemoveEntity(this NetState ns, Serial serial) + var writer = new SpanWriter(buffer); + writer.Write((byte)0x1D); // Packet ID + writer.Write(serial); + } + + public static void SendRemoveEntity(this NetState ns, Serial serial) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[RemoveEntityLength].InitializePacket(); - CreateRemoveEntity(buffer, serial); - - ns.Send(buffer); + return; } - public static int CreateWorldEntity(Span buffer, IEntity entity, bool isHS) + Span buffer = stackalloc byte[RemoveEntityLength].InitializePacket(); + CreateRemoveEntity(buffer, serial); + + ns.Send(buffer); + } + + public static int CreateWorldEntity(Span buffer, IEntity entity, bool isHS) + { + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return buffer.Length; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xF3); // Packet ID - writer.Write((short)0x1); // command - - int type = 0; - int gfx = 0; - int amount = 1; - int hue = 0; - byte light = 0; - int flags = 0; - - if (entity is BaseMulti multi) - { - type = 2; - gfx = multi.ItemID & (isHS ? 0xFFFF : 0x7FFF); - hue = multi.Hue; - amount = multi.Amount; - } - else if (entity is Item item) - { - // type = 3 if is damageable - gfx = item.ItemID & (isHS ? 0xFFFF : 0x7FFF); - hue = item.Hue; - amount = item.Amount; - light = (byte)item.Light; - flags = item.GetPacketFlags(); - } - else if (entity is Mobile mobile) - { - type = 1; - gfx = mobile.Body; - hue = mobile.Hue; - flags = mobile.GetPacketFlags(true); - } - - writer.Write((byte)type); - writer.Write(entity.Serial); - writer.Write((ushort)gfx); - writer.Write((byte)0); - - writer.Write((short)amount); // Min - writer.Write((short)amount); // Max - - writer.Write((short)(entity.X & 0x7FFF)); - writer.Write((short)(entity.Y & 0x3FFF)); - writer.Write((sbyte)entity.Z); - - writer.Write(light); - writer.Write((short)hue); - writer.Write((byte)flags); - - if (isHS) - { - writer.Write((short)0); - } - - return writer.Position; + return buffer.Length; } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0xF3); // Packet ID + writer.Write((short)0x1); // command + + int type = 0; + int gfx = 0; + int amount = 1; + int hue = 0; + byte light = 0; + int flags = 0; + + if (entity is BaseMulti multi) + { + type = 2; + gfx = multi.ItemID & (isHS ? 0xFFFF : 0x7FFF); + hue = multi.Hue; + amount = multi.Amount; + } + else if (entity is Item item) + { + // type = 3 if is damageable + gfx = item.ItemID & (isHS ? 0xFFFF : 0x7FFF); + hue = item.Hue; + amount = item.Amount; + light = (byte)item.Light; + flags = item.GetPacketFlags(); + } + else if (entity is Mobile mobile) + { + type = 1; + gfx = mobile.Body; + hue = mobile.Hue; + flags = mobile.GetPacketFlags(true); + } + + writer.Write((byte)type); + writer.Write(entity.Serial); + writer.Write((ushort)gfx); + writer.Write((byte)0); + + writer.Write((short)amount); // Min + writer.Write((short)amount); // Max + + writer.Write((short)(entity.X & 0x7FFF)); + writer.Write((short)(entity.Y & 0x3FFF)); + writer.Write((sbyte)entity.Z); + + writer.Write(light); + writer.Write((short)hue); + writer.Write((byte)flags); + + if (isHS) + { + writer.Write((short)0); + } + + return writer.Position; } } diff --git a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs index 7f2a0a3f1..543203f6e 100644 --- a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs @@ -17,108 +17,107 @@ using System; using System.Buffers; using System.Collections.Generic; -namespace Server.Network +namespace Server.Network; + +public class EquipInfoAttribute { - public class EquipInfoAttribute + public EquipInfoAttribute(int number, int charges = -1) { - public EquipInfoAttribute(int number, int charges = -1) - { - Number = number; - Charges = charges; - } - - public int Number { get; } - - public int Charges { get; } + Number = number; + Charges = charges; } - public static class OutgoingEquipmentPackets + public int Number { get; } + + public int Charges { get; } +} + +public static class OutgoingEquipmentPackets +{ + public static void SendDisplayEquipmentInfo( + this NetState ns, + Serial serial, int number, string crafterName, bool unidentified, List attrs + ) { - public static void SendDisplayEquipmentInfo( - this NetState ns, - Serial serial, int number, string crafterName, bool unidentified, List attrs - ) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - crafterName = crafterName.DefaultIfNullOrEmpty(""); - - var length = 17 + - (crafterName.Length > 0 ? 6 + crafterName.Length : 0) + - (unidentified ? 4 : 0) + - attrs.Count * 6; - - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)length); - writer.Write((ushort)0x10); // Subpacket - writer.Write(serial); - writer.Write(number); - - if (crafterName.Length > 0) - { - writer.Write(-3); // crafted by - - writer.Write((ushort)crafterName.Length); - writer.WriteAscii(crafterName); - } - - if (unidentified) - { - writer.Write(-4); - } - - for (var i = 0; i < attrs.Count; ++i) - { - var attr = attrs[i]; - writer.Write(attr.Number); - writer.Write((short)attr.Charges); - } - - writer.Write(-1); - - ns.Send(writer.Span); + return; } - public static void SendEquipUpdate(this NetState ns, Item item) + crafterName = crafterName.DefaultIfNullOrEmpty(""); + + var length = 17 + + (crafterName.Length > 0 ? 6 + crafterName.Length : 0) + + (unidentified ? 4 : 0) + + attrs.Count * 6; + + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0x10); // Subpacket + writer.Write(serial); + writer.Write(number); + + if (crafterName.Length > 0) { - if (ns == null) - { - return; - } + writer.Write(-3); // crafted by - Serial parentSerial; - - var parent = item.Parent as Mobile; - var hue = item.Hue; - - if (parent != null) - { - parentSerial = parent.Serial; - - if (parent.SolidHueOverride >= 0) - { - hue = parent.SolidHueOverride; - } - } - else - { - Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); - parentSerial = Serial.Zero; - } - - var writer = new SpanWriter(stackalloc byte[15]); - writer.Write((byte)0x2E); // Packet ID - writer.Write(item.Serial); - writer.Write((short)item.ItemID); - writer.Write((ushort)item.Layer); - writer.Write(parentSerial); - writer.Write((short)hue); - - ns.Send(writer.Span); + writer.Write((ushort)crafterName.Length); + writer.WriteAscii(crafterName); } + + if (unidentified) + { + writer.Write(-4); + } + + for (var i = 0; i < attrs.Count; ++i) + { + var attr = attrs[i]; + writer.Write(attr.Number); + writer.Write((short)attr.Charges); + } + + writer.Write(-1); + + ns.Send(writer.Span); + } + + public static void SendEquipUpdate(this NetState ns, Item item) + { + if (ns.CannotSendPackets()) + { + return; + } + + Serial parentSerial; + + var parent = item.Parent as Mobile; + var hue = item.Hue; + + if (parent != null) + { + parentSerial = parent.Serial; + + if (parent.SolidHueOverride >= 0) + { + hue = parent.SolidHueOverride; + } + } + else + { + Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); + parentSerial = Serial.Zero; + } + + var writer = new SpanWriter(stackalloc byte[15]); + writer.Write((byte)0x2E); // Packet ID + writer.Write(item.Serial); + writer.Write((short)item.ItemID); + writer.Write((ushort)item.Layer); + writer.Write(parentSerial); + writer.Write((short)hue); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingGumpPackets.cs b/Projects/Server/Network/Packets/OutgoingGumpPackets.cs index a314b42cf..885ca7f1a 100644 --- a/Projects/Server/Network/Packets/OutgoingGumpPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingGumpPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2020 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingGumpPackets.cs * * * @@ -18,210 +18,209 @@ using System.Buffers; using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; +using Server.Buffers; using Server.Collections; using Server.Gumps; using Server.Logging; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingGumpPackets { - public static class OutgoingGumpPackets + private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets)); + + public static void SendCloseGump(this NetState ns, int typeId, int buttonId) { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets)); - - public static void SendCloseGump(this NetState ns, int typeId, int buttonId) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[13]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)13); - - writer.Write((short)0x04); - writer.Write(typeId); - writer.Write(buttonId); - - ns.Send(writer.Span); + return; } - private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly OrderedHashSet _stringsList = new(32); + var writer = new SpanWriter(stackalloc byte[13]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)13); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WritePacked(ReadOnlySpan span, ref SpanWriter writer) + writer.Write((short)0x04); + writer.Write(typeId); + writer.Write(buttonId); + + ns.Send(writer.Span); + } + + private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray(0x20000); + private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray(0x20000); + private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray(0x20000); + private static readonly OrderedHashSet _stringsList = new(32); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WritePacked(ReadOnlySpan span, ref SpanWriter writer) + { + var length = span.Length; + + if (length == 0) { - var length = span.Length; - - if (length == 0) - { - writer.Write(0); - return; - } - - var wantLength = Zlib.MaxPackSize(length); - var packBuffer = _packBuffer; - byte[] rentedBuffer = null; - - if (wantLength > packBuffer.Length) - { - packBuffer = rentedBuffer = ArrayPool.Shared.Rent(wantLength); - } - - var packLength = wantLength; - - var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default); - - if (error != ZlibError.Okay) - { - logger.Warning($"Gump compression failed {error}"); - - writer.Write(4); - writer.Write(0); - return; - } - - writer.Write(4 + packLength); - writer.Write(length); - writer.Write(packBuffer.AsSpan(0, packLength)); - - if (rentedBuffer != null) - { - ArrayPool.Shared.Return(rentedBuffer); - } + writer.Write(0); + return; } - public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries) + var wantLength = Zlib.MaxPackSize(length); + var packBuffer = _packBuffer; + byte[] rentedBuffer = null; + + if (wantLength > packBuffer.Length) { - switches = 0; - entries = 0; - - if (ns == null) - { - return; - } - - var packed = ns.Unpack; - - var layoutWriter = new SpanWriter(_layoutBuffer); - - - if (gump.UseWebRender) - { - layoutWriter.Write(Gump.WebRender); - } - - if (!gump.Draggable) - { - layoutWriter.Write(Gump.NoMove); - } - - if (!gump.Closable) - { - layoutWriter.Write(Gump.NoClose); - } - - if (!gump.Disposable) - { - layoutWriter.Write(Gump.NoDispose); - } - - if (!gump.Resizable) - { - layoutWriter.Write(Gump.NoResize); - } - - foreach (var entry in gump.Entries) - { - entry.AppendTo(ref layoutWriter, _stringsList, ref entries, ref switches); - } - - var stringsWriter = new SpanWriter(_stringsBuffer); - - foreach (var str in _stringsList) - { - var s = str ?? ""; - stringsWriter.Write((ushort)s.Length); - stringsWriter.WriteBigUni(s); - } - - int maxLength; - if (packed) - { - var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); - var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); - maxLength = 40 + worstLayoutLength + worstStringsLength; - } - else - { - maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; - } - - var writer = new SpanWriter(maxLength); - writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID - writer.Seek(2, SeekOrigin.Current); - - writer.Write(gump.Serial); - writer.Write(gump.TypeID); - writer.Write(gump.X); - writer.Write(gump.Y); - - if (packed) - { - layoutWriter.Write((byte)0); // Layout text terminator - WritePacked(layoutWriter.Span, ref writer); - - writer.Write(_stringsList.Count); - WritePacked(stringsWriter.Span, ref writer); - } - else - { - writer.Write((ushort)layoutWriter.BytesWritten); - writer.Write(layoutWriter.Span); - - writer.Write((ushort)_stringsList.Count); - writer.Write(stringsWriter.Span); - } - - writer.WritePacketLength(); - - ns.Send(writer.Span); - - layoutWriter.Dispose(); // Just in case - stringsWriter.Dispose(); // Just in case - - if (_stringsList.Count > 0) - { - _stringsList.Clear(); - } + packBuffer = rentedBuffer = STArrayPool.Shared.Rent(wantLength); } - public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption) + var packLength = wantLength; + + var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default); + + if (error != ZlibError.Okay) { - if (ns == null) - { - return; - } + logger.Warning("Gump compression failed: {Error}", error); - unknown ??= ""; - caption ??= ""; + writer.Write(4); + writer.Write(0); + return; + } - var length = 15 + unknown.Length + caption.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x8B); // Packet ID - writer.Write((ushort)length); + writer.Write(4 + packLength); + writer.Write(length); + writer.Write(packBuffer.AsSpan(0, packLength)); - writer.Write(serial); - writer.Write((short)gumpId); - writer.Write((short)(unknown.Length + 1)); - writer.WriteAsciiNull(unknown); - writer.Write((short)(caption.Length + 1)); - writer.WriteAsciiNull(caption); - - ns.Send(writer.Span); + if (rentedBuffer != null) + { + STArrayPool.Shared.Return(rentedBuffer); } } + + public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries) + { + switches = 0; + entries = 0; + + if (ns.CannotSendPackets()) + { + return; + } + + var packed = ns.Unpack; + + var layoutWriter = new SpanWriter(_layoutBuffer); + + if (gump.UseWebRender) + { + layoutWriter.Write(Gump.WebRender); + } + + if (!gump.Draggable) + { + layoutWriter.Write(Gump.NoMove); + } + + if (!gump.Closable) + { + layoutWriter.Write(Gump.NoClose); + } + + if (!gump.Disposable) + { + layoutWriter.Write(Gump.NoDispose); + } + + if (!gump.Resizable) + { + layoutWriter.Write(Gump.NoResize); + } + + foreach (var entry in gump.Entries) + { + entry.AppendTo(ref layoutWriter, _stringsList, ref entries, ref switches); + } + + var stringsWriter = new SpanWriter(_stringsBuffer); + + foreach (var str in _stringsList) + { + var s = str ?? ""; + stringsWriter.Write((ushort)s.Length); + stringsWriter.WriteBigUni(s); + } + + int maxLength; + if (packed) + { + var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); + var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); + maxLength = 40 + worstLayoutLength + worstStringsLength; + } + else + { + maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; + } + + var writer = new SpanWriter(maxLength); + writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID + writer.Seek(2, SeekOrigin.Current); + + writer.Write(gump.Serial); + writer.Write(gump.TypeID); + writer.Write(gump.X); + writer.Write(gump.Y); + + if (packed) + { + layoutWriter.Write((byte)0); // Layout text terminator + WritePacked(layoutWriter.Span, ref writer); + + writer.Write(_stringsList.Count); + WritePacked(stringsWriter.Span, ref writer); + } + else + { + writer.Write((ushort)layoutWriter.BytesWritten); + writer.Write(layoutWriter.Span); + + writer.Write((ushort)_stringsList.Count); + writer.Write(stringsWriter.Span); + } + + writer.WritePacketLength(); + + ns.Send(writer.Span); + + layoutWriter.Dispose(); // Just in case + stringsWriter.Dispose(); // Just in case + + if (_stringsList.Count > 0) + { + _stringsList.Clear(); + } + } + + public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption) + { + if (ns.CannotSendPackets()) + { + return; + } + + unknown ??= ""; + caption ??= ""; + + var length = 15 + unknown.Length + caption.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x8B); // Packet ID + writer.Write((ushort)length); + + writer.Write(serial); + writer.Write((short)gumpId); + writer.Write((short)(unknown.Length + 1)); + writer.WriteAsciiNull(unknown); + writer.Write((short)(caption.Length + 1)); + writer.WriteAsciiNull(caption); + + ns.Send(writer.Span); + } } diff --git a/Projects/Server/Network/Packets/OutgoingItemPackets.cs b/Projects/Server/Network/Packets/OutgoingItemPackets.cs index 47997e7de..7f72363ce 100644 --- a/Projects/Server/Network/Packets/OutgoingItemPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingItemPackets.cs @@ -17,84 +17,83 @@ using System; using System.Buffers; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingItemPackets { - public static class OutgoingItemPackets + public static int CreateWorldItem(Span buffer, Item item) { - public static int CreateWorldItem(Span buffer, Item item) + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - // This assumes the packet was sliced properly - return buffer.Length; - } - - var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF; - var amount = item.Amount; - var hasAmount = amount != 0; - var serial = hasAmount ? item.Serial.Value | 0x80000000 : item.Serial.Value & 0x7FFFFFFF; - var loc = item.Location; - var hue = item.Hue; - var flags = item.GetPacketFlags(); - var direction = (int)item.Direction; - var hasDirection = direction != 0; - var hasHue = hue != 0; - var hasFlags = flags != 0; - var x = hasDirection ? loc.X | 0x8000 : loc.X & 0x7FFF; - var y = (loc.Y & 0x3FFF) | (hasHue ? 0x8000 : 0) | (hasFlags ? 0x4000 : 0); - var length = 14 + (hasAmount ? 2 : 0) + - (hasDirection ? 1 : 0) + - (hasHue ? 2 : 0) + - (hasFlags ? 1 : 0); - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x1A); // Packet ID - writer.Write((ushort)length); - writer.Write(serial); - writer.Write((ushort)itemID); - - if (hasAmount) - { - writer.Write((ushort)amount); - } - - writer.Write((ushort)x); - writer.Write((ushort)y); - - if (hasDirection) - { - writer.Write((byte)direction); - } - - writer.Write((sbyte)loc.Z); - - if (hasHue) - { - writer.Write((ushort)hue); - } - - if (hasFlags) - { - writer.Write((byte)flags); - } - - return writer.BytesWritten; + // This assumes the packet was sliced properly + return buffer.Length; } - public static void SendWorldItem(this NetState ns, Item item) + var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF; + var amount = item.Amount; + var hasAmount = amount != 0; + var serial = hasAmount ? item.Serial.Value | 0x80000000 : item.Serial.Value & 0x7FFFFFFF; + var loc = item.Location; + var hue = item.Hue; + var flags = item.GetPacketFlags(); + var direction = (int)item.Direction; + var hasDirection = direction != 0; + var hasHue = hue != 0; + var hasFlags = flags != 0; + var x = hasDirection ? loc.X | 0x8000 : loc.X & 0x7FFF; + var y = (loc.Y & 0x3FFF) | (hasHue ? 0x8000 : 0) | (hasFlags ? 0x4000 : 0); + var length = 14 + (hasAmount ? 2 : 0) + + (hasDirection ? 1 : 0) + + (hasHue ? 2 : 0) + + (hasFlags ? 1 : 0); + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x1A); // Packet ID + writer.Write((ushort)length); + writer.Write(serial); + writer.Write((ushort)itemID); + + if (hasAmount) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); - - var length = ns.StygianAbyss ? - OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) : - CreateWorldItem(buffer, item); - - ns.Send(buffer[..length]); + writer.Write((ushort)amount); } + + writer.Write((ushort)x); + writer.Write((ushort)y); + + if (hasDirection) + { + writer.Write((byte)direction); + } + + writer.Write((sbyte)loc.Z); + + if (hasHue) + { + writer.Write((ushort)hue); + } + + if (hasFlags) + { + writer.Write((byte)flags); + } + + return writer.BytesWritten; + } + + public static void SendWorldItem(this NetState ns, Item item) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + + var length = ns.StygianAbyss ? + OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) : + CreateWorldItem(buffer, item); + + ns.Send(buffer[..length]); } } diff --git a/Projects/Server/Network/Packets/OutgoingLightPackets.cs b/Projects/Server/Network/Packets/OutgoingLightPackets.cs index 7f738a004..f47042f0f 100644 --- a/Projects/Server/Network/Packets/OutgoingLightPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingLightPackets.cs @@ -16,27 +16,26 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingLightPackets { - public static class OutgoingLightPackets + public static void SendPersonalLightLevel(this NetState ns, Serial serial, int level) { - public static void SendPersonalLightLevel(this NetState ns, Serial serial, int level) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[6]); - writer.Write((byte)0x4E); // Packet ID - writer.Write(serial); - writer.Write((byte)level); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendGlobalLightLevel(this NetState ns, int level = 0) => - ns?.Send(stackalloc byte[] { 0x4F, (byte)level }); + var writer = new SpanWriter(stackalloc byte[6]); + writer.Write((byte)0x4E); // Packet ID + writer.Write(serial); + writer.Write((byte)level); + + ns.Send(writer.Span); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendGlobalLightLevel(this NetState ns, int level = 0) => + ns?.Send(stackalloc byte[] { 0x4F, (byte)level }); } diff --git a/Projects/Server/Network/Packets/OutgoingMapPackets.cs b/Projects/Server/Network/Packets/OutgoingMapPackets.cs index be692358d..a0e71abb7 100644 --- a/Projects/Server/Network/Packets/OutgoingMapPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMapPackets.cs @@ -16,50 +16,49 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingMapPackets { - public static class OutgoingMapPackets + private static byte[] _mapPatchesPacket = new byte[41]; + + public static void SendMapPatches(this NetState ns) { - private static byte[] _mapPatchesPacket = new byte[41]; - - public static void SendMapPatches(this NetState ns) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - if (_mapPatchesPacket[0] == 0) - { - var writer = new SpanWriter(_mapPatchesPacket); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)41); // Length - writer.Write((ushort)0x18); // Subpacket - writer.Write(4); - - for (int i = 0; i < 4; i++) - { - var map = Map.Maps[i]; - - writer.Write(map?.Tiles.Patch.StaticBlocks ?? 0); - writer.Write(map?.Tiles.Patch.LandBlocks ?? 0); - } - } - - ns.Send(_mapPatchesPacket); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendInvalidMap(this NetState ns) => ns?.Send(stackalloc byte[] { 0xC6 }); - - public static void SendMapChange(this NetState ns, Map map) + if (_mapPatchesPacket[0] == 0) { - if (map == null) - { - return; - } + var writer = new SpanWriter(_mapPatchesPacket); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)41); // Length + writer.Write((ushort)0x18); // Subpacket + writer.Write(4); - ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x06, 0x00, 0x08, (byte)map.MapID }); + for (int i = 0; i < 4; i++) + { + var map = Map.Maps[i]; + + writer.Write(map?.Tiles.Patch.StaticBlocks ?? 0); + writer.Write(map?.Tiles.Patch.LandBlocks ?? 0); + } } + + ns.Send(_mapPatchesPacket); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendInvalidMap(this NetState ns) => ns?.Send(stackalloc byte[] { 0xC6 }); + + public static void SendMapChange(this NetState ns, Map map) + { + if (map == null) + { + return; + } + + ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x06, 0x00, 0x08, (byte)map.MapID }); } } diff --git a/Projects/Server/Network/Packets/OutgoingMenuPackets.cs b/Projects/Server/Network/Packets/OutgoingMenuPackets.cs index 75c059d07..71db481cc 100644 --- a/Projects/Server/Network/Packets/OutgoingMenuPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMenuPackets.cs @@ -5,210 +5,209 @@ using Server.ContextMenus; using Server.Menus.ItemLists; using Server.Menus.Questions; -namespace Server.Network +namespace Server.Network; + +[Flags] +public enum CMEFlags { - [Flags] - public enum CMEFlags + None = 0x00, + Disabled = 0x01, + Arrow = 0x02, + Highlighted = 0x04, + Colored = 0x20 +} + +public static class OutgoingMenuPackets +{ + public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu) { - None = 0x00, - Disabled = 0x01, - Arrow = 0x02, - Highlighted = 0x04, - Colored = 0x20 + if (ns == null || menu == null) + { + return; + } + + var question = menu.Question?.Trim(); + var questionLength = question?.Length ?? 0; + + var entries = menu.Entries; + int entriesLength = (byte)entries.Length; + + var maxLength = 11 + questionLength; + for (int i = 0; i < entriesLength; i++) + { + maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed + } + + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x7C); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(menu.Serial); + writer.Write((ushort)0); + + writer.Write((byte)questionLength); + + if (question != null) + { + writer.WriteAscii(question); + } + + writer.Write((byte)entriesLength); + + for (var i = 0; i < entriesLength; ++i) + { + var e = entries[i]; + + writer.Write((ushort)e.ItemID); + writer.Write((short)e.Hue); + + var name = e.Name?.Trim(); + + if (name == null) + { + writer.Write((byte)0); + } + else + { + var nameLength = name.Length; + writer.Write((byte)nameLength); + writer.WriteAscii(name); + } + } + + writer.WritePacketLength(); + ns.Send(writer.Span); } - public static class OutgoingMenuPackets + public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu) { - public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu) + if (ns == null || menu == null) { - if (ns == null || menu == null) - { - return; - } - - var question = menu.Question?.Trim(); - var questionLength = question?.Length ?? 0; - - var entries = menu.Entries; - int entriesLength = (byte)entries.Length; - - var maxLength = 11 + questionLength; - for (int i = 0; i < entriesLength; i++) - { - maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed - } - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x7C); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(menu.Serial); - writer.Write((ushort)0); - - writer.Write((byte)questionLength); - - if (question != null) - { - writer.WriteAscii(question); - } - - writer.Write((byte)entriesLength); - - for (var i = 0; i < entriesLength; ++i) - { - var e = entries[i]; - - writer.Write((ushort)e.ItemID); - writer.Write((short)e.Hue); - - var name = e.Name?.Trim(); - - if (name == null) - { - writer.Write((byte)0); - } - else - { - var nameLength = name.Length; - writer.Write((byte)nameLength); - writer.WriteAscii(name); - } - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + return; } - public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu) + var question = menu.Question?.Trim(); + var questionLength = question?.Length ?? 0; + + var answers = menu.Answers; + int answersLength = (byte)answers.Length; + + var maxLength = 11 + questionLength; + for (int i = 0; i < answersLength; i++) { - if (ns == null || menu == null) - { - return; - } - - var question = menu.Question?.Trim(); - var questionLength = question?.Length ?? 0; - - var answers = menu.Answers; - int answersLength = (byte)answers.Length; - - var maxLength = 11 + questionLength; - for (int i = 0; i < answersLength; i++) - { - maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed - } - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x7C); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(menu.Serial); - writer.Write((ushort)0); - writer.Write((byte)questionLength); - - if (question != null) - { - writer.WriteAscii(question); - } - - writer.Write((byte)answersLength); - - for (var i = 0; i < answersLength; ++i) - { - writer.Write(0); - - var answer = answers[i]?.Trim(); - - if (answer == null) - { - writer.Write((byte)0); - } - else - { - var nameLength = answer.Length; - writer.Write((byte)nameLength); - writer.WriteAscii(answer); - } - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed } - public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu) + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x7C); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(menu.Serial); + writer.Write((ushort)0); + writer.Write((byte)questionLength); + + if (question != null) { - if (ns == null || menu == null) - { - return; - } - - var newCommand = ns.NewHaven && menu.RequiresNewPacket; - - var entries = menu.Entries; - var entriesLength = (byte)entries.Length; - var maxLength = 12 + entriesLength * 8; - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0xBF); // Packet ID - writer.Seek(2, SeekOrigin.Current); // Length - writer.Write((short)0x14); // Subpacket - writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command - - var target = menu.Target; - writer.Write(target.Serial); - writer.Write(entriesLength); - - var p = target switch - { - Mobile _ => target.Location, - Item item => item.GetWorldLocation(), - _ => Point3D.Zero - }; - - for (var i = 0; i < entriesLength; ++i) - { - var e = entries[i]; - - var range = e.Range; - - if (range == -1) - { - range = Core.GlobalUpdateRange; - } - - var flags = e.Flags; - if (!(e.Enabled && menu.From.InRange(p, range))) - { - flags |= CMEFlags.Disabled; - } - - if (newCommand) - { - writer.Write(e.Number); - writer.Write((short)i); - writer.Write((short)flags); - } - else - { - writer.Write((short)i); - writer.Write((ushort)(e.Number - 3000000)); - - var color = e.Color & 0xFFFF; - - if (color != 0xFFFF) - { - flags |= CMEFlags.Colored; - } - - writer.Write((short)flags); - - if ((flags & CMEFlags.Colored) != 0) - { - writer.Write((short)color); - } - } - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + writer.WriteAscii(question); } + + writer.Write((byte)answersLength); + + for (var i = 0; i < answersLength; ++i) + { + writer.Write(0); + + var answer = answers[i]?.Trim(); + + if (answer == null) + { + writer.Write((byte)0); + } + else + { + var nameLength = answer.Length; + writer.Write((byte)nameLength); + writer.WriteAscii(answer); + } + } + + writer.WritePacketLength(); + ns.Send(writer.Span); + } + + public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu) + { + if (ns == null || menu == null) + { + return; + } + + var newCommand = ns.NewHaven && menu.RequiresNewPacket; + + var entries = menu.Entries; + var entriesLength = (byte)entries.Length; + var maxLength = 12 + entriesLength * 8; + + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0xBF); // Packet ID + writer.Seek(2, SeekOrigin.Current); // Length + writer.Write((short)0x14); // Subpacket + writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command + + var target = menu.Target; + writer.Write(target.Serial); + writer.Write(entriesLength); + + var p = target switch + { + Mobile _ => target.Location, + Item item => item.GetWorldLocation(), + _ => Point3D.Zero + }; + + for (var i = 0; i < entriesLength; ++i) + { + var e = entries[i]; + + var range = e.Range; + + if (range == -1) + { + range = Core.GlobalUpdateRange; + } + + var flags = e.Flags; + if (!(e.Enabled && menu.From.InRange(p, range))) + { + flags |= CMEFlags.Disabled; + } + + if (newCommand) + { + writer.Write(e.Number); + writer.Write((short)i); + writer.Write((short)flags); + } + else + { + writer.Write((short)i); + writer.Write((ushort)(e.Number - 3000000)); + + var color = e.Color & 0xFFFF; + + if (color != 0xFFFF) + { + flags |= CMEFlags.Colored; + } + + writer.Write((short)flags); + + if ((flags & CMEFlags.Colored) != 0) + { + writer.Write((short)color); + } + } + } + + writer.WritePacketLength(); + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs index a7a8d8e46..77763f92f 100644 --- a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs @@ -19,264 +19,263 @@ using System.IO; using System.Runtime.CompilerServices; using Server.Prompts; -namespace Server.Network +namespace Server.Network; + +[Flags] +public enum AffixType : byte { - [Flags] - public enum AffixType : byte + Append = 0x00, + Prepend = 0x01, + System = 0x02 +} + +public static class OutgoingMessagePackets +{ + public static void SendMessageLocalized( + this NetState ns, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" + ) { - Append = 0x00, - Prepend = 0x01, - System = 0x02 + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)]; + var length = CreateMessageLocalized( + buffer, serial, graphic, type, hue, font, number, name, args + ); + + ns.Send(buffer[..length]); } - public static class OutgoingMessagePackets + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2; + + public static int CreateMessageLocalized( + Span buffer, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" + ) { - public static void SendMessageLocalized( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" - ) + if (buffer[0] != 0) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)]; - var length = CreateMessageLocalized( - buffer, serial, graphic, type, hue, font, number, name, args - ); - - ns.Send(buffer[..length]); + return buffer.Length; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2; + name ??= ""; + args ??= ""; - public static int CreateMessageLocalized( - Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" - ) + if (hue == 0) { - if (buffer[0] != 0) - { - return buffer.Length; - } + hue = 0x3B2; + } - name ??= ""; - args ??= ""; + var writer = new SpanWriter(buffer); + writer.Write((byte)0xC1); + writer.Seek(2, SeekOrigin.Current); + writer.Write(serial); + writer.Write((short)graphic); + writer.Write((byte)type); + writer.Write((short)hue); + writer.Write((short)font); + writer.Write(number); + writer.WriteAscii(name, 30); + writer.WriteLittleUniNull(args); - if (hue == 0) - { - hue = 0x3B2; - } + writer.WritePacketLength(); + return writer.Position; + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xC1); - writer.Seek(2, SeekOrigin.Current); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - writer.Write(number); + public static void SendMessageLocalizedAffix( + this NetState ns, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, + AffixType affixType, string affix = "", string args = "" + ) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket(); + var length = CreateMessageLocalizedAffix( + buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args + ); + + ns.Send(buffer[..length]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxMessageLocalizedAffixLength(string affix, string args) => + 52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2; + + public static int CreateMessageLocalizedAffix( + Span buffer, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, + AffixType affixType, string affix = "", string args = "" + ) + { + if (buffer[0] != 0) + { + return buffer.Length; + } + + name ??= ""; + affix ??= ""; + args ??= ""; + + if (hue == 0) + { + hue = 0x3B2; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0xCC); + writer.Seek(2, SeekOrigin.Current); + writer.Write(serial); + writer.Write((short)graphic); + writer.Write((byte)type); + writer.Write((short)hue); + writer.Write((short)font); + writer.Write(number); + writer.Write((byte)affixType); + writer.WriteAscii(name, 30); + writer.WriteAsciiNull(affix); + writer.WriteBigUniNull(args); + + writer.WritePacketLength(); + return writer.Position; + } + + public static void SendMessage( + this NetState ns, + Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text + ) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket(); + var length = CreateMessage( + buffer, + serial, + graphic, + type, + hue, + font, + ascii, + lang, + name, + text + ); + + ns.Send(buffer[..length]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2; + + public static int CreateMessage( + Span buffer, + Serial serial, + int graphic, + MessageType type, + int hue, + int font, + bool ascii, + string lang, + string name, + string text + ) + { + if (buffer[0] != 0) + { + return buffer.Length; + } + + name ??= ""; + text ??= ""; + lang ??= "ENU"; + + if (hue == 0) + { + hue = 0x3B2; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)(ascii ? 0x1C : 0xAE)); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(serial); + writer.Write((short)graphic); + writer.Write((byte)type); + writer.Write((short)hue); + writer.Write((short)font); + if (ascii) + { writer.WriteAscii(name, 30); - writer.WriteLittleUniNull(args); - - writer.WritePacketLength(); - return writer.Position; + writer.WriteAsciiNull(text); } - - public static void SendMessageLocalizedAffix( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, - AffixType affixType, string affix = "", string args = "" - ) + else { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket(); - var length = CreateMessageLocalizedAffix( - buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args - ); - - ns.Send(buffer[..length]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedAffixLength(string affix, string args) => - 52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2; - - public static int CreateMessageLocalizedAffix( - Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, - AffixType affixType, string affix = "", string args = "" - ) - { - if (buffer[0] != 0) - { - return buffer.Length; - } - - name ??= ""; - affix ??= ""; - args ??= ""; - - if (hue == 0) - { - hue = 0x3B2; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xCC); - writer.Seek(2, SeekOrigin.Current); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - writer.Write(number); - writer.Write((byte)affixType); + writer.WriteAscii(lang, 4); writer.WriteAscii(name, 30); - writer.WriteAsciiNull(affix); - writer.WriteBigUniNull(args); - - writer.WritePacketLength(); - return writer.Position; - } - - public static void SendMessage( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text - ) - { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket(); - var length = CreateMessage( - buffer, - serial, - graphic, - type, - hue, - font, - ascii, - lang, - name, - text - ); - - ns.Send(buffer[..length]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2; - - public static int CreateMessage( - Span buffer, - Serial serial, - int graphic, - MessageType type, - int hue, - int font, - bool ascii, - string lang, - string name, - string text - ) - { - if (buffer[0] != 0) - { - return buffer.Length; - } - - name ??= ""; - text ??= ""; - lang ??= "ENU"; - - if (hue == 0) - { - hue = 0x3B2; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)(ascii ? 0x1C : 0xAE)); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - if (ascii) - { - writer.WriteAscii(name, 30); - writer.WriteAsciiNull(text); - } - else - { - writer.WriteAscii(lang, 4); - writer.WriteAscii(name, 30); - writer.WriteBigUniNull(text); - } - - writer.WritePacketLength(); - return writer.Position; - } - - public static void SendFollowMessage(this NetState ns, Serial s1, Serial s2) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[9]); - writer.Write((byte)0x15); // Packet ID - writer.Write(s1); - writer.Write(s2); - - ns.Send(writer.Span); - } - - public static void SendPrompt(this NetState ns, Prompt prompt) - { - if (ns == null || prompt == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[21]); - writer.Write((byte)0xC2); // Packet ID - writer.Write((ushort)21); - writer.Write(prompt.Serial); - writer.Write(prompt.Serial); - writer.Clear(10); - - ns.Send(writer.Span); - } - - public static void SendHelpResponse(this NetState ns, Serial s, string text) - { - text = text?.Trim() ?? ""; - - if (ns == null || text.Length == 0) - { - return; - } - - var length = 9 + text.Length * 2; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xB7); - writer.Write((ushort)length); - writer.Write(s); writer.WriteBigUniNull(text); - - ns.Send(writer.Span); } + + writer.WritePacketLength(); + return writer.Position; + } + + public static void SendFollowMessage(this NetState ns, Serial s1, Serial s2) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[9]); + writer.Write((byte)0x15); // Packet ID + writer.Write(s1); + writer.Write(s2); + + ns.Send(writer.Span); + } + + public static void SendPrompt(this NetState ns, Prompt prompt) + { + if (ns == null || prompt == null) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[21]); + writer.Write((byte)0xC2); // Packet ID + writer.Write((ushort)21); + writer.Write(prompt.Serial); + writer.Write(prompt.Serial); + writer.Clear(10); + + ns.Send(writer.Span); + } + + public static void SendHelpResponse(this NetState ns, Serial s, string text) + { + text = text?.Trim() ?? ""; + + if (ns == null || text.Length == 0) + { + return; + } + + var length = 9 + text.Length * 2; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB7); + writer.Write((ushort)length); + writer.Write(s); + writer.WriteBigUniNull(text); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs index 36a7a61ef..271af7d97 100644 --- a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs @@ -17,693 +17,698 @@ using System; using System.Buffers; using System.IO; using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingMobilePackets { - public static class OutgoingMobilePackets + public const int BondedStatusPacketLength = 11; + public const int DeathAnimationPacketLength = 13; + public const int MobileMovingPacketLength = 17; + public const int MobileMovingPacketCacheHeight = 7 * 2; // 7 notoriety, 2 client versions + public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength * MobileMovingPacketCacheHeight; + public const int AttributeMaximum = 100; + public const int MobileAttributePacketLength = 9; + public const int MobileAttributesPacketLength = 17; + public const int MobileAnimationPacketLength = 14; + public const int NewMobileAnimationPacketLength = 10; + public const int MobileHealthbarPacketLength = 12; + public const int MobileStatusCompactLength = 43; + public const int MobileStatusLength = 70; + public const int MobileStatusAOSLength = 88; + public const int MobileStatusMLLength = 91; + public const int MobileStatusHSLength = 121; + + public static bool ExtendedStatus { get; private set; } = true; + + public static void Configure() { - public const int BondedStatusPacketLength = 11; - public const int DeathAnimationPacketLength = 13; - public const int MobileMovingPacketLength = 17; - public const int MobileMovingPacketCacheHeight = 16; // 8 notoriety, 2 client versions - public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength * MobileMovingPacketCacheHeight; - public const int AttributeMaximum = 100; - public const int MobileAttributePacketLength = 9; - public const int MobileAttributesPacketLength = 17; - public const int MobileAnimationPacketLength = 14; - public const int NewMobileAnimationPacketLength = 10; - public const int MobileHealthbarPacketLength = 12; - public const int MobileStatusCompactLength = 43; - public const int MobileStatusMaxLength = 121; + ExtendedStatus = ServerConfiguration.GetSetting("client.showExtendedStatus", true); + } - public static bool ExtendedStatus { get; set; } - - public static void Initialize() + public static void CreateBondedStatus(Span buffer, Serial serial, bool bonded) + { + if (buffer[0] != 0) { - ExtendedStatus = ServerConfiguration.GetOrUpdateSetting("extendedStatus", false); + return; } - public static void CreateBondedStatus(Span buffer, Serial serial, bool bonded) - { - if (buffer[0] != 0) - { - return; - } + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)11); // Length + writer.Write((ushort)0x19); // Subpacket ID + writer.Write((byte)0); // Command + writer.Write(serial); + writer.Write(bonded); + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)11); // Length - writer.Write((ushort)0x19); // Subpacket ID - writer.Write((byte)0); // Command - writer.Write(serial); - writer.Write(bonded); + public static void SendBondedStatus(this NetState ns, Serial serial, bool bonded) + { + if (ns.CannotSendPackets()) + { + return; } - public static void SendBondedStatus(this NetState ns, Serial serial, bool bonded) + var writer = new SpanWriter(stackalloc byte[11]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)11); // Length + writer.Write((ushort)0x19); // Subpacket ID + writer.Write((byte)0); // Command + writer.Write(serial); + writer.Write(bonded); + + ns.Send(writer.Span); + } + + public static void CreateDeathAnimation(Span buffer, Serial killed, Serial corpse) + { + if (buffer[0] != 0) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[11]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)11); // Length - writer.Write((ushort)0x19); // Subpacket ID - writer.Write((byte)0); // Command - writer.Write(serial); - writer.Write(bonded); - - ns.Send(writer.Span); + return; } - public static void CreateDeathAnimation(Span buffer, Serial killed, Serial corpse) - { - if (buffer[0] != 0) - { - return; - } + var writer = new SpanWriter(buffer); + writer.Write((byte)0xAF); // Packet ID + writer.Write(killed); + writer.Write(corpse); + writer.Write(0); // ?? + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xAF); // Packet ID - writer.Write(killed); - writer.Write(corpse); - writer.Write(0); // ?? + public static void SendDeathAnimation(this NetState ns, Serial killed, Serial corpse) + { + if (ns.CannotSendPackets()) + { + return; } - public static void SendDeathAnimation(this NetState ns, Serial killed, Serial corpse) - { - if (ns == null) - { - return; - } + Span span = stackalloc byte[DeathAnimationPacketLength]; + CreateDeathAnimation(span, killed, corpse); + ns.Send(span); + } - Span span = stackalloc byte[DeathAnimationPacketLength]; - CreateDeathAnimation(span, killed, corpse); - ns.Send(span); + public static void CreateMobileMoving(Span buffer, Mobile m, int noto, bool stygianAbyss) + { + if (buffer[0] != 0) + { + return; } - public static void CreateMobileMoving(Span buffer, Mobile m, int noto, bool stygianAbyss) + var loc = m.Location; + var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x77); // Packet ID + writer.Write(m.Serial); + writer.Write((short)m.Body); + writer.Write((short)loc.m_X); + writer.Write((short)loc.m_Y); + writer.Write((sbyte)loc.m_Z); + writer.Write((byte)m.Direction); + writer.Write((short)hue); + writer.Write((byte)m.GetPacketFlags(stygianAbyss)); + writer.Write((byte)noto); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMobileMoving(this NetState ns, Mobile source, Mobile target) => + ns.SendMobileMoving(target, Notoriety.Compute(source, target)); + + public static void SendMobileMoving(this NetState ns, Mobile target, int noto) + { + if (ns.CannotSendPackets()) { - if (buffer[0] != 0) - { - return; - } - - var loc = m.Location; - var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x77); // Packet ID - writer.Write(m.Serial); - writer.Write((short)m.Body); - writer.Write((short)loc.m_X); - writer.Write((short)loc.m_Y); - writer.Write((sbyte)loc.m_Z); - writer.Write((byte)m.Direction); - writer.Write((short)hue); - writer.Write((byte)m.GetPacketFlags(stygianAbyss)); - writer.Write((byte)noto); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMobileMoving(this NetState ns, Mobile source, Mobile target) => - ns.SendMobileMoving(target, Notoriety.Compute(source, target)); + Span buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); + CreateMobileMoving(buffer, target, noto, ns.StygianAbyss); + ns.Send(buffer); + } - public static void SendMobileMoving(this NetState ns, Mobile target, int noto) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMobileMovingUsingCache(this NetState ns, Span cache, Mobile source, Mobile target) => + ns.SendMobileMovingUsingCache(cache, target, Notoriety.Compute(source, target)); + + // Requires a buffer of 14 packets, 17 bytes per packet (238 bytes). + // Requires cache to have the first byte of each packet initially zeroed. + public static void SendMobileMovingUsingCache(this NetState ns, Span cache, Mobile target, int noto) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); - CreateMobileMoving(buffer, target, noto, ns.StygianAbyss); - ns.Send(buffer); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMobileMovingUsingCache(this NetState ns, Span2D cache, Mobile source, Mobile target) => - ns.SendMobileMovingUsingCache(cache, target, Notoriety.Compute(source, target)); + var stygianAbyss = ns.StygianAbyss; + // Indexes 0-6 for pre-SA, and 7-13 for SA + var row = noto + (stygianAbyss ? 6 : -1); + var buffer = cache.Slice(row * MobileMovingPacketLength, MobileMovingPacketLength); + CreateMobileMoving(buffer, target, noto, stygianAbyss); - // Requires a buffer of 16 packets, 17bytes per packet (272 bytes). - // Requires cache to have the first byte of each packet zeroed. - public static void SendMobileMovingUsingCache(this NetState ns, Span2D cache, Mobile target, int noto) + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteAttribute( + this ref SpanWriter writer, int max, int cur, bool normalize = false, bool reverse = false + ) + { + if (normalize && max != 0) { - if (ns == null) + if (reverse) { - return; - } - - var stygianAbyss = ns.StygianAbyss; - var row = noto * 2 + (stygianAbyss ? 1 : 0); - var buffer = cache.GetRowSpan(row); - CreateMobileMoving(buffer, target, noto, stygianAbyss); - - ns.Send(buffer); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WriteAttribute( - this ref SpanWriter writer, int max, int cur, bool normalize = false, bool reverse = false - ) - { - if (normalize && max != 0) - { - if (reverse) - { - writer.Write((short)(cur * AttributeMaximum / max)); - writer.Write((short)AttributeMaximum); - } - else - { - writer.Write((short)AttributeMaximum); - writer.Write((short)(cur * AttributeMaximum / max)); - } + writer.Write((short)(cur * AttributeMaximum / max)); + writer.Write((short)AttributeMaximum); } else { - if (reverse) - { - writer.Write((short)cur); - writer.Write((short)max); - } - else - { - writer.Write((short)max); - writer.Write((short)cur); - } + writer.Write((short)AttributeMaximum); + writer.Write((short)(cur * AttributeMaximum / max)); } } - - public static void SendMobileHits(this NetState ns, Mobile m, bool normalize = false) + else { - if (ns == null) + if (reverse) { - return; - } - - Span span = stackalloc byte[MobileAttributePacketLength]; - CreateMobileHits(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileHits(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xA1); // Packet ID - writer.Write(m.Serial); - writer.WriteAttribute(m.HitsMax, m.Hits, normalize); - } - - public static void SendMobileMana(this NetState ns, Mobile m, bool normalize = false) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAttributePacketLength]; - CreateMobileMana(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileMana(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xA2); // Packet ID - writer.Write(m.Serial); - writer.WriteAttribute(m.ManaMax, m.Mana, normalize); - } - - public static void SendMobileStam(this NetState ns, Mobile m, bool normalize = false) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAttributePacketLength]; - CreateMobileStam(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileStam(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xA3); // Packet ID - writer.Write(m.Serial); - writer.WriteAttribute(m.StamMax, m.Stam, normalize); - } - - public static void SendMobileAttributes(this NetState ns, Mobile m, bool normalize = false) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAttributesPacketLength]; - CreateMobileAttributes(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileAttributes(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x2D); // Packet ID - writer.Write(m.Serial); - - writer.WriteAttribute(m.HitsMax, m.Hits, normalize); - writer.WriteAttribute(m.ManaMax, m.Mana, normalize); - writer.WriteAttribute(m.StamMax, m.Stam, normalize); - } - - public static void SendMobileName(this NetState ns, Mobile m) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[37]); - writer.Write((byte)0x98); // Packet ID - writer.Write((ushort)37); - writer.Write(m.Serial); - writer.WriteAscii(m.Name ?? "", 29); - writer.Write((byte)0); // Null terminator - - ns.Send(writer.Span); - } - - public static void CreateMobileAnimation( - Span buffer, - Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay - ) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x6E); // Packet ID - writer.Write(mobile); - writer.Write((short)action); - writer.Write((short)frameCount); - writer.Write((short)repeatCount); - writer.Write(!forward); // protocol has really "reverse" but I find this more intuitive - writer.Write(repeat); - writer.Write((byte)delay); - } - - public static void SendMobileAnimation( - this NetState ns, - Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay - ) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAnimationPacketLength]; - CreateMobileAnimation(span, mobile, action, frameCount, repeatCount, forward, repeat, delay); - ns.Send(span); - } - - public static void CreateNewMobileAnimation( - Span buffer, - Serial mobile, int action, int frameCount, int delay - ) - { - var writer = new SpanWriter(buffer); - writer.Write((byte)0xE2); // Packet ID - writer.Write(mobile); - writer.Write((short)action); - writer.Write((short)frameCount); - writer.Write((byte)delay); - } - - public static void SendNewMobileAnimation(this NetState ns, Serial mobile, int action, int frameCount, int delay) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[NewMobileAnimationPacketLength]; - CreateNewMobileAnimation(span, mobile, action, frameCount, delay); - ns.Send(span); - } - - public static void SendMobileHealthbar(this NetState ns, Mobile m, Healthbar healthbar) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileHealthbarPacketLength]; - CreateMobileHealthbar(span, m, healthbar); - ns.Send(span); - } - - public static void CreateMobileHealthbar(Span buffer, Mobile m, Healthbar healthbar) - { - if (buffer[0] != 0) - { - return; - } - - switch (healthbar) - { - case Healthbar.Poison: - { - CreateMobileHealthbar(buffer, m.Serial, Healthbar.Poison, m.Poison?.Level + 1 ?? 0); - break; - } - case Healthbar.Yellow: - { - CreateMobileHealthbar(buffer, m.Serial, Healthbar.Yellow, m.Blessed || m.YellowHealthbar ? 1 : 0); - break; - } - default: - { - Console.WriteLine("Packets: Invalid Healthbar {0} in {1}", healthbar, nameof(CreateMobileHealthbar)); - CreateMobileHealthbar(buffer, m.Serial, Healthbar.Normal, 0); - break; - } - } - } - - public static void CreateMobileHealthbar(Span buffer, Serial serial, Healthbar healthbar, int level) - { - var writer = new SpanWriter(buffer); - writer.Write((byte)0x17); // Packet ID - writer.Write((ushort)12); - writer.Write(serial); - writer.Write((short)1); // Show bar - writer.Write((short)healthbar); - writer.Write((byte)level); // 0 is off for that bar type - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateMobileStatusCompact(Span buffer, Mobile m, bool canBeRenamed) => - CreateMobileStatus(buffer, null, m, 0, canBeRenamed); - - public static void SendMobileStatusCompact(this NetState ns, Mobile m, bool canBeRenamed) - { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[MobileStatusCompactLength]; - CreateMobileStatusCompact(buffer, m, canBeRenamed); - - ns.Send(buffer); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMobileStatus(this NetState ns, Mobile m) => ns.SendMobileStatus(m, m); - - public static void SendMobileStatus(this NetState ns, Mobile beholder, Mobile beheld) - { - if (ns == null || beheld == null) - { - return; - } - - Span buffer = stackalloc byte[MobileStatusMaxLength]; - int version; - - if (beholder != beheld) - { - version = 0; - } - else if (Core.HS && ns.ExtendedStatus) - { - version = 6; - } - else if (Core.ML && ns.SupportsExpansion(Expansion.ML)) - { - /* - * For the ML era, the version value must be 5 if the original UO distribution - * is used and the client is not lower than version 5 - */ - version = ExtendedStatus ? 6 : 5; + writer.Write((short)cur); + writer.Write((short)max); } else { - version = Core.AOS ? 4 : 3; + writer.Write((short)max); + writer.Write((short)cur); } + } + } - var length = CreateMobileStatus(buffer, beholder, beheld, version, beheld.CanBeRenamedBy(beholder)); - ns.Send(buffer[..length]); + public static void SendMobileHits(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; } - public static int CreateMobileStatus( - Span buffer, Mobile beholder, Mobile beheld, int version, bool canBeRenamed - ) + Span span = stackalloc byte[MobileAttributePacketLength]; + CreateMobileHits(span, m, normalize); + ns.Send(span); + } + + public static void CreateMobileHits(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return buffer.Length; - } + return; + } - var name = beheld.Name ?? ""; + var writer = new SpanWriter(buffer); + writer.Write((byte)0xA1); // Packet ID + writer.Write(m.Serial); + writer.WriteAttribute(m.HitsMax, m.Hits, normalize); + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0x11); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(beheld.Serial); - writer.WriteAscii(name, 30); - writer.WriteAttribute(beheld.HitsMax, beheld.Hits, version == 0, true); - writer.Write(canBeRenamed); - writer.Write((byte)version); + public static void SendMobileMana(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; + } - if (version <= 0) - { - writer.WritePacketLength(); - return writer.Position; - } + Span span = stackalloc byte[MobileAttributePacketLength]; + CreateMobileMana(span, m, normalize); + ns.Send(span); + } - writer.Write(beheld.Female); + public static void CreateMobileMana(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) + { + return; + } - writer.Write((short)beheld.Str); - writer.Write((short)beheld.Dex); - writer.Write((short)beheld.Int); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xA2); // Packet ID + writer.Write(m.Serial); + writer.WriteAttribute(m.ManaMax, m.Mana, normalize); + } - writer.Write((short)beheld.Stam); - writer.Write((short)beheld.StamMax); - writer.Write((short)beheld.Mana); - writer.Write((short)beheld.ManaMax); + public static void SendMobileStam(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; + } - writer.Write(beheld.TotalGold); - writer.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); - writer.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); + Span span = stackalloc byte[MobileAttributePacketLength]; + CreateMobileStam(span, m, normalize); + ns.Send(span); + } - if (version >= 5) - { - writer.Write((short)beheld.MaxWeight); - writer.Write((byte)(beheld.Race?.RaceID + 1 ?? 0)); // Would be 0x00 if it's a non-ML enabled account but... - } + public static void CreateMobileStam(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) + { + return; + } - writer.Write((short)beheld.StatCap); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xA3); // Packet ID + writer.Write(m.Serial); + writer.WriteAttribute(m.StamMax, m.Stam, normalize); + } - writer.Write((byte)beheld.Followers); - writer.Write((byte)beheld.FollowersMax); + public static void SendMobileAttributes(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; + } - if (version >= 4) - { - writer.Write((short)beheld.FireResistance); // Fire - writer.Write((short)beheld.ColdResistance); // Cold - writer.Write((short)beheld.PoisonResistance); // Poison - writer.Write((short)beheld.EnergyResistance); // Energy - writer.Write((short)beheld.Luck); // Luck + Span span = stackalloc byte[MobileAttributesPacketLength]; + CreateMobileAttributes(span, m, normalize); + ns.Send(span); + } - var weapon = beheld.Weapon; + public static void CreateMobileAttributes(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) + { + return; + } - int min = 0, max = 0; - weapon?.GetStatusDamage(beheld, out min, out max); - writer.Write((short)min); // Damage min - writer.Write((short)max); // Damage max + var writer = new SpanWriter(buffer); + writer.Write((byte)0x2D); // Packet ID + writer.Write(m.Serial); - writer.Write(beheld.TithingPoints); - } + writer.WriteAttribute(m.HitsMax, m.Hits, normalize); + writer.WriteAttribute(m.ManaMax, m.Mana, normalize); + writer.WriteAttribute(m.StamMax, m.Stam, normalize); + } - if (version >= 6) - { - for (var i = 0; i < 15; ++i) + public static void SendMobileName(this NetState ns, Mobile m) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[37]); + writer.Write((byte)0x98); // Packet ID + writer.Write((ushort)37); + writer.Write(m.Serial); + writer.WriteAscii(m.Name ?? "", 29); + writer.Write((byte)0); // Null terminator + + ns.Send(writer.Span); + } + + public static void CreateMobileAnimation( + Span buffer, + Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay + ) + { + if (buffer[0] != 0) + { + return; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x6E); // Packet ID + writer.Write(mobile); + writer.Write((short)action); + writer.Write((short)frameCount); + writer.Write((short)repeatCount); + writer.Write(!forward); // protocol has really "reverse" but I find this more intuitive + writer.Write(repeat); + writer.Write((byte)delay); + } + + public static void SendMobileAnimation( + this NetState ns, + Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay + ) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span span = stackalloc byte[MobileAnimationPacketLength]; + CreateMobileAnimation(span, mobile, action, frameCount, repeatCount, forward, repeat, delay); + ns.Send(span); + } + + public static void CreateNewMobileAnimation( + Span buffer, + Serial mobile, int action, int frameCount, int delay + ) + { + var writer = new SpanWriter(buffer); + writer.Write((byte)0xE2); // Packet ID + writer.Write(mobile); + writer.Write((short)action); + writer.Write((short)frameCount); + writer.Write((byte)delay); + } + + public static void SendNewMobileAnimation(this NetState ns, Serial mobile, int action, int frameCount, int delay) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span span = stackalloc byte[NewMobileAnimationPacketLength]; + CreateNewMobileAnimation(span, mobile, action, frameCount, delay); + ns.Send(span); + } + + public static void SendMobileHealthbar(this NetState ns, Mobile m, Healthbar healthbar) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span span = stackalloc byte[MobileHealthbarPacketLength]; + CreateMobileHealthbar(span, m, healthbar); + ns.Send(span); + } + + public static void CreateMobileHealthbar(Span buffer, Mobile m, Healthbar healthbar) + { + if (buffer[0] != 0) + { + return; + } + + switch (healthbar) + { + case Healthbar.Poison: { - writer.Write((short)beheld.GetAOSStatus(i)); + CreateMobileHealthbar(buffer, m.Serial, Healthbar.Poison, m.Poison?.Level + 1 ?? 0); + break; } - } + case Healthbar.Yellow: + { + CreateMobileHealthbar(buffer, m.Serial, Healthbar.Yellow, m.Blessed || m.YellowHealthbar ? 1 : 0); + break; + } + default: + { + Console.WriteLine("Packets: Invalid Healthbar {0} in {1}", healthbar, nameof(CreateMobileHealthbar)); + CreateMobileHealthbar(buffer, m.Serial, Healthbar.Normal, 0); + break; + } + } + } + public static void CreateMobileHealthbar(Span buffer, Serial serial, Healthbar healthbar, int level) + { + var writer = new SpanWriter(buffer); + writer.Write((byte)0x17); // Packet ID + writer.Write((ushort)12); + writer.Write(serial); + writer.Write((short)1); // Show bar + writer.Write((short)healthbar); + writer.Write((byte)level); // 0 is off for that bar type + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateMobileStatusCompact(Span buffer, Mobile m, bool canBeRenamed) => + CreateMobileStatus(buffer, m, 0, canBeRenamed); + + public static void SendMobileStatusCompact(this NetState ns, Mobile m, bool canBeRenamed) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[MobileStatusCompactLength]; + CreateMobileStatusCompact(buffer, m, canBeRenamed); + + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMobileStatus(this NetState ns, Mobile m) => ns.SendMobileStatus(m, m); + + public static void SendMobileStatus(this NetState ns, Mobile beholder, Mobile beheld) + { + if (ns.CannotSendPackets() || beheld == null) + { + return; + } + + int version; + int length; + + if (beholder != beheld) + { + version = 0; + length = MobileStatusCompactLength; + } + else if (ExtendedStatus && ns.ExtendedStatus) + { + version = 6; + length = MobileStatusHSLength; + } + else if (Core.ML && ns.SupportsExpansion(Expansion.ML)) + { + version = 5; + length = MobileStatusMLLength; + } + else if (Core.AOS) + { + version = 4; + length = MobileStatusAOSLength; + } + else + { + version = 3; + length = MobileStatusLength; + } + + Span buffer = stackalloc byte[length]; + CreateMobileStatus(buffer, beheld, version, beheld.CanBeRenamedBy(beholder)); + ns.Send(buffer); + } + + public static void CreateMobileStatus(Span buffer, Mobile beheld, int version, bool canBeRenamed) + { + if (buffer[0] != 0) + { + return; + } + + var name = beheld.Name ?? ""; + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x11); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(beheld.Serial); + writer.WriteAscii(name, 30); + writer.WriteAttribute(beheld.HitsMax, beheld.Hits, version == 0, true); + writer.Write(canBeRenamed); + writer.Write((byte)version); + + if (version <= 0) + { writer.WritePacketLength(); - return writer.Position; + return; } - public static void SendMobileUpdate(this NetState ns, Mobile m) + writer.Write(beheld.Female); + + writer.Write((short)beheld.Str); + writer.Write((short)beheld.Dex); + writer.Write((short)beheld.Int); + + writer.Write((short)beheld.Stam); + writer.Write((short)beheld.StamMax); + writer.Write((short)beheld.Mana); + writer.Write((short)beheld.ManaMax); + + writer.Write(beheld.TotalGold); + writer.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); + writer.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); + + if (version >= 5) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[19]); - writer.Write((byte)0x20); // Packet ID - writer.Write(m.Serial); - writer.Write((short)m.Body); - writer.Write((byte)0); - writer.Write((short)(m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue)); - writer.Write((byte)m.GetPacketFlags(ns.StygianAbyss)); - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((short)0); - writer.Write((byte)m.Direction); - writer.Write((sbyte)m.Z); - - ns.Send(writer.Span); + writer.Write((short)beheld.MaxWeight); + writer.Write((byte)(beheld.Race?.RaceID + 1 ?? 0)); // Would be 0x00 if it's a non-ML enabled account but... } - public static void SendMobileIncoming(this NetState ns, Mobile beholder, Mobile beheld) - { - if (ns == null) - { - return; - } + writer.Write((short)beheld.StatCap); - Span layers = stackalloc bool[256]; + writer.Write((byte)beheld.Followers); + writer.Write((byte)beheld.FollowersMax); + + if (version >= 4) + { + writer.Write((short)beheld.FireResistance); // Fire + writer.Write((short)beheld.ColdResistance); // Cold + writer.Write((short)beheld.PoisonResistance); // Poison + writer.Write((short)beheld.EnergyResistance); // Energy + writer.Write((short)beheld.Luck); // Luck + + var weapon = beheld.Weapon; + + int min = 0, max = 0; + weapon?.GetStatusDamage(beheld, out min, out max); + writer.Write((short)min); // Damage min + writer.Write((short)max); // Damage max + + writer.Write(beheld.TithingPoints); + } + + if (version >= 6) + { + for (var i = 0; i < 15; ++i) + { + writer.Write((short)beheld.GetAOSStatus(i)); + } + } + + writer.WritePacketLength(); + } + + public static void SendMobileUpdate(this NetState ns, Mobile m) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[19]); + writer.Write((byte)0x20); // Packet ID + writer.Write(m.Serial); + writer.Write((short)m.Body); + writer.Write((byte)0); + writer.Write((short)(m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue)); + writer.Write((byte)m.GetPacketFlags(ns.StygianAbyss)); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((short)0); + writer.Write((byte)m.Direction); + writer.Write((sbyte)m.Z); + + ns.Send(writer.Span); + } + + public static void SendMobileIncoming(this NetState ns, Mobile beholder, Mobile beheld) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span layers = stackalloc bool[256]; #if NO_LOCAL_INIT layers.Clear(); #endif - var eq = beheld.Items; - var maxLength = 23 + (eq.Count + 2) * 9; - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x78); // Packet ID - writer.Seek(2, SeekOrigin.Current); + var eq = beheld.Items; + var maxLength = 23 + (eq.Count + 2) * 9; + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x78); // Packet ID + writer.Seek(2, SeekOrigin.Current); - var sa = ns.StygianAbyss; - var newPacket = ns.NewMobileIncoming; - var itemIdMask = newPacket ? 0xFFFF : 0x7FFF; + var sa = ns.StygianAbyss; + var newPacket = ns.NewMobileIncoming; + var itemIdMask = newPacket ? 0xFFFF : 0x7FFF; - var hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.Hue; + var hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.Hue; - writer.Write(beheld.Serial); - writer.Write((short)beheld.Body); - writer.Write((short)beheld.X); - writer.Write((short)beheld.Y); - writer.Write((sbyte)beheld.Z); - writer.Write((byte)beheld.Direction); - writer.Write((short)hue); - writer.Write((byte)beheld.GetPacketFlags(sa)); - writer.Write((byte)Notoriety.Compute(beholder, beheld)); + writer.Write(beheld.Serial); + writer.Write((short)beheld.Body); + writer.Write((short)beheld.X); + writer.Write((short)beheld.Y); + writer.Write((sbyte)beheld.Z); + writer.Write((byte)beheld.Direction); + writer.Write((short)hue); + writer.Write((byte)beheld.GetPacketFlags(sa)); + writer.Write((byte)Notoriety.Compute(beholder, beheld)); - for (var i = 0; i < eq.Count; ++i) + for (var i = 0; i < eq.Count; ++i) + { + var item = eq[i]; + var layer = (byte)item.Layer; + + if (item.Deleted || !beholder.CanSee(item) || layers[layer]) { - var item = eq[i]; - var layer = (byte)item.Layer; - - if (item.Deleted || !beholder.CanSee(item) || layers[layer]) - { - continue; - } - - layers[layer] = true; - hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : item.Hue; - - var itemID = item.ItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - writer.Write(item.Serial); - writer.Write((ushort)itemID); - writer.Write(layer); - - if (writeHue) - { - writer.Write((short)hue); - } + continue; } - if (beheld.HairItemID > 0 && !layers[(int)Layer.Hair]) + layers[layer] = true; + hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : item.Hue; + + var itemID = item.ItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) { - layers[(int)Layer.Hair] = true; - hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.HairHue; - - var itemID = beheld.HairItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - writer.Write(HairInfo.FakeSerial(beheld.Serial)); - writer.Write((ushort)itemID); - writer.Write((byte)Layer.Hair); - - if (writeHue) - { - writer.Write((short)hue); - } + itemID |= 0x8000; } - if (beheld.FacialHairItemID > 0 && !layers[(int)Layer.FacialHair]) + writer.Write(item.Serial); + writer.Write((ushort)itemID); + writer.Write(layer); + + if (writeHue) { - layers[(int)Layer.FacialHair] = true; - hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.FacialHairHue; - - var itemID = beheld.FacialHairItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - writer.Write(FacialHairInfo.FakeSerial(beheld.Serial)); - writer.Write((ushort)itemID); - writer.Write((byte)Layer.FacialHair); - - if (writeHue) - { - writer.Write((short)hue); - } + writer.Write((short)hue); } - - writer.Write(0); // terminate - - writer.WritePacketLength(); - ns.Send(writer.Span); } + + if (beheld.HairItemID > 0 && !layers[(int)Layer.Hair]) + { + layers[(int)Layer.Hair] = true; + hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.HairHue; + + var itemID = beheld.HairItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + writer.Write(HairInfo.FakeSerial(beheld.Serial)); + writer.Write((ushort)itemID); + writer.Write((byte)Layer.Hair); + + if (writeHue) + { + writer.Write((short)hue); + } + } + + if (beheld.FacialHairItemID > 0 && !layers[(int)Layer.FacialHair]) + { + layers[(int)Layer.FacialHair] = true; + hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.FacialHairHue; + + var itemID = beheld.FacialHairItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + writer.Write(FacialHairInfo.FakeSerial(beheld.Serial)); + writer.Write((ushort)itemID); + writer.Write((byte)Layer.FacialHair); + + if (writeHue) + { + writer.Write((short)hue); + } + } + + writer.Write(0); // terminate + + writer.WritePacketLength(); + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs index 78ee3c892..b366a7dec 100644 --- a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs @@ -16,102 +16,101 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public enum SpeedControlSetting { - public enum SpeedControlSetting + Disable, + Mount, + Walk +} + +public static class OutgoingMovementPackets +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) => + ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x6, 0x00, 0x26, (byte)speedControl }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMovePlayer(this NetState ns, Direction d) => ns?.Send(stackalloc byte[] { 0x97, (byte)d }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMovementAck(this NetState ns, int seq, Mobile m) => + ns.SendMovementAck(seq, Notoriety.Compute(m, m)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMovementAck(this NetState ns, int seq, int noto) => + ns?.Send(stackalloc byte[] { 0x22, (byte)seq, (byte)noto }); + + public static void SendMovementRej(this NetState ns, int seq, Mobile m) { - Disable, - Mount, - Walk + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[8]); + writer.Write((byte)0x21); // Packet ID + writer.Write((byte)seq); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((byte)m.Direction); + writer.Write((sbyte)m.Z); + + ns.Send(writer.Span); } - public static class OutgoingMovementPackets + public static void SendInitialFastwalkStack(this NetState ns, uint[] keys) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) => - ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x6, 0x00, 0x26, (byte)speedControl }); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMovePlayer(this NetState ns, Direction d) => ns?.Send(stackalloc byte[] { 0x97, (byte)d }); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMovementAck(this NetState ns, int seq, Mobile m) => - ns.SendMovementAck(seq, Notoriety.Compute(m, m)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMovementAck(this NetState ns, int seq, int noto) => - ns?.Send(stackalloc byte[] { 0x22, (byte)seq, (byte)noto }); - - public static void SendMovementRej(this NetState ns, int seq, Mobile m) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[8]); - writer.Write((byte)0x21); // Packet ID - writer.Write((byte)seq); - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((byte)m.Direction); - writer.Write((sbyte)m.Z); - - ns.Send(writer.Span); + return; } - public static void SendInitialFastwalkStack(this NetState ns, uint[] keys) + var writer = new SpanWriter(stackalloc byte[29]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)29); + writer.Write((ushort)0x1); // Subpacket + writer.Write(keys[0]); + writer.Write(keys[1]); + writer.Write(keys[2]); + writer.Write(keys[3]); + writer.Write(keys[4]); + writer.Write(keys[5]); + + ns.Send(writer.Span); + } + + public static void SendFastwalkStackKey(this NetState ns, uint key = 0) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[29]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)29); - writer.Write((ushort)0x1); // Subpacket - writer.Write(keys[0]); - writer.Write(keys[1]); - writer.Write(keys[2]); - writer.Write(keys[3]); - writer.Write(keys[4]); - writer.Write(keys[5]); - - ns.Send(writer.Span); + return; } - public static void SendFastwalkStackKey(this NetState ns, uint key = 0) + var writer = new SpanWriter(stackalloc byte[9]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)9); + writer.Write((ushort)0x2); // Subpacket + writer.Write(key); + + ns.Send(writer.Span); + } + + public static void SendTimeSyncResponse(this NetState ns) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[9]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)9); - writer.Write((ushort)0x2); // Subpacket - writer.Write(key); - - ns.Send(writer.Span); + return; } - public static void SendTimeSyncResponse(this NetState ns) - { - if (ns == null) - { - return; - } + var writer = new SpanWriter(stackalloc byte[25]); + writer.Write((byte)0xF2); // Packet ID - var writer = new SpanWriter(stackalloc byte[25]); - writer.Write((byte)0xF2); // Packet ID + writer.Write(Core.TickCount); // ?? + writer.Write(Core.TickCount); // ?? + writer.Write(Core.TickCount); // ?? - writer.Write(Core.TickCount); // ?? - writer.Write(Core.TickCount); // ?? - writer.Write(Core.TickCount); // ?? - - ns.Send(writer.Span); - } + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingPackets.cs b/Projects/Server/Network/Packets/OutgoingPackets.cs new file mode 100644 index 000000000..a20356fe9 --- /dev/null +++ b/Projects/Server/Network/Packets/OutgoingPackets.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; + +namespace Server.Network; + +public static class OutgoingPackets +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CannotSendPackets(this NetState ns) => ns?.Connection == null || ns.BlockAllPackets; +} diff --git a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs index 051ac8f7e..af33c444e 100644 --- a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs @@ -17,338 +17,337 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public enum LRReason : byte { - public enum LRReason : byte + CannotLift, + OutOfRange, + OutOfSight, + TryToSteal, + AreHolding, + Inspecific +} + +public static class OutgoingPlayerPackets +{ + public const int DragEffectPacketLength = 26; + + public static void SendStatLockInfo(this NetState ns, Mobile m) { - CannotLift, - OutOfRange, - OutOfSight, - TryToSteal, - AreHolding, - Inspecific + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[12]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)12); + writer.Write((short)0x19); + writer.Write((byte)2); + writer.Write(m.Serial); + writer.Write((byte)0); + + var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock; + + writer.Write((byte)lockBits); + + ns.Send(writer.Span); } - public static class OutgoingPlayerPackets + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendChangeUpdateRange(this NetState ns, byte range) => + ns?.Send(stackalloc byte[] { 0xC8, range }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendDeathStatus(this NetState ns, bool dead) => + ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 }); + + public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer) { - public const int DragEffectPacketLength = 26; - - public static void SendStatLockInfo(this NetState ns, Mobile m) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[12]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)12); - writer.Write((short)0x19); - writer.Write((byte)2); - writer.Write(m.Serial); - writer.Write((byte)0); - - var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock; - - writer.Write((byte)lockBits); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendChangeUpdateRange(this NetState ns, byte range) => - ns?.Send(stackalloc byte[] { 0xC8, range }); + header ??= ""; + body ??= ""; + footer ??= ""; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendDeathStatus(this NetState ns, bool dead) => - ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 }); + var length = 12 + header.Length + footer.Length * 2 + body.Length * 2; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB8); // Packet ID + writer.Write((ushort)length); + writer.Write(m); + writer.WriteAsciiNull(header); + writer.WriteBigUniNull(footer); + writer.WriteBigUniNull(body); - public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer) + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendLiftReject(this NetState ns, LRReason reason) => + ns?.Send(stackalloc byte[] { 0x27, (byte)reason }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendLogoutAck(this NetState ns) => ns?.Send(stackalloc byte[] { 0xD1, 0x01 }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendWeather(this NetState ns, byte type, byte density, byte temp) => + ns?.Send(stackalloc byte[] { 0x65, type, density, temp }); + + public static void SendServerChange(this NetState ns, Point3D p, Map map) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - header ??= ""; - body ??= ""; - footer ??= ""; - - var length = 12 + header.Length + footer.Length * 2 + body.Length * 2; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xB8); // Packet ID - writer.Write((ushort)length); - writer.Write(m); - writer.WriteAsciiNull(header); - writer.WriteBigUniNull(footer); - writer.WriteBigUniNull(body); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendLiftReject(this NetState ns, LRReason reason) => - ns?.Send(stackalloc byte[] { 0x27, (byte)reason }); + var writer = new SpanWriter(stackalloc byte[16]); + writer.Write((byte)0x76); // Packet ID + writer.Write((short)p.X); + writer.Write((short)p.Y); + writer.Write((short)p.Z); + writer.Write((byte)0); + writer.Write((short)0); + writer.Write((short)0); + writer.Write((short)map.Width); + writer.Write((short)map.Height); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendLogoutAck(this NetState ns) => ns?.Send(stackalloc byte[] { 0xD1, 0x01 }); + ns.Send(writer.Span); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendWeather(this NetState ns, byte type, byte density, byte temp) => - ns?.Send(stackalloc byte[] { 0x65, type, density, temp }); - - public static void SendServerChange(this NetState ns, Point3D p, Map map) + public static void SendSkillsUpdate(this NetState ns, Skills skills) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[16]); - writer.Write((byte)0x76); // Packet ID - writer.Write((short)p.X); - writer.Write((short)p.Y); - writer.Write((short)p.Z); - writer.Write((byte)0); - writer.Write((short)0); - writer.Write((short)0); - writer.Write((short)map.Width); - writer.Write((short)map.Height); - - ns.Send(writer.Span); + return; } - public static void SendSkillsUpdate(this NetState ns, Skills skills) + var length = 6 + 9 * skills.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x3A); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)0x02); // type: absolute, capped + + for (var i = 0; i < skills.Length; ++i) { - if (ns == null) - { - return; - } + var s = skills[i]; - var length = 6 + 9 * skills.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x3A); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)0x02); // type: absolute, capped - - for (var i = 0; i < skills.Length; ++i) - { - var s = skills[i]; - - var v = s.NonRacialValue; - var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); - - writer.Write((ushort)(s.Info.SkillID + 1)); - writer.Write((ushort)uv); - writer.Write((ushort)s.BaseFixedPoint); - writer.Write((byte)s.Lock); - writer.Write((ushort)s.CapFixedPoint); - } - - writer.Write((short)0); // terminate - - ns.Send(writer.Span); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendSequence(this NetState ns, byte sequence) => ns?.Send(stackalloc byte[] { 0x7B, sequence }); - - public static void SendSkillChange(this NetState ns, Skill skill) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[13]); - writer.Write((byte)0x3A); // Packet ID - writer.Write((ushort)13); - - var v = skill.NonRacialValue; + var v = s.NonRacialValue; var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); - writer.Write((byte)0xDF); // type: delta, capped - writer.Write((ushort)skill.Info.SkillID); + writer.Write((ushort)(s.Info.SkillID + 1)); writer.Write((ushort)uv); - writer.Write((ushort)skill.BaseFixedPoint); - writer.Write((byte)skill.Lock); - writer.Write((ushort)skill.CapFixedPoint); - - ns.Send(writer.Span); + writer.Write((ushort)s.BaseFixedPoint); + writer.Write((byte)s.Lock); + writer.Write((ushort)s.CapFixedPoint); } - public static void SendLaunchBrowser(this NetState ns, string uri) + writer.Write((short)0); // terminate + + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendSequence(this NetState ns, byte sequence) => ns?.Send(stackalloc byte[] { 0x7B, sequence }); + + public static void SendSkillChange(this NetState ns, Skill skill) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - uri ??= ""; - - var length = 4 + uri.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA5); // Packet ID - writer.Write((ushort)length); - writer.WriteAsciiNull(uri); - - ns.Send(writer.Span); + return; } - public static void CreateDragEffect( - Span buffer, - Serial srcSerial, Point3D srcLocation, - Serial trgSerial, Point3D trgLocation, - int itemID, int hue, int amount - ) + var writer = new SpanWriter(stackalloc byte[13]); + writer.Write((byte)0x3A); // Packet ID + writer.Write((ushort)13); + + var v = skill.NonRacialValue; + var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); + + writer.Write((byte)0xDF); // type: delta, capped + writer.Write((ushort)skill.Info.SkillID); + writer.Write((ushort)uv); + writer.Write((ushort)skill.BaseFixedPoint); + writer.Write((byte)skill.Lock); + writer.Write((ushort)skill.CapFixedPoint); + + ns.Send(writer.Span); + } + + public static void SendLaunchBrowser(this NetState ns, string uri) + { + if (ns.CannotSendPackets()) { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x23); // Packet ID - writer.Write((short)itemID); - writer.Write((byte)0); - writer.Write((short)hue); - writer.Write((short)amount); - writer.Write(srcSerial); - writer.Write((short)srcLocation.X); - writer.Write((short)srcLocation.Y); - writer.Write((sbyte)srcLocation.Z); - writer.Write(trgSerial); - writer.Write((short)trgLocation.X); - writer.Write((short)trgLocation.Y); - writer.Write((sbyte)trgLocation.Z); + return; } - public static void SendDragEffect( - this NetState ns, - Serial srcSerial, Point3D srcLocation, - Serial trgSerial, Point3D trgLocation, - int itemID, int hue, int amount - ) + uri ??= ""; + + var length = 4 + uri.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA5); // Packet ID + writer.Write((ushort)length); + writer.WriteAsciiNull(uri); + + ns.Send(writer.Span); + } + + public static void CreateDragEffect( + Span buffer, + Serial srcSerial, Point3D srcLocation, + Serial trgSerial, Point3D trgLocation, + int itemID, int hue, int amount + ) + { + if (buffer[0] != 0) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[DragEffectPacketLength].InitializePacket(); - CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount); - ns.Send(buffer); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void SendSeasonChange(this NetState ns, byte season, bool playSound) => - ns?.Send(stackalloc byte[]{ 0xBC, season, *(byte*)&playSound }); + var writer = new SpanWriter(buffer); + writer.Write((byte)0x23); // Packet ID + writer.Write((short)itemID); + writer.Write((byte)0); + writer.Write((short)hue); + writer.Write((short)amount); + writer.Write(srcSerial); + writer.Write((short)srcLocation.X); + writer.Write((short)srcLocation.Y); + writer.Write((sbyte)srcLocation.Z); + writer.Write(trgSerial); + writer.Write((short)trgLocation.X); + writer.Write((short)trgLocation.Y); + writer.Write((sbyte)trgLocation.Z); + } - public static void SendDisplayPaperdoll(this NetState ns, Serial m, string title, bool warmode, bool canLift) + public static void SendDragEffect( + this NetState ns, + Serial srcSerial, Point3D srcLocation, + Serial trgSerial, Point3D trgLocation, + int itemID, int hue, int amount + ) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - byte flags = 0x00; - - if (warmode) - { - flags |= 0x01; - } - - if (canLift) - { - flags |= 0x02; - } - - var writer = new SpanWriter(stackalloc byte[66]); - writer.Write((byte)0x88); // Packet ID - writer.Write(m); - writer.WriteAscii(title, 60); - writer.Write(flags); - - ns.Send(writer.Span); + return; } - public static void SendPlayMusic(this NetState ns, MusicName music) + Span buffer = stackalloc byte[DragEffectPacketLength].InitializePacket(); + CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount); + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void SendSeasonChange(this NetState ns, byte season, bool playSound) => + ns?.Send(stackalloc byte[]{ 0xBC, season, *(byte*)&playSound }); + + public static void SendDisplayPaperdoll(this NetState ns, Serial m, string title, bool warmode, bool canLift) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[3]); - writer.Write((byte)0x6D); // Packet ID - writer.Write((short)music); - - ns.Send(writer.Span); + return; } - public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF }); + byte flags = 0x00; - public static void SendScrollMessage(this NetState ns, int type, int tip, string text) + if (warmode) { - if (ns == null) - { - return; - } - - text ??= ""; - - var length = 10 + text.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA6); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)type); - writer.Write(tip); - writer.Write((ushort)text.Length); - writer.WriteAscii(text); - - ns.Send(writer.Span); + flags |= 0x01; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendCurrentTime(this NetState ns) => ns.SendCurrentTime(Core.Now); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendCurrentTime(this NetState ns, DateTime date) => - ns?.Send(stackalloc byte[] { 0x5B, (byte)date.Hour, (byte)date.Minute, (byte)date.Second }); - - public static void SendPathfindMessage(this NetState ns, Point3D p) + if (canLift) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[7]); - writer.Write((byte)0x38); // Packet ID - writer.Write((short)p.X); - writer.Write((short)p.Y); - writer.Write((short)p.Z); - - ns.Send(writer.Span); + flags |= 0x02; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendPingAck(this NetState ns, byte ping) => ns?.Send(stackalloc byte[] { 0x73, ping }); + var writer = new SpanWriter(stackalloc byte[66]); + writer.Write((byte)0x88); // Packet ID + writer.Write(m); + writer.WriteAscii(title, 60); + writer.Write(flags); - public static void SendDisplayHuePicker(this NetState ns, Serial huePickerSerial, int huePickerItemID) + ns.Send(writer.Span); + } + + public static void SendPlayMusic(this NetState ns, MusicName music) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[9]); - writer.Write((byte)0x95); // Packet ID - writer.Write(huePickerSerial); - writer.Write((short)0); - writer.Write((short)huePickerItemID); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[3]); + writer.Write((byte)0x6D); // Packet ID + writer.Write((short)music); + + ns.Send(writer.Span); + } + + public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF }); + + public static void SendScrollMessage(this NetState ns, int type, int tip, string text) + { + if (ns.CannotSendPackets()) + { + return; + } + + text ??= ""; + + var length = 10 + text.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA6); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)type); + writer.Write(tip); + writer.Write((ushort)text.Length); + writer.WriteAscii(text); + + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendCurrentTime(this NetState ns) => ns.SendCurrentTime(Core.Now); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendCurrentTime(this NetState ns, DateTime date) => + ns?.Send(stackalloc byte[] { 0x5B, (byte)date.Hour, (byte)date.Minute, (byte)date.Second }); + + public static void SendPathfindMessage(this NetState ns, Point3D p) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[7]); + writer.Write((byte)0x38); // Packet ID + writer.Write((short)p.X); + writer.Write((short)p.Y); + writer.Write((short)p.Z); + + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendPingAck(this NetState ns, byte ping) => ns?.Send(stackalloc byte[] { 0x73, ping }); + + public static void SendDisplayHuePicker(this NetState ns, Serial huePickerSerial, int huePickerItemID) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[9]); + writer.Write((byte)0x95); // Packet ID + writer.Write(huePickerSerial); + writer.Write((short)0); + writer.Write((short)huePickerItemID); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs b/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs index eef3336ee..4459d3038 100644 --- a/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs @@ -16,106 +16,105 @@ using System.Buffers; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public enum TradeFlag : byte { - public enum TradeFlag : byte + Display, + Close, + Update, + UpdateGold, + UpdateLedger +} + +public static class OutgoingSecureTradePackets +{ + public static void SendDisplaySecureTrade( + this NetState ns, Mobile them, Container first, Container second, string name + ) { - Display, - Close, - Update, - UpdateGold, - UpdateLedger + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[47]); + writer.Write((byte)0x6F); // Packet ID + writer.Write((ushort)47); // Length + writer.Write((byte)TradeFlag.Display); + writer.Write(them.Serial); + writer.Write(first.Serial); + writer.Write(second.Serial); + writer.Write(true); + + writer.WriteAscii(name ?? "", 30); + + ns.Send(writer.Span); } - public static class OutgoingSecureTradePackets + public static void SendCloseSecureTrade(this NetState ns, Container cont) { - public static void SendDisplaySecureTrade( - this NetState ns, Mobile them, Container first, Container second, string name - ) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[47]); - writer.Write((byte)0x6F); // Packet ID - writer.Write((ushort)47); // Length - writer.Write((byte)TradeFlag.Display); - writer.Write(them.Serial); - writer.Write(first.Serial); - writer.Write(second.Serial); - writer.Write(true); - - writer.WriteAscii(name ?? "", 30); - - ns.Send(writer.Span); + return; } - public static void SendCloseSecureTrade(this NetState ns, Container cont) + var writer = new SpanWriter(stackalloc byte[17]); + writer.Write((byte)0x6F); // Packet ID + writer.Write((ushort)17); // Length + writer.Write((byte)TradeFlag.Close); + writer.Write(cont.Serial); + writer.Write(0); + writer.Write(0); + writer.Write(false); + + ns.Send(writer.Span); + } + + public static void SendUpdateSecureTrade(this NetState ns, Container cont, bool first, bool second) => + ns.SendUpdateSecureTrade(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0); + + public static void SendUpdateSecureTrade(this NetState ns, Container cont, TradeFlag flag, int first, int second) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[17]); - writer.Write((byte)0x6F); // Packet ID - writer.Write((ushort)17); // Length - writer.Write((byte)TradeFlag.Close); - writer.Write(cont.Serial); - writer.Write(0); - writer.Write(0); - writer.Write(false); - - ns.Send(writer.Span); + return; } - public static void SendUpdateSecureTrade(this NetState ns, Container cont, bool first, bool second) => - ns.SendUpdateSecureTrade(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0); + var writer = new SpanWriter(stackalloc byte[17]); + writer.Write((byte)0x6F); // Packet ID + writer.Write((ushort)17); // Length + writer.Write((byte)flag); + writer.Write(cont.Serial); + writer.Write(first); + writer.Write(second); + writer.Write(false); - public static void SendUpdateSecureTrade(this NetState ns, Container cont, TradeFlag flag, int first, int second) + ns.Send(writer.Span); + } + + public static void SendSecureTradeEquip(this NetState ns, Item item, Mobile m) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[17]); - writer.Write((byte)0x6F); // Packet ID - writer.Write((ushort)17); // Length - writer.Write((byte)flag); - writer.Write(cont.Serial); - writer.Write(first); - writer.Write(second); - writer.Write(false); - - ns.Send(writer.Span); + return; } - public static void SendSecureTradeEquip(this NetState ns, Item item, Mobile m) + var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); + writer.Write((byte)0x25); // Packet ID + writer.Write(item.Serial); + writer.Write((short)item.ItemID); + writer.Write((byte)0); + writer.Write((short)item.Amount); + writer.Write((short)item.X); + writer.Write((short)item.Y); + if (ns.ContainerGridLines) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); - writer.Write((byte)0x25); // Packet ID - writer.Write(item.Serial); - writer.Write((short)item.ItemID); writer.Write((byte)0); - writer.Write((short)item.Amount); - writer.Write((short)item.X); - writer.Write((short)item.Y); - if (ns.ContainerGridLines) - { - writer.Write((byte)0); - } - writer.Write(m.Serial); - writer.Write((short)item.Hue); - - ns.Send(writer.Span); } + writer.Write(m.Serial); + writer.Write((short)item.Hue); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingTargetPackets.cs b/Projects/Server/Network/Packets/OutgoingTargetPackets.cs index 04b3a7e08..3de19ae85 100644 --- a/Projects/Server/Network/Packets/OutgoingTargetPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingTargetPackets.cs @@ -16,56 +16,55 @@ using System.Buffers; using Server.Targeting; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingTargetPackets { - public static class OutgoingTargetPackets + public static void SendMultiTargetReq(this NetState ns, MultiTarget t) { - public static void SendMultiTargetReq(this NetState ns, MultiTarget t) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 30 : 26]); - writer.Write((byte)0x99); // Packet ID - writer.Write(t.AllowGround); - writer.Write(t.TargetID); - writer.Write((byte)t.Flags); - writer.Clear(11); - writer.Write((short)t.MultiID); - writer.Write((short)t.Offset.X); - writer.Write((short)t.Offset.Y); - writer.Write((short)t.Offset.Z); - if (ns.HighSeas) - { - writer.Write(0); - } - - ns.Send(writer.Span); + return; } - public static void SendCancelTarget(this NetState ns) => - ns?.Send(stackalloc byte[] - { - 0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 - }); - - public static void SendTargetReq(this NetState ns, Target t) + var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 30 : 26]); + writer.Write((byte)0x99); // Packet ID + writer.Write(t.AllowGround); + writer.Write(t.TargetID); + writer.Write((byte)t.Flags); + writer.Clear(11); + writer.Write((short)t.MultiID); + writer.Write((short)t.Offset.X); + writer.Write((short)t.Offset.Y); + writer.Write((short)t.Offset.Z); + if (ns.HighSeas) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[19]); - writer.Write((byte)0x6C); // Packet ID - writer.Write(t.AllowGround); - writer.Write(t.TargetID); - writer.Write((byte)t.Flags); - writer.Clear(12); - - ns.Send(writer.Span); + writer.Write(0); } + + ns.Send(writer.Span); + } + + public static void SendCancelTarget(this NetState ns) => + ns?.Send(stackalloc byte[] + { + 0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }); + + public static void SendTargetReq(this NetState ns, Target t) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[19]); + writer.Write((byte)0x6C); // Packet ID + writer.Write(t.AllowGround); + writer.Write(t.TargetID); + writer.Write((byte)t.Flags); + writer.Clear(12); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs b/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs index f70dfa49d..429792d6d 100644 --- a/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs @@ -17,111 +17,110 @@ using System.Buffers; using System.Collections.Generic; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingVendorBuyPackets { - public static class OutgoingVendorBuyPackets + public static void SendVendorBuyContent(this NetState ns, List list) { - public static void SendVendorBuyContent(this NetState ns, List list) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var length = 5 + list.Count * (ns.ContainerGridLines ? 20 : 19); - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x3C); // Packet ID - writer.Write((ushort)length); - writer.Write((short)list.Count); - - for (var i = list.Count - 1; i >= 0; --i) - { - var bis = list[i]; - - writer.Write(bis.MySerial); - writer.Write((ushort)bis.ItemID); - writer.Write((byte)0); // itemID offset - writer.Write((ushort)bis.Amount); - writer.Write((short)(i + 1)); // x - writer.Write((short)1); // y - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location? - } - writer.Write(bis.ContainerSerial); - writer.Write((ushort)bis.Hue); - } - - ns.Send(writer.Span); + return; } - public static void SendDisplayBuyList(this NetState ns, Serial vendor) + var length = 5 + list.Count * (ns.ContainerGridLines ? 20 : 19); + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x3C); // Packet ID + writer.Write((ushort)length); + writer.Write((short)list.Count); + + for (var i = list.Count - 1; i >= 0; --i) { - if (ns == null) - { - return; - } + var bis = list[i]; - var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); - writer.Write((byte)0x24); // Packet ID - writer.Write(vendor); - writer.Write((short)0x30); // Vendor Buy Window - if (ns.HighSeas) + writer.Write(bis.MySerial); + writer.Write((ushort)bis.ItemID); + writer.Write((byte)0); // itemID offset + writer.Write((ushort)bis.Amount); + writer.Write((short)(i + 1)); // x + writer.Write((short)1); // y + if (ns.ContainerGridLines) { - writer.Write((short)0x0); + writer.Write((byte)0); // Grid Location? } - - ns.Send(writer.Span); + writer.Write(bis.ContainerSerial); + writer.Write((ushort)bis.Hue); } - public static void SendVendorBuyList(this NetState ns, Mobile vendor, List list) + ns.Send(writer.Span); + } + + public static void SendDisplayBuyList(this NetState ns, Serial vendor) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var length = 8; - for (int i = 0; i < list.Count; i++) - { - length += 6 + list[i].Description?.Length ?? 0; - } - - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x74); // Packet ID - writer.Write((ushort)length); - writer.Write((vendor.FindItemOnLayer(Layer.ShopBuy) as Container)?.Serial ?? Serial.MinusOne); - writer.Write((byte)list.Count); - - for (var i = 0; i < list.Count; ++i) - { - var bis = list[i]; - - writer.Write(bis.Price); - - var desc = bis.Description ?? ""; - - writer.Write((byte)(desc.Length + 1)); - writer.WriteAsciiNull(desc); - } - - ns.Send(writer.Span); + return; } - public static void SendEndVendorBuy(this NetState ns, Serial vendor) + var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); + writer.Write((byte)0x24); // Packet ID + writer.Write(vendor); + writer.Write((short)0x30); // Vendor Buy Window + if (ns.HighSeas) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[8]); - writer.Write((byte)0x3B); // Packet ID - writer.Write((ushort)8); - writer.Write(vendor); - writer.Write((byte)0); // Buy count - - ns.Send(writer.Span); + writer.Write((short)0x0); } + + ns.Send(writer.Span); + } + + public static void SendVendorBuyList(this NetState ns, Mobile vendor, List list) + { + if (ns.CannotSendPackets()) + { + return; + } + + var length = 8; + for (int i = 0; i < list.Count; i++) + { + length += 6 + list[i].Description?.Length ?? 0; + } + + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x74); // Packet ID + writer.Write((ushort)length); + writer.Write((vendor.FindItemOnLayer(Layer.ShopBuy) as Container)?.Serial ?? Serial.MinusOne); + writer.Write((byte)list.Count); + + for (var i = 0; i < list.Count; ++i) + { + var bis = list[i]; + + writer.Write(bis.Price); + + var desc = bis.Description ?? ""; + + writer.Write((byte)(desc.Length + 1)); + writer.WriteAsciiNull(desc); + } + + ns.Send(writer.Span); + } + + public static void SendEndVendorBuy(this NetState ns, Serial vendor) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[8]); + writer.Write((byte)0x3B); // Packet ID + writer.Write((ushort)8); + writer.Write(vendor); + writer.Write((byte)0); // Buy count + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs b/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs index 5393a4802..90489b668 100644 --- a/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs @@ -18,66 +18,65 @@ using System.Buffers; using System.Collections.Generic; using System.IO; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingVendorSellPackets { - public static class OutgoingVendorSellPackets + public static void SendVendorSellList(this NetState ns, Serial vendor, List list) { - public static void SendVendorSellList(this NetState ns, Serial vendor, List list) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var maxLength = 9; - for (int i = 0; i < list.Count; i++) - { - var sis = list[i]; - var item = sis.Item; - maxLength += 14 + Math.Max(item.Name?.Length ?? 0, sis.Name?.Length ?? 0); - } - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x9E); // Packet ID - writer.Seek(2, SeekOrigin.Current); - - writer.Write(vendor); - writer.Write((ushort)list.Count); - - for (var i = 0; i < list.Count; i++) - { - var sis = list[i]; - var item = sis.Item; - writer.Write(item.Serial); - writer.Write((ushort)item.ItemID); - writer.Write((ushort)item.Hue); - writer.Write((ushort)item.Amount); - writer.Write((ushort)sis.Price); - - var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? ""); - - writer.Write((ushort)name.Length); - writer.WriteAscii(name); - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + return; } - public static void SendEndVendorSell(this NetState ns, Serial vendor) + var maxLength = 9; + for (int i = 0; i < list.Count; i++) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[8]); - writer.Write((byte)0x3B); // Packet ID - writer.Write((ushort)8); - writer.Write(vendor); - writer.Write((byte)0); - - ns.Send(writer.Span); + var sis = list[i]; + var item = sis.Item; + maxLength += 14 + Math.Max(item.Name?.Length ?? 0, sis.Name?.Length ?? 0); } + + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x9E); // Packet ID + writer.Seek(2, SeekOrigin.Current); + + writer.Write(vendor); + writer.Write((ushort)list.Count); + + for (var i = 0; i < list.Count; i++) + { + var sis = list[i]; + var item = sis.Item; + writer.Write(item.Serial); + writer.Write((ushort)item.ItemID); + writer.Write((ushort)item.Hue); + writer.Write((ushort)item.Amount); + writer.Write((ushort)sis.Price); + + var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? ""); + + writer.Write((ushort)name.Length); + writer.WriteAscii(name); + } + + writer.WritePacketLength(); + ns.Send(writer.Span); + } + + public static void SendEndVendorSell(this NetState ns, Serial vendor) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[8]); + writer.Write((byte)0x3B); // Packet ID + writer.Write((ushort)8); + writer.Write(vendor); + writer.Write((byte)0); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/PacketContainerBuilder.cs b/Projects/Server/Network/Packets/PacketContainerBuilder.cs index deee3e501..4719004d6 100644 --- a/Projects/Server/Network/Packets/PacketContainerBuilder.cs +++ b/Projects/Server/Network/Packets/PacketContainerBuilder.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PacketContainerBuilder.cs * * * @@ -14,102 +14,101 @@ *************************************************************************/ using System; -using System.Buffers; using System.Buffers.Binary; using System.Runtime.CompilerServices; +using Server.Buffers; -namespace Server.Network +namespace Server.Network; + +public ref struct PacketContainerBuilder { - public ref struct PacketContainerBuilder + public const int MinPacketLength = 5; + + private bool _finished; + private int _count; + + private byte[] _arrayToReturnToPool; + private Span _bytes; + + public PacketContainerBuilder(Span initialBuffer) { - public const int MinPacketLength = 5; + _arrayToReturnToPool = null; + _finished = false; + _count = 0; - private bool _finished; - private int _count; + _bytes = initialBuffer; + _bytes[0] = 0xF7; // Packet ID + Length = MinPacketLength; // Length + Count + } - private byte[] _arrayToReturnToPool; - private Span _bytes; + public int Length { get; set; } - public PacketContainerBuilder(Span initialBuffer) + public int Capacity => _bytes.Length; + + [MethodImpl(MethodImplOptions.NoInlining)] + public ReadOnlySpan Finalize() + { + if (!_finished) { - _arrayToReturnToPool = null; - _finished = false; - _count = 0; - - _bytes = initialBuffer; - _bytes[0] = 0xF7; // Packet ID - Length = MinPacketLength; // Length + Count + BinaryPrimitives.WriteUInt16BigEndian(_bytes[1..3], (ushort)Length); + BinaryPrimitives.WriteUInt16BigEndian(_bytes[3..5], (ushort)_count); } - public int Length { get; set; } + return _bytes[..Length]; + } - public int Capacity => _bytes.Length; - - [MethodImpl(MethodImplOptions.NoInlining)] - public ReadOnlySpan Finalize() + [MethodImpl(MethodImplOptions.NoInlining)] + public Span GetSpan(int bytesNeeded) + { + if (_finished) { - if (!_finished) - { - BinaryPrimitives.WriteUInt16BigEndian(_bytes[1..3], (ushort)Length); - BinaryPrimitives.WriteUInt16BigEndian(_bytes[3..5], (ushort)_count); - } - - return _bytes[..Length]; + throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); } - [MethodImpl(MethodImplOptions.NoInlining)] - public Span GetSpan(int bytesNeeded) + if (Length > _bytes.Length - bytesNeeded) { - if (_finished) - { - throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); - } - - if (Length > _bytes.Length - bytesNeeded) - { - Grow(bytesNeeded); - } - - return _bytes[Length..]; + Grow(bytesNeeded); } - [MethodImpl(MethodImplOptions.NoInlining)] - public void Advance(int bytesWritten) - { - if (_finished) - { - throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); - } + return _bytes[Length..]; + } - _count++; - Length += bytesWritten; + [MethodImpl(MethodImplOptions.NoInlining)] + public void Advance(int bytesWritten) + { + if (_finished) + { + throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); } - [MethodImpl(MethodImplOptions.NoInlining)] - private void Grow(int additionalCapacityBeyondPos) + _count++; + Length += bytesWritten; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacityBeyondPos) + { + var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2); + byte[] poolArray = STArrayPool.Shared.Rent(newLength); + + _bytes[..Length].CopyTo(poolArray); + + byte[] toReturn = _arrayToReturnToPool; + _bytes = _arrayToReturnToPool = poolArray; + if (toReturn != null) { - var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2); - byte[] poolArray = ArrayPool.Shared.Rent(newLength); - - _bytes[..Length].CopyTo(poolArray); - - byte[] toReturn = _arrayToReturnToPool; - _bytes = _arrayToReturnToPool = poolArray; - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } + STArrayPool.Shared.Return(toReturn); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + byte[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) { - byte[] toReturn = _arrayToReturnToPool; - this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } + STArrayPool.Shared.Return(toReturn); } } } diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs index b78e67a41..c82a92537 100644 --- a/Projects/Server/Network/TcpServer.cs +++ b/Projects/Server/Network/TcpServer.cs @@ -77,7 +77,7 @@ namespace Server.Network foreach (var ipep in listeningAddresses) { - logger.Information("Listening: {0}:{1}", ipep.Address, ipep.Port); + logger.Information("Listening: {Address}:{Port}", ipep.Address, ipep.Port); } ListeningAddresses = listeningAddresses.ToArray(); @@ -119,12 +119,12 @@ namespace Server.Network // WSAEADDRINUSE if (se.ErrorCode == 10048) { - logger.Warning("Listener: {0}:{1}: Failed (In Use)", ipep.Address, ipep.Port); + logger.Warning("Listener: {Address}:{Port}: Failed (In Use)", ipep.Address, ipep.Port); } // WSAEADDRNOTAVAIL else if (se.ErrorCode == 10049) { - logger.Warning("Listener {0}:{1}: Failed (Unavailable)", ipep.Address, ipep.Port); + logger.Warning("Listener {Address}:{Port}: Failed (Unavailable)", ipep.Address, ipep.Port); } else { @@ -142,7 +142,7 @@ namespace Server.Network while (++count <= MaxConnectionsPerLoop && _connectedQueue.TryDequeue(out var ns)) { Instances.Add(ns); - ns.LogInfo("Connected. [{0} Online]", Instances.Count); + ns.LogInfo($"Connected. [{Instances.Count} Online]"); } } @@ -166,7 +166,7 @@ namespace Server.Network if (socket.RemoteEndPoint is IPEndPoint ipep) { var ip = ipep.Address.ToString(); - logger.Warning("Listener {0}: Failed (Maximum connections reached)", ip); + logger.Warning("Listener {Address}: Failed (Maximum connections reached)", ip); NetState.TraceDisconnect("Maximum connections reached.", ip); } diff --git a/Projects/Server/ObjectPropertyList.cs b/Projects/Server/ObjectPropertyList.cs deleted file mode 100644 index 369cfd2ef..000000000 --- a/Projects/Server/ObjectPropertyList.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Buffers; -using System.IO; -using System.Runtime.CompilerServices; -using Server.Network; - -namespace Server -{ - public interface IPropertyListObject : IEntity - { - ObjectPropertyList PropertyList { get; } - - void GetProperties(ObjectPropertyList list); - } - - public sealed class ObjectPropertyList - { - // Each of these are localized to "~1_NOTHING~" which allows the string argument to be used - private static readonly int[] m_StringNumbers = - { - 1042971, - 1070722 - }; - - private int _hash; - private int _strings; - private byte[] _buffer; - private int _position; - - public ObjectPropertyList(IEntity e) - { - Entity = e; - _buffer = GC.AllocateUninitializedArray(64); - - var writer = new SpanWriter(_buffer); - writer.Write((byte)0xD6); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write((ushort)1); - writer.Write(e.Serial); - writer.Write((ushort)0); - _position = writer.Position + 4; // Hash - } - - public IEntity Entity { get; } - - public int Hash => 0x40000000 + _hash; - - public int Header { get; set; } - - public string HeaderArgs { get; set; } - - public static bool Enabled { get; set; } - - public byte[] Buffer => _buffer; - - public void Reset() - { - _position = 15; - _hash = 0; - _strings = 0; - Header = 0; - HeaderArgs = null; - } - - public void Flush() - { - Resize(_buffer.Length * 2); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Resize(int amount) - { - var newBuffer = GC.AllocateUninitializedArray(amount); - _buffer.AsSpan(0, Math.Min(amount, _buffer.Length)).CopyTo(newBuffer); - _buffer = newBuffer; - } - - public void Terminate() - { - int length = _position + 4; - if (length != _buffer.Length) - { - Resize(length); - } - - var writer = new SpanWriter(_buffer); - writer.Seek(_position, SeekOrigin.Begin); - writer.Write(0); - - writer.Seek(11, SeekOrigin.Begin); - writer.Write(_hash); - writer.WritePacketLength(); - } - - public void AddHash(int val) - { - _hash ^= val & 0x3FFFFFF; - _hash ^= (val >> 26) & 0x3F; - } - - public void Add(int number, string arguments = null) - { - if (number == 0) - { - return; - } - - arguments ??= ""; - - if (Header == 0) - { - Header = number; - HeaderArgs = arguments; - } - - AddHash(number); - if (arguments.Length > 0) - { - AddHash(arguments.GetHashCode(StringComparison.Ordinal)); - } - - int strLength = arguments.Length * 2; - int length = _position + 6 + strLength; - while (length > _buffer.Length) - { - Flush(); - } - - var writer = new SpanWriter(_buffer.AsSpan(_position)); - writer.Write(number); - writer.Write((ushort)strLength); - writer.WriteLittleUni(arguments); - - _position += writer.BytesWritten; - } - - 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)); - } - - private int GetStringNumber() => m_StringNumbers[_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)); - } - } -} diff --git a/Projects/Server/Poison.cs b/Projects/Server/Poison.cs index e9ddee248..f309370a5 100644 --- a/Projects/Server/Poison.cs +++ b/Projects/Server/Poison.cs @@ -76,35 +76,5 @@ namespace Server return null; } - - public static void Serialize(Poison p, IGenericWriter writer) - { - if (p == null) - { - writer.Write((byte)0); - } - else - { - writer.Write((byte)1); - writer.Write((byte)p.Level); - } - } - - public static Poison Deserialize(IGenericReader reader) - { - switch (reader.ReadByte()) - { - case 1: return GetPoison(reader.ReadByte()); - case 2: - // no longer used, safe to remove? - reader.ReadInt(); - reader.ReadDouble(); - reader.ReadInt(); - reader.ReadTimeSpan(); - break; - } - - return null; - } } } diff --git a/Projects/Server/Serialization/IRawSerializable.cs b/Projects/Server/PropertyList/IObjectPropertyListEntity.cs similarity index 76% rename from Projects/Server/Serialization/IRawSerializable.cs rename to Projects/Server/PropertyList/IObjectPropertyListEntity.cs index 731b56bd0..4ba8d3735 100644 --- a/Projects/Server/Serialization/IRawSerializable.cs +++ b/Projects/Server/PropertyList/IObjectPropertyListEntity.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: IRawSerializable.cs * + * File: IObjectPropertyListEntity.cs * * * * 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 * @@ -13,11 +13,11 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server +namespace Server; + +public interface IObjectPropertyListEntity : IEntity { - public interface IRawSerializable - { - void Deserialize(IGenericReader reader); - void Serialize(IGenericWriter writer); - } + ObjectPropertyList PropertyList { get; } + + void GetProperties(IPropertyList list); } diff --git a/Projects/Server/PropertyList/IPropertyList.cs b/Projects/Server/PropertyList/IPropertyList.cs new file mode 100644 index 000000000..41b5fa845 --- /dev/null +++ b/Projects/Server/PropertyList/IPropertyList.cs @@ -0,0 +1,46 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: IPropertyList.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Runtime.CompilerServices; +using Server.Text; + +namespace Server; + +public interface IPropertyList : ISelfInterpolatedStringHandler +{ + public void Reset(); + public void Terminate(); + + public void Add(int number); + + /** Convenience method for $"{argument}". */ + public void Add(int number, string argument); + + /** Convenience method for $"{text}". */ + public void Add(string text); + + /** Convenience method for $"{value}". */ + public void Add(int number, int value); + + /** Convenience method for $"{value:#}". */ + public void AddLocalized(int value); + + /** Convenience method for $"{value:#}". */ + public void AddLocalized(int number, int value); + + // String Interpolation + public void Add([InterpolatedStringHandlerArgument("")] ref InterpolatedStringHandler handler); + public void Add(int number, [InterpolatedStringHandlerArgument("")] ref InterpolatedStringHandler handler); +} diff --git a/Projects/Server/PropertyList/ObjectPropertyList.cs b/Projects/Server/PropertyList/ObjectPropertyList.cs new file mode 100644 index 000000000..fd147c65b --- /dev/null +++ b/Projects/Server/PropertyList/ObjectPropertyList.cs @@ -0,0 +1,511 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ObjectPropertyList.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +#nullable enable +using System; +using System.Buffers; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using Server.Buffers; +using Server.Network; +using Server.Text; + +namespace Server; + +public sealed class ObjectPropertyList : IPropertyList, IDisposable +{ + // Each of these are localized to "~1_NOTHING~" which allows the string argument to be used + private static readonly int[] _stringNumbers = + { + 1042971, + 1070722, + 1114057, // ~1_val~ + 1114778, // ~1_val~ + 1114779 // ~1_val~ + }; + + private int _hash; + private int _stringNumbersIndex; + private byte[] _buffer; + private int _bufferPos; + + // For string interpolation + private int _pos; + private char[]? _arrayToReturnToPool; + + public ObjectPropertyList(IEntity? e) + { + Entity = e; + _buffer = GC.AllocateUninitializedArray(64); + + var writer = new SpanWriter(_buffer); + writer.Write((byte)0xD6); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write((ushort)1); + writer.Write(e?.Serial ?? Serial.Zero); + writer.Write((ushort)0); + _bufferPos = writer.Position + 4; // Hash + } + + public IEntity? Entity { get; } + + public int Hash => 0x40000000 + _hash; + + public int Header { get; set; } + + public string HeaderArgs { get; set; } + + public static bool Enabled { get; set; } + + public byte[] Buffer => _buffer; + + public void Reset() + { + _bufferPos = 15; + _hash = 0; + _stringNumbersIndex = 0; + Header = 0; + HeaderArgs = null; + STArrayPool.Shared.Return(_arrayToReturnToPool); + _pos = 0; + } + + private void Flush() + { + Resize(_buffer.Length * 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Resize(int amount) + { + var newBuffer = GC.AllocateUninitializedArray(amount); + _buffer.AsSpan(0, Math.Min(amount, _buffer.Length)).CopyTo(newBuffer); + _buffer = newBuffer; + } + + public void Terminate() + { + int length = _bufferPos + 4; + if (length != _buffer.Length) + { + Resize(length); + } + + var writer = new SpanWriter(_buffer); + writer.Seek(_bufferPos, SeekOrigin.Begin); + writer.Write(0); + + writer.Seek(11, SeekOrigin.Begin); + writer.Write(_hash); + writer.WritePacketLength(); + } + + private void AddHash(int val) + { + _hash ^= val & 0x3FFFFFF; + _hash ^= (val >> 26) & 0x3F; + } + + public void Add(int number) + { + if (number == 0) + { + return; + } + + if (Header == 0) + { + Header = number; + HeaderArgs = ""; + } + + AddHash(number); + + int length = _bufferPos + 6; + while (length > _buffer.Length) + { + Flush(); + } + + var writer = new SpanWriter(_buffer.AsSpan(_bufferPos)); + writer.Write(number); + writer.Write((ushort)0); + _bufferPos += 6; + } + + public void Add(int number, string? arguments) => InternalAdd(number, $"{arguments}"); + public void Add(string argument) => InternalAdd(GetStringNumber(), $"{argument}"); + public void Add(int number, int value) => InternalAdd(number, $"{value}"); + public void AddLocalized(int value) => InternalAdd(GetStringNumber(), $"{value:#}"); + public void AddLocalized(int number, int value) => InternalAdd(number, $"{value:#}"); + + private int GetStringNumber() => _stringNumbers[_stringNumbersIndex++ % _stringNumbers.Length]; + + // String Interpolation + public void Add( + [InterpolatedStringHandlerArgument("")] + ref IPropertyList.InterpolatedStringHandler handler + ) => InternalAdd(GetStringNumber(), ref handler); + + public void Add( + int number, + [InterpolatedStringHandlerArgument("")] + ref IPropertyList.InterpolatedStringHandler handler + ) => InternalAdd(number, ref handler); + + private void InternalAdd( + int number, + [InterpolatedStringHandlerArgument("")] + ref IPropertyList.InterpolatedStringHandler handler) + { + if (number == 0) + { + return; + } + + var chars = _arrayToReturnToPool.AsSpan(0, _pos); + + if (Header == 0) + { + Header = number; + HeaderArgs = chars.ToString(); + } + + AddHash(number); + AddHash(string.GetHashCode(chars, StringComparison.Ordinal)); + + int strLength = chars.Length * 2; + int length = _bufferPos + 6 + strLength; + while (length > _buffer.Length) + { + Flush(); + } + + var writer = new SpanWriter(_buffer.AsSpan(_bufferPos)); + writer.Write(number); + writer.Write((ushort)strLength); + writer.Write(chars, TextEncoding.UnicodeLE); + + _bufferPos += writer.BytesWritten; + } + + public void InitializeInterpolation(int literalLength, int formattedCount) + { + _arrayToReturnToPool ??= STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); + _pos = 0; + } + + // Copied from RawInterpolatedStringHandler + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetDefaultLength(int literalLength, int formattedCount) => + Math.Max(256, literalLength + formattedCount * 11); + + public void AppendLiteral(string value) + { + if (value.Length == 1) + { + Span chars = _arrayToReturnToPool.AsSpan(); + int pos = _pos; + if ((uint)pos < (uint)chars.Length) + { + chars[pos] = value[0]; + _pos = pos + 1; + } + else + { + GrowThenCopyString(value); + } + return; + } + + AppendStringDirect(value); + } + + private void AppendStringDirect(string value) + { + if (value.TryCopyTo(_arrayToReturnToPool.AsSpan(_pos..))) + { + _pos += value.Length; + } + else + { + GrowThenCopyString(value); + } + } + + public void AppendFormatted(T value) + { + string? s; + if (value is IFormattable) + { + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_arrayToReturnToPool.AsSpan(_pos..), out charsWritten, default, null)) + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format: null, null); + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + + public void AppendFormatted(T value, string? format) + { + // We support localization '#' cliloc formatter for custom property lists + // This allows someone to build an IPropertyList that creates HTML using the same syntax as LocalizationInterpolationHandler + if (format == "#") + { + AppendLiteral("#"); + format = null; + } + + string? s; + if (value is IFormattable) + { + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_arrayToReturnToPool.AsSpan(_pos..), out charsWritten, format, null)) + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format, null); + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + + public void AppendFormatted(T value, int alignment) + { + int startingPos = _pos; + AppendFormatted(value); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + + public void AppendFormatted(T value, int alignment, string? format) + { + int startingPos = _pos; + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + + public void AppendFormatted(ReadOnlySpan value) + { + if (value.TryCopyTo(_arrayToReturnToPool.AsSpan(_pos..))) + { + _pos += value.Length; + } + else + { + GrowThenCopySpan(value); + } + } + + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + AppendFormatted(value); + return; + } + + EnsureCapacityForAdditionalChars(value.Length + paddingRequired); + var chars = _arrayToReturnToPool.AsSpan(); + if (leftAlign) + { + value.CopyTo(chars[_pos..]); + _pos += value.Length; + chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + } + else + { + chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + value.CopyTo(chars[_pos..]); + _pos += value.Length; + } + } + + public void AppendFormatted(string? value) + { + if (value?.TryCopyTo(_arrayToReturnToPool.AsSpan(_pos..)) == true) + { + _pos += value.Length; + } + else + { + AppendFormattedSlow(value); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendFormattedSlow(string? value) + { + if (value is not null) + { + EnsureCapacityForAdditionalChars(value.Length); + value.CopyTo(_arrayToReturnToPool.AsSpan(_pos..)); + _pos += value.Length; + } + } + + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => + AppendFormatted(value, alignment, format); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => + AppendFormatted(value, alignment, format); + + private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) + { + Debug.Assert(startingPos >= 0 && startingPos <= _pos); + Debug.Assert(alignment != 0); + + int charsWritten = _pos - startingPos; + + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingNeeded = alignment - charsWritten; + if (paddingNeeded > 0) + { + EnsureCapacityForAdditionalChars(paddingNeeded); + + var chars = _arrayToReturnToPool.AsSpan(); + if (leftAlign) + { + chars.Slice(_pos, paddingNeeded).Fill(' '); + } + else + { + chars.Slice(startingPos, charsWritten).CopyTo(chars[(startingPos + paddingNeeded)..]); + chars.Slice(startingPos, paddingNeeded).Fill(' '); + } + + _pos += paddingNeeded; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureCapacityForAdditionalChars(int additionalChars) + { + if (_arrayToReturnToPool.Length - _pos < additionalChars) + { + Grow(additionalChars); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopyString(string value) + { + Grow(value.Length); + value.CopyTo(_arrayToReturnToPool.AsSpan(_pos..)); + _pos += value.Length; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopySpan(ReadOnlySpan value) + { + Grow(value.Length); + value.CopyTo(_arrayToReturnToPool.AsSpan(_pos..)); + _pos += value.Length; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalChars) + { + Debug.Assert(additionalChars > _arrayToReturnToPool.Length - _pos); + GrowCore((uint)_pos + (uint)additionalChars); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow() + { + GrowCore((uint)_arrayToReturnToPool.Length + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void GrowCore(uint requiredMinCapacity) + { + uint newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_arrayToReturnToPool.Length * 2, 0x3FFFFFDF)); + int arraySize = (int)Math.Clamp(newCapacity, 256, int.MaxValue); + + char[] newArray = STArrayPool.Shared.Rent(arraySize); + _arrayToReturnToPool.AsSpan(.._pos).CopyTo(newArray); + + char[] toReturn = _arrayToReturnToPool; + _arrayToReturnToPool = newArray; + + STArrayPool.Shared.Return(toReturn); + } + + public void Dispose() + { + STArrayPool.Shared.Return(_arrayToReturnToPool); + _arrayToReturnToPool = null; + } + + ~ObjectPropertyList() + { + STArrayPool.Shared.Return(_arrayToReturnToPool); + _arrayToReturnToPool = null; + } +} diff --git a/Projects/Server/Random/RandomSources.cs b/Projects/Server/Random/RandomSources.cs index d99add499..49f8a6188 100644 --- a/Projects/Server/Random/RandomSources.cs +++ b/Projects/Server/Random/RandomSources.cs @@ -17,10 +17,14 @@ namespace Server.Random { public static class RandomSources { - private static IRandomSource m_Source; - private static IRandomSource m_SecureSource; + private static IRandomSource _source; + private static IRandomSource _secureSource; - public static IRandomSource Source => m_Source ??= new Xoshiro256PlusPlus(); - public static IRandomSource SecureSource => m_SecureSource ??= new SecureRandom(); + public static IRandomSource Source => _source ??= new Xoshiro256PlusPlus(); + public static IRandomSource SecureSource => _secureSource ??= new SecureRandom(); + + public static void SetRng(IRandomSource newSource) => _source = newSource; + + public static void SetSecureRng(IRandomSource newSource) => _secureSource = newSource; } } diff --git a/Projects/Server/Regions/Region.cs b/Projects/Server/Regions/Region.cs index f6a9a1ae5..6f1b903e5 100644 --- a/Projects/Server/Regions/Region.cs +++ b/Projects/Server/Regions/Region.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json; using Server.Json; +using Server.Logging; using Server.Network; using Server.Targeting; @@ -82,10 +83,11 @@ namespace Server public class Region : IComparable { - public static readonly int DefaultPriority = 50; + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Region)); - public static readonly int MinZ = sbyte.MinValue; - public static readonly int MaxZ = sbyte.MaxValue + 1; + public const int DefaultPriority = 50; + public const int MinZ = sbyte.MinValue; + public const int MaxZ = sbyte.MaxValue + 1; public Region(string name, Map map, int priority, params Rectangle2D[] area) : this( name, @@ -156,7 +158,7 @@ namespace Server if (Area.Length == 0) { - Console.WriteLine("Empty area for region '{0}'", this); + logger.Debug("Empty area for region '{Region}'", this); } if (json.GetProperty("go", options, out Point3D go)) @@ -330,8 +332,8 @@ namespace Server { var rect = Area[i]; - var start = Map.Bound(new Point2D(rect.Start)); - var end = Map.Bound(new Point2D(rect.End)); + var start = Map.Bound(new Point2D(rect.Start.X, rect.Start.Y)); + var end = Map.Bound(new Point2D(rect.End.X, rect.End.Y)); var startSector = Map.GetSector(start); var endSector = Map.GetSector(end); @@ -368,7 +370,7 @@ namespace Server if (Children.Count > 0) { - Console.WriteLine("Warning: Unregistering region '{0}' with children", this); + logger.Warning("Unregistering region '{Region}' with children", this); } if (Parent != null) diff --git a/Projects/Server/Regions/RegionLoader.cs b/Projects/Server/Regions/RegionLoader.cs index 73ec7a3c9..15fe2a9b1 100644 --- a/Projects/Server/Regions/RegionLoader.cs +++ b/Projects/Server/Regions/RegionLoader.cs @@ -13,7 +13,6 @@ * along with this program. If not, see . * *************************************************************************/ -using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -64,7 +63,7 @@ namespace Server if (failures.Count == 0) { logger.Information( - "Regions loaded ({0} regions, {1} failures) ({2:F2} seconds)", + "Regions loaded ({Count} regions, {FailureCount} failures) ({Duration:F2} seconds)", count, failures.Count, stopwatch.Elapsed.TotalSeconds @@ -73,13 +72,13 @@ namespace Server else { logger.Warning( - "Failed loading regions ({0} regions, {1} failures) ({2:F2} seconds)", + "Failed loading regions ({Count} regions, {FailureCount} failures) ({Duration:F2} seconds)", count, failures.Count, stopwatch.Elapsed.TotalSeconds ); - logger.Warning(string.Join(Environment.NewLine, failures)); + logger.Warning("{Failures}", failures); } } } diff --git a/Projects/Server/Serial.cs b/Projects/Server/Serial.cs index 3f90dbb21..7a83163fa 100644 --- a/Projects/Server/Serial.cs +++ b/Projects/Server/Serial.cs @@ -16,120 +16,148 @@ using System; using System.Runtime.CompilerServices; -namespace Server +namespace Server; + +public readonly struct Serial : IComparable, IComparable, IEquatable, ISpanFormattable { - public readonly struct Serial : IComparable, IComparable, IEquatable + public static readonly Serial MinusOne = new(0xFFFFFFFF); + public static readonly Serial Zero = new(0); + + private Serial(uint serial) => Value = serial; + + public uint Value { get; } + + public bool IsMobile { - public static readonly Serial MinusOne = new(0xFFFFFFFF); - public static readonly Serial Zero = new(0); - - private Serial(uint serial) => Value = serial; - - public uint Value { get; } - - public bool IsMobile - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Value > 0 && Value < World.ItemOffset; - } - - public bool IsItem - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Value >= World.ItemOffset && Value < World.MaxItemSerial; - } - - public bool IsValid - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Value > 0; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => Value.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompareTo(Serial other) => Value.CompareTo(other.Value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompareTo(uint other) => Value.CompareTo(other); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) => - obj switch - { - Serial serial => this == serial, - uint u => Value == u, - _ => false - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Serial l, Serial r) => l.Value == r.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Serial l, uint r) => l.Value == r; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Serial l, Serial r) => l.Value != r.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Serial l, uint r) => l.Value != r; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >(Serial l, Serial r) => l.Value > r.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >(Serial l, uint r) => l.Value > r; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <(Serial l, Serial r) => l.Value < r.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <(Serial l, uint r) => l.Value < r; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >=(Serial l, Serial r) => l.Value >= r.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator >=(Serial l, uint r) => l.Value >= r; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <=(Serial l, Serial r) => l.Value <= r.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator <=(Serial l, uint r) => l.Value <= r; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Serial operator +(Serial l, Serial r) => (Serial)(l.Value + r.Value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Serial operator +(Serial l, uint r) => (Serial)(l.Value + r); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Serial operator ++(Serial l) => (Serial)(l.Value + 1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Serial operator -(Serial l, Serial r) => (Serial)(l.Value - r.Value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Serial operator -(Serial l, uint r) => (Serial)(l.Value - r); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Serial operator --(Serial l) => (Serial)(l.Value - 1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"0x{Value:X8}"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator uint(Serial a) => a.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Serial(uint a) => new(a); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Serial other) => Value == other.Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ToInt32() => (int)Value; + get => Value > 0 && Value < World.ItemOffset; } + + public bool IsItem + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Value >= World.ItemOffset && Value < World.MaxItemSerial; + } + + public bool IsValid + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Value > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => Value.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Serial other) => Value.CompareTo(other.Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(uint other) => Value.CompareTo(other); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => + obj switch + { + Serial serial => this == serial, + uint u => Value == u, + _ => false + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Serial l, Serial r) => l.Value == r.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Serial l, uint r) => l.Value == r; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Serial l, Serial r) => l.Value != r.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Serial l, uint r) => l.Value != r; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Serial l, Serial r) => l.Value > r.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Serial l, uint r) => l.Value > r; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Serial l, Serial r) => l.Value < r.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Serial l, uint r) => l.Value < r; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Serial l, Serial r) => l.Value >= r.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Serial l, uint r) => l.Value >= r; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Serial l, Serial r) => l.Value <= r.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Serial l, uint r) => l.Value <= r; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial operator +(Serial l, Serial r) => (Serial)(l.Value + r.Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial operator +(Serial l, uint r) => (Serial)(l.Value + r); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial operator ++(Serial l) => (Serial)(l.Value + 1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial operator -(Serial l, Serial r) => (Serial)(l.Value - r.Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial operator -(Serial l, uint r) => (Serial)(l.Value - r); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Serial operator --(Serial l) => (Serial)(l.Value - 1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() => $"0x{Value:X8}"; + + public string ToString(string format, IFormatProvider formatProvider) => ToString(); + + public bool TryFormat( + Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider + ) + { + if (format != null) + { + return Value.TryFormat(destination, out charsWritten, format, provider); + } + + if (destination.Length < 10) + { + charsWritten = 0; + return false; + } + + destination[0] = '0'; + destination[1] = 'x'; + + var result = Value.TryFormat(destination[2..], out charsWritten, "X8", provider); + if (result) + { + charsWritten += 2; + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator uint(Serial a) => a.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Serial(uint a) => new(a); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Serial other) => Value == other.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ToInt32() => (int)Value; } diff --git a/Projects/Server/Serialization/Attributes/AfterDeserializationAttribute.cs b/Projects/Server/Serialization/Attributes/AfterDeserializationAttribute.cs deleted file mode 100644 index 97386a53d..000000000 --- a/Projects/Server/Serialization/Attributes/AfterDeserializationAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AfterDeserializationAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that this method should be executed after deserializing the object. - /// Method must have no parameters and return void. - /// - [AttributeUsage(AttributeTargets.Method)] - public class AfterDeserializationAttribute : Attribute - { - /// - /// Indicates whether the source generator should execute the method this is attached to immediately, or when - /// this is set to false, execute it using a Timer Delay. - /// - /// Note: Use false when the after deserialization involves deleting objects. This is to prevent corrupted - /// deserialization by removing an object before it has finished deserializing. - /// - public bool Synchronous { get; set; } - - public AfterDeserializationAttribute(bool synchronous = true) => Synchronous = true; - } -} diff --git a/Projects/Server/Serialization/Attributes/DeltaDateTimeAttribute.cs b/Projects/Server/Serialization/Attributes/DeltaDateTimeAttribute.cs deleted file mode 100644 index aa77bdacb..000000000 --- a/Projects/Server/Serialization/Attributes/DeltaDateTimeAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: DeltaDateTimeAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that a serializable DateTime field or property is for delta time (duration) - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class DeltaDateTimeAttribute : Attribute - { - } -} diff --git a/Projects/Server/Serialization/Attributes/InternStringAttribute.cs b/Projects/Server/Serialization/Attributes/InternStringAttribute.cs deleted file mode 100644 index 968b0dd39..000000000 --- a/Projects/Server/Serialization/Attributes/InternStringAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: InternalizeString.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that a serializable string field or property should be internalized on deserialization - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class InternStringAttribute : Attribute - { - } -} diff --git a/Projects/Server/Serialization/Attributes/InvalidatePropertiesAttribute.cs b/Projects/Server/Serialization/Attributes/InvalidatePropertiesAttribute.cs deleted file mode 100644 index b6f20f41e..000000000 --- a/Projects/Server/Serialization/Attributes/InvalidatePropertiesAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: InvalidatePropertiesAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that this field will execute an InvalidateProperties when the value is set - /// and the value is different from the current value. - /// - [AttributeUsage(AttributeTargets.Field)] - public class InvalidatePropertiesAttribute : Attribute - { - } -} diff --git a/Projects/Server/Serialization/Attributes/SerializableFieldAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableFieldAttribute.cs deleted file mode 100755 index 52a3dc097..000000000 --- a/Projects/Server/Serialization/Attributes/SerializableFieldAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableFieldAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that this field or property should be serialized. - /// When used on a field, the source generator will generate the property entirely. - /// When used on a property, the user must call this.MarkDirty() after reassigning the value or modifying the value internally. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class SerializableFieldAttribute : Attribute - { - public int Order { get; } - public string PropertyGetter { get; } - public string? PropertySetter { get; } - public bool IsVirtual { get; } - - public SerializableFieldAttribute( - int order, - string getter = "public", - string setter = "public", - bool isVirtual = false - ) - { - Order = order; - PropertyGetter = getter; - PropertySetter = setter; - IsVirtual = isVirtual; - } - } -} diff --git a/Projects/Server/Serialization/Attributes/SerializableFieldAttributeAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableFieldAttributeAttribute.cs deleted file mode 100644 index 1cf8d6bb5..000000000 --- a/Projects/Server/Serialization/Attributes/SerializableFieldAttributeAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableFieldAttributeAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that this field will need this attribute on the generated property - /// [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] - /// -or- - /// [SerializableFieldAttr(typeof(CommandPropertyAttribute), AccessLevel.GameMaster)] - /// - [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] - public sealed class SerializableFieldAttrAttribute : Attribute - { - public string AttributeString { get; } - public Type AttributeType { get; } - public object[] Arguments { get; } - - public SerializableFieldAttrAttribute(string attrString) => AttributeString = attrString; - - public SerializableFieldAttrAttribute(Type type, params object[] args) - { - if (typeof(Attribute).IsAssignableFrom(type)) - { - throw new ArgumentException($"Argument {nameof(type)} must be an attribute."); - } - - AttributeType = type; - Arguments = args; - } - } -} diff --git a/Projects/Server/Serialization/Attributes/SerializableFieldDefaultAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableFieldDefaultAttribute.cs deleted file mode 100644 index 35ba44256..000000000 --- a/Projects/Server/Serialization/Attributes/SerializableFieldDefaultAttribute.cs +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableFieldDefaultAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that the field with the same order should use this default value - /// while deserializing. The default is used when the save flag indicates that we don't need to serialize the value - /// because this default can be used instead. - /// - /// Note: This is only used for the current version, not previous versions. Previous versions will always use a default - /// value for that type if it is not deserialized. - /// - [AttributeUsage(AttributeTargets.Method)] - public sealed class SerializableFieldDefaultAttribute : Attribute - { - public int Order { get; } - - public SerializableFieldDefaultAttribute(int order) => Order = order; - } -} diff --git a/Projects/Server/Serialization/Attributes/SerializableFieldSaveFlagAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableFieldSaveFlagAttribute.cs deleted file mode 100644 index fb9c16184..000000000 --- a/Projects/Server/Serialization/Attributes/SerializableFieldSaveFlagAttribute.cs +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableFieldSaveFlagAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that the field with the same order value should use a save flag. - /// - [AttributeUsage(AttributeTargets.Method)] - public sealed class SerializableFieldSaveFlagAttribute : Attribute - { - public int Order { get; } - - public SerializableFieldSaveFlagAttribute(int order) => Order = order; - } -} diff --git a/Projects/Server/Serialization/Attributes/SerializableParentAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableParentAttribute.cs deleted file mode 100644 index 12526d7f3..000000000 --- a/Projects/Server/Serialization/Attributes/SerializableParentAttribute.cs +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializableParentAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that this field or property indicates the ISerializable parent of this class. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class SerializableParentAttribute : Attribute - { - public SerializableParentAttribute() - { - } - } -} diff --git a/Projects/Server/Serialization/Attributes/TimerDriftAttribute.cs b/Projects/Server/Serialization/Attributes/TimerDriftAttribute.cs deleted file mode 100644 index 87bb7174d..000000000 --- a/Projects/Server/Serialization/Attributes/TimerDriftAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: TimerDriftAttribute.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server -{ - /// - /// Hints to the source generator that this serializable timer field or property will drift - /// during deserialization. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class TimerDriftAttribute : Attribute - { - } -} diff --git a/Projects/Server/Serialization/BufferReader.cs b/Projects/Server/Serialization/BufferReader.cs index 91641c343..7ba58bb16 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -159,13 +159,15 @@ namespace Server public BitArray ReadBitArray() { - var length = ((IGenericReader)this).ReadEncodedInt(); + var bitLength = ((IGenericReader)this).ReadEncodedInt(); + var length = BitArray.GetByteArrayLengthFromBitLength(bitLength); + if (length > _buffer.Length - _position) { throw new OutOfMemoryException(); } - var bitArray = new BitArray(_buffer.AsSpan(_position, length)); + var bitArray = new BitArray(_buffer.AsSpan(_position, length), bitLength); _position += length; return bitArray; } diff --git a/Projects/Server/Serialization/BufferWriter.cs b/Projects/Server/Serialization/BufferWriter.cs index 167d24edb..044bb8302 100644 --- a/Projects/Server/Serialization/BufferWriter.cs +++ b/Projects/Server/Serialization/BufferWriter.cs @@ -138,9 +138,9 @@ namespace Server public void Write(BitArray bitArray) { var byteLength = BitArray.GetByteArrayLengthFromBitLength(bitArray.Length); - FlushIfNeeded(byteLength + 4); - ((IGenericWriter)this).WriteEncodedInt(byteLength); + ((IGenericWriter)this).WriteEncodedInt(bitArray.Length); + FlushIfNeeded(byteLength); bitArray.CopyTo(_buffer.AsSpan((int)Index, byteLength)); Index += byteLength; } diff --git a/Projects/Server/Serialization/ISerializableExtensions.cs b/Projects/Server/Serialization/ISerializableExtensions.cs index b98b239ef..15ecb4f1c 100644 --- a/Projects/Server/Serialization/ISerializableExtensions.cs +++ b/Projects/Server/Serialization/ISerializableExtensions.cs @@ -24,7 +24,10 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MarkDirty(this ISerializable entity) { - entity.SavePosition = -1; + if (entity != null) + { + entity.SavePosition = -1; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Serialization/Persistence.cs b/Projects/Server/Serialization/Persistence.cs index c4abd1458..8c76165c7 100644 --- a/Projects/Server/Serialization/Persistence.cs +++ b/Projects/Server/Serialization/Persistence.cs @@ -46,6 +46,8 @@ namespace Server ); } + public static void Unregister(string name) => _registry.RemoveWhere(entry => entry.Name == name); + public static void Load(string path) { // This should probably not be parallel since Mobiles must be loaded before Items diff --git a/Projects/Server/Serialization/SerializationExtensions.cs b/Projects/Server/Serialization/SerializationExtensions.cs new file mode 100644 index 000000000..a9d6d94a2 --- /dev/null +++ b/Projects/Server/Serialization/SerializationExtensions.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using Server.Guilds; + +namespace Server; + +public static class SerializationExtensions +{ + public static T ReadEntity(this IGenericReader reader) where T : class, ISerializable + { + Serial serial = reader.ReadSerial(); + var typeT = typeof(T); + + T entity; + + // Add to this list when creating new serializable types + if (typeof(BaseGuild).IsAssignableFrom(typeT)) + { + entity = World.FindGuild(serial) as T; + // If we check for `entity.Deleted` here during deserialization then all guilds are deleted because + // Deleted -> Disbanded -> No leader, which is the case before deserialization. + // TODO: Use a deleted flag instead, and actively check for dibanded guilds properly. + } + else + { + entity = World.FindEntity(serial) as T; + if (entity?.Deleted == false) + { + return entity; + } + } + + return entity?.Created <= reader.LastSerialized ? entity : null; + } + + public static List ReadEntityList(this IGenericReader reader) where T : class, ISerializable + { + var count = reader.ReadInt(); + + var list = new List(count); + + for (var i = 0; i < count; ++i) + { + var entity = reader.ReadEntity(); + if (entity != null) + { + list.Add(entity); + } + } + + return list; + } + + public static HashSet ReadEntitySet(this IGenericReader reader) where T : class, ISerializable + { + var count = reader.ReadInt(); + + var set = new HashSet(count); + + for (var i = 0; i < count; ++i) + { + var entity = reader.ReadEntity(); + if (entity != null) + { + set.Add(entity); + } + } + + return set; + } + + public static void Write(this IGenericWriter writer, ISerializable value) + { + writer.Write(value?.Deleted != false ? Serial.MinusOne : value.Serial); + } + + public static void Write(this IGenericWriter writer, ICollection coll) where T : class, ISerializable + { + writer.Write(coll.Count); + foreach (var entry in coll) + { + writer.Write(entry); + } + } + + public static void Write( + this IGenericWriter writer, ICollection coll, Action action + ) where T : class, ISerializable + { + if (coll == null) + { + writer.Write(0); + return; + } + + writer.Write(coll.Count); + foreach (var entry in coll) + { + action(writer, entry); + } + } + + public static void Write(this IGenericWriter writer, Poison p) + { + if (p == null) + { + writer.Write(false); + } + else + { + writer.Write(true); + writer.Write((byte)p.Level); + } + } + + public static Poison ReadPoison(this IGenericReader reader) => + reader.ReadBool() ? Poison.GetPoison(reader.ReadByte()) : null; +} diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index 5293d55e6..0e310043d 100755 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -21,6 +21,8 @@ + + @@ -35,18 +37,16 @@ - - - - - - TargetFramework=netstandard2.0 - Analyzer - false - all - + + + + + + + + diff --git a/Projects/Server/Skills.cs b/Projects/Server/Skills.cs index 75bfaf0c9..6ad91ebbf 100644 --- a/Projects/Server/Skills.cs +++ b/Projects/Server/Skills.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using Server.Network; @@ -137,7 +136,6 @@ namespace Server if (Lock is < SkillLock.Up or > SkillLock.Locked) { - Console.WriteLine("Bad skill lock -> {0}.{1}", owner.Owner, Lock); Lock = SkillLock.Up; } } @@ -277,30 +275,30 @@ namespace Server double bonusObey = 0.0, bonusNotObey = 0.0; - for (var i = 0; i < mods.Count; ++i) + foreach (var mod in mods) { - var mod = mods[i]; - - if (mod.Skill == (SkillName)Info.SkillID) + if (mod.Skill != (SkillName)Info.SkillID) { - if (mod.Relative) + continue; + } + + if (mod.Relative) + { + if (mod.ObeyCap) { - if (mod.ObeyCap) - { - bonusObey += mod.Value; - } - else - { - bonusNotObey += mod.Value; - } + bonusObey += mod.Value; } else { - bonusObey = 0.0; - bonusNotObey = 0.0; - value = mod.Value; + bonusNotObey += mod.Value; } } + else + { + bonusObey = 0.0; + bonusNotObey = 0.0; + value = mod.Value; + } } value += bonusNotObey; diff --git a/Projects/Server/Text/ISelfInterpolatedStringHandler.cs b/Projects/Server/Text/ISelfInterpolatedStringHandler.cs new file mode 100644 index 000000000..d9beb2635 --- /dev/null +++ b/Projects/Server/Text/ISelfInterpolatedStringHandler.cs @@ -0,0 +1,71 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ISelfInterpolatedStringHandler.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; + +namespace Server.Text; + +public interface ISelfInterpolatedStringHandler +{ + public void Add([InterpolatedStringHandlerArgument("")] ref InterpolatedStringHandler handler); + public void InitializeInterpolation(int literalLength, int formattedCount); + public void AppendLiteral(string value); + public void AppendFormatted(T value); + public void AppendFormatted(T value, string? format); + public void AppendFormatted(T value, int alignment); + public void AppendFormatted(T value, int alignment, string? format); + public void AppendFormatted(ReadOnlySpan value); + public void AppendFormatted(ReadOnlySpan value, int alignment, string? format = null); + public void AppendFormatted(object? value, int alignment = 0, string? format = null); + public void AppendFormatted(string? value); + public void AppendFormatted(string? value, int alignment, string? format = null); + + [InterpolatedStringHandler] + public ref struct InterpolatedStringHandler + { + private ISelfInterpolatedStringHandler _parent; + + public InterpolatedStringHandler(int literalLength, int formattedCount, ISelfInterpolatedStringHandler parent) + { + _parent = parent; + _parent.InitializeInterpolation(literalLength, formattedCount); + } + + public void AppendLiteral(string value) => _parent.AppendLiteral(value); + + public void AppendFormatted(T value) => _parent.AppendFormatted(value); + + public void AppendFormatted(T value, string? format) => _parent.AppendFormatted(value, format); + + public void AppendFormatted(T value, int alignment) => _parent.AppendFormatted(value, alignment); + + public void AppendFormatted(T value, int alignment, string? format) => + _parent.AppendFormatted(value, alignment, format); + + public void AppendFormatted(ReadOnlySpan value) => _parent.AppendFormatted(value); + + public void AppendFormatted(ReadOnlySpan value, int alignment, string? format = null) => + _parent.AppendFormatted(value, alignment, format); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => + _parent.AppendFormatted(value, alignment, format); + + public void AppendFormatted(string? value) => _parent.AppendFormatted(value); + + public void AppendFormatted(string? value, int alignment, string? format = null) => + _parent.AppendFormatted(value, alignment, format); + } +} diff --git a/Projects/Server/Text/StringHelpers.cs b/Projects/Server/Text/StringHelpers.cs index cf97b6a7c..7e22c6341 100644 --- a/Projects/Server/Text/StringHelpers.cs +++ b/Projects/Server/Text/StringHelpers.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright (C) 2019-2020 - ModernUO Development Team * + * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: StringHelpers.cs * * * @@ -14,276 +14,293 @@ *************************************************************************/ using System; -using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Server.Buffers; -namespace Server +namespace Server; + +public static class StringHelpers { - public static class StringHelpers + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string DefaultIfNullOrEmpty(this string value, string def) => + string.IsNullOrWhiteSpace(value) ? def : value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Remove( + this ReadOnlySpan a, + ReadOnlySpan b, + StringComparison comparison, + Span buffer, + out int size + ) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string DefaultIfNullOrEmpty(this string value, string def) => - string.IsNullOrWhiteSpace(value) ? def : value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Remove( - this ReadOnlySpan a, - ReadOnlySpan b, - StringComparison comparison, - Span buffer, - out int size - ) + size = 0; + if (a == null || a.Length == 0) { - size = 0; - if (a == null || a.Length == 0) + return; + } + + var sliced = a; + + while (true) + { + var indexOf = sliced.IndexOf(b, comparison); + if (indexOf == -1) { - return; + indexOf = sliced.Length; } - var sliced = a; - - while (true) + if (size + indexOf > buffer.Length) { - var indexOf = sliced.IndexOf(b, comparison); - if (indexOf == -1) - { - indexOf = sliced.Length; - } + throw new OutOfMemoryException(nameof(buffer)); + } - if (size + indexOf > buffer.Length) - { - throw new OutOfMemoryException(nameof(buffer)); - } + sliced[..indexOf].CopyTo(buffer[size..]); + size += indexOf; - sliced[..indexOf].CopyTo(buffer[size..]); - size += indexOf; + if (indexOf == sliced.Length) + { + break; + } - if (indexOf == sliced.Length) + sliced = sliced[(indexOf + 1)..]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Remove(this ReadOnlySpan a, ReadOnlySpan b, StringComparison comparison) + { + if (a == null) + { + return null; + } + + if (a.Length == 0) + { + return ""; + } + + Span span = a.Length < 1024 ? stackalloc char[a.Length] : null; + char[] chrs; + if (span == null) + { + chrs = STArrayPool.Shared.Rent(a.Length); + span = chrs.AsSpan(); + } + else + { + chrs = null; + } + + a.Remove(b, comparison, span, out var size); + + var str = span[..size].ToString(); + + if (chrs != null) + { + STArrayPool.Shared.Return(chrs); + } + + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Capitalize(this string value) + { + if (string.IsNullOrEmpty(value)) + { + return value; + } + + Span span = value.Length < 1024 ? stackalloc char[value.Length] : null; + char[] chrs; + if (span == null) + { + chrs = STArrayPool.Shared.Rent(value.Length); + span = chrs.AsSpan(); + } + else + { + chrs = null; + } + + var sliced = value.AsSpan(); + // Copy over the previous span + sliced.CopyTo(span); + + var index = 0; + + while (true) + { + // Special case for titles - words that don't get capitalized + if (sliced.InsensitiveStartsWith("the ")) + { + sliced = sliced[4..]; + index += 4; + continue; + } + + var indexOf = sliced.IndexOf(' '); + span[index] = char.ToUpperInvariant(sliced[0]); + + if (indexOf == -1) + { + break; + } + + if (indexOf == sliced.Length - 1) + { + break; + } + + sliced = sliced[(indexOf + 1)..]; + index += indexOf + 1; + } + + var str = span.ToString(); + + if (chrs != null) + { + STArrayPool.Shared.Return(chrs); + } + + return str; + } + + public static string TrimMultiline(this string str, string lineSeparator = "\n") + { + var parts = str.Split(lineSeparator); + for (var i = 0; i < parts.Length; i++) + { + parts[i] = parts[i].Trim(); + } + + return string.Join(lineSeparator, parts); + } + + public static string IndentMultiline(this string str, string indent = "\t", string lineSeparator = "\n") + { + var parts = str.Split(lineSeparator); + for (var i = 0; i < parts.Length; i++) + { + parts[i] = $"{indent}{parts[i]}"; + } + + return string.Join(lineSeparator, parts); + } + + public static List Wrap(this string value, int perLine, int maxLines) + { + if ((value = value?.Trim() ?? "").Length <= 0) + { + return null; + } + + var span = value.AsSpan(); + var list = new List(maxLines); + var lineLength = 0; + + while (span.Length > 0) + { + var spaceIndex = span[lineLength..].IndexOf(' '); + if (spaceIndex == -1) + { + spaceIndex = span.Length - lineLength; // End of the string + } + + var newLineLength = lineLength + spaceIndex; + + // If the previous line is exactly perLine or not too long and we are at the end + if (newLineLength == perLine || newLineLength < perLine && newLineLength == span.Length) + { + list.Add(span[..newLineLength].ToString()); + if (list.Count == maxLines || newLineLength == span.Length) { break; } - sliced = sliced[(indexOf + 1)..]; + span = span[(newLineLength + 1)..]; + lineLength = 0; } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Remove(this ReadOnlySpan a, ReadOnlySpan b, StringComparison comparison) - { - if (a == null) + // We haven't hit perLine and are not sure if we can continue adding more words without going over + else if (newLineLength < perLine) { - return null; + lineLength = newLineLength + 1; } - - if (a.Length == 0) + // We already tried making the line longer, and it was too long, so fall back to the old line + else if (lineLength > 0 && lineLength <= perLine) { - return ""; - } + list.Add(span[..(lineLength - 1)].ToString()); + if (list.Count == maxLines) + { + break; + } - Span span = a.Length < 1024 ? stackalloc char[a.Length] : null; - char[] chrs; - if (span == null) - { - chrs = ArrayPool.Shared.Rent(a.Length); - span = chrs.AsSpan(); + span = span[lineLength..]; + lineLength = 0; } + // We have a really long single word with no spaces and have to forcibly break it up. else { - chrs = null; - } + lineLength = newLineLength; + var index = 0; - a.Remove(b, comparison, span, out var size); - - var str = span[..size].ToString(); - - if (chrs != null) - { - ArrayPool.Shared.Return(chrs); - } - - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Capitalize(this string value) - { - if (string.IsNullOrEmpty(value)) - { - return value; - } - - Span span = value.Length < 1024 ? stackalloc char[value.Length] : null; - char[] chrs; - if (span == null) - { - chrs = ArrayPool.Shared.Rent(value.Length); - span = chrs.AsSpan(); - } - else - { - chrs = null; - } - - var sliced = value.AsSpan(); - // Copy over the previous span - sliced.CopyTo(span); - - var index = 0; - - while (true) - { - // Special case for titles - words that don't get capitalized - if (sliced.InsensitiveStartsWith("the ")) + while (index < lineLength) { - sliced = sliced[4..]; - index += 4; - continue; - } + lineLength -= perLine; - var indexOf = sliced.IndexOf(' '); - span[index] = char.ToUpperInvariant(sliced[0]); - - if (indexOf == -1) - { - break; - } - - if (indexOf == sliced.Length - 1) - { - break; - } - - sliced = sliced[(indexOf + 1)..]; - index += indexOf + 1; - } - - var str = span.ToString(); - - if (chrs != null) - { - ArrayPool.Shared.Return(chrs); - } - - return str; - } - - public static string TrimMultiline(this string str, string lineSeparator = "\n") - { - var parts = str.Split(lineSeparator); - for (var i = 0; i < parts.Length; i++) - { - parts[i] = parts[i].Trim(); - } - - return string.Join(lineSeparator, parts); - } - - public static string IndentMultiline(this string str, string indent = "\t", string lineSeparator = "\n") - { - var parts = str.Split(lineSeparator); - for (var i = 0; i < parts.Length; i++) - { - parts[i] = $"{indent}{parts[i]}"; - } - - return string.Join(lineSeparator, parts); - } - - public static List Wrap(this string value, int perLine, int maxLines) - { - if ((value = value?.Trim() ?? "").Length <= 0) - { - return null; - } - - var span = value.AsSpan(); - var list = new List(maxLines); - var lineLength = 0; - - while (span.Length > 0) - { - var spaceIndex = span[lineLength..].IndexOf(' '); - if (spaceIndex == -1) - { - spaceIndex = span.Length - lineLength; // End of the string - } - - var newLineLength = lineLength + spaceIndex; - - // If the previous line is exactly perLine or not too long and we are at the end - if (newLineLength == perLine || newLineLength < perLine && newLineLength == span.Length) - { - list.Add(span[..newLineLength].ToString()); - if (list.Count == maxLines || newLineLength == span.Length) - { - break; - } - - span = span[(newLineLength + 1)..]; - lineLength = 0; - } - // We haven't hit perLine and are not sure if we can continue adding more words without going over - else if (newLineLength < perLine) - { - lineLength = newLineLength + 1; - } - // We already tried making the line longer, and it was too long, so fall back to the old line - else if (lineLength > 0 && lineLength <= perLine) - { - list.Add(span[..(lineLength - 1)].ToString()); + var length = perLine - (span[index] == ' ' ? 1 : 0); + list.Add(span.Slice(index, length).ToString()); if (list.Count == maxLines) { break; } - span = span[lineLength..]; - lineLength = 0; + index += perLine; } - // We have a really long single word with no spaces and have to forcibly break it up. - else - { - lineLength = newLineLength; - var index = 0; - while (index < lineLength) - { - lineLength -= perLine; - - var length = perLine - (span[index] == ' ' ? 1 : 0); - list.Add(span.Slice(index, length).ToString()); - if (list.Count == maxLines) - { - break; - } - - index += perLine; - } - - span = span[(newLineLength - lineLength)..]; - } + span = span[(newLineLength - lineLength)..]; } - - return list; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfTerminator(this Span buffer, int sizeT) => - sizeT switch - { - 2 => MemoryMarshal.Cast(buffer).IndexOf((char)0) * 2, - 4 => MemoryMarshal.Cast(buffer).IndexOf((uint)0) * 4, - _ => buffer.IndexOf((byte)0) - }; + return list; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfTerminator(this ReadOnlySpan buffer, int sizeT) => - sizeT switch + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfTerminator(this Span buffer, int sizeT) => + sizeT switch + { + 2 => MemoryMarshal.Cast(buffer).IndexOf((char)0) * 2, + 4 => MemoryMarshal.Cast(buffer).IndexOf((uint)0) * 4, + _ => buffer.IndexOf((byte)0) + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfTerminator(this ReadOnlySpan buffer, int sizeT) => + sizeT switch + { + 2 => MemoryMarshal.Cast(buffer).IndexOf((char)0) * 2, + 4 => MemoryMarshal.Cast(buffer).IndexOf((uint)0) * 4, + _ => buffer.IndexOf((byte)0) + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReplaceAny(this Span chars, ReadOnlySpan invalidChars, ReadOnlySpan replacementChars) + { + while (true) + { + var indexOf = chars.IndexOfAny(invalidChars); + if (indexOf == -1) { - 2 => MemoryMarshal.Cast(buffer).IndexOf((char)0) * 2, - 4 => MemoryMarshal.Cast(buffer).IndexOf((uint)0) * 4, - _ => buffer.IndexOf((byte)0) - }; + break; + } + + var chr = chars[indexOf]; + + chars[indexOf] = replacementChars[invalidChars.IndexOf(chr)]; + chars = chars[(indexOf + 1)..]; + } } } diff --git a/Projects/Server/Text/TextEncoding.cs b/Projects/Server/Text/TextEncoding.cs index c84e01def..41d6df2b2 100644 --- a/Projects/Server/Text/TextEncoding.cs +++ b/Projects/Server/Text/TextEncoding.cs @@ -105,7 +105,7 @@ namespace Server.Text public static int GetBytesUtf8(this ReadOnlySpan str, Span buffer) => UTF8.GetBytes(str, buffer); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetByteLengthForEncoding(Encoding encoding) => + public static int GetByteLengthForEncoding(this Encoding encoding) => encoding.BodyName switch { "utf-16BE" => 2, diff --git a/Projects/Server/TileMatrix/TileMatrix.cs b/Projects/Server/TileMatrix/TileMatrix.cs index aaea1f638..90d13c5e1 100644 --- a/Projects/Server/TileMatrix/TileMatrix.cs +++ b/Projects/Server/TileMatrix/TileMatrix.cs @@ -31,7 +31,7 @@ namespace Server public FileStream DataStream { get; } public BinaryReader IndexReader { get; } - private static bool Pre6000ClientSupport; + public static bool Pre6000ClientSupport { get; private set; } public static void Configure() { @@ -71,9 +71,9 @@ namespace Server if (fileIndex != 0x7F) { - fileIndex = Pre6000ClientSupport && mapID == 1 ? 0 : fileIndex; + var mapFileIndex = Pre6000ClientSupport && mapID == 1 ? 0 : fileIndex; - var mapPath = Core.FindDataFile($"map{fileIndex}.mul", false); + var mapPath = Core.FindDataFile($"map{mapFileIndex}.mul", false); if (mapPath != null) { @@ -81,7 +81,7 @@ namespace Server } else { - mapPath = Core.FindDataFile($"map{fileIndex}LegacyMUL.uop", false); + mapPath = Core.FindDataFile($"map{mapFileIndex}LegacyMUL.uop", false); if (mapPath != null) { @@ -90,11 +90,11 @@ namespace Server } else { - logger.Warning($"map{fileIndex}.mul was not found."); + logger.Warning("{File} was not found.", $"map{mapFileIndex}.mul"); } } - var indexPath = Core.FindDataFile($"staidx{fileIndex}.mul", false); + var indexPath = Core.FindDataFile($"staidx{mapFileIndex}.mul", false); if (indexPath != null) { @@ -103,10 +103,10 @@ namespace Server } else { - logger.Warning($"staidx{fileIndex}.mul was not found."); + logger.Warning("{File} was not found.", $"staidx{mapFileIndex}.mul"); } - var staticsPath = Core.FindDataFile($"statics{fileIndex}.mul", false); + var staticsPath = Core.FindDataFile($"statics{mapFileIndex}.mul", false); if (staticsPath != null) { @@ -114,7 +114,7 @@ namespace Server } else { - logger.Warning($"statics{fileIndex}.mul was not found."); + logger.Warning("{File} was not found.", $"statics{fileIndex}.mul"); } } @@ -137,7 +137,7 @@ namespace Server _staticPatches = new int[BlockWidth][]; _landPatches = new int[BlockWidth][]; - Patch = new TileMatrixPatch(this, mapID); + Patch = new TileMatrixPatch(this, fileIndex); } public StaticTile[][][] EmptyStaticBlock => _emptyStaticBlock; @@ -365,11 +365,11 @@ namespace Server m_TileBuffer = new StaticTile[count]; } - var staTiles = m_TileBuffer; //new StaticTile[tileCount]; + var staTiles = m_TileBuffer; fixed (StaticTile* pTiles = staTiles) { - NativeReader.Read(DataStream, pTiles, length); + DataStream.Read(new Span(pTiles, length)); if (m_Lists == null) { @@ -455,7 +455,7 @@ namespace Server fixed (LandTile* pTiles = tiles) { - NativeReader.Read(MapStream, pTiles, 192); + MapStream.Read(new Span(pTiles, 192)); } return tiles; diff --git a/Projects/Server/TileMatrix/TileMatrixLoader.cs b/Projects/Server/TileMatrix/TileMatrixLoader.cs index 0d2e2563e..548024a88 100644 --- a/Projects/Server/TileMatrix/TileMatrixLoader.cs +++ b/Projects/Server/TileMatrix/TileMatrixLoader.cs @@ -46,11 +46,11 @@ namespace Server if (exception == null) { - logger.Information("Maps loaded ({0:F2} seconds)", stopwatch.Elapsed.TotalSeconds); + logger.Information("Maps loaded ({Duration:F2} seconds)", stopwatch.Elapsed.TotalSeconds); } else { - logger.Error(exception, "Loading maps failed ({0:F2} seconds)", stopwatch.Elapsed.TotalSeconds); + logger.Error(exception, "Loading maps failed ({Duration:F2} seconds)", stopwatch.Elapsed.TotalSeconds); throw exception; } } diff --git a/Projects/Server/TileMatrix/TileMatrixPatch.cs b/Projects/Server/TileMatrix/TileMatrixPatch.cs index 2e876a348..83b097558 100644 --- a/Projects/Server/TileMatrix/TileMatrixPatch.cs +++ b/Projects/Server/TileMatrix/TileMatrixPatch.cs @@ -63,10 +63,9 @@ namespace Server fsData.Seek(4, SeekOrigin.Current); var tiles = new LandTile[64]; - fixed (LandTile* pTiles = tiles) { - NativeReader.Read(fsData, pTiles, 192); + fsData.Read(new Span(pTiles, 192)); } matrix.SetLandBlock(x, y, tiles); @@ -81,8 +80,8 @@ namespace Server using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); using var fsIndex = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read); using var fsLookup = new FileStream(lookupPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var indexReader = new BinaryReader(fsIndex); - var lookupReader = new BinaryReader(fsLookup); + using var indexReader = new BinaryReader(fsIndex); + using var lookupReader = new BinaryReader(fsLookup); var count = (int)(indexReader.BaseStream.Length / 4); @@ -127,7 +126,7 @@ namespace Server fixed (StaticTile* pTiles = staTiles) { - NativeReader.Read(fsData, pTiles, length); + fsData.Read(new Span(pTiles, length)); StaticTile* pCur = pTiles, pEnd = pTiles + tileCount; @@ -153,9 +152,6 @@ namespace Server } } - indexReader.Close(); - lookupReader.Close(); - return count; } } diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index 5d98180bb..dd2a75cd0 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -147,7 +147,7 @@ namespace Server { if (Running) { - logger.Error($"Timer is returned while still running!\n{new StackTrace()}"); + logger.Error("Timer is returned while still running!\n{StackTrace}", new StackTrace()); return; } @@ -163,7 +163,7 @@ namespace Server if (_poolCount >= _poolCapacity) { #if DEBUG_TIMERS - logger.Warning($"DelayCallTimer pool reached maximum of {_poolCapacity} timers"); + logger.Warning("DelayCallTimer pool reached maximum of {Capacity} timers", _poolCapacity); _allowFinalization = true; #endif return; @@ -181,7 +181,7 @@ namespace Server if (timer != null) { #if DEBUG_TIMERS - logger.Information($"Getting from pool: ({_poolCount} / {_poolCapacity})"); + logger.Information("Getting from pool: ({Count} / {Capacity})", _poolCount, _poolCapacity); #endif timer.Init(delay, interval, count); @@ -197,7 +197,7 @@ namespace Server _timerPoolDepletionAmount++; #if DEBUG_TIMERS - logger.Warning($"Timer pool depleted and timer was allocated.\n{new StackTrace()}"); + logger.Warning("Timer pool depleted and timer was allocated.\n{StackTrace}", new StackTrace()); #endif return new DelayCallTimer(delay, interval, count, callback); } @@ -211,7 +211,7 @@ namespace Server { if (!_allowFinalization) { - logger.Warning($"Pooled timer was not returned to the pool.\n{_stackTraces[GetHashCode()]}"); + logger.Warning("Pooled timer was not returned to the pool.\n{StackTrace}", _stackTraces[GetHashCode()]); } } #endif diff --git a/Projects/Server/Timer/Timer.Pool.cs b/Projects/Server/Timer/Timer.Pool.cs index cb92fc18f..fbdc0d91e 100644 --- a/Projects/Server/Timer/Timer.Pool.cs +++ b/Projects/Server/Timer/Timer.Pool.cs @@ -41,8 +41,9 @@ namespace Server var amountToRefill = Math.Min(_maxPoolCapacity, amountToGrow); var maximumHit = amountToGrow > amountToRefill ? " Maximum pool size has been reached." : ""; + var warningMessage = $"Timer pool depleted by {{Amount}}. Refilling with {{AmountRefill}}.{maximumHit}"; - logger.Warning($"Timer pool depleted by {_timerPoolDepletionAmount}. Refilling with {amountToRefill}.{maximumHit}"); + logger.Warning(warningMessage, _timerPoolDepletionAmount, amountToRefill); RefillPoolAsync(amountToRefill); _timerPoolDepletionAmount = 0; } @@ -62,7 +63,7 @@ namespace Server _poolHead = head; _poolCount += amount; #if DEBUG_TIMERS - logger.Information($"Returning to pool. ({_poolCount} / {_poolCapacity})"); + logger.Information("Returning to pool. ({Count} / {Capacity})", _poolCount, _poolCapacity); #endif } @@ -84,7 +85,7 @@ namespace Server internal static void RefillPool(int amount, out DelayCallTimer head, out DelayCallTimer tail) { #if DEBUG_TIMERS - logger.Information($"Filling pool with {amount} timers."); + logger.Information("Filling pool with {Amount} timers.", amount); #endif head = null; diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index 5e9d66334..8b0621b21 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -199,7 +199,7 @@ namespace Server // TODO: Handle timers > 17yrs #if DEBUG_TIMERS - logger.Error($"Timer is more than max duration. ({originalDelay})"); + logger.Error("Timer is more than max duration. ({Duration})", originalDelay); #endif } @@ -221,12 +221,17 @@ namespace Server continue; } - var name = t.ToString(); + while (t != null) + { + var name = t.ToString(); - hash.TryGetValue(name, out var count); - hash[name] = count + 1; + hash.TryGetValue(name, out var count); + hash[name] = count + 1; - total++; + total++; + + t = t?._nextTimer; + } } } @@ -260,6 +265,11 @@ namespace Server foreach (var t in _rings) { + if (t == null) + { + continue; + } + for (var i = 0; i < _ringSize; i++) { var node = t[i]; diff --git a/Projects/Server/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs new file mode 100644 index 000000000..8de7a6de9 --- /dev/null +++ b/Projects/Server/Utilities/Html.cs @@ -0,0 +1,48 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Html.cs * + * * + * 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 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Runtime.CompilerServices; + +namespace Server.Utilities; + +public static class Html +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, int color) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, int color, int size) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, string color) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string text, string color, int size) => $"{text}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, int color) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, int color, int size) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, string color) => $"
{text}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string text, string color, int size) => $"
{text}
"; +} diff --git a/Projects/Server/Utilities/PathUtility.cs b/Projects/Server/Utilities/PathUtility.cs index 6f22c46db..b85b7b07d 100644 --- a/Projects/Server/Utilities/PathUtility.cs +++ b/Projects/Server/Utilities/PathUtility.cs @@ -17,70 +17,69 @@ using System; using System.IO; using Server.Text; -namespace Server +namespace Server; + +public static class PathUtility { - public static class PathUtility + public static string EnsureDirectory(string dir) { - public static string EnsureDirectory(string dir) + var path = GetFullPath(dir, Core.BaseDirectory); + Directory.CreateDirectory(path); + + return path; + } + + public static void EnsureDirectory(this FileInfo fi) + { + var dir = GetFullPath(fi.DirectoryName, Core.BaseDirectory); + if (dir != null) { - var path = GetFullPath(dir, Core.BaseDirectory); - Directory.CreateDirectory(path); - - return path; - } - - public static void EnsureDirectory(this FileInfo fi) - { - var dir = GetFullPath(fi.DirectoryName, Core.BaseDirectory); - if (dir != null) - { - Directory.CreateDirectory(dir); - } - } - - public static void EnsureDirectory(this DirectoryInfo di) - { - var file = GetFullPath(di.FullName, Core.BaseDirectory); - Directory.CreateDirectory(file); - } - - public static string GetFullPath(string relativeOrAbsolutePath) => - GetFullPath(relativeOrAbsolutePath, Core.BaseDirectory); - - public static string GetFullPath(string relativeOrAbsolutePath, string basePath) => - relativeOrAbsolutePath switch - { - null => null, - "" => basePath, - _ => Path.IsPathRooted(relativeOrAbsolutePath) - ? relativeOrAbsolutePath - : Path.GetFullPath(relativeOrAbsolutePath, basePath) - }; - - public static string EnsureRandomPath(string basePath) - { - Span bytes = stackalloc byte[8]; - Utility.RandomBytes(bytes); - return EnsureDirectory(Path.Combine(basePath, bytes.ToHexString())); - } - - public static void CopyDirectory(string sourcePath, string destinationPath, bool recursive = true) - { - var searchOptions = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - foreach (var file in Directory.EnumerateFiles(sourcePath, "*", searchOptions)) - { - var fi = new FileInfo(file); - var relativePath = Path.GetRelativePath(sourcePath, fi.DirectoryName!); - var destFolder = Path.Combine(destinationPath, relativePath); - EnsureDirectory(destFolder); - fi.CopyTo(Path.Combine(destFolder, fi.Name)); - } - } - - public static void MoveDirectory(string sourcePath, string destinationPath) - { - CopyDirectory(sourcePath, destinationPath); - Directory.Delete(sourcePath, true); + Directory.CreateDirectory(dir); } } + + public static void EnsureDirectory(this DirectoryInfo di) + { + var file = GetFullPath(di.FullName, Core.BaseDirectory); + Directory.CreateDirectory(file); + } + + public static string GetFullPath(string relativeOrAbsolutePath) => + GetFullPath(relativeOrAbsolutePath, Core.BaseDirectory); + + public static string GetFullPath(string relativeOrAbsolutePath, string basePath) => + relativeOrAbsolutePath switch + { + null => null, + "" => basePath, + _ => Path.IsPathRooted(relativeOrAbsolutePath) + ? relativeOrAbsolutePath + : Path.GetFullPath(relativeOrAbsolutePath, basePath) + }; + + public static string EnsureRandomPath(string basePath) + { + Span bytes = stackalloc byte[8]; + Utility.RandomBytes(bytes); + return EnsureDirectory(Path.Combine(basePath, bytes.ToHexString())); + } + + public static void CopyDirectory(string sourcePath, string destinationPath, bool recursive = true) + { + var searchOptions = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + foreach (var file in Directory.EnumerateFiles(sourcePath, "*", searchOptions)) + { + var fi = new FileInfo(file); + var relativePath = Path.GetRelativePath(sourcePath, fi.DirectoryName!); + var destFolder = Path.Combine(destinationPath, relativePath); + EnsureDirectory(destFolder); + fi.CopyTo(Path.Combine(destFolder, fi.Name)); + } + } + + public static void MoveDirectory(string sourcePath, string destinationPath) + { + CopyDirectory(sourcePath, destinationPath); + Directory.Delete(sourcePath, true); + } } diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index faba17ece..0fe992f60 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -11,1522 +11,1507 @@ using System.Xml; using Microsoft.Toolkit.HighPerformance; using Server.Buffers; using Server.Collections; +using Server.Logging; using Server.Random; using Server.Text; -namespace Server +namespace Server; + +public static class Utility { - public static class Utility + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Utility)); + + private static Dictionary _ipAddressTable; + + private static SkillName[] _allSkills = { - private static Dictionary _ipAddressTable; + 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, + // SkillName.Mysticism, + // SkillName.Imbuing, + SkillName.Throwing + }; - private static readonly SkillName[] m_AllSkills = + private static readonly SkillName[] m_CombatSkills = + { + SkillName.Archery, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling + }; + + private static readonly SkillName[] m_CraftSkills = + { + SkillName.Alchemy, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.Inscribe, + SkillName.Tailoring, + SkillName.Tinkering + }; + + private static readonly Stack m_ConsoleColors = new(); + + public static void Separate(StringBuilder sb, string value, string separator) + { + if (sb.Length > 0) { - 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 readonly SkillName[] m_CombatSkills = - { - SkillName.Archery, - SkillName.Swords, - SkillName.Macing, - SkillName.Fencing, - SkillName.Wrestling - }; - - private static readonly SkillName[] m_CraftSkills = - { - SkillName.Alchemy, - SkillName.Blacksmith, - SkillName.Fletching, - SkillName.Carpentry, - SkillName.Cartography, - SkillName.Cooking, - SkillName.Inscribe, - SkillName.Tailoring, - SkillName.Tinkering - }; - - private static readonly Stack m_ConsoleColors = new(); - - public static void Separate(StringBuilder sb, string value, string separator) - { - if (sb.Length > 0) - { - sb.Append(separator); - } - - sb.Append(value); + sb.Append(separator); } - public static string Intern(string str) => str?.Length > 0 ? string.Intern(str) : str; + sb.Append(value); + } - public static void Intern(ref string str) + public static string Intern(string str) => str?.Length > 0 ? string.Intern(str) : str; + + public static void Intern(ref string str) + { + str = Intern(str); + } + + public static IPAddress Intern(IPAddress ipAddress) + { + if (ipAddress == null) { - str = Intern(str); + return null; } - public static IPAddress Intern(IPAddress ipAddress) + if (ipAddress.IsIPv4MappedToIPv6) { - if (ipAddress == null) - { - return null; - } - - if (ipAddress.IsIPv4MappedToIPv6) - { - ipAddress = ipAddress.MapToIPv4(); - } - - _ipAddressTable ??= new Dictionary(); - - if (!_ipAddressTable.TryGetValue(ipAddress, out var interned)) - { - interned = ipAddress; - _ipAddressTable[ipAddress] = interned; - } - - return interned; + ipAddress = ipAddress.MapToIPv4(); } - public static void Intern(ref IPAddress ipAddress) + _ipAddressTable ??= new Dictionary(); + + if (!_ipAddressTable.TryGetValue(ipAddress, out var interned)) { - ipAddress = Intern(ipAddress); + interned = ipAddress; + _ipAddressTable[ipAddress] = interned; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IPv4ToAddress(IPAddress ipAddress) + return interned; + } + + public static void Intern(ref IPAddress ipAddress) + { + ipAddress = Intern(ipAddress); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint IPv4ToAddress(IPAddress ipAddress) + { + if (ipAddress.IsIPv4MappedToIPv6) { - if (ipAddress.IsIPv4MappedToIPv6) + ipAddress = ipAddress.MapToIPv4(); + } + else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + return 0; + } + + Span integer = stackalloc byte[4]; + ipAddress.TryWriteBytes(integer, out var bytesWritten); + return bytesWritten != 4 ? 0 : BinaryPrimitives.ReadUInt32BigEndian(integer); + } + + public static bool IPMatchClassC(IPAddress ip1, IPAddress ip2) + { + var a = IPv4ToAddress(ip1); + var b = IPv4ToAddress(ip2); + + return a == 0 || b == 0 ? ip1.Equals(ip2) : (a & 0xFFFFFF) == (b & 0xFFFFFF); + } + + public static bool IPMatchCIDR(IPAddress cidrAddress, IPAddress address, int cidrLength) + { + if (cidrAddress.AddressFamily == AddressFamily.InterNetwork) + { + if (address.AddressFamily == AddressFamily.InterNetworkV6) { - ipAddress = ipAddress.MapToIPv4(); - } - else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) - { - return 0; + return false; } - Span integer = stackalloc byte[4]; - ipAddress.TryWriteBytes(integer, out var bytesWritten); - return bytesWritten != 4 ? 0 : BinaryPrimitives.ReadUInt32BigEndian(integer); + cidrLength += 96; } - public static bool IPMatchClassC(IPAddress ip1, IPAddress ip2) - { - var a = IPv4ToAddress(ip1); - var b = IPv4ToAddress(ip2); + cidrAddress = cidrAddress.MapToIPv6(); + address = address.MapToIPv6(); - return a == 0 || b == 0 ? ip1.Equals(ip2) : (a & 0xFFFFFF) == (b & 0xFFFFFF); + cidrLength = Math.Clamp(cidrLength, 0, 128); + + Span cidrBytes = stackalloc byte[16]; + cidrAddress.TryWriteBytes(cidrBytes, out var _); + + Span addrBytes = stackalloc byte[16]; + address.TryWriteBytes(addrBytes, out var _); + + var i = 0; + int offset; + + if (cidrLength < 32) + { + offset = cidrLength; } - - public static bool IPMatchCIDR(IPAddress cidrAddress, IPAddress address, int cidrLength) + else { - if (cidrAddress.AddressFamily == AddressFamily.InterNetwork) + var index = Math.DivRem(cidrLength, 32, out offset); + while (index > 0) { - if (address.AddressFamily == AddressFamily.InterNetworkV6) + if ( + BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)) != + BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)) + ) { return false; } - cidrLength += 96; + i += 4; + --index; } - - cidrAddress = cidrAddress.MapToIPv6(); - address = address.MapToIPv6(); - - cidrLength = Math.Clamp(cidrLength, 0, 128); - - Span cidrBytes = stackalloc byte[16]; - cidrAddress.TryWriteBytes(cidrBytes, out var _); - - Span addrBytes = stackalloc byte[16]; - address.TryWriteBytes(addrBytes, out var _); - - var i = 0; - int offset; - - if (cidrLength < 32) - { - offset = cidrLength; - } - else - { - var index = Math.DivRem(cidrLength, 32, out offset); - while (index > 0) - { - if ( - BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)) != - BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)) - ) - { - return false; - } - - i += 4; - --index; - } - } - - if (offset == 0) - { - return true; - } - - var c = BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)); - var a = BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)); - - var mask = (1 << (32 - offset)) - 1; - var min = ~mask & c; - var max = c | mask; - - return a >= min && a <= max; } - public static bool IsValidIP(string val) => IPMatch(val, IPAddress.Any, out var valid) || valid; - - public static bool IPMatch(string val, IPAddress ip) => IPMatch(val, ip, out _); - - public static bool IPMatch(string val, IPAddress ip, out bool valid) + if (offset == 0) { - var family = ip.AddressFamily; - var useIPv6 = family == AddressFamily.InterNetworkV6 || val.ContainsOrdinal(':'); - - ip = useIPv6 ? ip.MapToIPv6() : ip.MapToIPv4(); - - Span ipBytes = stackalloc byte[useIPv6 ? 16 : 4]; - ip.TryWriteBytes(ipBytes, out _); - - return useIPv6 ? IPv6Match(val, ipBytes, out valid) : IPv4Match(val, ipBytes, out valid); + return true; } - public static bool IPv4Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) + var c = BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)); + var a = BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)); + + var mask = (1 << (32 - offset)) - 1; + var min = ~mask & c; + var max = c | mask; + + return a >= min && a <= max; + } + + public static bool IsValidIP(string val) => IPMatch(val, IPAddress.Any, out var valid) || valid; + + public static bool IPMatch(string val, IPAddress ip) => IPMatch(val, ip, out _); + + public static bool IPMatch(string val, IPAddress ip, out bool valid) + { + var family = ip.AddressFamily; + var useIPv6 = family == AddressFamily.InterNetworkV6 || val.ContainsOrdinal(':'); + + ip = useIPv6 ? ip.MapToIPv6() : ip.MapToIPv4(); + + Span ipBytes = stackalloc byte[useIPv6 ? 16 : 4]; + ip.TryWriteBytes(ipBytes, out _); + + return useIPv6 ? IPv6Match(val, ipBytes, out valid) : IPv4Match(val, ipBytes, out valid); + } + + public static bool IPv4Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) + { + var match = true; + valid = true; + var end = val.Length; + var byteIndex = 0; + var section = 0; + var number = 0; + var isRange = false; + var intBase = 10; + var endOfSection = false; + var sectionStart = 0; + + var num = ip[byteIndex++]; + + for (var i = 0; i < end; i++) { - var match = true; - valid = true; - var end = val.Length; - var byteIndex = 0; - var section = 0; - var number = 0; - var isRange = false; - var intBase = 10; - var endOfSection = false; - var sectionStart = 0; - - var num = ip[byteIndex++]; - - for (var i = 0; i < end; i++) - { - var chr = val[i]; - if (section >= 4) - { - valid = false; - return false; - } - - switch (chr) - { - default: - { - if (!Uri.IsHexDigit(chr)) - { - valid = false; - return false; - } - - number = number * intBase + Uri.FromHex(chr); - break; - } - case 'x': - case 'X': - { - if (i == sectionStart) - { - intBase = 16; - break; - } - - valid = false; - return false; - } - case '-': - { - if (i == sectionStart || i + 1 == end || val[i + 1] == '.') - { - valid = false; - return false; - } - - // Only allows a single range in a section - if (isRange) - { - valid = false; - return false; - } - - isRange = true; - match = match && num >= number; - number = 0; - break; - } - case '*': - { - if (i != sectionStart || i + 1 < end && val[i + 1] != '.') - { - valid = false; - return false; - } - - isRange = true; - number = 255; - break; - } - case '.': - { - endOfSection = true; - break; - } - } - - if (endOfSection || i + 1 == end) - { - if (number is < 0 or > 255) - { - valid = false; - return false; - } - - match = match && (isRange ? num <= number : number == num); - - if (++section < 4) - { - num = ip[byteIndex++]; - } - - intBase = 10; - number = 0; - endOfSection = false; - sectionStart = i + 1; - isRange = false; - } - } - - return match; - } - - public static bool IPv6Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) - { - valid = true; - - // Start must be two `::` or a number - if (val[0] == ':' && val[1] != ':') + var chr = val[i]; + if (section >= 4) { valid = false; return false; } - var match = true; - var end = val.Length; - var byteIndex = 2; - var section = 0; - var number = 0; - var isRange = false; - var endOfSection = false; - var sectionStart = 0; - var hasCompressor = false; - - var num = BinaryPrimitives.ReadUInt16BigEndian(ip[..2]); - - for (int i = 0; i < end; i++) + switch (chr) { - if (section > 7) + default: + { + if (!Uri.IsHexDigit(chr)) + { + valid = false; + return false; + } + + number = number * intBase + Uri.FromHex(chr); + break; + } + case 'x': + case 'X': + { + if (i == sectionStart) + { + intBase = 16; + break; + } + + valid = false; + return false; + } + case '-': + { + if (i == sectionStart || i + 1 == end || val[i + 1] == '.') + { + valid = false; + return false; + } + + // Only allows a single range in a section + if (isRange) + { + valid = false; + return false; + } + + isRange = true; + match = match && num >= number; + number = 0; + break; + } + case '*': + { + if (i != sectionStart || i + 1 < end && val[i + 1] != '.') + { + valid = false; + return false; + } + + isRange = true; + number = 255; + break; + } + case '.': + { + endOfSection = true; + break; + } + } + + if (endOfSection || i + 1 == end) + { + if (number is < 0 or > 255) { valid = false; return false; } - var chr = val[i]; - // We are starting a new sequence, check the previous one then continue - switch (chr) + match = match && (isRange ? num <= number : number == num); + + if (++section < 4) { - default: - { - if (!Uri.IsHexDigit(chr)) - { - valid = false; - return false; - } - - number = number * 16 + Uri.FromHex(chr); - break; - } - case '?': - { - Console.WriteLine("IP Match '?' character is not supported."); - valid = false; - return false; - } - // Range - case '-': - { - if (i == sectionStart || i + 1 == end || val[i + 1] == ':') - { - valid = false; - return false; - } - - // Only allows a single range in a section - if (isRange) - { - valid = false; - return false; - } - - isRange = true; - - // Check low part of the range - match = match && num >= number; - number = 0; - break; - } - // Wild section - case '*': - { - if (i != sectionStart || i + 1 < end && val[i + 1] != ':') - { - valid = false; - return false; - } - - isRange = true; - number = 65535; - break; - } - case ':': - { - endOfSection = true; - break; - } - } - - if (!endOfSection && i + 1 != end) - { - continue; - } - - if (++i == end || val[i] != ':' || section > 0) - { - match = match && (isRange ? num <= number : number == num); - - // IPv4 matching at the end - if (section == 6 && num == 0xFFFF) - { - var ipv4 = val[(i + 1)..]; - if (ipv4.Contains('.')) - { - return IPv4Match(ipv4, ip[^4..], out valid); - } - } - - if (i == end) - { - break; - } - - num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); - byteIndex += 2; - - ++section; - } - - if (i < end && val[i] == ':') - { - if (hasCompressor) - { - valid = false; - return false; - } - - int newSection; - - if (i + 1 < end) - { - var remainingColons = val[(i + 1)..].Count(':'); - // double colon must be at least 2 sections - // we need at least 1 section remaining out of 8 - // This means 8 - 2 would be 6 sections (5 colons) - newSection = section + 2 + (5 - remainingColons); - if (newSection > 7) - { - valid = false; - return false; - } - } - else - { - newSection = 7; - } - - var zeroEnd = (newSection + 1) * 2; - do - { - if (match) - { - if (num != 0) - { - match = false; - } - - num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); - } - - byteIndex += 2; - } while (byteIndex < zeroEnd); - - section = newSection; - hasCompressor = true; - } - else - { - i--; + num = ip[byteIndex++]; } + intBase = 10; number = 0; endOfSection = false; sectionStart = i + 1; isRange = false; } - - return match; } - public static string FixHtml(string str) + return match; + } + + public static bool IPv6Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) + { + valid = true; + + // Start must be two `::` or a number + if (val[0] == ':' && val[1] != ':') { - if (string.IsNullOrEmpty(str)) - { - return str; - } - - using var sb = new ValueStringBuilder(str, stackalloc char[Math.Min(40960, str.Length)]); - ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; - ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; - sb.ReplaceAny(invalid, replacement, 0, sb.Length); - - return sb.ToString(); + valid = false; + return false; } - public static int InsensitiveCompare(string first, string second) => first.InsensitiveCompare(second); + var match = true; + var end = val.Length; + var byteIndex = 2; + var section = 0; + var number = 0; + var isRange = false; + var endOfSection = false; + var sectionStart = 0; + var hasCompressor = false; - public static bool InsensitiveStartsWith(string first, string second) => first.InsensitiveStartsWith(second); + var num = BinaryPrimitives.ReadUInt16BigEndian(ip[..2]); - public static Direction GetDirection(Point3D from, Point3D to) => GetDirection(from.X, from.Y, to.X, to.Y); - - public static Direction GetDirection(Point2D from, Point2D to) => GetDirection(from.X, from.Y, to.X, to.Y); - - public static Direction GetDirection(int fromX, int fromY, int toX, int toY) + for (int i = 0; i < end; i++) { - var dx = toX - fromX; - var dy = toY - fromY; - - var adx = Abs(dx); - var ady = Abs(dy); - - if (adx >= ady * 3) + if (section > 7) { - return dx > 0 ? Direction.East : Direction.West; + valid = false; + return false; } - if (ady >= adx * 3) + var chr = val[i]; + // We are starting a new sequence, check the previous one then continue + switch (chr) { - return dy > 0 ? Direction.South : Direction.North; - } - - if (dx > 0) - { - return dy > 0 ? Direction.Down : Direction.Right; - } - - return dy > 0 ? Direction.Left : Direction.Up; - } - - public static object GetArrayCap(Array array, int index, object emptyValue = null) => - array.Length > 0 ? array.GetValue(Math.Clamp(index, 0, array.Length - 1)) : emptyValue; - - public static SkillName RandomSkill() => - m_AllSkills[Random( - m_AllSkills.Length - (Core.ML ? 0 : - Core.SE ? 1 : - Core.AOS ? 3 : 6) - )]; - - public static SkillName RandomCombatSkill() => m_CombatSkills.RandomElement(); - - public static SkillName RandomCraftSkill() => m_CraftSkills.RandomElement(); - - public static void FixPoints(ref Point3D top, ref Point3D bottom) - { - if (bottom.m_X < top.m_X) - { - (top.m_X, bottom.m_X) = (bottom.m_X, top.m_X); - } - - if (bottom.m_Y < top.m_Y) - { - (top.m_Y, bottom.m_Y) = (bottom.m_Y, top.m_Y); - } - - if (bottom.m_Z < top.m_Z) - { - (top.m_Z, bottom.m_Z) = (bottom.m_Z, top.m_Z); - } - } - - public static void FormatBuffer(this TextWriter op, ReadOnlySpan first, ReadOnlySpan second, int totalLength) - { - op.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); - op.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); - - if (totalLength <= 0) - { - op.WriteLine("0000 "); - return; - } - - Span lineBytes = stackalloc byte[16]; - Span lineChars = stackalloc char[47]; - for (var i = 0; i < totalLength; i += 16) - { - var length = Math.Min(totalLength - i, 16); - if (i < first.Length) - { - var firstLength = Math.Min(length, first.Length - i); - first.Slice(i, firstLength).CopyTo(lineBytes); - - if (firstLength < length) + default: { - second[..(length - first.Length - i)].CopyTo(lineBytes[(length - firstLength)..]); + if (!Uri.IsHexDigit(chr)) + { + valid = false; + return false; + } + + number = number * 16 + Uri.FromHex(chr); + break; + } + case '?': + { + logger.Debug("IP Match '?' character is not supported."); + valid = false; + return false; + } + // Range + case '-': + { + if (i == sectionStart || i + 1 == end || val[i + 1] == ':') + { + valid = false; + return false; + } + + // Only allows a single range in a section + if (isRange) + { + valid = false; + return false; + } + + isRange = true; + + // Check low part of the range + match = match && num >= number; + number = 0; + break; + } + // Wild section + case '*': + { + if (i != sectionStart || i + 1 < end && val[i + 1] != ':') + { + valid = false; + return false; + } + + isRange = true; + number = 65535; + break; + } + case ':': + { + endOfSection = true; + break; + } + } + + if (!endOfSection && i + 1 != end) + { + continue; + } + + if (++i == end || val[i] != ':' || section > 0) + { + match = match && (isRange ? num <= number : number == num); + + // IPv4 matching at the end + if (section == 6 && num == 0xFFFF) + { + var ipv4 = val[(i + 1)..]; + if (ipv4.Contains('.')) + { + return IPv4Match(ipv4, ip[^4..], out valid); + } + } + + if (i == end) + { + break; + } + + num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); + byteIndex += 2; + + ++section; + } + + if (i < end && val[i] == ':') + { + if (hasCompressor) + { + valid = false; + return false; + } + + int newSection; + + if (i + 1 < end) + { + var remainingColons = val[(i + 1)..].Count(':'); + // double colon must be at least 2 sections + // we need at least 1 section remaining out of 8 + // This means 8 - 2 would be 6 sections (5 colons) + newSection = section + 2 + (5 - remainingColons); + if (newSection > 7) + { + valid = false; + return false; } } else { - second.Slice(i - first.Length, length).CopyTo(lineBytes); + newSection = 7; } - var charsWritten = ((ReadOnlySpan)lineBytes[..length]).ToSpacedHexString(lineChars); - - op.Write("{0:X4} ", i); - op.Write(lineChars[..charsWritten]); - op.WriteLine(); - } - } - - public static void PushColor(ConsoleColor color) - { - try - { - m_ConsoleColors.Push(Console.ForegroundColor); - Console.ForegroundColor = color; - } - catch - { - // ignored - } - } - - public static void PopColor() - { - try - { - Console.ForegroundColor = m_ConsoleColors.Pop(); - } - catch - { - // ignored - } - } - - public static bool NumberBetween(double num, int bound1, int bound2, double allowance) - { - if (bound1 > bound2) - { - (bound1, bound2) = (bound2, bound1); - } - - return num < bound2 + allowance && num > bound1 - allowance; - } - - 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 = true) - { - m.HairItemID = m.Race.RandomHair(m); - - if (randomHue) - { - m.HairHue = m.Race.RandomHairHue(); - } - } - - public static void AssignRandomFacialHair(Mobile m, int hue) - { - m.FacialHairItemID = m.Race.RandomFacialHair(m); - m.FacialHairHue = hue; - } - - public static void AssignRandomFacialHair(Mobile m, bool randomHue = true) - { - m.FacialHairItemID = m.Race.RandomFacialHair(m); - - if (randomHue) - { - m.FacialHairHue = m.Race.RandomHairHue(); - } - } - - // Using this instead of Linq Cast<> means we can ditch the yield and enforce contravariance - public static HashSet SafeConvertSet(this IEnumerable coll) - where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); - - public static List SafeConvertList(this IEnumerable coll) - where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); - - public static TColl SafeConvert(this IEnumerable coll) - where TOutput : TInput where TColl : ICollection, new() - { - var outputList = new TColl(); - - foreach (var entry in coll) - { - if (entry is TOutput outEntry) + var zeroEnd = (newSection + 1) * 2; + do { - outputList.Add(outEntry); + if (match) + { + if (num != 0) + { + match = false; + } + + num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); + } + + byteIndex += 2; + } while (byteIndex < zeroEnd); + + section = newSection; + hasCompressor = true; + } + else + { + i--; + } + + number = 0; + endOfSection = false; + sectionStart = i + 1; + isRange = false; + } + + return match; + } + + public static string FixHtml(string str) + { + if (string.IsNullOrEmpty(str)) + { + return str; + } + + using var sb = new ValueStringBuilder(str, stackalloc char[Math.Min(128, str.Length)]); + ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; + ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; + sb.ReplaceAny(invalid, replacement, 0, sb.Length); + + return sb.ToString(); + } + + public static void FixHtml(Span chars) + { + if (chars.Length == 0) + { + return; + } + + ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; + ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; + + chars.ReplaceAny(invalid, replacement); + } + + public static int InsensitiveCompare(string first, string second) => first.InsensitiveCompare(second); + + public static bool InsensitiveStartsWith(string first, string second) => first.InsensitiveStartsWith(second); + + public static Direction GetDirection(Point3D from, Point3D to) => GetDirection(from.X, from.Y, to.X, to.Y); + + public static Direction GetDirection(Point2D from, Point2D to) => GetDirection(from.X, from.Y, to.X, to.Y); + + public static Direction GetDirection(int fromX, int fromY, int toX, int toY) + { + var dx = toX - fromX; + var dy = toY - fromY; + + var adx = Abs(dx); + var ady = Abs(dy); + + if (adx >= ady * 3) + { + return dx > 0 ? Direction.East : Direction.West; + } + + if (ady >= adx * 3) + { + return dy > 0 ? Direction.South : Direction.North; + } + + if (dx > 0) + { + return dy > 0 ? Direction.Down : Direction.Right; + } + + return dy > 0 ? Direction.Left : Direction.Up; + } + + public static object GetArrayCap(Array array, int index, object emptyValue = null) => + array.Length > 0 ? array.GetValue(Math.Clamp(index, 0, array.Length - 1)) : emptyValue; + + public static SkillName RandomSkill() => + _allSkills[Random(_allSkills.Length - (Core.ML ? 0 : Core.SE ? 1 : Core.AOS ? 3 : 6))]; + + public static SkillName RandomCombatSkill() => m_CombatSkills.RandomElement(); + + public static SkillName RandomCraftSkill() => m_CraftSkills.RandomElement(); + + public static void FixPoints(ref Point3D top, ref Point3D bottom) + { + if (bottom.m_X < top.m_X) + { + (top.m_X, bottom.m_X) = (bottom.m_X, top.m_X); + } + + if (bottom.m_Y < top.m_Y) + { + (top.m_Y, bottom.m_Y) = (bottom.m_Y, top.m_Y); + } + + if (bottom.m_Z < top.m_Z) + { + (top.m_Z, bottom.m_Z) = (bottom.m_Z, top.m_Z); + } + } + + public static void FormatBuffer(this TextWriter op, ReadOnlySpan first, ReadOnlySpan second, int totalLength) + { + op.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); + op.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); + + if (totalLength <= 0) + { + op.WriteLine("0000 "); + return; + } + + Span lineBytes = stackalloc byte[16]; + Span lineChars = stackalloc char[47]; + for (var i = 0; i < totalLength; i += 16) + { + var length = Math.Min(totalLength - i, 16); + if (i < first.Length) + { + var firstLength = Math.Min(length, first.Length - i); + first.Slice(i, firstLength).CopyTo(lineBytes); + + if (firstLength < length) + { + second[..(length - first.Length - i)].CopyTo(lineBytes[(length - firstLength)..]); } } - - return outputList; - } - - public static bool ToBoolean(string value) => - bool.TryParse(value, out var b) && b || - value.InsensitiveEquals("enabled") || - value.InsensitiveEquals("on") || - !value.InsensitiveEquals("disabled") && !value.InsensitiveEquals("off"); - - public static double ToDouble(string value) - { -#pragma warning disable CA1806 // Do not ignore method results - double.TryParse(value, out var d); -#pragma warning restore CA1806 // Do not ignore method results - - return d; - } - - public static TimeSpan ToTimeSpan(string value) - { -#pragma warning disable CA1806 // Do not ignore method results - TimeSpan.TryParse(value, out var t); -#pragma warning restore CA1806 // Do not ignore method results - - return t; - } - - public static int ToInt32(ReadOnlySpan value) - { - int i; - -#pragma warning disable CA1806 // Do not ignore method results - if (value.StartsWithOrdinal("0x")) - { - int.TryParse(value[2..], NumberStyles.HexNumber, null, out i); - } else { - int.TryParse(value, out i); + second.Slice(i - first.Length, length).CopyTo(lineBytes); } -#pragma warning restore CA1806 // Do not ignore method results - return i; + var charsWritten = ((ReadOnlySpan)lineBytes[..length]).ToSpacedHexString(lineChars); + + op.Write("{0:X4} ", i); + op.Write(lineChars[..charsWritten]); + op.WriteLine(); + } + } + + public static void PushColor(ConsoleColor color) + { + try + { + m_ConsoleColors.Push(Console.ForegroundColor); + Console.ForegroundColor = color; + } + catch + { + // ignored + } + } + + public static void PopColor() + { + try + { + Console.ForegroundColor = m_ConsoleColors.Pop(); + } + catch + { + // ignored + } + } + + public static bool NumberBetween(double num, int bound1, int bound2, double allowance) + { + if (bound1 > bound2) + { + (bound1, bound2) = (bound2, bound1); } - public static uint ToUInt32(ReadOnlySpan value) + return num < bound2 + allowance && num > bound1 - allowance; + } + + 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 = true) + { + m.HairItemID = m.Race.RandomHair(m); + + if (randomHue) { - uint i; + m.HairHue = m.Race.RandomHairHue(); + } + } + + public static void AssignRandomFacialHair(Mobile m, int hue) + { + m.FacialHairItemID = m.Race.RandomFacialHair(m); + m.FacialHairHue = hue; + } + + public static void AssignRandomFacialHair(Mobile m, bool randomHue = true) + { + m.FacialHairItemID = m.Race.RandomFacialHair(m); + + if (randomHue) + { + m.FacialHairHue = m.Race.RandomHairHue(); + } + } + + // Using this instead of Linq Cast<> means we can ditch the yield and enforce contravariance + public static HashSet SafeConvertSet(this IEnumerable coll) + where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); + + public static List SafeConvertList(this IEnumerable coll) + where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); + + public static TColl SafeConvert(this IEnumerable coll) + where TOutput : TInput where TColl : ICollection, new() + { + var outputList = new TColl(); + + foreach (var entry in coll) + { + if (entry is TOutput outEntry) + { + outputList.Add(outEntry); + } + } + + return outputList; + } + + public static bool ToBoolean(string value) => + bool.TryParse(value, out var b) && b || + value.InsensitiveEquals("enabled") || + value.InsensitiveEquals("on") || + !value.InsensitiveEquals("disabled") && !value.InsensitiveEquals("off"); + + public static double ToDouble(string value) + { +#pragma warning disable CA1806 // Do not ignore method results + double.TryParse(value, out var d); +#pragma warning restore CA1806 // Do not ignore method results + + return d; + } + + public static TimeSpan ToTimeSpan(string value) + { +#pragma warning disable CA1806 // Do not ignore method results + TimeSpan.TryParse(value, out var t); +#pragma warning restore CA1806 // Do not ignore method results + + return t; + } + + public static int ToInt32(ReadOnlySpan value) + { + int i; #pragma warning disable CA1806 // Do not ignore method results - if (value.InsensitiveStartsWith("0x")) - { - uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i); - } - else - { - uint.TryParse(value, out i); - } + if (value.StartsWithOrdinal("0x")) + { + int.TryParse(value[2..], NumberStyles.HexNumber, null, out i); + } + else + { + int.TryParse(value, out i); + } #pragma warning restore CA1806 // Do not ignore method results - return i; - } + return i; + } - public static bool ToInt32(ReadOnlySpan value, out int i) => - value.InsensitiveStartsWith("0x") - ? int.TryParse(value[2..], NumberStyles.HexNumber, null, out i) - : int.TryParse(value, out i); + public static uint ToUInt32(ReadOnlySpan value) + { + uint i; - public static bool ToUInt32(ReadOnlySpan value, out uint i) => - value.InsensitiveStartsWith("0x") - ? uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i) - : uint.TryParse(value, out i); - - public static int GetXMLInt32(string intString, int defaultValue) +#pragma warning disable CA1806 // Do not ignore method results + if (value.InsensitiveStartsWith("0x")) { - try - { - return XmlConvert.ToInt32(intString); - } - catch - { - return int.TryParse(intString, out var val) ? val : defaultValue; - } + uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i); } - - public static uint GetXMLUInt32(string uintString, uint defaultValue) + else { - try - { - return XmlConvert.ToUInt32(uintString); - } - catch - { - return uint.TryParse(uintString, out var val) ? val : defaultValue; - } + uint.TryParse(value, out i); } +#pragma warning restore CA1806 // Do not ignore method results - public static DateTime GetXMLDateTime(string dateTimeString, DateTime defaultValue) + return i; + } + + public static bool ToInt32(ReadOnlySpan value, out int i) => + value.InsensitiveStartsWith("0x") + ? int.TryParse(value[2..], NumberStyles.HexNumber, null, out i) + : int.TryParse(value, out i); + + public static bool ToUInt32(ReadOnlySpan value, out uint i) => + value.InsensitiveStartsWith("0x") + ? uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i) + : uint.TryParse(value, out i); + + public static int GetXMLInt32(string intString, int defaultValue) + { + try { - try - { - return XmlConvert.ToDateTime(dateTimeString, XmlDateTimeSerializationMode.Utc); - } - catch - { - return DateTime.TryParse(dateTimeString, out var d) ? d : defaultValue; - } + return XmlConvert.ToInt32(intString); } - - public static TimeSpan GetXMLTimeSpan(string timeSpanString, TimeSpan defaultValue) + catch { - try - { - return XmlConvert.ToTimeSpan(timeSpanString); - } - catch - { - return defaultValue; - } + return int.TryParse(intString, out var val) ? val : defaultValue; } + } - public static string GetAttribute(XmlElement node, string attributeName, string defaultValue = null) => - node?.Attributes[attributeName]?.Value ?? defaultValue; - - public static string GetText(XmlElement node, string defaultValue) => node?.InnerText ?? defaultValue; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InRange(int p1X, int p1Y, int p2X, int p2Y, int range) => - p1X >= p2X - range - && p1X <= p2X + range - && p1Y >= p2Y - range - && p1Y <= p2Y + range; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InRange(Point2D p1, Point2D p2, int range) => - InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InUpdateRange(Point2D p1, Point2D p2) => InRange(p1, p2, 18); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InRange(Point3D p1, Point3D p2, int range) => - InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InUpdateRange(Point3D p1, Point3D p2) => InRange(p1, p2, 18); - - // 4d6+8 would be: Utility.Dice( 4, 6, 8 ) - public static int Dice(uint amount, uint sides, int bonus) + public static uint GetXMLUInt32(string uintString, uint defaultValue) + { + try { - var total = 0; - - for (var i = 0; i < amount; ++i) - { - total += (int)RandomSources.Source.Next(1, sides); - } - - return total + bonus; + return XmlConvert.ToUInt32(uintString); } - - public static void Shuffle(this IList list) + catch { - var count = list.Count; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } + return uint.TryParse(uintString, out var val) ? val : defaultValue; } + } - public static void Shuffle(this Span list) + public static DateTime GetXMLDateTime(string dateTimeString, DateTime defaultValue) + { + try { - var count = list.Length; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } + return XmlConvert.ToDateTime(dateTimeString, XmlDateTimeSerializationMode.Utc); + } + catch + { + return DateTime.TryParse(dateTimeString, out var d) ? d : defaultValue; + } + } + + public static TimeSpan GetXMLTimeSpan(string timeSpanString, TimeSpan defaultValue) + { + try + { + return XmlConvert.ToTimeSpan(timeSpanString); + } + catch + { + return defaultValue; + } + } + + public static string GetAttribute(XmlElement node, string attributeName, string defaultValue = null) => + node?.Attributes[attributeName]?.Value ?? defaultValue; + + public static string GetText(XmlElement node, string defaultValue) => node?.InnerText ?? defaultValue; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InRange(int p1X, int p1Y, int p2X, int p2Y, int range) => + p1X >= p2X - range + && p1X <= p2X + range + && p1Y >= p2Y - range + && p1Y <= p2Y + range; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InRange(Point2D p1, Point2D p2, int range) => + InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InUpdateRange(Point2D p1, Point2D p2) => InRange(p1, p2, 18); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InRange(Point3D p1, Point3D p2, int range) => + InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InUpdateRange(Point3D p1, Point3D p2) => InRange(p1, p2, 18); + + // 4d6+8 would be: Utility.Dice( 4, 6, 8 ) + public static int Dice(uint amount, uint sides, int bonus) + { + var total = 0; + + for (var i = 0; i < amount; ++i) + { + total += (int)RandomSources.Source.Next(1, sides); } - /** + return total + bonus; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Shuffle(this IList list) + { + var count = list.Count; + for (var i = 0; i < count; i++) + { + var r = RandomMinMax(i, count - 1); + (list[r], list[i]) = (list[i], list[r]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Shuffle(this Span list) + { + var count = list.Length; + for (var i = 0; i < count; i++) + { + var r = RandomMinMax(i, count - 1); + (list[r], list[i]) = (list[i], list[r]); + } + } + + /** * Gets a random sample from the source list. * Not meant for unbounded lists. Does not shuffle or modify source. */ - public static T[] RandomSample(this T[] source, int count) + public static T[] RandomSample(this T[] source, int count) + { + if (count <= 0) { - if (count <= 0) - { - return Array.Empty(); - } - - var length = source.Length; - Span list = stackalloc bool[length]; - var sampleList = new T[count]; - - var i = 0; - do - { - var rand = Random(length); - if (!(list[rand] && (list[rand] = true))) - { - sampleList[i++] = source[rand]; - } - } while (i < count); - - return sampleList; + return Array.Empty(); } - public static List RandomSample(this List source, int count) + var length = source.Length; + Span list = stackalloc bool[length]; + var sampleList = new T[count]; + + var i = 0; + do { - if (count <= 0) + var rand = Random(length); + if (!(list[rand] && (list[rand] = true))) { - return new List(); + sampleList[i++] = source[rand]; } + } while (i < count); - var length = source.Count; - Span list = stackalloc bool[length]; - var sampleList = new List(count); + return sampleList; + } - var i = 0; - do - { - var rand = Random(length); - if (!(list[rand] && (list[rand] = true))) - { - sampleList[i++] = source[rand]; - } - } while (i < count); - - return sampleList; + public static List RandomSample(this List source, int count) + { + if (count <= 0) + { + return new List(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomList(params T[] list) => list.RandomElement(); + var length = source.Count; + Span list = stackalloc bool[length]; + var sampleList = new List(count); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomElement(this IList list) => list.RandomElement(default); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T TakeRandomElement(this IList list) + var i = 0; + do { + var rand = Random(length); + if (!(list[rand] && (list[rand] = true))) + { + sampleList[i++] = source[rand]; + } + } while (i < count); + + return sampleList; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T RandomList(params T[] list) => list.RandomElement(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T RandomElement(this IList list) => list.RandomElement(default); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T TakeRandomElement(this IList list) + { + if (list.Count == 0) + { + return default; + } + + var index = Random(list.Count); + var value = list[index]; + list.RemoveAt(index); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T RandomElement(this IList list, T valueIfZero) => + list.Count == 0 ? valueIfZero : list[Random(list.Count)]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool RandomBool() => RandomSources.Source.NextBool(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RandomMinMax(double min, double max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.NextDouble() * (max - min); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RandomMinMax(uint min, uint max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.Next(max - min + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RandomMinMax(int min, int max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + (int)RandomSources.Source.Next((uint)(max - min + 1)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long RandomMinMax(long min, long max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.Next(max - min + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Random(int from, int count) => RandomSources.Source.Next(from, count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Random(int count) => RandomSources.Source.Next(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Random(uint count) => RandomSources.Source.Next(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RandomBytes(Span buffer) => RandomSources.Source.NextBytes(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RandomDouble() => RandomSources.Source.NextDouble(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point3D RandomPointIn(Rectangle2D rect, Map map) + { + var x = Random(rect.X, rect.Width); + var y = Random(rect.Y, rect.Height); + + return new Point3D(x, y, map.GetAverageZ(x, y)); + } + + /// + /// Random pink, blue, green, orange, red or yellow hue + /// + public static int RandomNondyedHue() + { + return Random(6) switch + { + 0 => RandomPinkHue(), + 1 => RandomBlueHue(), + 2 => RandomGreenHue(), + 3 => RandomOrangeHue(), + 4 => RandomRedHue(), + 5 => RandomYellowHue(), + _ => 0 + }; + } + + /// + /// Random hue in the range 1201-1254 + /// + public static int RandomPinkHue() => Random(1201, 54); + + /// + /// Random hue in the range 1301-1354 + /// + public static int RandomBlueHue() => Random(1301, 54); + + /// + /// Random hue in the range 1401-1454 + /// + public static int RandomGreenHue() => Random(1401, 54); + + /// + /// Random hue in the range 1501-1554 + /// + public static int RandomOrangeHue() => Random(1501, 54); + + /// + /// Random hue in the range 1601-1654 + /// + public static int RandomRedHue() => Random(1601, 54); + + /// + /// Random hue in the range 1701-1754 + /// + public static int RandomYellowHue() => Random(1701, 54); + + /// + /// Random hue in the range 1801-1908 + /// + public static int RandomNeutralHue() => Random(1801, 108); + + /// + /// Random hue in the range 2001-2018 + /// + public static int RandomSnakeHue() => Random(2001, 18); + + /// + /// Random hue in the range 2101-2130 + /// + public static int RandomBirdHue() => Random(2101, 30); + + /// + /// Random hue in the range 2201-2224 + /// + public static int RandomSlimeHue() => Random(2201, 24); + + /// + /// Random hue in the range 2301-2318 + /// + public static int RandomAnimalHue() => Random(2301, 18); + + /// + /// Random hue in the range 2401-2430 + /// + public static int RandomMetalHue() => Random(2401, 30); + + public static int ClipDyedHue(int hue) => hue < 2 ? 2 : + hue > 1001 ? 1001 : hue; + + /// + /// Random hue in the range 2-1001 + /// + public static int RandomDyedHue() => Random(2, 1000); + + /// + /// Random hue from 0x62, 0x71, 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 + /// + public static int RandomBrightHue() => + RandomDouble() < 0.1 + ? RandomList(0x62, 0x71) + : RandomList(0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Clamp(this T val, T min, T max) where T : IComparable => + val.CompareTo(min) < 0 ? min : + val.CompareTo(max) > 0 ? max : val; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Min(T val, T min) where T : IComparable => val.CompareTo(min) < 0 ? val : min; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Max(T val, T max) where T : IComparable => val.CompareTo(max) > 0 ? val : max; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tidy(this List list) where T : ISerializable + { + for (int i = list.Count - 1; i >= 0; i--) + { + var entry = list[i]; + if (entry?.Deleted != false) + { + list.RemoveAt(i); + } + } + + list.TrimExcess(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tidy(this HashSet set) where T : ISerializable + { + set.RemoveWhere(entry => entry?.Deleted != false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tidy(this Dictionary dictionary) + { + var serializable = typeof(ISerializable); + var serializableKey = typeof(K).IsAssignableTo(serializable); + var serializableValue = typeof(V).IsAssignableTo(serializable); + + if (!serializableKey && !serializableValue) + { + return; + } + + using var queue = PooledRefQueue.Create(); + foreach (var (key, value) in dictionary) + { + if (serializableKey) + { + if (key == null || ((ISerializable)key).Deleted) + { + queue.Enqueue(key); + } + } + else + { + if (value == null || ((ISerializable)value).Deleted) + { + queue.Enqueue(key); + } + } + } + + while (queue.Count > 0) + { + dictionary.Remove(queue.Dequeue()); + } + + dictionary.TrimExcess(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int NumberOfSetBits(this ulong i) + { + i -= (i >> 1) & 0x5555555555555555UL; + i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL); + return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Abs(this int value) + { + int mask = value >> 31; + return (value + mask) ^ mask; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetTimeStamp() => Core.Now.ToTimeStamp(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToTimeStamp(this DateTime dt) => dt.ToString("yyyy-MM-dd-HH-mm-ss"); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref List list, T value) + { + list ??= new List(); + list.Add(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref HashSet set, T value) + { + set ??= new HashSet(); + set.Add(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Dictionary dict, K key, V value) + { + dict ??= new Dictionary(); + dict.Add(key, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref List list, T value) + { + if (list != null) + { + var removed = list.Remove(value); + if (list.Count == 0) { - return default; + list = null; } - var index = Random(list.Count); - var value = list[index]; - list.RemoveAt(index); - return value; + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomElement(this IList list, T valueIfZero) => - list.Count == 0 ? valueIfZero : list[Random(list.Count)]; + return false; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool RandomBool() => RandomSources.Source.NextBool(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint RandomMinMax(uint min, uint max) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref HashSet set, T value) + { + if (set != null) { - if (min > max) + var removed = set.Remove(value); + + if (set.Count == 0) { - (min, max) = (max, min); - } - else if (min == max) - { - return min; + set = null; } - return min + RandomSources.Source.Next(max - min + 1); + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int RandomMinMax(int min, int max) + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref Dictionary dict, K key) + { + if (dict != null) { - if (min > max) + var removed = dict.Remove(key); + + if (dict.Count == 0) { - (min, max) = (max, min); - } - else if (min == max) - { - return min; + dict = null; } - return min + (int)RandomSources.Source.Next((uint)(max - min + 1)); + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long RandomMinMax(long min, long max) + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref Dictionary dict, K key, out V value) + { + if (dict != null) { - if (min > max) + var removed = dict.Remove(key, out value); + + if (dict.Count == 0) { - (min, max) = (max, min); - } - else if (min == max) - { - return min; + dict = null; } - return min + RandomSources.Source.Next(max - min + 1); + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Random(int from, int count) => RandomSources.Source.Next(from, count); + value = default; + return false; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Random(int count) => RandomSources.Source.Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Random(uint count) => RandomSources.Source.Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RandomBytes(Span buffer) => RandomSources.Source.NextBytes(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double RandomDouble() => RandomSources.Source.NextDouble(); - - /// - /// Random pink, blue, green, orange, red or yellow hue - /// - public static int RandomNondyedHue() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replace(ref List list, T oldValue, T newValue) + { + if (oldValue != null && newValue != null) { - return Random(6) switch + var index = list?.IndexOf(oldValue) ?? -1; + + if (index >= 0) { - 0 => RandomPinkHue(), - 1 => RandomBlueHue(), - 2 => RandomGreenHue(), - 3 => RandomOrangeHue(), - 4 => RandomRedHue(), - 5 => RandomYellowHue(), - _ => 0 - }; - } - - /// - /// Random hue in the range 1201-1254 - /// - public static int RandomPinkHue() => Random(1201, 54); - - /// - /// Random hue in the range 1301-1354 - /// - public static int RandomBlueHue() => Random(1301, 54); - - /// - /// Random hue in the range 1401-1454 - /// - public static int RandomGreenHue() => Random(1401, 54); - - /// - /// Random hue in the range 1501-1554 - /// - public static int RandomOrangeHue() => Random(1501, 54); - - /// - /// Random hue in the range 1601-1654 - /// - public static int RandomRedHue() => Random(1601, 54); - - /// - /// Random hue in the range 1701-1754 - /// - public static int RandomYellowHue() => Random(1701, 54); - - /// - /// Random hue in the range 1801-1908 - /// - public static int RandomNeutralHue() => Random(1801, 108); - - /// - /// Random hue in the range 2001-2018 - /// - public static int RandomSnakeHue() => Random(2001, 18); - - /// - /// Random hue in the range 2101-2130 - /// - public static int RandomBirdHue() => Random(2101, 30); - - /// - /// Random hue in the range 2201-2224 - /// - public static int RandomSlimeHue() => Random(2201, 24); - - /// - /// Random hue in the range 2301-2318 - /// - public static int RandomAnimalHue() => Random(2301, 18); - - /// - /// Random hue in the range 2401-2430 - /// - public static int RandomMetalHue() => Random(2401, 30); - - public static int ClipDyedHue(int hue) => hue < 2 ? 2 : - hue > 1001 ? 1001 : hue; - - /// - /// Random hue in the range 2-1001 - /// - public static int RandomDyedHue() => Random(2, 1000); - - /// - /// Random hue from 0x62, 0x71, 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 - /// - public static int RandomBrightHue() => - RandomDouble() < 0.1 - ? RandomList(0x62, 0x71) - : RandomList(0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Clamp(this T val, T min, T max) where T : IComparable => - val.CompareTo(min) < 0 ? min : - val.CompareTo(max) > 0 ? max : val; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Min(T val, T min) where T : IComparable => val.CompareTo(min) < 0 ? val : min; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Max(T val, T max) where T : IComparable => val.CompareTo(max) > 0 ? val : max; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Tidy(this List list) where T : ISerializable - { - for (int i = list.Count - 1; i >= 0; i--) - { - var entry = list[i]; - if (entry?.Deleted != false) - { - list.RemoveAt(i); - } - } - - list.TrimExcess(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Tidy(this HashSet set) where T : ISerializable - { - set.RemoveWhere(entry => entry?.Deleted != false); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Tidy(this Dictionary dictionary) - { - var serializable = typeof(ISerializable); - var serializableKey = typeof(K).IsAssignableTo(serializable); - var serializableValue = typeof(V).IsAssignableTo(serializable); - - if (!serializableKey && !serializableValue) - { - return; - } - - using var queue = PooledRefQueue.Create(); - foreach (var (key, value) in dictionary) - { - if (serializableKey) - { - if (key == null || ((ISerializable)key).Deleted) - { - queue.Enqueue(key); - } - } - else - { - if (value == null || ((ISerializable)value).Deleted) - { - queue.Enqueue(key); - } - } - } - - while (queue.Count > 0) - { - dictionary.Remove(queue.Dequeue()); - } - - dictionary.TrimExcess(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int NumberOfSetBits(this ulong i) - { - i -= (i >> 1) & 0x5555555555555555UL; - i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL); - return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Abs(this int value) - { - int mask = value >> 31; - return (value + mask) ^ mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long Abs(this long value) - { - long mask = value >> 63; - return (value + mask) ^ mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CountDigits(this uint value) - { - int digits = 1; - if (value >= 100000) - { - value /= 100000; - digits += 5; - } - - if (value < 10) - { - // no-op - } - else if (value < 100) - { - digits++; - } - else if (value < 1000) - { - digits += 2; - } - else if (value < 10000) - { - digits += 3; + list![index] = newValue; } else - { - digits += 4; - } - - return digits; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CountDigits(this int value) - { - int absValue = Abs(value); - - int digits = 1; - if (absValue >= 100000) - { - absValue /= 100000; - digits += 5; - } - - if (absValue < 10) - { - // no-op - } - else if (absValue < 100) - { - digits++; - } - else if (absValue < 1000) - { - digits += 2; - } - else if (absValue < 10000) - { - digits += 3; - } - else - { - digits += 4; - } - - if (value < 0) - { - digits += 1; // negative - } - - return digits; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint DivRem(uint a, uint b, out uint result) - { - uint div = a / b; - result = a - div * b; - return div; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetTimeStamp() => Core.Now.ToTimeStamp(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string ToTimeStamp(this DateTime dt) => dt.ToString("yyyy-MM-dd-HH-mm-ss"); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref List list, T value) - { - list ??= new List(); - list.Add(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref HashSet set, T value) - { - set ??= new HashSet(); - set.Add(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref Dictionary dict, K key, V value) - { - dict ??= new Dictionary(); - dict.Add(key, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref List list, T value) - { - if (list != null) - { - var removed = list.Remove(value); - - if (list.Count == 0) - { - list = null; - } - - return removed; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref HashSet set, T value) - { - if (set != null) - { - var removed = set.Remove(value); - - if (set.Count == 0) - { - set = null; - } - - return removed; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref Dictionary dict, K key) - { - if (dict != null) - { - var removed = dict.Remove(key); - - if (dict.Count == 0) - { - dict = null; - } - - return removed; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref Dictionary dict, K key, out V value) - { - if (dict != null) - { - var removed = dict.Remove(key, out value); - - if (dict.Count == 0) - { - dict = null; - } - - return removed; - } - - value = default; - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Replace(ref List list, T oldValue, T newValue) - { - if (oldValue != null && newValue != null) - { - var index = list?.IndexOf(oldValue) ?? -1; - - if (index >= 0) - { - list![index] = newValue; - } - else - { - Add(ref list, newValue); - } - } - else if (oldValue != null) - { - Remove(ref list, oldValue); - } - else if (newValue != null) { Add(ref list, newValue); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Replace(ref Dictionary dict, K key, V oldValue, V newValue) + else if (oldValue != null) { - if (newValue != null) - { - Add(ref dict, key, newValue); - } - else if (oldValue != null) - { - Remove(ref dict, key); - } + Remove(ref list, oldValue); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(ref List list) + else if (newValue != null) { - list.Clear(); - list = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(ref HashSet set) - { - set.Clear(); - set = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(ref Dictionary dict) - { - dict.Clear(); - dict = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DateToComponents( - DateTime date, - out int year, - out int month, - out int day, - out DayOfWeek dayOfWeek, - out int hour, - out int min, - out int sec - ) - { - year = date.Year; - month = date.Month; - day = date.Day; - dayOfWeek = date.DayOfWeek; - hour = date.Hour; - min = date.Minute; - sec = date.Second; + Add(ref list, newValue); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replace(ref Dictionary dict, K key, V oldValue, V newValue) + { + if (newValue != null) + { + Add(ref dict, key, newValue); + } + else if (oldValue != null) + { + Remove(ref dict, key); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref List list) + { + list.Clear(); + list = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref HashSet set) + { + set.Clear(); + set = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref Dictionary dict) + { + dict.Clear(); + dict = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DateToComponents( + DateTime date, + out int year, + out int month, + out int day, + out DayOfWeek dayOfWeek, + out int hour, + out int min, + out int sec + ) + { + year = date.Year; + month = date.Month; + day = date.Day; + dayOfWeek = date.DayOfWeek; + hour = date.Hour; + min = date.Minute; + sec = date.Second; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] Combine(this IList source, params IList[] arrays) => + source.Combine(false, arrays); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] CombinePooled(this IList source, params IList[] arrays) => + source.Combine(true, arrays); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] Combine(this IList source, bool pooled, params IList[] arrays) + { + var totalLength = source.Count; + foreach (var arr in arrays) + { + totalLength += arr.Count; + } + + if (totalLength == 0) + { + return Array.Empty(); + } + + var combined = pooled ? STArrayPool.Shared.Rent(totalLength) : new T[totalLength]; + + source.CopyTo(combined, 0); + var position = source.Count; + foreach (var arr in arrays) + { + arr.CopyTo(combined, position); + position += arr.Count; + } + + return combined; + } } diff --git a/Projects/Server/World/EntityPersistence.cs b/Projects/Server/World/EntityPersistence.cs index bf2f415df..59a9d5e80 100644 --- a/Projects/Server/World/EntityPersistence.cs +++ b/Projects/Server/World/EntityPersistence.cs @@ -200,6 +200,12 @@ namespace Server continue; } + if (entry.Length == 0) + { + t.Delete(); + continue; + } + var buffer = GC.AllocateUninitializedArray(entry.Length); if (br == null) { @@ -310,6 +316,12 @@ namespace Server { var saveBuffer = entity.SaveBuffer; + // If nothing was serialized we expect the object to be deleted on deserialization + if (saveBuffer.Position == 0) + { + return; + } + // Resize to the exact size saveBuffer.Resize((int)saveBuffer.Position); diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index 1db4b3e30..68b3fa7bf 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -284,11 +284,11 @@ namespace Server watch.Stop(); - logger.Information(string.Format("World loaded ({1} items, {2} mobiles) ({0:F2} seconds)", + logger.Information("World loaded ({ItemCount} items, {MobileCount} mobiles) ({Duration:F2} seconds)", watch.Elapsed.TotalSeconds, Items.Count, Mobiles.Count - )); + ); WorldState = WorldState.Running; } @@ -304,7 +304,7 @@ namespace Server { if (_pendingAdd.ContainsKey(entity.Serial)) { - logger.Warning("Entity {0} was both pending both deletion and addition after save", entity); + logger.Warning("Entity {Entity} was both pending both deletion and addition after save", entity); } RemoveEntity(entity); @@ -402,7 +402,7 @@ namespace Server watch.Stop(); - logger.Information("Writing world save snapshot done ({0:F2} seconds)", watch.Elapsed.TotalSeconds); + logger.Information("Writing world save snapshot done ({Duration:F2} seconds)", watch.Elapsed.TotalSeconds); } catch (Exception ex) { @@ -431,7 +431,7 @@ namespace Server m_DiskWriteHandle.Set(); - Timer.StartTimer(FinishWorldSave); + Core.LoopContext.Post(FinishWorldSave); } private static void ProcessDecay() @@ -505,7 +505,7 @@ namespace Server if (exception == null) { var duration = watch.Elapsed.TotalSeconds; - logger.Information("World save completed ({0:F2} seconds)", duration); + logger.Information("World save completed ({Duration:F2} seconds)", duration); // Only broadcast if it took at least 150ms if (duration >= 0.15) @@ -657,99 +657,5 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RemoveGuild(BaseGuild guild) => Guilds.Remove(guild.Serial); - - public static T ReadEntity(this IGenericReader reader) where T : class, ISerializable - { - Serial serial = reader.ReadSerial(); - var typeT = typeof(T); - - T entity; - - // Add to this list when creating new serializable types - if (typeof(BaseGuild).IsAssignableFrom(typeT)) - { - entity = FindGuild(serial) as T; - // If we check for `entity.Deleted` here during deserialization then all guilds are deleted because - // Deleted -> Disbanded -> No leader, which is the case before deserialization. - // TODO: Use a deleted flag instead, and actively check for dibanded guilds properly. - } - else - { - entity = FindEntity(serial) as T; - if (entity?.Deleted == false) - { - return entity; - } - } - - return entity?.Created <= reader.LastSerialized ? entity : null; - } - - public static List ReadEntityList(this IGenericReader reader) where T : class, ISerializable - { - var count = reader.ReadInt(); - - var list = new List(count); - - for (var i = 0; i < count; ++i) - { - var entity = reader.ReadEntity(); - if (entity != null) - { - list.Add(entity); - } - } - - return list; - } - - public static HashSet ReadEntitySet(this IGenericReader reader) where T : class, ISerializable - { - var count = reader.ReadInt(); - - var set = new HashSet(count); - - for (var i = 0; i < count; ++i) - { - var entity = reader.ReadEntity(); - if (entity != null) - { - set.Add(entity); - } - } - - return set; - } - - public static void Write(this IGenericWriter writer, ISerializable value) - { - writer.Write(value?.Deleted != false ? Serial.MinusOne : value.Serial); - } - - public static void Write(this IGenericWriter writer, ICollection coll) where T : class, ISerializable - { - writer.Write(coll.Count); - foreach (var entry in coll) - { - writer.Write(entry); - } - } - - public static void Write( - this IGenericWriter writer, ICollection coll, Action action - ) where T : class, ISerializable - { - if (coll == null) - { - writer.Write(0); - return; - } - - writer.Write(coll.Count); - foreach (var entry in coll) - { - action(writer, entry); - } - } } } diff --git a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs index 380ab8954..1719f5295 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs @@ -1,5 +1,4 @@ using System; -using System.Security.Cryptography; using Server.Accounting; using Server.Accounting.Security; using Xunit; diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index fc960bae1..195cec9a4 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -3,10 +3,10 @@ false - - + + - + diff --git a/Projects/UOContent/Accounting/AccessRestrictions.cs b/Projects/UOContent/Accounting/AccessRestrictions.cs index 08ad2484c..4522e558c 100644 --- a/Projects/UOContent/Accounting/AccessRestrictions.cs +++ b/Projects/UOContent/Accounting/AccessRestrictions.cs @@ -22,14 +22,14 @@ namespace Server if (Firewall.IsBlocked(ip)) { - logger.Information("Client: {0}: Firewall blocked connection attempt.", ip); + logger.Information("Client: {IP}: Firewall blocked connection attempt.", ip); e.AllowConnection = false; return; } if (IPLimiter.SocketBlock && !IPLimiter.Verify(ip)) { - logger.Warning("Client: {0}: Past IP limit threshold", ip); + logger.Warning("Client: {IP}: Past IP limit threshold", ip); using (var op = new StreamWriter("ipLimits.log", true)) { diff --git a/Projects/UOContent/Accounting/Account.Migrations.cs b/Projects/UOContent/Accounting/Account.Migrations.cs index dd9148b01..e53d5c75f 100644 --- a/Projects/UOContent/Accounting/Account.Migrations.cs +++ b/Projects/UOContent/Accounting/Account.Migrations.cs @@ -18,7 +18,7 @@ namespace Server.Accounting _lastLogin = content.LastLogin; _totalGold = content.TotalGold; _totalPlat = content.TotalPlat; - _mobiles = content.Mobiles; + _rawMobiles = content.Mobiles; _comments = content.Comments; _tags = content.Tags; _loginIPs = content.LoginIPs; @@ -47,10 +47,10 @@ namespace Server.Accounting _totalPlat = reader.ReadInt(); var length = reader.ReadInt(); - _mobiles = new Mobile[length]; + _rawMobiles = new Mobile[length]; for (int i = 0; i < length; i++) { - _mobiles[i] = reader.ReadEntity(); + _rawMobiles[i] = reader.ReadEntity(); } length = reader.ReadInt(); diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index 81538bf8b..25dd20570 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Net; +using System.Runtime.CompilerServices; using System.Xml; +using ModernUO.Serialization; using Server.Accounting.Security; using Server.Misc; using Server.Mobiles; @@ -10,7 +12,7 @@ using Server.Network; namespace Server.Accounting { - [Serializable(4)] + [SerializationGenerator(4)] public partial class Account : IAccount, IComparable, ISerializable { public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0); @@ -55,7 +57,7 @@ namespace Server.Accounting public int _totalPlat; [SerializableField(8, "private", "private")] - private Mobile[] _mobiles; + private Mobile[] _rawMobiles; private List _comments; @@ -104,9 +106,9 @@ namespace Server.Accounting { get { - for (var i = 0; i < _mobiles.Length; i++) + for (var i = 0; i < _rawMobiles.Length; i++) { - if (_mobiles[i] is PlayerMobile m && m.NetState != null) + if (_rawMobiles[i] is PlayerMobile m && m.NetState != null) { return _totalGameTime + (Core.Now - m.SessionStart); } @@ -138,7 +140,7 @@ namespace Server.Accounting _lastLogin = Core.Now; _totalGameTime = TimeSpan.Zero; - _mobiles = new Mobile[7]; + _rawMobiles = new Mobile[7]; _ipRestrictions = Array.Empty(); _loginIPs = Array.Empty(); @@ -186,26 +188,26 @@ namespace Server.Accounting _totalGold = Utility.GetXMLInt32(Utility.GetText(node["totalGold"], "0"), 0); _totalPlat = Utility.GetXMLInt32(Utility.GetText(node["totalPlat"], "0"), 0); - _mobiles = LoadMobiles(node); + _rawMobiles = LoadMobiles(node); _comments = LoadComments(node); _tags = LoadTags(node); _loginIPs = LoadAddressList(node); _ipRestrictions = LoadAccessCheck(node); - for (var i = 0; i < _mobiles.Length; ++i) + for (var i = 0; i < _rawMobiles.Length; ++i) { - if (_mobiles[i] != null) + if (_rawMobiles[i] != null) { - _mobiles[i].Account = this; + _rawMobiles[i].Account = this; } } var totalGameTime = Utility.GetXMLTimeSpan(Utility.GetText(node["totalGameTime"], null), TimeSpan.Zero); if (totalGameTime == TimeSpan.Zero) { - for (var i = 0; i < _mobiles.Length; i++) + for (var i = 0; i < _rawMobiles.Length; i++) { - if (_mobiles[i] is PlayerMobile m) + if (_rawMobiles[i] is PlayerMobile m) { totalGameTime += m.GameTime; } @@ -331,19 +333,19 @@ namespace Server.Accounting _tags = null; } - for (var i = 0; i < _mobiles.Length; ++i) + for (var i = 0; i < _rawMobiles.Length; ++i) { - if (_mobiles[i] != null) + if (_rawMobiles[i] != null) { - _mobiles[i].Account = this; + _rawMobiles[i].Account = this; } } if (_totalGameTime == TimeSpan.Zero) { - for (var i = 0; i < _mobiles.Length; i++) + for (var i = 0; i < _rawMobiles.Length; i++) { - if (_mobiles[i] is PlayerMobile m) + if (_rawMobiles[i] is PlayerMobile m) { _totalGameTime += m.GameTime; } @@ -380,7 +382,7 @@ namespace Server.Accounting m.Delete(); m.Account = null; - _mobiles[i] = null; + _rawMobiles[i] = null; } if (_loginIPs.Length != 0 && AccountHandler.IPTable.ContainsKey(_loginIPs[0])) @@ -446,13 +448,12 @@ namespace Server.Accounting /// Gets the maximum amount of characters allowed to be created on this account. Values other than 1, 5, 6, or 7 are not /// supported by the client. ///
- public int Limit => Core.SA ? 7 : - Core.AOS ? 6 : 5; + public int Limit => Core.SA ? 7 : Core.AOS ? 6 : 5; /// /// Gets the maximum amount of characters that this account can hold. /// - public int Length => _mobiles.Length; + public int Length => _rawMobiles.Length; /// /// Gets or sets the character at a specified index for this account. Out of bound index values are handled; null returned @@ -462,9 +463,9 @@ namespace Server.Accounting { get { - if (index >= 0 && index < _mobiles.Length) + if (index >= 0 && index < _rawMobiles.Length) { - var m = _mobiles[index]; + var m = _rawMobiles[index]; if (m?.Deleted != true) { @@ -474,7 +475,7 @@ namespace Server.Accounting // This is the only place that clears a mobile for garbage collection // outside of an entire account deletion. m.Account = null; - _mobiles[index] = null; + _rawMobiles[index] = null; this.MarkDirty(); } @@ -482,19 +483,19 @@ namespace Server.Accounting } set { - if (index >= 0 && index < _mobiles.Length) + if (index >= 0 && index < _rawMobiles.Length) { - if (_mobiles[index] != null) + if (_rawMobiles[index] != null) { - _mobiles[index].Account = null; + _rawMobiles[index].Account = null; } - _mobiles[index] = value; + _rawMobiles[index] = value; this.MarkDirty(); - if (_mobiles[index] != null) + if (_rawMobiles[index] != null) { - _mobiles[index].Account = this; + _rawMobiles[index].Account = this; } } } @@ -820,9 +821,9 @@ namespace Server.Accounting { Young = false; - for (var i = 0; i < _mobiles.Length; i++) + for (var i = 0; i < _rawMobiles.Length; i++) { - if (_mobiles[i] is PlayerMobile { Young: true } m) + if (_rawMobiles[i] is PlayerMobile { Young: true } m) { m.Young = false; @@ -1178,5 +1179,52 @@ namespace Server.Accounting m_Account.CheckYoung(); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new(_rawMobiles); + + public Enumerator Mobiles + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetEnumerator(); + } + + public ref struct Enumerator + { + private readonly Mobile[] _mobiles; + private int _index; + private Mobile _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(Mobile[] mobs) + { + _mobiles = mobs; + _index = 0; + _current = default; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + Mobile[] localList = _mobiles; + + while ((uint)_index < (uint)localList.Length) + { + _current = _mobiles[_index++]; + if (_current?.Deleted == false) + { + return true; + } + } + + return false; + } + + public Mobile Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } } } diff --git a/Projects/UOContent/Accounting/AccountAttackLimiter.cs b/Projects/UOContent/Accounting/AccountAttackLimiter.cs index 97bbc90df..53143327b 100644 --- a/Projects/UOContent/Accounting/AccountAttackLimiter.cs +++ b/Projects/UOContent/Accounting/AccountAttackLimiter.cs @@ -17,16 +17,16 @@ namespace Server.Accounting Enabled = ServerConfiguration.GetOrUpdateSetting("accountAttackLimiter.enable", true); } - public static void Initialize() + public static unsafe void Initialize() { if (!Enabled) { return; } - IncomingPackets.RegisterThrottler(0x80, Throttle); - IncomingPackets.RegisterThrottler(0x91, Throttle); - IncomingPackets.RegisterThrottler(0xCF, Throttle); + IncomingPackets.RegisterThrottler(0x80, &Throttle); + IncomingPackets.RegisterThrottler(0x91, &Throttle); + IncomingPackets.RegisterThrottler(0xCF, &Throttle); } public static bool Throttle(int packetId, NetState ns, out bool drop) diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs index 642660d05..f93022830 100644 --- a/Projects/UOContent/Accounting/AccountHandler.cs +++ b/Projects/UOContent/Accounting/AccountHandler.cs @@ -8,462 +8,457 @@ using Server.Logging; using Server.Network; using Server.Regions; -namespace Server.Misc +namespace Server.Misc; + +public static class AccountHandler { - public static class AccountHandler + private static readonly ILogger logger = LogFactory.GetLogger(typeof(AccountHandler)); + + private static int MaxAccountsPerIP; + private static bool AutoAccountCreation; + private static bool RestrictDeletion = !TestCenter.Enabled; + private static TimeSpan DeleteDelay = TimeSpan.FromDays(7.0); + private static bool PasswordCommandEnabled; + + private static CityInfo[] OldHavenStartingCities = { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(AccountHandler)); + new("Yew", "The Empath Abbey", 633, 858, 0), + new("Minoc", "The Barnacle", 2476, 413, 15), + new("Britain", "Sweet Dreams Inn", 1496, 1628, 10), + new("Moonglow", "The Scholars Inn", 4408, 1168, 0), + new("Trinsic", "The Traveler's Inn", 1845, 2745, 0), + new("Magincia", "The Great Horns Tavern", 3734, 2222, 20), + new("Jhelom", "The Mercenary Inn", 1374, 3826, 0), + new("Skara Brae", "The Falconer's Inn", 618, 2234, 0), + new("Vesper", "The Ironwood Inn", 2771, 976, 0), + new("Haven", "Buckler's Hideaway", 3667, 2625, 0) + }; - private static int MaxAccountsPerIP; - private static bool AutoAccountCreation; - private static readonly bool RestrictDeletion = !TestCenter.Enabled; - private static readonly TimeSpan DeleteDelay = TimeSpan.FromDays(7.0); - private static bool PasswordCommandEnabled; + private static CityInfo[] StartingCities = + { + new("New Haven", "New Haven Bank", 1150168, 3667, 2625, 0), + new("Yew", "The Empath Abbey", 1075072, 633, 858, 0), + new("Minoc", "The Barnacle", 1075073, 2476, 413, 15), + new("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20), + new("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0), + new("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0), + new("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0), + new("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0), + new("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0) + }; - private static readonly CityInfo[] StartingCities = + private static Dictionary m_IPTable; + + private static char[] m_ForbiddenChars = { '<', '>', ':', '"', '/', '\\', '|', '?', '*' }; + + public static AccessLevel LockdownLevel { get; set; } + + public static Dictionary IPTable + { + get { - new("New Haven", "New Haven Bank", 1150168, 3667, 2625, 0), - new("Yew", "The Empath Abbey", 1075072, 633, 858, 0), - new("Minoc", "The Barnacle", 1075073, 2476, 413, 15), - new("Britain", "The Wayfarer's Inn", 1075074, 1602, 1591, 20), - new("Moonglow", "The Scholars Inn", 1075075, 4408, 1168, 0), - new("Trinsic", "The Traveler's Inn", 1075076, 1845, 2745, 0), - new("Jhelom", "The Mercenary Inn", 1075078, 1374, 3826, 0), - new("Skara Brae", "The Falconer's Inn", 1075079, 618, 2234, 0), - new("Vesper", "The Ironwood Inn", 1075080, 2771, 976, 0) - }; - - /* Old Haven/Magincia Locations - new CityInfo( "Britain", "Sweet Dreams Inn", 1496, 1628, 10 ); - // .. - // Trinsic - new CityInfo( "Magincia", "The Great Horns Tavern", 3734, 2222, 20 ), - // Jhelom - // .. - new CityInfo( "Haven", "Buckler's Hideaway", 3667, 2625, 0 ) - - if (Core.AOS) - { - //CityInfo haven = new CityInfo( "Haven", "Uzeraan's Mansion", 3618, 2591, 0 ); - CityInfo haven = new CityInfo( "Haven", "Uzeraan's Mansion", 3503, 2574, 14 ); - StartingCities[StartingCities.Length - 1] = haven; - } - */ - - private static Dictionary m_IPTable; - - private static readonly char[] m_ForbiddenChars = - { - '<', '>', ':', '"', '/', '\\', '|', '?', '*' - }; - - public static AccessLevel LockdownLevel { get; set; } - - public static Dictionary IPTable - { - get + if (m_IPTable == null) { - if (m_IPTable == null) + m_IPTable = new Dictionary(); + + foreach (Account a in Accounts.GetAccounts()) { - m_IPTable = new Dictionary(); - - foreach (Account a in Accounts.GetAccounts()) + if (a.LoginIPs.Length > 0) { - if (a.LoginIPs.Length > 0) - { - var ip = a.LoginIPs[0]; - m_IPTable[ip] = (m_IPTable.TryGetValue(ip, out var value) ? value : 0) + 1; - } - } - } - - return m_IPTable; - } - } - - public static void Configure() - { - MaxAccountsPerIP = ServerConfiguration.GetOrUpdateSetting("accountHandler.maxAccountsPerIP", 1); - AutoAccountCreation = ServerConfiguration.GetOrUpdateSetting("accountHandler.enableAutoAccountCreation", true); - PasswordCommandEnabled = ServerConfiguration.GetOrUpdateSetting( - "accountHandler.enablePlayerPasswordCommand", - false - ); - } - - public static void Initialize() - { - EventSink.DeleteRequest += EventSink_DeleteRequest; - EventSink.AccountLogin += EventSink_AccountLogin; - EventSink.GameLogin += EventSink_GameLogin; - - if (PasswordCommandEnabled) - { - CommandSystem.Register("Password", AccessLevel.Player, Password_OnCommand); - } - } - - [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) - { - var from = e.Mobile; - - if (from.Account is not Account acct) - { - return; - } - - var accessList = acct.LoginIPs; - - if (accessList.Length == 0) - { - return; - } - - var ns = from.NetState; - - if (ns == null) - { - return; - } - - if (e.Length == 0) - { - from.SendMessage("You must specify the new password."); - return; - } - - 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; - } - - var pass = e.GetString(0); - var pass2 = e.GetString(1); - - if (pass != pass2) - { - from.SendMessage("The passwords do not match."); - return; - } - - var isSafe = true; - - for (var i = 0; isSafe && i < pass.Length; ++i) - { - isSafe = pass[i] >= 0x20 && pass[i] < 0x7F; - } - - if (!isSafe) - { - from.SendMessage("That is not a valid password."); - return; - } - - try - { - var ipAddress = ns.Address; - - if (Utility.IPMatchClassC(accessList[0], ipAddress)) - { - acct.SetPassword(pass); - from.SendMessage("The password to your account has changed."); - } - else - { - var entry = PageQueue.GetEntry(from); - - if (entry != null) - { - if (entry.Message.StartsWithOrdinal("[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." - ); - - /* The next available Counselor/Game Master will respond as soon as possible. - * Please check your Journal for messages every few minutes. - */ - from.SendLocalizedMessage(501234, "", 0x35); - - PageQueue.Enqueue( - new PageEntry( - from, - $"[Automated: Change Password]
Desired password: {pass}
Current IP address: {ipAddress}
Account IP address: {accessList[0]}", - PageType.Account - ) - ); + var ip = a.LoginIPs[0]; + m_IPTable[ip] = (m_IPTable.TryGetValue(ip, out var value) ? value : 0) + 1; } } } - catch - { - // ignored - } + + return m_IPTable; + } + } + + public static void Configure() + { + MaxAccountsPerIP = ServerConfiguration.GetOrUpdateSetting("accountHandler.maxAccountsPerIP", 1); + AutoAccountCreation = ServerConfiguration.GetOrUpdateSetting("accountHandler.enableAutoAccountCreation", true); + PasswordCommandEnabled = ServerConfiguration.GetOrUpdateSetting( + "accountHandler.enablePlayerPasswordCommand", + false + ); + } + + public static void Initialize() + { + EventSink.DeleteRequest += EventSink_DeleteRequest; + EventSink.AccountLogin += EventSink_AccountLogin; + EventSink.GameLogin += EventSink_GameLogin; + + if (PasswordCommandEnabled) + { + CommandSystem.Register("Password", AccessLevel.Player, Password_OnCommand); + } + } + + [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) + { + var from = e.Mobile; + + if (from.Account is not Account acct) + { + return; } - private static void EventSink_DeleteRequest(NetState state, int index) + var accessList = acct.LoginIPs; + + if (accessList.Length == 0) { - if (state.Account is not Account acct) + return; + } + + var ns = from.NetState; + + if (ns == null) + { + return; + } + + if (e.Length == 0) + { + from.SendMessage("You must specify the new password."); + return; + } + + 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; + } + + var pass = e.GetString(0); + var pass2 = e.GetString(1); + + if (pass != pass2) + { + from.SendMessage("The passwords do not match."); + return; + } + + var isSafe = true; + + for (var i = 0; isSafe && i < pass.Length; ++i) + { + isSafe = pass[i] >= 0x20 && pass[i] < 0x7F; + } + + if (!isSafe) + { + from.SendMessage("That is not a valid password."); + return; + } + + try + { + var ipAddress = ns.Address; + + if (Utility.IPMatchClassC(accessList[0], ipAddress)) { - state.Disconnect("Attempted to delete a character but the account could not be found."); - return; + acct.SetPassword(pass); + from.SendMessage("The password to your account has changed."); } + else + { + var entry = PageQueue.GetEntry(from); - DeleteResultType res; + if (entry != null) + { + if (entry.Message.StartsWithOrdinal("[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." + ); - if (index < 0 || index >= acct.Length) + /* The next available Counselor/Game Master will respond as soon as possible. + * Please check your Journal for messages every few minutes. + */ + from.SendLocalizedMessage(501234, "", 0x35); + + PageQueue.Enqueue( + new PageEntry( + from, + $"[Automated: Change Password]
Desired password: {pass}
Current IP address: {ipAddress}
Account IP address: {accessList[0]}", + PageType.Account + ) + ); + } + } + } + catch + { + // ignored + } + } + + private static void EventSink_DeleteRequest(NetState state, int index) + { + if (state.Account is not Account acct) + { + state.Disconnect("Attempted to delete a character but the account could not be found."); + return; + } + + DeleteResultType res; + + if (index < 0 || index >= acct.Length) + { + res = DeleteResultType.BadRequest; + } + else + { + var m = acct[index]; + + if (m == null) + { + res = DeleteResultType.CharNotExist; + } + else if (m.NetState != null) + { + res = DeleteResultType.CharBeingPlayed; + } + else if (acct.AccessLevel == AccessLevel.Player && RestrictDeletion && Core.Now < m.Created + DeleteDelay) + { + res = DeleteResultType.CharTooYoung; + } + // Don't need to check current location, if netstate is null, they're logged out + else if ( + m.AccessLevel == AccessLevel.Player && + Region.Find(m.LogoutLocation, m.LogoutMap).IsPartOf() + ) { res = DeleteResultType.BadRequest; } else { - var m = acct[index]; + state.LogInfo($"Deleting character {index} ({m.Serial})"); - if (m == null) - { - res = DeleteResultType.CharNotExist; - } - else if (m.NetState != null) - { - res = DeleteResultType.CharBeingPlayed; - } - else if (RestrictDeletion && Core.Now < m.Created + DeleteDelay) - { - res = DeleteResultType.CharTooYoung; - } - else if (m.AccessLevel == AccessLevel.Player && - Region.Find(m.LogoutLocation, m.LogoutMap).IsPartOf() - ) // Don't need to check current location, if netstate is null, they're logged out - { - res = DeleteResultType.BadRequest; - } - else - { - state.LogInfo("Deleting character {0} (0x{1:X})", index, m.Serial.Value); + acct.Comments.Add(new AccountComment("System", $"Character #{index + 1} {m} deleted by {state}")); - acct.Comments.Add(new AccountComment("System", $"Character #{index + 1} {m} deleted by {state}")); - - m.Delete(); - state.SendCharacterListUpdate(acct); - return; - } + m.Delete(); + state.SendCharacterListUpdate(acct); + return; } - - state.SendCharacterDeleteResult(res); - state.SendCharacterListUpdate(acct); - } - public static bool CanCreate(IPAddress ip) => - !IPTable.TryGetValue(ip, out var result) || result < MaxAccountsPerIP; + state.SendCharacterDeleteResult(res); + state.SendCharacterListUpdate(acct); + } - private static bool IsForbiddenChar(char c) + public static bool CanCreate(IPAddress ip) => + !IPTable.TryGetValue(ip, out var result) || result < MaxAccountsPerIP; + + private static bool IsForbiddenChar(char c) + { + for (var i = 0; i < m_ForbiddenChars.Length; ++i) { - for (var i = 0; i < m_ForbiddenChars.Length; ++i) + if (c == m_ForbiddenChars[i]) { - if (c == m_ForbiddenChars[i]) + return true; + } + } + + return false; + } + + private static Account CreateAccount(NetState state, string un, string pw) + { + if (un.Length == 0 || pw.Length == 0) + { + return null; + } + + var isSafe = !(un.StartsWithOrdinal(" ") || + un.EndsWithOrdinal(" ") || + un.EndsWithOrdinal(".")); + + for (var i = 0; isSafe && i < un.Length; ++i) + { + isSafe = un[i] >= 0x20 && un[i] < 0x7F && !IsForbiddenChar(un[i]); + } + + for (var i = 0; isSafe && i < pw.Length; ++i) + { + isSafe = pw[i] >= 0x20 && pw[i] < 0x7F; + } + + if (!isSafe) + { + return null; + } + + if (!CanCreate(state.Address)) + { + logger.Information( + $"Login: {{NetState}} Account '{{Username}}' not created, ip already has {{AccountCount}} account{(MaxAccountsPerIP == 1 ? "" : "s")}.", + state, + un, + MaxAccountsPerIP + ); + + return null; + } + + logger.Information("Login: {NetState}: Creating new account '{Username}'", state, un); + + var 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; + + logger.Information("Login: {NetState}: Past IP limit threshold", e.State); + + using var op = new StreamWriter("ipLimits.log", true); + op.WriteLine($"{e.State}\tPast IP limit threshold\t{Core.Now}"); + + return; + } + + var un = e.Username; + var pw = e.Password; + + e.Accepted = false; + + if (Accounts.GetAccount(un) is not Account acct) + { + // To prevent someone from making an account of just '' or a bunch of meaningless spaces + if (AutoAccountCreation && un.Trim().Length > 0) + { + e.State.Account = acct = CreateAccount(e.State, un, pw); + e.Accepted = acct?.CheckAccess(e.State) ?? false; + + if (!e.Accepted) + { + e.RejectReason = ALRReason.BadComm; + } + } + else + { + logger.Information("Login: {NetState} Invalid username '{Username}'", e.State, un); + e.RejectReason = ALRReason.Invalid; + } + } + else if (!acct.HasAccess(e.State)) + { + logger.Information("Login: {NetState} Access denied for '{Username}'", e.State, un); + e.RejectReason = LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass; + } + else if (!acct.CheckPassword(pw)) + { + logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, un); + e.RejectReason = ALRReason.BadPass; + } + else if (acct.Banned) + { + logger.Information("Login: {NetState} Banned account '{Username}'", e.State, un); + e.RejectReason = ALRReason.Blocked; + } + else + { + logger.Information("Login: {NetState} Valid credentials for '{Username}'", 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; + + logger.Warning("Login: {NetState} Past IP limit threshold", e.State); + + using var op = new StreamWriter("ipLimits.log", true); + op.WriteLine($"{e.State}\tPast IP limit threshold\t{Core.Now}"); + + return; + } + + var un = e.Username; + var pw = e.Password; + + if (Accounts.GetAccount(un) is not Account acct) + { + e.Accepted = false; + } + else if (!acct.HasAccess(e.State)) + { + logger.Information("Login: {NetState} Access denied for '{Username}'", e.State, un); + e.Accepted = false; + } + else if (!acct.CheckPassword(pw)) + { + logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, un); + e.Accepted = false; + } + else if (acct.Banned) + { + logger.Information("Login: {NetState} Banned account '{Username}'", e.State, un); + e.Accepted = false; + } + else + { + acct.LogAccess(e.State); + + logger.Information("Login: {NetState} Account '{Username}' at character list", e.State, un); + e.State.Account = acct; + e.Accepted = true; + e.CityInfo = TileMatrix.Pre6000ClientSupport ? OldHavenStartingCities : StartingCities; + } + + if (!e.Accepted) + { + AccountAttackLimiter.RegisterInvalidAccess(e.State); + } + } + + public static bool CheckAccount(Mobile mobCheck, Mobile accCheck) + { + if (accCheck?.Account is Account a) + { + for (var i = 0; i < a.Length; ++i) + { + if (a[i] == mobCheck) { return true; } } - - return false; } - private static Account CreateAccount(NetState state, string un, string pw) - { - if (un.Length == 0 || pw.Length == 0) - { - return null; - } - - var isSafe = !(un.StartsWithOrdinal(" ") || - un.EndsWithOrdinal(" ") || - un.EndsWithOrdinal(".")); - - for (var i = 0; isSafe && i < un.Length; ++i) - { - isSafe = un[i] >= 0x20 && un[i] < 0x7F && !IsForbiddenChar(un[i]); - } - - for (var i = 0; isSafe && i < pw.Length; ++i) - { - isSafe = pw[i] >= 0x20 && pw[i] < 0x7F; - } - - if (!isSafe) - { - return null; - } - - if (!CanCreate(state.Address)) - { - logger.Information( - "Login: {0}: Account '{1}' not created, ip already has {2} account{3}.", - state, - un, - MaxAccountsPerIP, - MaxAccountsPerIP == 1 ? "" : "s" - ); - return null; - } - - logger.Information("Login: {0}: Creating new account '{1}'", state, un); - - var 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; - - logger.Information("Login: {0}: Past IP limit threshold", e.State); - - using var op = new StreamWriter("ipLimits.log", true); - op.WriteLine("{0}\tPast IP limit threshold\t{1}", e.State, Core.Now); - - return; - } - - var un = e.Username; - var pw = e.Password; - - e.Accepted = false; - - if (Accounts.GetAccount(un) is not Account acct) - { - // To prevent someone from making an account of just '' or a bunch of meaningless spaces - if (AutoAccountCreation && un.Trim().Length > 0) - { - e.State.Account = acct = CreateAccount(e.State, un, pw); - e.Accepted = acct?.CheckAccess(e.State) ?? false; - - if (!e.Accepted) - { - e.RejectReason = ALRReason.BadComm; - } - } - else - { - logger.Information("Login: {0}: Invalid username '{1}'", e.State, un); - e.RejectReason = ALRReason.Invalid; - } - } - else if (!acct.HasAccess(e.State)) - { - logger.Information("Login: {0}: Access denied for '{1}'", e.State, un); - e.RejectReason = LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass; - } - else if (!acct.CheckPassword(pw)) - { - logger.Information("Login: {0}: Invalid password for '{1}'", e.State, un); - e.RejectReason = ALRReason.BadPass; - } - else if (acct.Banned) - { - logger.Information("Login: {0}: Banned account '{1}'", e.State, un); - e.RejectReason = ALRReason.Blocked; - } - else - { - logger.Information("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; - - logger.Warning("Login: {0}: Past IP limit threshold", e.State); - - using var op = new StreamWriter("ipLimits.log", true); - op.WriteLine("{0}\tPast IP limit threshold\t{1}", e.State, Core.Now); - - return; - } - - var un = e.Username; - var pw = e.Password; - - if (Accounts.GetAccount(un) is not Account acct) - { - e.Accepted = false; - } - else if (!acct.HasAccess(e.State)) - { - logger.Information("Login: {0}: Access denied for '{1}'", e.State, un); - e.Accepted = false; - } - else if (!acct.CheckPassword(pw)) - { - logger.Information("Login: {0}: Invalid password for '{1}'", e.State, un); - e.Accepted = false; - } - else if (acct.Banned) - { - logger.Information("Login: {0}: Banned account '{1}'", e.State, un); - e.Accepted = false; - } - else - { - acct.LogAccess(e.State); - - logger.Information("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); - } - } - - public static bool CheckAccount(Mobile mobCheck, Mobile accCheck) - { - if (accCheck?.Account is Account a) - { - for (var i = 0; i < a.Length; ++i) - { - if (a[i] == mobCheck) - { - return true; - } - } - } - - return false; - } + return false; } } diff --git a/Projects/UOContent/Assistants/AssistantConfiguration.cs b/Projects/UOContent/Assistants/AssistantConfiguration.cs new file mode 100644 index 000000000..1c29e4bb0 --- /dev/null +++ b/Projects/UOContent/Assistants/AssistantConfiguration.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; +using Server.Json; + +namespace Server.Assistants; + +public static class AssistantConfiguration +{ + private const string _path = "Configuration/assistants.json"; + + public static AssistantSettings Settings { get; private set; } + + private const string _defaultWarningMessage = "The server was unable to negotiate features with your assistant. " + + "You must download and run an updated version of UOSteam" + + " or Razor." + + "

Make sure you've checked the option Negotiate features with server, " + + "once you have this box checked you may log in and play normally." + + "

You will be disconnected shortly."; + + public static void Configure() + { + var path = Path.Join(Core.BaseDirectory, _path); + + if (File.Exists(path)) + { + Settings = JsonConfig.Deserialize(path); + } + else + { + Settings = new AssistantSettings + { + WarnOnFailure = true, + KickOnFailure = true, + DisallowedFeatures = AssistantFeatures.None, + DisconnectDelay = TimeSpan.FromSeconds(15.0), + WarningMessage = _defaultWarningMessage + }; + + Save(path); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DisallowFeature(AssistantFeatures feature) => SetDisallowed(feature, true); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void AllowFeature(AssistantFeatures feature) => SetDisallowed(feature, false); + + public static void SetDisallowed(AssistantFeatures feature, bool value) + { + if (value) + { + Settings.DisallowedFeatures |= feature; + } + else + { + Settings.DisallowedFeatures &= ~feature; + } + + Save(); + } + + private static void Save(string path = null) + { + path ??= Path.Join(Core.BaseDirectory, _path); + JsonConfig.Serialize(path, Settings); + } +} + +public record AssistantSettings +{ + [JsonPropertyName("warnOnFailure")] + public bool WarnOnFailure { get; set; } + + [JsonPropertyName("kickOnFailure")] + public bool KickOnFailure { get; set; } + + [JsonPropertyName("disallowedFeatures")] + [JsonConverter(typeof(FlagsConverter))] + public AssistantFeatures DisallowedFeatures { get; set; } + + [JsonPropertyName("disconnectDelay")] + public TimeSpan DisconnectDelay { get; set; } + + [JsonPropertyName("warningMessage")] + public string WarningMessage { get; set; } +} diff --git a/Projects/UOContent/Assistants/AssistantFeatures.cs b/Projects/UOContent/Assistants/AssistantFeatures.cs new file mode 100644 index 000000000..a72566312 --- /dev/null +++ b/Projects/UOContent/Assistants/AssistantFeatures.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server.Assistants; + +[Flags] +public enum AssistantFeatures : ulong +{ + None = 0, + + FilterWeather = 1ul << 0, // Weather Filter + FilterLight = 1ul << 1, // Light Filter + SmartTarget = 1ul << 2, // Smart Last Target + RangedTarget = 1ul << 3, // Range Check Last Target + AutoOpenDoors = 1ul << 4, // Automatically Open Doors + DequipOnCast = 1ul << 5, // Unequip Weapon on spell cast + AutoPotionEquip = 1ul << 6, // Un/Re-equip weapon on potion use + PoisonedChecks = 1ul << 7, // Block heal If poisoned/Macro If Poisoned condition/Heal or Cure self + LoopedMacros = 1ul << 8, // Disallow Looping macros, For loops, and macros that call other macros + UseOnceAgent = 1ul << 9, // The use once agent + RestockAgent = 1ul << 10, // The restock agent + SellAgent = 1ul << 11, // The sell agent + BuyAgent = 1ul << 12, // The buy agent + PotionHotkeys = 1ul << 13, // All potion hotkeys + RandomTargets = 1ul << 14, // All random target hotkeys (not target next, last target, target self) + ClosestTargets = 1ul << 15, // All closest target hotkeys + OverheadHealth = 1ul << 16, // Health and Mana/Stam messages shown over player's heads + + // AssistUO Only + AutolootAgent = 1ul << 17, // The autoloot agent + + BoneCutterAgent = 1ul << 18, // The bone cutter agent + JScriptMacros = 1ul << 19, // Javascript macro engine + AutoRemount = 1ul << 20, // Auto remount after dismount + AutoBandage = 1 << 21, // Auto bandage friends, self, last and mount option + EnemyTargetShare = 1 << 22, // Enemy target share on guild, party or alliance chat + FilterSeason = 1 << 23, // Season Filter + SpellTargetShare = 1 << 24, // Spell target share on guild, party or alliance chat + + All = ~None // Every feature possible +} diff --git a/Projects/UOContent/Assistants/AssistantHandler.cs b/Projects/UOContent/Assistants/AssistantHandler.cs new file mode 100644 index 000000000..e31cc2a82 --- /dev/null +++ b/Projects/UOContent/Assistants/AssistantHandler.cs @@ -0,0 +1,157 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using Server.Gumps; +using Server.Network; + +namespace Server.Assistants; + +public static class AssistantHandler +{ + public static bool Enabled { get; private set; } + + private static Dictionary _handshakes = new(); + + public static unsafe void Configure() + { + Enabled = ServerConfiguration.GetOrUpdateSetting("assistants.enableNegotiation", false); + + EventSink.Login += OnLogin; + + IncomingPackets.Register(0xBE, 0, false, &AssistVersion); + AssistantProtocol.Register(0xFF, false, &HandshakeResponse); + } + + private static void FailedNegotiation(Mobile m) + { + var isPlayer = m.AccessLevel <= AccessLevel.Player; + var willKick = AssistantConfiguration.Settings.KickOnFailure; + var delay = AssistantConfiguration.Settings.DisconnectDelay; + + if (willKick && isPlayer && delay <= TimeSpan.Zero) + { + m.NetState.Disconnect("Failed to negotiate assistant features."); + return; + } + + if (AssistantConfiguration.Settings.WarnOnFailure) + { + var warningGump = new WarningGump( + 1060635, + 30720, + AssistantConfiguration.Settings.WarningMessage, + 0xFFC000, + 420, + 250, + null, + false + ); + + m.SendGump(warningGump); + } + + if (isPlayer) + { + _handshakes[m] = Timer.DelayCall(delay, OnForceDisconnect, m); + } + + m.NetState.LogInfo("Failed to negotiate assistant features."); + } + + private static void OnLogin(Mobile m) + { + if (m?.NetState?.Running != true || !Enabled) + { + return; + } + + m.NetState.SendAssistVersionReq(); + m.NetState.SendAssistHandshake(); + + if (_handshakes.TryGetValue(m, out var t)) + { + t?.Stop(); + } + + _handshakes[m] = Timer.DelayCall(TimeSpan.FromSeconds(30), OnTimeout, m); + } + + public static void AssistVersion(NetState state, CircularBufferReader reader, int packetLength) + { + // We are not supporting the old UOAssist protocol + // var assistVersion = reader.ReadInt32(); + // var clientVersion = reader.ReadAscii(); + + // Instead we are supporting razor community edition. + var assistVersion = reader.ReadAscii(); + state.Assistant = assistVersion.Contains(' ') ? assistVersion : $"RazorCE {assistVersion}"; + } + + private static void HandshakeResponse(NetState state, CircularBufferReader reader, int packetLength) + { + Mobile m = state.Mobile; + + if (m == null || !_handshakes.TryGetValue(m, out var t)) + { + return; + } + + t?.Stop(); + _handshakes.Remove(m); + } + + private static void OnTimeout(Mobile m) + { + if (m == null || !_handshakes.TryGetValue(m, out var t)) + { + return; + } + + t?.Stop(); + _handshakes.Remove(m); + + if (m.NetState?.Running != true || !Enabled) + { + return; + } + + FailedNegotiation(m); + } + + private static void OnForceDisconnect(Mobile m) + { + if (m == null) + { + return; + } + + m.NetState?.Disconnect($"Player {m} kicked (Failed assistant handshake)"); + _handshakes.Remove(m); + } + + public static void SendAssistVersionReq(this NetState ns) + { + if (ns.CannotSendPackets()) + { + return; + } + + ns.Send(stackalloc byte[] { 0xBE, 0x00, 0x03 }); + } + + public static void SendAssistHandshake(this NetState ns) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[12]); + writer.Write((byte)0xF0); // Packet ID + writer.Write((ushort)12); + writer.Write((byte)0xFE); // Command + writer.Write((ulong)AssistantConfiguration.Settings.DisallowedFeatures); + + ns.Send(writer.Span); + } +} diff --git a/Projects/UOContent/Assistants/AssistantProtocol.cs b/Projects/UOContent/Assistants/AssistantProtocol.cs new file mode 100644 index 000000000..90bad5eeb --- /dev/null +++ b/Projects/UOContent/Assistants/AssistantProtocol.cs @@ -0,0 +1,19 @@ +namespace Server.Network; + +public static class AssistantProtocol +{ + private static PacketHandler[] _handlers; + + public static void Configure() + { + _handlers = ProtocolExtensions.Register(new AssistantsProtocolInfo()); + } + + public static unsafe void Register(int cmd, bool ingame, delegate* onReceive) => + _handlers[cmd] = new PacketHandler(cmd, 0, ingame, onReceive); + + private struct AssistantsProtocolInfo : IProtocolExtensionsInfo + { + public int PacketId => 0xF0; + } +} diff --git a/Projects/UOContent/Commands/BoundingBoxPicker.cs b/Projects/UOContent/Commands/BoundingBoxPicker.cs index fa6c1028b..4212c87fa 100644 --- a/Projects/UOContent/Commands/BoundingBoxPicker.cs +++ b/Projects/UOContent/Commands/BoundingBoxPicker.cs @@ -37,20 +37,22 @@ namespace Server protected override void OnTarget(Mobile from, object targeted) { - if (targeted is not IPoint3D p) + if (targeted is not IPoint3D ip) { return; } - if (p is Item item) + Point3D p = ip switch { - p = item.GetWorldTop(); - } + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; 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); + from.Target = new PickTarget(p, false, from.Map, m_Callback); } else if (from.Map != m_Map) { @@ -59,7 +61,7 @@ namespace Server else if (m_Map != null && m_Map != Map.Internal && m_Callback != null) { var start = m_Store; - var end = new Point3D(p); + var end = p; Utility.FixPoints(ref start, ref end); diff --git a/Projects/UOContent/Commands/Dupe.cs b/Projects/UOContent/Commands/Dupe.cs index 3cbf9de10..3b90a0adf 100644 --- a/Projects/UOContent/Commands/Dupe.cs +++ b/Projects/UOContent/Commands/Dupe.cs @@ -47,11 +47,13 @@ namespace Server.Commands for (var i = 0; i < props.Length; i++) { + var p = props[i]; try { - if (props[i].CanRead && props[i].CanWrite) + // Do not set the parent since it screws up mobile/container totals and weights. + if (p.CanRead && p.CanWrite && p.Name != "Parent") { - props[i].SetValue(dest, props[i].GetValue(src, null), null); + p.SetValue(dest, p.GetValue(src, null), null); } } catch @@ -97,14 +99,12 @@ namespace Server.Commands if (m_InBag) { - if (copy.Parent is Container cont) + pack = copy.Parent switch { - pack = cont; - } - else if (copy.Parent is Mobile m) - { - pack = m.Backpack; - } + Container cont => cont, + Mobile m => m.Backpack, + _ => pack + }; } else { @@ -130,7 +130,6 @@ namespace Server.Commands { CopyProperties(newItem, copy); copy.OnAfterDuped(newItem); - newItem.Parent = null; if (pack != null) { @@ -141,7 +140,9 @@ namespace Server.Commands newItem.MoveToWorld(from.Location, from.Map); } + newItem.UpdateTotals(); newItem.InvalidateProperties(); + newItem.Delta(ItemDelta.Update); CommandLogging.WriteLine( from, diff --git a/Projects/UOContent/Commands/Generic/Commands/Commands.cs b/Projects/UOContent/Commands/Generic/Commands/Commands.cs index 944b267b1..6d8630976 100644 --- a/Projects/UOContent/Commands/Generic/Commands/Commands.cs +++ b/Projects/UOContent/Commands/Generic/Commands/Commands.cs @@ -554,21 +554,19 @@ namespace Server.Commands.Generic public override void Execute(CommandEventArgs e, object obj) { - if (obj is not IPoint3D p) + if (obj is not IPoint3D ip) { return; } - if (p is Item item) + Point3D p = ip switch { - p = item.GetWorldTop(); - } - else if (p is Mobile m) - { - p = m.Location; - } + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; - Add.Invoke(e.Mobile, new Point3D(p), new Point3D(p), e.Arguments); + Add.Invoke(e.Mobile, p, p, e.Arguments); } } diff --git a/Projects/UOContent/Commands/Generic/Commands/Interface.cs b/Projects/UOContent/Commands/Generic/Commands/Interface.cs index b8c4b91e0..b9af25440 100644 --- a/Projects/UOContent/Commands/Generic/Commands/Interface.cs +++ b/Projects/UOContent/Commands/Generic/Commands/Interface.cs @@ -394,7 +394,7 @@ namespace Server.Commands.Generic case 4: // Go there { m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item)); - InvokeCommand($"Go {m_Item.Serial.Value}"); + InvokeCommand($"Go {m_Item.Serial}"); break; } case 5: // Move to target @@ -567,7 +567,7 @@ namespace Server.Commands.Generic case 4: // Go there { m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile)); - InvokeCommand($"Go {m_Mobile.Serial.Value}"); + InvokeCommand($"Go {m_Mobile.Serial}"); break; } case 5: // Bring them here diff --git a/Projects/UOContent/Commands/Logging.cs b/Projects/UOContent/Commands/Logging.cs index 1ba8401d8..6fcfa65e3 100644 --- a/Projects/UOContent/Commands/Logging.cs +++ b/Projects/UOContent/Commands/Logging.cs @@ -46,7 +46,7 @@ namespace Server.Commands o switch { Mobile m => m.Account == null ? $"{m} (no account)" : $"{m} ('{m.Account.Username}')", - Item item => $"0x{item.Serial.Value:X} ({item.GetType().Name})", + Item item => $"{item.Serial} ({item.GetType().Name})", _ => o }; diff --git a/Projects/UOContent/Commands/Object Creation/Add.cs b/Projects/UOContent/Commands/Object Creation/Add.cs index a54c84e94..69aee68fd 100644 --- a/Projects/UOContent/Commands/Object Creation/Add.cs +++ b/Projects/UOContent/Commands/Object Creation/Add.cs @@ -35,22 +35,22 @@ namespace Server.Commands { var sb = new StringBuilder(); - sb.AppendFormat("{0} {1} building ", from.AccessLevel, CommandLogging.Format(from)); + sb.Append($"{from.AccessLevel} {CommandLogging.Format(from)} building "); if (start == end) { - sb.AppendFormat("at {0} in {1}", start, from.Map); + sb.Append($"at {start} in {from.Map}"); } else { - sb.AppendFormat("from {0} to {1} in {2}", start, end, from.Map); + sb.Append($"from {start} to {end} in {from.Map}"); } sb.Append(':'); for (var i = 0; i < args.Length; ++i) { - sb.AppendFormat(" \"{0}\"", args[i]); + sb.Append($" \"{args[i]}\""); } CommandLogging.WriteLine(from, sb.ToString()); @@ -321,7 +321,7 @@ namespace Server.Commands { var built = Build(from, ctor, values, props, realProps, ref sendError); - sb.AppendFormat("0x{0:X}; ", built.Serial.Value); + sb.Append($"{built.Serial}; "); if (built is Item item) { @@ -353,7 +353,7 @@ namespace Server.Commands var built = Build(from, ctor, values, props, realProps, ref sendError); - sb.AppendFormat("0x{0:X}; ", built.Serial.Value); + sb.Append($"{built.Serial}; "); if (built is Item item) { diff --git a/Projects/UOContent/Commands/Object Creation/AddGump.cs b/Projects/UOContent/Commands/Object Creation/AddGump.cs index 0cab15174..07446d1be 100644 --- a/Projects/UOContent/Commands/Object Creation/AddGump.cs +++ b/Projects/UOContent/Commands/Object Creation/AddGump.cs @@ -286,19 +286,21 @@ namespace Server.Gumps protected override void OnTarget(Mobile from, object o) { - if (o is IPoint3D p) + if (o is not IPoint3D ip) { - p = p switch - { - Item item => item.GetWorldTop(), - Mobile m => m.Location, - _ => p - }; - - Commands.Add.Invoke(from, new Point3D(p), new Point3D(p), new[] { m_Type.Name }); - - from.Target = new InternalTarget(m_Type, m_SearchResults, m_SearchString, m_Page); + return; } + + Point3D p = ip switch + { + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; + + Commands.Add.Invoke(from, new Point3D(p), new Point3D(p), new[] { m_Type.Name }); + + from.Target = new InternalTarget(m_Type, m_SearchResults, m_SearchString, m_Page); } protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) diff --git a/Projects/UOContent/Commands/Object Creation/CAGLoader.cs b/Projects/UOContent/Commands/Object Creation/CAGLoader.cs index 47c54071f..bb3329264 100644 --- a/Projects/UOContent/Commands/Object Creation/CAGLoader.cs +++ b/Projects/UOContent/Commands/Object Creation/CAGLoader.cs @@ -20,78 +20,82 @@ using System.Text.Json; using System.Text.Json.Serialization; using Server.Items; using Server.Json; +using Server.Logging; using Server.Utilities; -namespace Server.Commands +namespace Server.Commands; + +public static class CAGLoader { - public static class CAGLoader + private static readonly ILogger logger = LogFactory.GetLogger(typeof(CAGLoader)); + + public static CAGCategory Load() { - public static CAGCategory Load() + var root = new CAGCategory("Add Menu"); + var path = Path.Combine(Core.BaseDirectory, "Data/categorization.json"); + + var list = JsonConfig.Deserialize>(path); + if (list == null) { - var root = new CAGCategory("Add Menu"); - var path = Path.Combine(Core.BaseDirectory, "Data/categorization.json"); + throw new JsonException($"Failed to deserialize {path}."); + } - var list = JsonConfig.Deserialize>(path); - if (list == null) + // Not an optimized solution + foreach (var cag in list) + { + var parent = root; + // Navigate through the dot notation categories until we find the last one + var categories = cag.Category.Split("."); + for (var i = 0; i < categories.Length; i++) { - throw new JsonException($"Failed to deserialize {path}."); - } + var category = categories[i]; - // Not an optimized solution - foreach (var cag in list) - { - var parent = root; - // Navigate through the dot notation categories until we find the last one - var categories = cag.Category.Split("."); - for (var i = 0; i < categories.Length; i++) + // No children, so let's make one + if (parent.Nodes == null) { - var category = categories[i]; + var cat = new CAGCategory(category, parent); + parent.Nodes = new CAGNode[] { cat }; + parent = cat; + continue; + } - // No children, so let's make one - if (parent.Nodes == null) + var oldParent = parent; + for (var j = 0; j < parent.Nodes.Length; j++) + { + var node = parent.Nodes[j]; + if (category == node.Title && node is CAGCategory cat) { - var cat = new CAGCategory(category, parent); - parent.Nodes = new CAGNode[] { cat }; - parent = cat; - continue; - } - - var oldParent = parent; - for (var j = 0; j < parent.Nodes.Length; j++) - { - var node = parent.Nodes[j]; - if (category == node.Title && node is CAGCategory cat) - { - parent = cat; - break; - } - } - - // Didn't find the child, let's add it - if (oldParent == parent) - { - var nodes = parent.Nodes; - parent.Nodes = new CAGNode[nodes.Length + 1]; - Array.Copy(nodes, parent.Nodes, nodes.Length); - var cat = new CAGCategory(category, parent); - parent.Nodes[^1] = cat; parent = cat; + break; } } - // Set the objects associated with the child most node - parent.Nodes = new CAGNode[cag.Objects.Length]; - for (var i = 0; i < cag.Objects.Length; i++) + // Didn't find the child, let's add it + if (oldParent == parent) { - var cagObj = cag.Objects[i]; - cagObj.Parent = parent; - parent.Nodes[i] = cagObj; + var nodes = parent.Nodes; + parent.Nodes = new CAGNode[nodes.Length + 1]; + Array.Copy(nodes, parent.Nodes, nodes.Length); + var cat = new CAGCategory(category, parent); + parent.Nodes[^1] = cat; + parent = cat; + } + } - // Set ItemID and Hue - if (cagObj.Hue == null || cagObj.ItemID == null) + // Set the objects associated with the child most node + var pooledList = new List(cag.Objects.Length); + for (var i = 0; i < cag.Objects.Length; i++) + { + var cagObj = cag.Objects[i]; + cagObj.Parent = parent; + + // Set ItemID and Hue + if (cagObj.Hue == null || cagObj.ItemID == null) + { + var type = cagObj.Type; + + try { - var type = cagObj.Type; - if (type.IsAssignableTo(typeof(Item))) { var item = cagObj.Type.CreateInstance(); @@ -138,19 +142,28 @@ namespace Server.Commands m.Delete(); } } + catch (Exception e) + { + logger.Warning(e, "Failed to instantiate type {Type}.", type); + continue; + } } + + pooledList.Add(cagObj); } - return root; + parent.Nodes = pooledList.ToArray(); } - } - public record CAGJson - { - [JsonPropertyName("category")] - public string Category { get; init; } - - [JsonPropertyName("objects")] - public CAGObject[] Objects { get; init; } + return root; } } + +public record CAGJson +{ + [JsonPropertyName("category")] + public string Category { get; init; } + + [JsonPropertyName("objects")] + public CAGObject[] Objects { get; init; } +} diff --git a/Projects/UOContent/Commands/Properties.cs b/Projects/UOContent/Commands/Properties.cs index 21a3e25b0..8d888bb1f 100644 --- a/Projects/UOContent/Commands/Properties.cs +++ b/Projects/UOContent/Commands/Properties.cs @@ -72,7 +72,7 @@ namespace Server.Commands if (attr == null) { - failReason = $"Property '${propertyName}' not found."; + failReason = $"Property '{propertyName}' not found."; return null; } @@ -331,7 +331,7 @@ namespace Server.Commands return $"{p.Name} = {toString}"; } - using var builder = new ValueStringBuilder(); + using var builder = ValueStringBuilder.Create(); for (var i = 0; i < chain.Length; i++) { builder.Append(chain[i].Name); diff --git a/Projects/UOContent/Commands/Statics.cs b/Projects/UOContent/Commands/Statics.cs deleted file mode 100644 index 8a64c3021..000000000 --- a/Projects/UOContent/Commands/Statics.cs +++ /dev/null @@ -1,780 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Server.Commands; -using Server.Gumps; -using Server.Items; - -namespace Server -{ - public static class Statics - { - public delegate void FreezeCallback(Mobile from, bool okay, StateInfo si); - - 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 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?"; - - private static readonly Point3D NullP3D = new(int.MinValue, int.MinValue, int.MinValue); - - private static byte[] m_Buffer; - - private static StaticTile[] m_TileBuffer = new StaticTile[128]; - - public static void Initialize() - { - CommandSystem.Register("Freeze", AccessLevel.Administrator, Freeze_OnCommand); - CommandSystem.Register("FreezeMap", AccessLevel.Administrator, FreezeMap_OnCommand); - CommandSystem.Register("FreezeWorld", AccessLevel.Administrator, FreezeWorld_OnCommand); - - CommandSystem.Register("Unfreeze", AccessLevel.Administrator, Unfreeze_OnCommand); - CommandSystem.Register("UnfreezeMap", AccessLevel.Administrator, UnfreezeMap_OnCommand); - CommandSystem.Register("UnfreezeWorld", AccessLevel.Administrator, UnfreezeWorld_OnCommand); - } - - [Usage("Freeze")] - [Description("Makes a targeted area of dynamic items static.")] - public static void Freeze_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - BoundingBoxPicker.Begin(from, (map, start, end) => FreezeBox_Callback(from, map, start, end)); - } - - [Usage("FreezeMap")] - [Description("Makes every dynamic item in your map static.")] - public static void FreezeMap_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - var map = from.Map; - - if (map != null && map != Map.Internal) - { - SendWarning( - from, - "You are about to freeze all items in {0}.", - BaseFreezeWarning, - map, - NullP3D, - NullP3D, - 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, - FreezeWarning_Callback - ); - } - - public static void SendWarning( - Mobile m, string header, string baseWarning, Map map, Point3D start, Point3D end, - FreezeCallback callback - ) - { - m.SendGump( - new WarningGump( - 1060635, - 30720, - string.Format(baseWarning, string.Format(header, map)), - 0xFFC000, - 420, - 400, - okay => callback(m, okay, new StateInfo(map, start, end)) - ) - ); - } - - private static void FreezeBox_Callback(Mobile from, Map map, Point3D start, Point3D end) - { - SendWarning( - from, - "You are about to freeze a section of items.", - BaseFreezeWarning, - map, - start, - end, - FreezeWarning_Callback - ); - } - - private static void FreezeWarning_Callback(Mobile from, bool okay, StateInfo si) - { - if (!okay) - { - return; - } - - Freeze(from, si.m_Map, si.m_Start, si.m_End); - } - - public static void Freeze(Mobile from, Map targetMap, Point3D start3d, Point3D end3d) - { - var mapTable = new Dictionary>(); - - 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 (var item in World.Items.Values) - { - if (targetMap != null && item.Map != targetMap) - { - continue; - } - - if (item.Parent != null) - { - continue; - } - - if (item is Static or BaseFloor or BaseWall) - { - var itemMap = item.Map; - - if (itemMap == null || itemMap == Map.Internal) - { - continue; - } - - if (!mapTable.TryGetValue(itemMap, out var table)) - { - mapTable[itemMap] = table = new Dictionary(); - } - - var p = new Point2D(item.X >> 3, item.Y >> 3); - - if (!table.TryGetValue(p, out var state)) - { - 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 - ); - - var eable = - targetMap.GetItemsInBounds(new Rectangle2D(start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1)); - - foreach (var item in eable) - { - if (item is Static or BaseFloor or BaseWall) - { - var itemMap = item.Map; - - if (itemMap == null || itemMap == Map.Internal) - { - continue; - } - - if (!mapTable.TryGetValue(itemMap, out var table)) - { - mapTable[itemMap] = table = new Dictionary(); - } - - var p = new Point2D(item.X >> 3, item.Y >> 3); - - if (!table.TryGetValue(p, out var state)) - { - 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 - ) - ); - return; - } - - var badDataFile = false; - - var totalFrozen = 0; - - foreach (var de in mapTable) - { - var map = de.Key; - var table = de.Value; - - var matrix = map.Tiles; - - using var idxStream = OpenWrite(matrix.IndexStream); - using var mulStream = OpenWrite(matrix.DataStream); - if (idxStream == null || mulStream == null) - { - badDataFile = true; - continue; - } - - var idxReader = new BinaryReader(idxStream); - - var idxWriter = new BinaryWriter(idxStream); - var mulWriter = new BinaryWriter(mulStream); - - foreach (var state in table.Values) - { - var oldTiles = ReadStaticBlock( - idxReader, - mulStream, - state.m_X, - state.m_Y, - matrix.BlockWidth, - matrix.BlockHeight, - out var oldTileCount - ); - - if (oldTileCount < 0) - { - continue; - } - - var newTileCount = 0; - var newTiles = new StaticTile[state.m_List.Count]; - - for (var i = 0; i < state.m_List.Count; ++i) - { - var item = state.m_List[i]; - - var xOffset = item.X - state.m_X * 8; - var yOffset = item.Y - state.m_Y * 8; - - if (xOffset is < 0 or >= 8 || yOffset is < 0 or >= 8) - { - continue; - } - - var newTile = new StaticTile( - (ushort)item.ItemID, - (byte)xOffset, - (byte)yOffset, - (sbyte)item.Z, - (short)item.Hue - ); - - newTiles[newTileCount++] = newTile; - - item.Delete(); - - ++totalFrozen; - } - - var mulPos = -1; - var length = -1; - var extra = 0; - - if (oldTileCount + newTileCount > 0) - { - mulWriter.Seek(0, SeekOrigin.End); - - mulPos = (int)mulWriter.BaseStream.Position; - length = (oldTileCount + newTileCount) * 7; - extra = 1; - - for (var i = 0; i < oldTileCount; ++i) - { - var toWrite = oldTiles[i]; - - mulWriter.Write((ushort)toWrite.ID); - mulWriter.Write((byte)toWrite.X); - mulWriter.Write((byte)toWrite.Y); - mulWriter.Write((sbyte)toWrite.Z); - mulWriter.Write((short)toWrite.Hue); - } - - for (var i = 0; i < newTileCount; ++i) - { - var toWrite = newTiles[i]; - - mulWriter.Write((ushort)toWrite.ID); - mulWriter.Write((byte)toWrite.X); - mulWriter.Write((byte)toWrite.Y); - mulWriter.Write((sbyte)toWrite.Z); - mulWriter.Write((short)toWrite.Hue); - } - - mulWriter.Flush(); - } - - var 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 - ) - ); - } - else - { - from.SendGump( - new NoticeGump( - 1060637, - 30720, - $"Freeze operation completed successfully.

{totalFrozen} item{(totalFrozen != 1 ? "s were" : " was")} frozen.

You must restart your client and update it's data files to see the changes.", - 0xFFC000, - 320, - 240 - ) - ); - } - } - - [Usage("Unfreeze")] - [Description("Makes a targeted area of static items dynamic.")] - public static void Unfreeze_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - BoundingBoxPicker.Begin(from, (map, start, end) => UnfreezeBox_Callback(from, map, start, end)); - } - - [Usage("UnfreezeMap")] - [Description("Makes every static item in your map dynamic.")] - public static void UnfreezeMap_OnCommand(CommandEventArgs e) - { - var 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, - 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, - UnfreezeWarning_Callback - ); - } - - private static void UnfreezeBox_Callback(Mobile from, Map map, Point3D start, Point3D end) - { - SendWarning( - from, - "You are about to unfreeze a section of items.", - BaseUnfreezeWarning, - map, - start, - end, - UnfreezeWarning_Callback - ); - } - - private static void UnfreezeWarning_Callback(Mobile from, bool okay, StateInfo si) - { - if (!okay) - { - return; - } - - 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); - - var xStartBlock = start.X >> 3; - var yStartBlock = start.Y >> 3; - var xEndBlock = end.X >> 3; - var yEndBlock = end.Y >> 3; - - int xTileStart = start.X, yTileStart = start.Y; - int xTileWidth = end.X - start.X + 1, yTileHeight = end.Y - start.Y + 1; - - var matrix = map.Tiles; - - using var idxStream = OpenWrite(matrix.IndexStream); - using var mulStream = OpenWrite(matrix.DataStream); - if (idxStream == null || mulStream == null) - { - badDataFile = true; - return; - } - - var idxReader = new BinaryReader(idxStream); - - var idxWriter = new BinaryWriter(idxStream); - var mulWriter = new BinaryWriter(mulStream); - - for (var x = xStartBlock; x <= xEndBlock; ++x) - { - for (var y = yStartBlock; y <= yEndBlock; ++y) - { - var oldTiles = ReadStaticBlock( - idxReader, - mulStream, - x, - y, - matrix.BlockWidth, - matrix.BlockHeight, - out var oldTileCount - ); - - if (oldTileCount < 0) - { - continue; - } - - var newTileCount = 0; - var newTiles = new StaticTile[oldTileCount]; - - int baseX = (x << 3) - xTileStart, baseY = (y << 3) - yTileStart; - - for (var i = 0; i < oldTileCount; ++i) - { - var oldTile = oldTiles[i]; - - var px = baseX + oldTile.X; - var py = baseY + oldTile.Y; - - if (px < 0 || px >= xTileWidth || py < 0 || py >= yTileHeight) - { - newTiles[newTileCount++] = oldTile; - } - else - { - ++totalUnfrozen; - - Item item = new Static(oldTile.ID); - - item.Hue = oldTile.Hue; - - item.MoveToWorld(new Point3D(px + xTileStart, py + yTileStart, oldTile.Z), map); - } - } - - var mulPos = -1; - var length = -1; - var extra = 0; - - if (newTileCount > 0) - { - mulWriter.Seek(0, SeekOrigin.End); - - mulPos = (int)mulWriter.BaseStream.Position; - length = newTileCount * 7; - extra = 1; - - for (var i = 0; i < newTileCount; ++i) - { - var toWrite = newTiles[i]; - - mulWriter.Write((ushort)toWrite.ID); - mulWriter.Write((byte)toWrite.X); - mulWriter.Write((byte)toWrite.Y); - mulWriter.Write((sbyte)toWrite.Z); - mulWriter.Write((short)toWrite.Hue); - } - - mulWriter.Flush(); - } - - var 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) - { - var totalUnfrozen = 0; - var 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 - ) - ); - } - else - { - from.SendGump( - new NoticeGump( - 1060637, - 30720, - $"Unfreeze operation completed successfully.

{totalUnfrozen} item{(totalUnfrozen != 1 ? "s were" : " was")} unfrozen.

You must restart your client and update it's data files to see the changes.", - 0xFFC000, - 320, - 240 - ) - ); - } - } - - 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 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); - - var lookup = idxReader.ReadInt32(); - var 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]; - } - - var staTiles = m_TileBuffer; - - if (m_Buffer == null || length > m_Buffer.Length) - { - m_Buffer = GC.AllocateUninitializedArray(length); - } - - mulStream.Read(m_Buffer, 0, length); - - var index = 0; - - for (var i = 0; i < count; ++i) - { - staTiles[i] - .Set( - (ushort)(m_Buffer[index++] | (m_Buffer[index++] << 8)), - m_Buffer[index++], - m_Buffer[index++], - (sbyte)m_Buffer[index++], - (short)(m_Buffer[index++] | (m_Buffer[index++] << 8)) - ); - } - } - } - catch - { - count = -1; - } - - return m_TileBuffer; - } - - private class DeltaState - { - public readonly List m_List; - public readonly int m_X; - public readonly int m_Y; - - public DeltaState(Point2D p) - { - m_X = p.X; - m_Y = p.Y; - m_List = new List(); - } - } - - public 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; - } - } - } -} diff --git a/Projects/UOContent/Compression/TarArchive.cs b/Projects/UOContent/Compression/TarArchive.cs index 4905a3683..702866122 100755 --- a/Projects/UOContent/Compression/TarArchive.cs +++ b/Projects/UOContent/Compression/TarArchive.cs @@ -96,7 +96,7 @@ namespace Server.Compression new FileInfo(destinationArchiveFileName).EnsureDirectory(); - using var builder = new ValueStringBuilder(); + using var builder = ValueStringBuilder.Create(); var i = 0; foreach (var path in paths) { diff --git a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs index 2103ff656..42777a342 100644 --- a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using ModernUO.Serialization; namespace Server.Engines.BulkOrders { - [Serializable(1)] + [SerializationGenerator(1)] public abstract partial class BaseBOD : Item { public BaseBOD(int hue, int amountMax, bool requireExeptional, BulkMaterialType material) : this() diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs index 6ad18909a..9766a698a 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs @@ -258,11 +258,11 @@ namespace Server.Engines.BulkOrders } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1062344, Entries.Count.ToString()); // Deeds in book: ~1_val~ + list.Add(1062344, Entries.Count); // Deeds in book: ~1_val~ if (!string.IsNullOrEmpty(m_BookName)) { diff --git a/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs b/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs index b6b8c77b8..b918f3e79 100644 --- a/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Mobiles; namespace Server.Engines.BulkOrders { - [Serializable(1)] + [SerializationGenerator(1)] public abstract partial class LargeBOD : BaseBOD { [InvalidateProperties] @@ -37,7 +38,7 @@ namespace Server.Engines.BulkOrders public override int LabelNumber => 1045151; // a bulk order deed - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -53,11 +54,12 @@ namespace Server.Engines.BulkOrders list.Add(LargeBODGump.GetMaterialNumberFor(Material)); // All items must be made with x material. } - list.Add(1060656, AmountMax.ToString()); // amount to make: ~1_val~ + list.Add(1060656, AmountMax); // amount to make: ~1_val~ for (var i = 0; i < _entries.Length; ++i) { - list.Add(1060658 + i, "#{0}\t{1}", _entries[i].Details.Number, _entries[i].Amount); // ~1_val~: ~2_val~ + var entry = _entries[i]; + list.Add(1060658 + i, $"{entry.Details.Number:#}\t{entry.Amount}"); // ~1_val~: ~2_val~ } } diff --git a/Projects/UOContent/Engines/Bulk Orders/LargeSmithBOD.cs b/Projects/UOContent/Engines/Bulk Orders/LargeSmithBOD.cs index e2aace6c6..07fc832dd 100644 --- a/Projects/UOContent/Engines/Bulk Orders/LargeSmithBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/LargeSmithBOD.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Engines.BulkOrders { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeSmithBOD : LargeBOD { public static double[] m_BlacksmithMaterialChances = diff --git a/Projects/UOContent/Engines/Bulk Orders/LargeTailorBOD.cs b/Projects/UOContent/Engines/Bulk Orders/LargeTailorBOD.cs index 39d79e8e3..f8579e289 100644 --- a/Projects/UOContent/Engines/Bulk Orders/LargeTailorBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/LargeTailorBOD.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Engines.BulkOrders { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeTailorBOD : LargeBOD { public static double[] m_TailoringMaterialChances = diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs b/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs index 8f0489aa1..5d3d7c7a2 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs @@ -61,7 +61,7 @@ namespace Server.Engines.BulkOrders public override int LabelNumber => 1045151; // a bulk order deed - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -77,8 +77,8 @@ namespace Server.Engines.BulkOrders list.Add(SmallBODGump.GetMaterialNumberFor(Material)); // All items must be made with x material. } - list.Add(1060656, AmountMax.ToString()); // amount to make: ~1_val~ - list.Add(1060658, "#{0}\t{1}", m_Number, m_AmountCur); // ~1_val~: ~2_val~ + list.Add(1060656, AmountMax); // amount to make: ~1_val~ + list.Add(1060658, $"{m_Number:#}\t{m_AmountCur}"); // ~1_val~: ~2_val~ } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs b/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs index dc5b06095..d91f2a84e 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Craft; namespace Server.Engines.BulkOrders { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallSmithBOD : SmallBOD { public static double[] m_BlacksmithMaterialChances = diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs b/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs index 598779cbd..9a0c6ed13 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Craft; namespace Server.Engines.BulkOrders { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallTailorBOD : SmallBOD { public static double[] m_TailoringMaterialChances = diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index e4a68b0c5..16d89198d 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -934,22 +934,22 @@ namespace Server.Engines.CannedEvil return new Point3D(X + x, Y + y, Z - 15); } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add("champion spawn"); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList 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~ + list.Add(1060742); // active + list.Add(1060658, $"{"Type"}\t{m_Type}"); // ~1_val~: ~2_val~ + list.Add(1060659, $"{"Level"}\t{Level}"); // ~1_val~: ~2_val~ + var killRatio = 100.0 * ((double)m_Kills / MaxKills); + list.Add(1060660, $"{"Kills"}\t{m_Kills} of {MaxKills} ({killRatio:F1}%)"); // ~1_val~: ~2_val~ } else { @@ -961,11 +961,11 @@ namespace Server.Engines.CannedEvil { if (m_Active) { - LabelTo(from, "{0} (Active; Level: {1}; Kills: {2}/{3})", m_Type, Level, m_Kills, MaxKills); + LabelTo(from, $"{m_Type} (Active; Level: {Level}; Kills: {m_Kills}/{MaxKills})"); } else { - LabelTo(from, "{0} (Inactive)", m_Type); + LabelTo(from, $"{m_Type} (Inactive)"); } } diff --git a/Projects/UOContent/Engines/Chat/ChatPackets.cs b/Projects/UOContent/Engines/Chat/ChatPackets.cs index 30ad48adc..31c63bdb6 100644 --- a/Projects/UOContent/Engines/Chat/ChatPackets.cs +++ b/Projects/UOContent/Engines/Chat/ChatPackets.cs @@ -21,13 +21,13 @@ namespace Server.Engines.Chat { public static class ChatPackets { - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0xB5, 0x40, true, OpenChatWindowRequest); - IncomingPackets.Register(0xB3, 0, true, ChatAction); + IncomingPackets.Register(0xB5, 0x40, true, &OpenChatWindowRequest); + IncomingPackets.Register(0xB3, 0, true, &ChatAction); } - public static void OpenChatWindowRequest(NetState state, CircularBufferReader reader, ref int packetLength) + public static void OpenChatWindowRequest(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; @@ -48,7 +48,7 @@ namespace Server.Engines.Chat ChatUser.AddChatUser(from, chatName); } - public static void ChatAction(NetState state, CircularBufferReader reader, ref int packetLength) + public static void ChatAction(NetState state, CircularBufferReader reader, int packetLength) { if (!ChatSystem.Enabled) { @@ -73,7 +73,7 @@ namespace Server.Engines.Chat if (handler == null) { - state.LogInfo("Unknown chat action 0x{0:X}: {1}", actionID, param); + state.LogInfo($"Unknown chat action 0x{actionID:X}: {param}"); return; } @@ -103,7 +103,7 @@ namespace Server.Engines.Chat public static void SendChatMessage(this NetState ns, string lang, int number, string param1, string param2) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/ConPVP/DuelContext.cs b/Projects/UOContent/Engines/ConPVP/DuelContext.cs index 9e763c767..591f755bd 100644 --- a/Projects/UOContent/Engines/ConPVP/DuelContext.cs +++ b/Projects/UOContent/Engines/ConPVP/DuelContext.cs @@ -1817,12 +1817,18 @@ namespace Server.Engines.ConPVP public static void Debuff(Mobile mob) { - mob.RemoveStatMod("[Magic] Str Offset"); - mob.RemoveStatMod("[Magic] Dex Offset"); - mob.RemoveStatMod("[Magic] Int Offset"); + mob.RemoveStatMod("[Magic] Str Buff"); + mob.RemoveStatMod("[Magic] Dex Buff"); + mob.RemoveStatMod("[Magic] Int Buff"); + mob.RemoveStatMod("[Magic] Str Curse"); + mob.RemoveStatMod("[Magic] Dex Curse"); + mob.RemoveStatMod("[Magic] Int Curse"); mob.RemoveStatMod("Concussion"); mob.RemoveStatMod("blood-rose"); mob.RemoveStatMod("clarity-potion"); + mob.RemoveStatMod("RoseOfTrinsicPetal"); + mob.RemoveStatMod("Holy Bless"); + mob.RemoveStatMod("Holy Curse"); OrangePetals.RemoveContext(mob); @@ -2749,7 +2755,7 @@ namespace Server.Engines.ConPVP GumpWidth = 300; GumpHeight = 150; MessageColor = 0xFFC000; - MessageString = "Are you sure you wish to spectate this duel?"; + Message = "Are you sure you wish to spectate this duel?"; TitleColor = 0x7800; TitleNumber = 1062051; // Gate Warning diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs index 28a6c2794..f470806bf 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs @@ -163,23 +163,20 @@ namespace Server.Engines.ConPVP AddBorderedText(35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32); y += 20; - var sdText = "Off"; + string sdText; if (tourney.SuddenDeath > TimeSpan.Zero) { - sdText = $"{(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2}"; - - if (tourney.SuddenDeathRounds > 0) - { - sdText = $"{sdText} (first {tourney.SuddenDeathRounds} rounds)"; - } - else - { - sdText = $"{sdText} (all rounds)"; - } + sdText = tourney.SuddenDeathRounds > 0 ? + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (first {tourney.SuddenDeathRounds} rounds)" : + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (all rounds)"; + } + else + { + sdText = "Sudden Death: Off"; } - AddBorderedText(35, y, 240, 20, $"Sudden Death: {sdText}", LabelColor32, BlackColor32); + AddBorderedText(35, y, 240, 20, sdText, LabelColor32, BlackColor32); y += 20; y += 6; diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs index 04e235ac6..1bd50b67a 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs @@ -169,23 +169,20 @@ namespace Server.Engines.ConPVP AddBorderedText(35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32); y += 20; - var sdText = "Off"; + string sdText; if (tourney.SuddenDeath > TimeSpan.Zero) { - sdText = $"{(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2}"; - - if (tourney.SuddenDeathRounds > 0) - { - sdText = $"{sdText} (first {tourney.SuddenDeathRounds} rounds)"; - } - else - { - sdText = $"{sdText} (all rounds)"; - } + sdText = tourney.SuddenDeathRounds > 0 ? + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (first {tourney.SuddenDeathRounds} rounds)" : + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (all rounds)"; + } + else + { + sdText = "Sudden Death: Off"; } - AddBorderedText(35, y, 240, 20, $"Sudden Death: {sdText}", LabelColor32, BlackColor32); + AddBorderedText(35, y, 240, 20, sdText, LabelColor32, BlackColor32); y += 20; y += 6; diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs index 9dc149ce8..8ff340ed0 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs @@ -214,23 +214,20 @@ namespace Server.Engines.ConPVP AddHtml(35, y, 190, 20, $"Tiebreaker: {tieText}"); y += 20; - var sdText = "Off"; + string sdText; if (tourney.SuddenDeath > TimeSpan.Zero) { - sdText = $"{(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2}"; - - if (tourney.SuddenDeathRounds > 0) - { - sdText = $"{sdText} (first {tourney.SuddenDeathRounds} rounds)"; - } - else - { - sdText = $"{sdText} (all rounds)"; - } + sdText = tourney.SuddenDeathRounds > 0 ? + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (first {tourney.SuddenDeathRounds} rounds)" : + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (all rounds)"; + } + else + { + sdText = "Sudden Death: Off"; } - AddHtml(35, y, 240, 20, $"Sudden Death: {sdText}"); + AddHtml(35, y, 240, 20, sdText); y += 20; y += 8; diff --git a/Projects/UOContent/Engines/Craft/Core/CraftGump.cs b/Projects/UOContent/Engines/Craft/Core/CraftGump.cs index 9feeadb5a..29b1d469d 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftGump.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftGump.cs @@ -24,7 +24,7 @@ namespace Server.Engines.Craft private readonly BaseTool m_Tool; public CraftGump( - Mobile from, CraftSystem craftSystem, BaseTool tool, object notice, CraftPage page = CraftPage.None + Mobile from, CraftSystem craftSystem, BaseTool tool, TextDefinition notice, CraftPage page = CraftPage.None ) : base(40, 40) { m_From = from; @@ -48,13 +48,13 @@ namespace Server.Engines.Craft AddImageTiled(215, 37, 305, 250, 2624); AddAlphaRegion(10, 10, 510, 417); - if (craftSystem.GumpTitleNumber > 0) + if (craftSystem.GumpTitle.Number > 0) { - AddHtmlLocalized(10, 12, 510, 20, craftSystem.GumpTitleNumber, LabelColor); + AddHtmlLocalized(10, 12, 510, 20, craftSystem.GumpTitle.Number, LabelColor); } else { - AddHtml(10, 12, 510, 20, craftSystem.GumpTitleString); + AddHtml(10, 12, 510, 20, craftSystem.GumpTitle.String); } AddHtmlLocalized(10, 37, 200, 22, 1044010, LabelColor); //
CATEGORIES
@@ -106,20 +106,23 @@ namespace Server.Engines.Craft } // **************************************** - if (notice is int noticeInt && noticeInt > 0) + if (notice != null) { - AddHtmlLocalized(170, 295, 350, 40, noticeInt, LabelColor); - } - else if (notice is string) - { - AddHtml(170, 295, 350, 40, $"{notice}"); + if (notice.Number > 0) + { + AddHtmlLocalized(170, 295, 350, 40, notice.Number, LabelColor); + } + else + { + AddHtml(170, 295, 350, 40, $"{notice.String}"); + } } // If the system has more than one resource if (craftSystem.CraftSubRes.Init) { - var nameString = craftSystem.CraftSubRes.NameString; - var nameNumber = craftSystem.CraftSubRes.NameNumber; + var nameString = craftSystem.CraftSubRes.Name.String; + var nameNumber = craftSystem.CraftSubRes.Name.Number; var resIndex = context?.LastResourceIndex ?? -1; @@ -129,8 +132,8 @@ namespace Server.Engines.Craft { var subResource = craftSystem.CraftSubRes.GetAt(resIndex); - nameString = subResource.NameString; - nameNumber = subResource.NameNumber; + nameString = subResource.Name.String; + nameNumber = subResource.Name.Number; resourceType = subResource.ItemType; } @@ -162,8 +165,8 @@ namespace Server.Engines.Craft // For dragon scales if (craftSystem.CraftSubRes2.Init) { - var nameString = craftSystem.CraftSubRes2.NameString; - var nameNumber = craftSystem.CraftSubRes2.NameNumber; + var nameString = craftSystem.CraftSubRes2.Name.String; + var nameNumber = craftSystem.CraftSubRes2.Name.Number; var resIndex = context?.LastResourceIndex2 ?? -1; @@ -173,8 +176,8 @@ namespace Server.Engines.Craft { var subResource = craftSystem.CraftSubRes2.GetAt(resIndex); - nameString = subResource.NameString; - nameNumber = subResource.NameNumber; + nameString = subResource.Name.String; + nameNumber = subResource.Name.Number; resourceType = subResource.ItemType; } @@ -270,21 +273,21 @@ namespace Server.Engines.Craft AddButton(220, 60 + index * 20, 4005, 4007, GetButtonID(5, i)); - if (subResource.NameNumber > 0) + if (subResource.Name.Number > 0) { AddHtmlLocalized( 255, 63 + index * 20, 250, 18, - subResource.NameNumber, + subResource.Name.Number, resourceCount.ToString(), LabelColor ); } else { - AddLabel(255, 60 + index * 20, LabelHue, $"{subResource.NameString} ({resourceCount})"); + AddLabel(255, 60 + index * 20, LabelHue, $"{subResource.Name.String} ({resourceCount})"); } } } diff --git a/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs b/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs index ec0a307e5..ad8ccfd87 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs @@ -58,13 +58,13 @@ namespace Server.Engines.Craft AddHtmlLocalized(10, 277, 150, 22, 1044055, LabelColor); //
MATERIALS
AddHtmlLocalized(10, 362, 150, 22, 1044056, LabelColor); //
OTHER
- if (craftSystem.GumpTitleNumber > 0) + if (craftSystem.GumpTitle.Number > 0) { - AddHtmlLocalized(10, 12, 510, 20, craftSystem.GumpTitleNumber, LabelColor); + AddHtmlLocalized(10, 12, 510, 20, craftSystem.GumpTitle.Number, LabelColor); } else { - AddHtml(10, 12, 510, 20, craftSystem.GumpTitleString); + AddHtml(10, 12, 510, 20, craftSystem.GumpTitle.String); } AddButton(15, 387, 4014, 4016, 0); @@ -230,15 +230,11 @@ namespace Server.Engines.Craft for (var i = 0; i < m_CraftItem.Resources.Count - (cropScroll ? 1 : 0) && i < 4; i++) { - Type type; - string nameString; - int nameNumber; - var craftResource = m_CraftItem.Resources[i]; - type = craftResource.ItemType; - nameString = craftResource.NameString; - nameNumber = craftResource.NameNumber; + var type = craftResource.ItemType; + var nameString = craftResource.Name.String; + var nameNumber = craftResource.Name.Number; // Resource Mutation if (type == res.ResType && resIndex > -1) @@ -247,12 +243,12 @@ namespace Server.Engines.Craft type = subResource.ItemType; - nameString = subResource.NameString; + nameString = subResource.Name.String; nameNumber = subResource.GenericNameNumber; if (nameNumber <= 0) { - nameNumber = subResource.NameNumber; + nameNumber = subResource.Name.Number; } } // ****************** diff --git a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs index cece9b8dd..ea4edaf00 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Server.Commands; using Server.Factions; using Server.Items; +using Server.Logging; using Server.Mobiles; using Server.Utilities; @@ -25,6 +26,8 @@ namespace Server.Engines.Craft public class CraftItem { + private static readonly ILogger logger = LogFactory.GetLogger(typeof(CraftItem)); + private static readonly Dictionary _itemIds = new(); private static readonly int[] m_HeatSources = @@ -174,8 +177,8 @@ namespace Server.Engines.Craft { if (Recipe != null) { - Console.WriteLine( - "Warning: Attempted add of recipe #{0} to the crafting of {1} in CraftSystem {2}.", + logger.Warning( + "Attempted add of recipe #{Id} to the crafting of {ItemTypeName} in CraftSystem {CraftSystem}.", id, ItemType.Name, system @@ -190,16 +193,7 @@ namespace Server.Engines.Craft { var number = ItemIDOf(type); - if (number >= 0x4000) - { - number += 1078872; - } - else - { - number += 1020000; - } - - return number; + return number + (number >= 0x4000 ? 1078872 : 1020000); } public static int ItemIDOf(Type type) @@ -279,7 +273,7 @@ namespace Server.Engines.Craft Skills.Add(craftSkill); } - public bool ConsumeAttributes(Mobile from, ref object message, bool consume) + public bool ConsumeAttributes(Mobile from, ref TextDefinition message, bool consume) { bool consumMana; bool consumHits; @@ -393,7 +387,7 @@ namespace Server.Engines.Craft return inResourceTable; } - public bool Find(Mobile from, int[] itemIDs) + public static bool Find(Mobile from, int[] itemIDs) { var map = from.Map; @@ -449,7 +443,7 @@ namespace Server.Engines.Craft return contains; } - public bool IsQuantityType(Type[][] types) + public static bool IsQuantityType(Type[][] types) { for (int i = 0; i < types.Length; ++i) { @@ -474,6 +468,7 @@ namespace Server.Engines.Craft throw new ArgumentOutOfRangeException(nameof(types)); } + // TODO: Optimize allocation var items = new Item[types.Length][]; var totals = new int[types.Length]; @@ -487,13 +482,8 @@ namespace Server.Engines.Craft { totals[i] += items[i][j].Amount; } - else + else if (hq is not BaseBeverage beverage || beverage.Content == RequiredBeverage) { - if (hq is BaseBeverage beverage && beverage.Content != RequiredBeverage) - { - continue; - } - totals[i] += hq.Quantity; } } @@ -567,7 +557,7 @@ namespace Server.Engines.Craft } else { - if (hq is BaseBeverage beverage && beverage.Content != RequiredBeverage) + if ((hq as BaseBeverage)?.Content != RequiredBeverage) { continue; } @@ -581,13 +571,13 @@ namespace Server.Engines.Craft public bool ConsumeRes( Mobile from, Type typeRes, CraftSystem craftSystem, ref int resHue, ref int maxAmount, - ConsumeType consumeType, ref object message + ConsumeType consumeType, ref TextDefinition message ) => 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 + ConsumeType consumeType, ref TextDefinition message, bool isFailure ) { var ourPack = from.Backpack; @@ -651,14 +641,10 @@ namespace Server.Engines.Craft } } - if (types[i] == null) - { - types[i] = new[] { baseType }; - } - + types[i] ??= new[] { baseType }; amounts[i] = craftRes.Amount; - // For stackable items that can ben crafted more than one at a time + // For stackable items that can be crafted more than one at a time if (UseAllRes) { var tempAmount = ourPack.GetAmount(types[i]); @@ -671,13 +657,13 @@ namespace Server.Engines.Craft { res = Resources[i]; - if (res.MessageNumber > 0) + if (res.Message.Number > 0) { - message = res.MessageNumber; + message = res.Message.Number; } - else if (!string.IsNullOrEmpty(res.MessageString)) + else if (!string.IsNullOrEmpty(res.Message.String)) { - message = res.MessageString; + message = res.Message.String; } else { @@ -725,79 +711,50 @@ namespace Server.Engines.Craft int index; - // 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, OnResourceConsumed, CheckHueGrouping); - } - - resHue = m_ResHue; - } - // Consume Half ( for use all resource craft type ) - else if (consumeType == ConsumeType.Half) - { - for (var 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, OnResourceConsumed, CheckHueGrouping); - } - - resHue = m_ResHue; - } - else // ConstumeType.None ( it's basically used to know if the crafter has enough resource before starting the process ) + if (consumeType == ConsumeType.None) { index = -1; + var isQuantityType = IsQuantityType(types); + // TODO: Optimize this - if (IsQuantityType(types)) + for (var i = 0; i < types.Length; i++) { - for (var i = 0; i < types.Length; i++) + var quantity = isQuantityType + ? GetQuantity(ourPack, types[i]) + : ourPack.GetBestGroupAmount(types[i], true, CheckHueGrouping); + + if (quantity < amounts[i]) { - if (GetQuantity(ourPack, types[i]) < amounts[i]) + index = i; + break; + } + } + } + else + { + if (consumeType == ConsumeType.Half) + { + for (var i = 0; i < amounts.Length; i++) + { + amounts[i] /= 2; + + if (amounts[i] < 1) { - index = i; - break; - } - else - { - for (var j = 0; j < types.Length; j++) - { - if (ourPack.GetBestGroupAmount(types[j], true, CheckHueGrouping) < amounts[j]) - { - index = j; - break; - } - } + amounts[i] = 1; } } } + + m_ResHue = 0; + m_ResAmount = 0; + m_System = craftSystem; + + index = IsQuantityType(types) + ? ConsumeQuantity(ourPack, types, amounts) + : ourPack.ConsumeTotalGrouped(types, amounts, true, OnResourceConsumed, CheckHueGrouping); + + resHue = m_ResHue; } if (index == -1) @@ -812,13 +769,13 @@ namespace Server.Engines.Craft res = Resources[index]; - if (res.MessageNumber > 0) + if (res.Message.Number > 0) { - message = res.MessageNumber; + message = res.Message.Number; } - else if (!string.IsNullOrEmpty(res.MessageString)) + else if (!string.IsNullOrEmpty(res.Message.String)) { - message = res.MessageString; + message = res.Message.String; } else { @@ -842,7 +799,7 @@ namespace Server.Engines.Craft } } - private int CheckHueGrouping(Item a, Item b) => b.Hue.CompareTo(a.Hue); + private static int CheckHueGrouping(Item a, Item b) => b.Hue.CompareTo(a.Hue); public double GetExceptionalChance(CraftSystem system, double chance, Mobile from) { @@ -859,18 +816,16 @@ namespace Server.Engines.Craft bonus = talisman.ExceptionalBonus / 100.0; } - switch (system.ECA) + chance = system.ECA switch { - default: - chance -= 0.6; - break; - case CraftECA.FiftyPercentChanceMinusTenPercent: - chance = chance * 0.5 - 0.1; - break; - case CraftECA.ChanceMinusSixtyToFourtyFive: - chance -= Math.Clamp(0.60 - (from.Skills[system.MainSkill].Value - 95.0) * 0.03, 0.45, 0.60); - break; - } + CraftECA.FiftyPercentChanceMinusTenPercent => chance * 0.5 - 0.1, + CraftECA.ChanceMinusSixtyToFourtyFive => chance - Math.Clamp( + 0.60 - (from.Skills[system.MainSkill].Value - 95.0) * 0.03, + 0.45, + 0.60 + ), + _ => chance - 0.6 + }; return chance > 0 ? chance + bonus : chance; } @@ -932,24 +887,20 @@ namespace Server.Engines.Craft } } - double chance; + if (!allRequiredSkills) + { + return 0; + } - if (allRequiredSkills) - { - chance = craftSystem.GetChanceAtMin(this) + (valMainSkill - minMainSkill) / (maxMainSkill - minMainSkill) * - (1.0 - craftSystem.GetChanceAtMin(this)); - } - else - { - chance = 0.0; - } + double chance = craftSystem.GetChanceAtMin(this) + (valMainSkill - minMainSkill) / (maxMainSkill - minMainSkill) * + (1.0 - craftSystem.GetChanceAtMin(this)); if (allRequiredSkills && from.Talisman is BaseTalisman talisman && talisman.Skill == craftSystem.MainSkill) { chance += talisman.SuccessBonus / 100.0; } - if (allRequiredSkills && valMainSkill == maxMainSkill) + if (allRequiredSkills && valMainSkill >= maxMainSkill) { chance = 1.0; } @@ -959,113 +910,98 @@ namespace Server.Engines.Craft public void Craft(Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool) { - if (from.BeginAction()) - { - if (RequiredExpansion == Expansion.None || - from.NetState?.SupportsExpansion(RequiredExpansion) == true) - { - var chance = GetSuccessChance(from, typeRes, craftSystem, false, out var allRequiredSkills); - - if (allRequiredSkills && chance >= 0.0) - { - if (Recipe == null || (from as PlayerMobile)?.HasRecipe(Recipe) != false) - { - var badCraft = craftSystem.CanCraft(from, tool, ItemType); - - if (badCraft <= 0) - { - var resHue = 0; - var 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)) - { - var context = craftSystem.GetContext(from); - - context?.OnMade(this); - - var iMin = craftSystem.MinCraftEffect; - var iMax = craftSystem.MaxCraftEffect - iMin + 1; - var iRandom = Utility.Random(iMax); - iRandom += iMin + 1; - new InternalTimer(from, craftSystem, this, typeRes, tool, iRandom).Start(); - } - else - { - from.EndAction(); - from.SendGump(new CraftGump(from, craftSystem, tool, message)); - } - } - else - { - from.EndAction(); - from.SendGump(new CraftGump(from, craftSystem, tool, message)); - } - } - else - { - from.EndAction(); - from.SendGump(new CraftGump(from, craftSystem, tool, badCraft)); - } - } - else - { - from.EndAction(); - from.SendGump( - new CraftGump( - from, - craftSystem, - tool, - 1072847 // You must learn that recipe from a scroll. - ) - ); - } - } - else - { - from.EndAction(); - from.SendGump( - new CraftGump( - from, - craftSystem, - tool, - 1044153 // You don't have the required skills to attempt this item. - ) - ); - } - } - else - { - from.EndAction(); - from.SendGump( - new CraftGump( - from, - craftSystem, - tool, - RequiredExpansionMessage(RequiredExpansion) // The {0} expansion is required to attempt this item. - ) - ); - } - } - else + if (!from.BeginAction()) { from.SendLocalizedMessage(500119); // You must wait to perform another action + return; } + + if (RequiredExpansion != Expansion.None && from.NetState?.SupportsExpansion(RequiredExpansion) != true) + { + from.EndAction(); + from.SendGump( + new CraftGump( + from, + craftSystem, + tool, + // The {0} expansion is required to attempt this item. + RequiredExpansionMessage(RequiredExpansion) + ) + ); + return; + } + + var chance = GetSuccessChance(from, typeRes, craftSystem, false, out var allRequiredSkills); + + if (!allRequiredSkills || !(chance >= 0.0)) + { + from.EndAction(); + from.SendGump( + new CraftGump( + from, + craftSystem, + tool, + 1044153 // You don't have the required skills to attempt this item. + ) + ); + return; + } + + if (Recipe != null && (from as PlayerMobile)?.HasRecipe(Recipe) == false) + { + from.EndAction(); + from.SendGump( + new CraftGump( + from, + craftSystem, + tool, + 1072847 // You must learn that recipe from a scroll. + ) + ); + return; + } + + var badCraft = craftSystem.CanCraft(from, tool, ItemType); + + if (badCraft > 0) + { + from.EndAction(); + from.SendGump(new CraftGump(from, craftSystem, tool, badCraft)); + return; + } + + var resHue = 0; + var maxAmount = 0; + TextDefinition message = null; + + if (!ConsumeRes(from, typeRes, craftSystem, ref resHue, ref maxAmount, ConsumeType.None, ref message)) + { + from.EndAction(); + from.SendGump(new CraftGump(from, craftSystem, tool, message)); + return; + } + + message = null; + + if (!ConsumeAttributes(from, ref message, false)) + { + from.EndAction(); + from.SendGump(new CraftGump(from, craftSystem, tool, message)); + return; + } + + var context = craftSystem.GetContext(from); + + context?.OnMade(this); + + var iMin = craftSystem.MinCraftEffect; + var iMax = craftSystem.MaxCraftEffect - iMin + 1; + var iRandom = Utility.Random(iMax); + iRandom += iMin + 1; + new InternalTimer(from, craftSystem, this, typeRes, tool, iRandom).Start(); } - private TextDefinition RequiredExpansionMessage(Expansion expansion) + private static TextDefinition RequiredExpansionMessage(Expansion expansion) { return expansion switch { @@ -1097,7 +1033,7 @@ namespace Server.Engines.Craft } int checkResHue = 0, checkMaxAmount = 0; - object checkMessage = null; + TextDefinition checkMessage = null; // Not enough resource to craft it if (!(ConsumeRes( @@ -1115,13 +1051,13 @@ namespace Server.Engines.Craft { from.SendGump(new CraftGump(from, craftSystem, tool, checkMessage)); } - else if (checkMessage is int messageInt && messageInt > 0) + else if (checkMessage.Number > 0) { - from.SendLocalizedMessage(messageInt); + from.SendLocalizedMessage(checkMessage.Number); } else { - from.SendMessage(checkMessage.ToString()); + from.SendMessage(checkMessage.String); } return; @@ -1131,15 +1067,13 @@ namespace Server.Engines.Craft var ignored = 1; var endquality = 1; + var resHue = 0; + var maxAmount = 0; + TextDefinition message = null; + var num = 0; if (CheckSkills(from, typeRes, craftSystem, ref ignored, out var allRequiredSkills)) { - // Resource - var resHue = 0; - var maxAmount = 0; - - object message = null; - // Not enough resource to craft it if (!(ConsumeRes(from, typeRes, craftSystem, ref resHue, ref maxAmount, ConsumeType.All, ref message) && ConsumeAttributes(from, ref message, true))) @@ -1148,13 +1082,16 @@ namespace Server.Engines.Craft { from.SendGump(new CraftGump(from, craftSystem, tool, message)); } - else if (message is int messageIn && messageIn > 0) + else if (message != null) { - from.SendLocalizedMessage(messageIn); - } - else - { - from.SendMessage(message.ToString()); + if (message.Number > 0) + { + from.SendLocalizedMessage(message.Number); + } + else + { + from.SendMessage(message.String); + } } return; @@ -1162,15 +1099,13 @@ namespace Server.Engines.Craft tool.UsesRemaining--; - if (craftSystem is DefBlacksmithy) + if (craftSystem is DefBlacksmithy && + from.FindItemOnLayer(Layer.OneHanded) is AncientSmithyHammer hammer && hammer != tool) { - if (from.FindItemOnLayer(Layer.OneHanded) is AncientSmithyHammer hammer && hammer != tool) + hammer.UsesRemaining--; + if (hammer.UsesRemaining < 1) { - hammer.UsesRemaining--; - if (hammer.UsesRemaining < 1) - { - hammer.Delete(); - } + hammer.Delete(); } } @@ -1184,8 +1119,6 @@ namespace Server.Engines.Craft tool.Delete(); } - var num = 0; - Item item; if (customCraft != null) { @@ -1305,8 +1238,11 @@ namespace Server.Engines.Craft { from.SendLocalizedMessage(num); } + + return; } - else if (!allRequiredSkills) + + if (!allRequiredSkills) { if (tool?.Deleted == false && tool.UsesRemaining > 0) { @@ -1316,57 +1252,56 @@ namespace Server.Engines.Craft { from.SendLocalizedMessage(1044153); // You don't have the required skills to attempt this item. } + + return; } - else + + var consumeType = UseAllRes ? ConsumeType.Half : ConsumeType.All; + + // Not enough resource to craft it + if (!ConsumeRes(from, typeRes, craftSystem, ref resHue, ref maxAmount, consumeType, ref message, true)) { - var consumeType = UseAllRes ? ConsumeType.Half : ConsumeType.All; - var resHue = 0; - var 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?.Deleted == false && tool.UsesRemaining > 0) { - if (tool?.Deleted == false && tool.UsesRemaining > 0) + from.SendGump(new CraftGump(from, craftSystem, tool, message)); + } + else if (message != null) + { + if (message.Number > 0) { - from.SendGump(new CraftGump(from, craftSystem, tool, message)); - } - else if (message is int messageInt && messageInt > 0) - { - from.SendLocalizedMessage(messageInt); + from.SendLocalizedMessage(message.Number); } else { - from.SendMessage(message.ToString()); + from.SendMessage(message.String); } - - return; } - tool.UsesRemaining--; + return; + } - if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) - { - toolBroken = true; - } + tool.UsesRemaining--; - if (toolBroken) - { - tool.Delete(); - } + if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) + { + toolBroken = true; + } - // SkillCheck failed. - var num = craftSystem.PlayEndingEffect(from, true, true, toolBroken, endquality, false, this); + if (toolBroken) + { + tool.Delete(); + } - if (!tool.Deleted && tool.UsesRemaining > 0) - { - from.SendGump(new CraftGump(from, craftSystem, tool, num)); - } - else if (num > 0) - { - from.SendLocalizedMessage(num); - } + // SkillCheck failed. + num = craftSystem.PlayEndingEffect(from, true, true, toolBroken, endquality, false, this); + + if (!tool.Deleted && tool.UsesRemaining > 0) + { + from.SendGump(new CraftGump(from, craftSystem, tool, num)); + } + else if (num > 0) + { + from.SendLocalizedMessage(num); } } @@ -1403,88 +1338,87 @@ namespace Server.Engines.Craft if (m_iCount < m_iCountMax) { m_CraftSystem.PlayCraftEffect(m_From); + return; } - else + + m_From.EndAction(); + + var badCraft = m_CraftSystem.CanCraft(m_From, m_Tool, m_CraftItem.ItemType); + + if (badCraft > 0) { - m_From.EndAction(); - - var badCraft = m_CraftSystem.CanCraft(m_From, m_Tool, m_CraftItem.ItemType); - - if (badCraft > 0) + if (m_Tool?.Deleted == false && m_Tool.UsesRemaining > 0) { - if (m_Tool?.Deleted == false && m_Tool.UsesRemaining > 0) - { - m_From.SendGump(new CraftGump(m_From, m_CraftSystem, m_Tool, badCraft)); - } - else - { - m_From.SendLocalizedMessage(badCraft); - } - - return; - } - - var quality = 1; - - m_CraftItem.CheckSkills(m_From, m_TypeRes, m_CraftSystem, ref quality, out _, false); - - var context = m_CraftSystem.GetContext(m_From); - - if (context == null) - { - return; - } - - if (typeof(CustomCraft).IsAssignableFrom(m_CraftItem.ItemType)) - { - try - { - m_CraftItem.ItemType.CreateInstance( - m_From, - m_CraftItem, - m_CraftSystem, - m_TypeRes, - m_Tool, - quality - )?.EndCraftAction(); - } - catch (Exception e) - { - Console.WriteLine(e); - } - - return; - } - - var 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 - ) - ); + m_From.SendGump(new CraftGump(m_From, m_CraftSystem, m_Tool, badCraft)); } else { - if (context.MarkOption == CraftMarkOption.DoNotMark) - { - makersMark = false; - } - - m_CraftItem.CompleteCraft(quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null); + m_From.SendLocalizedMessage(badCraft); } + + return; + } + + var quality = 1; + + m_CraftItem.CheckSkills(m_From, m_TypeRes, m_CraftSystem, ref quality, out _, false); + + var context = m_CraftSystem.GetContext(m_From); + + if (context == null) + { + return; + } + + if (typeof(CustomCraft).IsAssignableFrom(m_CraftItem.ItemType)) + { + try + { + m_CraftItem.ItemType.CreateInstance( + m_From, + m_CraftItem, + m_CraftSystem, + m_TypeRes, + m_Tool, + quality + )?.EndCraftAction(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + + return; + } + + var 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); } } } diff --git a/Projects/UOContent/Engines/Craft/Core/CraftRes.cs b/Projects/UOContent/Engines/Craft/Core/CraftRes.cs index b4f60eb5c..dc6657b97 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftRes.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftRes.cs @@ -9,34 +9,27 @@ namespace Server.Engines.Craft ItemType = type; Amount = amount; - NameNumber = name; - MessageNumber = message; - - NameString = name; - MessageString = message; + Name = name; + Message = message; } public Type ItemType { get; } - public string MessageString { get; } + public TextDefinition Message { get; } - public int MessageNumber { get; } - - public string NameString { get; } - - public int NameNumber { get; } + public TextDefinition Name { get; } public int Amount { get; } public void SendMessage(Mobile from) { - if (MessageNumber > 0) + if (Message?.Number > 0) { - from.SendLocalizedMessage(MessageNumber); + from.SendLocalizedMessage(Message.Number); } - else if (!string.IsNullOrEmpty(MessageString)) + else if (!string.IsNullOrEmpty(Message?.String)) { - from.SendMessage(MessageString); + from.SendMessage(Message.String); } else { diff --git a/Projects/UOContent/Engines/Craft/Core/CraftSubRes.cs b/Projects/UOContent/Engines/Craft/Core/CraftSubRes.cs index c0c552029..69d4768b2 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftSubRes.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftSubRes.cs @@ -4,7 +4,7 @@ namespace Server.Engines.Craft { public class CraftSubRes { - public CraftSubRes(Type type, TextDefinition name, double reqSkill, object message) : this( + public CraftSubRes(Type type, TextDefinition name, double reqSkill, TextDefinition message) : this( type, name, reqSkill, @@ -14,11 +14,10 @@ namespace Server.Engines.Craft { } - public CraftSubRes(Type type, TextDefinition name, double reqSkill, int genericNameNumber, object message) + public CraftSubRes(Type type, TextDefinition name, double reqSkill, int genericNameNumber, TextDefinition message) { ItemType = type; - NameNumber = name; - NameString = name; + Name = name; RequiredSkill = reqSkill; GenericNameNumber = genericNameNumber; Message = message; @@ -26,13 +25,11 @@ namespace Server.Engines.Craft public Type ItemType { get; } - public string NameString { get; } - - public int NameNumber { get; } + public TextDefinition Name { get; } public int GenericNameNumber { get; } - public object Message { get; } + public TextDefinition Message { get; } public double RequiredSkill { get; } } diff --git a/Projects/UOContent/Engines/Craft/Core/CraftSubResCol.cs b/Projects/UOContent/Engines/Craft/Core/CraftSubResCol.cs index 2079f9e0b..b52ad86ad 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftSubResCol.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftSubResCol.cs @@ -11,9 +11,7 @@ namespace Server.Engines.Craft public Type ResType { get; set; } - public string NameString { get; set; } - - public int NameNumber { get; set; } + public TextDefinition Name { get; set; } public CraftSubRes GetAt(int index) => this[index]; diff --git a/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs b/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs index 4b00e03e3..1805aeed3 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs @@ -50,8 +50,7 @@ namespace Server.Engines.Craft public abstract SkillName MainSkill { get; } - public virtual int GumpTitleNumber => 0; - public virtual string GumpTitleString => ""; + public virtual TextDefinition GumpTitle => 0; public virtual CraftECA ECA => CraftECA.ChanceMinusSixty; @@ -269,65 +268,39 @@ namespace Server.Engines.Craft CraftItems[index].ForceNonExceptional = true; } - public void SetSubRes(Type type, string name) + public void SetSubRes(Type type, TextDefinition name) { CraftSubRes.ResType = type; - CraftSubRes.NameString = name; + CraftSubRes.Name = name; CraftSubRes.Init = true; } - public void SetSubRes(Type type, int name) - { - CraftSubRes.ResType = type; - CraftSubRes.NameNumber = name; - CraftSubRes.Init = true; - } - - public void AddSubRes(Type type, int name, double reqSkill, object message) - { - var craftSubRes = new CraftSubRes(type, name, reqSkill, message); - CraftSubRes.Add(craftSubRes); - } - - public void AddSubRes(Type type, int name, double reqSkill, int genericName, object message) + public void AddSubRes(Type type, TextDefinition name, double reqSkill, int genericName, TextDefinition message) { var craftSubRes = new CraftSubRes(type, name, reqSkill, genericName, message); CraftSubRes.Add(craftSubRes); } - public void AddSubRes(Type type, string name, double reqSkill, object message) + public void AddSubRes(Type type, TextDefinition name, double reqSkill, TextDefinition message) { var craftSubRes = new CraftSubRes(type, name, reqSkill, message); CraftSubRes.Add(craftSubRes); } - public void SetSubRes2(Type type, string name) + public void SetSubRes2(Type type, TextDefinition name) { CraftSubRes2.ResType = type; - CraftSubRes2.NameString = name; + CraftSubRes2.Name = name; CraftSubRes2.Init = true; } - public void SetSubRes2(Type type, int name) - { - CraftSubRes2.ResType = type; - CraftSubRes2.NameNumber = name; - CraftSubRes2.Init = true; - } - - public void AddSubRes2(Type type, int name, double reqSkill, object message) - { - var craftSubRes = new CraftSubRes(type, name, reqSkill, message); - CraftSubRes2.Add(craftSubRes); - } - - public void AddSubRes2(Type type, int name, double reqSkill, int genericName, object message) + public void AddSubRes2(Type type, TextDefinition name, double reqSkill, int genericName, TextDefinition message) { var craftSubRes = new CraftSubRes(type, name, reqSkill, genericName, message); CraftSubRes2.Add(craftSubRes); } - public void AddSubRes2(Type type, string name, double reqSkill, object message) + public void AddSubRes2(Type type, TextDefinition name, double reqSkill, TextDefinition message) { var craftSubRes = new CraftSubRes(type, name, reqSkill, message); CraftSubRes2.Add(craftSubRes); diff --git a/Projects/UOContent/Engines/Craft/Core/Enhance.cs b/Projects/UOContent/Engines/Craft/Core/Enhance.cs index c5a6c0377..2ba478f5d 100644 --- a/Projects/UOContent/Engines/Craft/Core/Enhance.cs +++ b/Projects/UOContent/Engines/Craft/Core/Enhance.cs @@ -22,7 +22,7 @@ namespace Server.Engines.Craft { public static EnhanceResult Invoke( Mobile from, CraftSystem craftSystem, BaseTool tool, Item item, - CraftResource resource, Type resType, ref object resMessage + CraftResource resource, Type resType, ref TextDefinition resMessage ) { if (item == null) @@ -410,7 +410,7 @@ namespace Server.Engines.Craft { if (targeted is Item item) { - object message = null; + TextDefinition message = null; var res = Enhance.Invoke( from, m_CraftSystem, diff --git a/Projects/UOContent/Engines/Craft/Core/Recipes.cs b/Projects/UOContent/Engines/Craft/Core/Recipes.cs index 1f5b491a7..7af458ef0 100644 --- a/Projects/UOContent/Engines/Craft/Core/Recipes.cs +++ b/Projects/UOContent/Engines/Craft/Core/Recipes.cs @@ -3,99 +3,96 @@ using System.Collections.Generic; using Server.Mobiles; using Server.Targeting; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class Recipe { - public class Recipe + private TextDefinition _td; + + public Recipe(int id, CraftSystem system, CraftItem item) { - private TextDefinition m_TD; + ID = id; + CraftSystem = system; + CraftItem = item; - public Recipe(int id, CraftSystem system, CraftItem item) + if (!Recipes.TryAdd(id, this)) { - ID = id; - CraftSystem = system; - CraftItem = item; + throw new Exception("Attempting to create recipe with preexisting ID."); + } - if (Recipes.ContainsKey(id)) + LargestRecipeID = Math.Max(id, LargestRecipeID); + } + + public static Dictionary Recipes { get; } = new(); + + public static int LargestRecipeID { get; private set; } + + public CraftSystem CraftSystem { get; set; } + + public CraftItem CraftItem { get; set; } + + public int ID { get; } + + public TextDefinition TextDefinition => _td ??= new TextDefinition(CraftItem.NameNumber, CraftItem.NameString); + + public static void Initialize() + { + CommandSystem.Register("LearnAllRecipes", AccessLevel.GameMaster, LearnAllRecipes_OnCommand); + CommandSystem.Register("ForgetAllRecipes", AccessLevel.GameMaster, ForgetAllRecipes_OnCommand); + } + + [Usage("LearnAllRecipes"), Description("Teaches a player all available recipes.")] + private static void LearnAllRecipes_OnCommand(CommandEventArgs e) + { + var m = e.Mobile; + m.SendMessage("Target a player to teach them all of the recipes."); + + m.BeginTarget( + -1, + false, + TargetFlags.None, + (from, targeted) => { - throw new Exception("Attempting to create recipe with preexisting ID."); + if (targeted is PlayerMobile mobile) + { + foreach (var kvp in Recipes) + { + mobile.AcquireRecipe(kvp.Key); + } + + from.SendMessage("You teach them all of the recipes."); + } + else + { + from.SendMessage("That is not a player!"); + } } + ); + } - Recipes.Add(id, this); - LargestRecipeID = Math.Max(id, LargestRecipeID); - } + [Usage("ForgetAllRecipes"), Description("Makes a player forget all the recipes they've learned.")] + private static void ForgetAllRecipes_OnCommand(CommandEventArgs e) + { + var m = e.Mobile; + m.SendMessage("Target a player to have them forget all of the recipes they've learned."); - public static Dictionary Recipes { get; } = new(); - - public static int LargestRecipeID { get; private set; } - - public CraftSystem CraftSystem { get; set; } - - public CraftItem CraftItem { get; set; } - - public int ID { get; } - - public TextDefinition TextDefinition => - m_TD ?? (m_TD = new TextDefinition(CraftItem.NameNumber, CraftItem.NameString)); - - public static void Initialize() - { - CommandSystem.Register("LearnAllRecipes", AccessLevel.GameMaster, LearnAllRecipes_OnCommand); - CommandSystem.Register("ForgetAllRecipes", AccessLevel.GameMaster, ForgetAllRecipes_OnCommand); - } - - [Usage("LearnAllRecipes"), Description("Teaches a player all available recipes.")] - private static void LearnAllRecipes_OnCommand(CommandEventArgs e) - { - var m = e.Mobile; - m.SendMessage("Target a player to teach them all of the recipes."); - - m.BeginTarget( - -1, - false, - TargetFlags.None, - (from, targeted) => + m.BeginTarget( + -1, + false, + TargetFlags.None, + (from, targeted) => + { + if (targeted is PlayerMobile mobile) { - if (targeted is PlayerMobile mobile) - { - foreach (var kvp in Recipes) - { - mobile.AcquireRecipe(kvp.Key); - } + mobile.ResetRecipes(); - from.SendMessage("You teach them all of the recipes."); - } - else - { - from.SendMessage("That is not a player!"); - } + from.SendMessage("They forget all their recipes."); } - ); - } - - [Usage("ForgetAllRecipes"), Description("Makes a player forget all the recipes they've learned.")] - private static void ForgetAllRecipes_OnCommand(CommandEventArgs e) - { - var m = e.Mobile; - m.SendMessage("Target a player to have them forget all of the recipes they've learned."); - - m.BeginTarget( - -1, - false, - TargetFlags.None, - (from, targeted) => + else { - if (targeted is PlayerMobile mobile) - { - mobile.ResetRecipes(); - - from.SendMessage("They forget all their recipes."); - } - else - { - from.SendMessage("That is not a player!"); - } + from.SendMessage("That is not a player!"); } - ); - } + } + ); } } diff --git a/Projects/UOContent/Engines/Craft/DefAlchemy.cs b/Projects/UOContent/Engines/Craft/DefAlchemy.cs index 0d1b66311..fbeaa3cd2 100644 --- a/Projects/UOContent/Engines/Craft/DefAlchemy.cs +++ b/Projects/UOContent/Engines/Craft/DefAlchemy.cs @@ -1,315 +1,317 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefAlchemy : CraftSystem { - public class DefAlchemy : CraftSystem + private static readonly Type typeofPotion = typeof(BasePotion); + + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefAlchemy(); + } - private static readonly Type typeofPotion = typeof(BasePotion); + private DefAlchemy() : base(1, 1, 1.25) + { + } - private DefAlchemy() : base(1, 1, 1.25) // base( 1, 1, 3.1 ) + public override SkillName MainSkill => SkillName.Alchemy; + + public override TextDefinition GumpTitle => 1044001; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) => 0.0; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Alchemy; - - public override int GumpTitleNumber => 1044001; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefAlchemy(); - - public override double GetChanceAtMin(CraftItem item) => 0.0; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckAccessible(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + return 1044263; // The tool must be on your person to use. } - public override void PlayCraftEffect(Mobile from) + return 0; + } + + public override void PlayCraftEffect(Mobile from) + { + from.PlaySound(0x242); + } + + public static bool IsPotion(Type type) => typeofPotion.IsAssignableFrom(type); + + public override int PlayEndingEffect( + Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, + bool makersMark, CraftItem item + ) + { + if (toolBroken) { - from.PlaySound(0x242); + from.SendLocalizedMessage(1044038); // You have worn out your tool } - public static bool IsPotion(Type type) => typeofPotion.IsAssignableFrom(type); - - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (failed) { - 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. - } - - return 1044043; // You failed to create the item, and some of your materials are lost. - } - - 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. - } - - return 500279; // You pour the potion into a bottle... + from.AddToBackpack(new Bottle()); + return 500287; // You fail to create a useful potion. } - return 1044154; // You create the item. + return 1044043; // You failed to create the item, and some of your materials are lost. } - public override void InitCraftList() + from.PlaySound(0x240); // Sound of a filling bottle + + if (IsPotion(item.ItemType)) { - int index; + if (quality == -1) + { + return 1048136; // You create the potion and pour it into a keg. + } - // 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); + return 500279; // You pour the potion into a bottle... + } - // 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); + return 1044154; // You create the item. + } - // Nightsight Potion - index = AddCraft( - typeof(NightSightPotion), - 1044532, - 1044542, - -25.0, - 25.0, - typeof(SpidersSilk), - 1044360, - 1, - 1044368 - ); - AddRes(index, typeof(Bottle), 1044529, 1, 500315); + public override void InitCraftList() + { + int index; - // 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); + // 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); - // 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); + // 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); - // Poison Potion + // 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); + + // Conflagration Potions 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, + typeof(ConflagrationPotion), + 1044109, + 1072096, 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, + typeof(GraveDust), + 1023983, 5, - 1044367 + 1044253 ); AddRes(index, typeof(Bottle), 1044529, 1, 500315); + SetNeededExpansion(index, Expansion.SE); index = AddCraft( - typeof(GreaterExplosionPotion), - 1044537, - 1044557, + typeof(GreaterConflagrationPotion), + 1044109, + 1072099, 65.0, 115.0, - typeof(SulfurousAsh), - 1044359, + typeof(GraveDust), + 1023983, 10, - 1044367 + 1044253 ); 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); - - // Conflagration Potions - index = AddCraft( - typeof(ConflagrationPotion), - 1044109, - 1072096, - 55.0, - 105.0, - typeof(GraveDust), - 1023983, - 5, - 1044253 - ); - AddRes(index, typeof(Bottle), 1044529, 1, 500315); - SetNeededExpansion(index, Expansion.SE); - index = AddCraft( - typeof(GreaterConflagrationPotion), - 1044109, - 1072099, - 65.0, - 115.0, - typeof(GraveDust), - 1023983, - 10, - 1044253 - ); - AddRes(index, typeof(Bottle), 1044529, 1, 500315); - SetNeededExpansion(index, Expansion.SE); - // Confusion Blast Potions - index = AddCraft( - typeof(ConfusionBlastPotion), - 1044109, - 1072106, - 55.0, - 105.0, - typeof(PigIron), - 1023978, - 5, - 1044253 - ); - AddRes(index, typeof(Bottle), 1044529, 1, 500315); - SetNeededExpansion(index, Expansion.SE); - index = AddCraft( - typeof(GreaterConfusionBlastPotion), - 1044109, - 1072109, - 65.0, - 115.0, - typeof(PigIron), - 1023978, - 10, - 1044253 - ); - AddRes(index, typeof(Bottle), 1044529, 1, 500315); - SetNeededExpansion(index, Expansion.SE); - } + SetNeededExpansion(index, Expansion.SE); + // Confusion Blast Potions + index = AddCraft( + typeof(ConfusionBlastPotion), + 1044109, + 1072106, + 55.0, + 105.0, + typeof(PigIron), + 1023978, + 5, + 1044253 + ); + AddRes(index, typeof(Bottle), 1044529, 1, 500315); + SetNeededExpansion(index, Expansion.SE); + index = AddCraft( + typeof(GreaterConfusionBlastPotion), + 1044109, + 1072109, + 65.0, + 115.0, + typeof(PigIron), + 1023978, + 10, + 1044253 + ); + AddRes(index, typeof(Bottle), 1044529, 1, 500315); + SetNeededExpansion(index, Expansion.SE); } } } diff --git a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs index b470983f2..bf1113c22 100644 --- a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs +++ b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs @@ -1,725 +1,715 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefBlacksmithy : CraftSystem { - public class DefBlacksmithy : CraftSystem + private static readonly Type typeofAnvil = typeof(AnvilAttribute); + private static readonly Type typeofForge = typeof(ForgeAttribute); + + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefBlacksmithy(); + } - private static readonly Type typeofAnvil = typeof(AnvilAttribute); - private static readonly Type typeofForge = typeof(ForgeAttribute); + private DefBlacksmithy() : base(1, 1, 1.25) + { + } - private DefBlacksmithy() : base(1, 1, 1.25) // base( 1, 2, 1.7 ) + public override SkillName MainSkill => SkillName.Blacksmith; + + public override TextDefinition GumpTitle => 1044002; + + public static CraftSystem CraftSystem { get; private set; } + + public override CraftECA ECA => CraftECA.ChanceMinusSixtyToFourtyFive; + + public override double GetChanceAtMin(CraftItem item) => 0.0; + + public static void CheckAnvilAndForge(Mobile from, int range, out bool anvil, out bool forge) + { + anvil = false; + forge = false; + + var map = from.Map; + + if (map == null) { - /* - - 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. - - */ + return; } - public override SkillName MainSkill => SkillName.Blacksmith; + var eable = map.GetItemsInRange(from.Location, range); - public override int GumpTitleNumber => 1044002; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefBlacksmithy(); - - public override CraftECA ECA => CraftECA.ChanceMinusSixtyToFourtyFive; - - public override double GetChanceAtMin(CraftItem item) => 0.0; - - public static void CheckAnvilAndForge(Mobile from, int range, out bool anvil, out bool forge) + foreach (var item in eable) { - anvil = false; - forge = false; + var type = item.GetType(); - var map = from.Map; + var isAnvil = type.IsDefined(typeofAnvil, false) || item.ItemID is 4015 or 4016 or 11733 or 11734; + var isForge = type.IsDefined(typeofForge, false) || item.ItemID is 4017 or >= 6522 and <= 6569 or 11736; - if (map == null) + if (isAnvil || isForge) { - return; - } - - var eable = map.GetItemsInRange(from.Location, range); - - foreach (var item in eable) - { - var type = item.GetType(); - - var isAnvil = type.IsDefined(typeofAnvil, false) || item.ItemID is 4015 or 4016 or 11733 or 11734; - var isForge = type.IsDefined(typeofForge, false) || item.ItemID is 4017 or >= 6522 and <= 6569 or 11736; - - if (isAnvil || isForge) + if (from.Z + 16 < item.Z || item.Z + 16 < from.Z || !from.InLOS(item)) { - if (from.Z + 16 < item.Z || item.Z + 16 < from.Z || !from.InLOS(item)) - { - continue; - } + continue; + } - anvil = anvil || isAnvil; - forge = forge || isForge; + anvil = anvil || isAnvil; + forge = forge || isForge; - if (anvil && forge) - { - break; - } + if (anvil && forge) + { + break; } } + } - eable.Free(); + eable.Free(); - for (var x = -range; (!anvil || !forge) && x <= range; ++x) + for (var x = -range; (!anvil || !forge) && x <= range; ++x) + { + for (var y = -range; (!anvil || !forge) && y <= range; ++y) { - for (var y = -range; (!anvil || !forge) && y <= range; ++y) + var tiles = map.Tiles.GetStaticTiles(from.X + x, from.Y + y, true); + + for (var i = 0; (!anvil || !forge) && i < tiles.Length; ++i) { - var tiles = map.Tiles.GetStaticTiles(from.X + x, from.Y + y, true); + var id = tiles[i].ID; - for (var i = 0; (!anvil || !forge) && i < tiles.Length; ++i) + var isAnvil = id is 4015 or 4016 or 11733 or 11734; + var isForge = id is 4017 or >= 6522 and <= 6569 or 11736; + + if (isAnvil || isForge) { - var id = tiles[i].ID; - - var isAnvil = id is 4015 or 4016 or 11733 or 11734; - var isForge = id is 4017 or >= 6522 and <= 6569 or 11736; - - 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))) { - 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; + continue; } + + anvil = anvil || isAnvil; + forge = forge || isForge; } } } } + } - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckTool(tool, from)) - { - return 1048146; // If you have a tool equipped, you must use that tool. - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - CheckAnvilAndForge(from, 2, out var anvil, out var forge); - - if (anvil && forge) - { - return 0; - } - - return 1044267; // You must be near an anvil and a forge to smith items. + return 1044038; // You have worn out your tool! } - public override void PlayCraftEffect(Mobile from) + if (!BaseTool.CheckTool(tool, from)) { - // To animate and synchronize with sound - // if (from.Body.Type == BodyType.Human && !from.Mounted) - // from.Animate( 9, 5, 1, true, false, 0 ); - // 0.7 second delay - from.PlaySound(0x2A); + return 1048146; // If you have a tool equipped, you must use that tool. } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (!BaseTool.CheckAccessible(tool, from)) { - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 1044263; // The tool must be on your person to use. } - public override void InitCraftList() + CheckAnvilAndForge(from, 2, out var anvil, out var forge); + + if (anvil && forge) { - /* - Syntax for a SIMPLE craft item - AddCraft( ObjectType, Group, MinSkill, MaxSkill, ResourceType, Amount, Message ) + return 0; + } - ObjectType : The type of the object you want to add to the build list. - Group : The group in which the object will be showed in the craft menu. - MinSkill : The minimum of skill value - MaxSkill : The maximum of skill value - ResourceType : The type of the resource the mobile need to create the item - Amount : The amount of the ResourceType it need to create the item - Message : String or Int for Localized. The message that will be sent to the mobile, if the specified resource is missing. + return 1044267; // You must be near an anvil and a forge to smith items. + } - Syntax for a COMPLEX craft item. A complex item is an item that need either more than - only one skill, or more than only one resource. + public override void PlayCraftEffect(Mobile from) + { + // To animate and synchronize with sound + // if (from.Body.Type == BodyType.Human && !from.Mounted) + // from.Animate( 9, 5, 1, true, false, 0 ); + // 0.7 second delay + from.PlaySound(0x2A); + } - Coming soon.... - */ + 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 + } - 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); - - 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); - - int index; - - 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 + if (failed) + { + if (lostMaterial) { - AddCraft(typeof(DragonBardingDeed), 1011078, 1053012, 72.5, 122.5, typeof(IronIngot), 1044036, 750, 1044037); + return 1044043; // You failed to create the item, and some of your materials are lost. } - if (Core.SE) - { - index = AddCraft(typeof(PlateMempo), 1011078, 1030180, 80.0, 130.0, typeof(IronIngot), 1044036, 18, 1044037); - SetNeededExpansion(index, Expansion.SE); + return 1044157; // You failed to create the item, but no materials were lost. + } - index = AddCraft(typeof(PlateDo), 1011078, 1030184, 87.0, 137.0, typeof(IronIngot), 1044036, 28, 1044037); - SetNeededExpansion(index, Expansion.SE); + if (quality == 0) + { + return 502785; // You were barely able to make this item. It's quality is below average. + } - index = AddCraft(typeof(PlateHiroSode), 1011078, 1030187, 80.0, 130.0, typeof(IronIngot), 1044036, 16, 1044037); - SetNeededExpansion(index, Expansion.SE); + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } - index = AddCraft(typeof(PlateSuneate), 1011078, 1030195, 65.0, 115.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } - index = AddCraft(typeof(PlateHaidate), 1011078, 1030200, 65.0, 115.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); - } + return 1044154; // You create the item. + } - 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); + public override void InitCraftList() + { + /* + Syntax for a SIMPLE craft item + AddCraft( ObjectType, Group, MinSkill, MaxSkill, ResourceType, Amount, Message ) - if (Core.SE) - { - index = AddCraft(typeof(ChainHatsuburi), 1011079, 1030175, 30.0, 80.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); + ObjectType : The type of the object you want to add to the build list. + Group : The group in which the object will be showed in the craft menu. + MinSkill : The minimum of skill value + MaxSkill : The maximum of skill value + ResourceType : The type of the resource the mobile need to create the item + Amount : The amount of the ResourceType it need to create the item + Message : String or Int for Localized. The message that will be sent to the mobile, if the specified resource is missing. - index = AddCraft(typeof(PlateHatsuburi), 1011079, 1030176, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); + Syntax for a COMPLEX craft item. A complex item is an item that need either more than + only one skill, or more than only one resource. - index = AddCraft(typeof(HeavyPlateJingasa), 1011079, 1030178, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); + Coming soon.... + */ - index = AddCraft(typeof(LightPlateJingasa), 1011079, 1030188, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); + 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); - index = AddCraft(typeof(SmallPlateJingasa), 1011079, 1030191, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); - SetNeededExpansion(index, Expansion.SE); + 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); - index = AddCraft(typeof(DecorativePlateKabuto), 1011079, 1030179, 90.0, 140.0, typeof(IronIngot), 1044036, 25, 1044037); - SetNeededExpansion(index, Expansion.SE); + int index; - index = AddCraft(typeof(PlateBattleKabuto), 1011079, 1030192, 90.0, 140.0, typeof(IronIngot), 1044036, 25, 1044037); - SetNeededExpansion(index, Expansion.SE); + 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); - index = AddCraft(typeof(StandardPlateKabuto), 1011079, 1030196, 90.0, 140.0, typeof(IronIngot), 1044036, 25, 1044037); - SetNeededExpansion(index, Expansion.SE); + if (Core.AOS) // exact pre-aos functionality unknown + { + AddCraft(typeof(DragonBardingDeed), 1011078, 1053012, 72.5, 122.5, typeof(IronIngot), 1044036, 750, 1044037); + } - if (Core.ML) - { - index = AddCraft(typeof(Circlet), 1011079, 1032645, 62.1, 112.1, typeof(IronIngot), 1044036, 6, 1044037); - SetNeededExpansion(index, Expansion.ML); + 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(RoyalCirclet), 1011079, 1032646, 70.0, 120.0, typeof(IronIngot), 1044036, 6, 1044037); - SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(PlateDo), 1011078, 1030184, 87.0, 137.0, typeof(IronIngot), 1044036, 28, 1044037); + SetNeededExpansion(index, Expansion.SE); - 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); - } - } + index = AddCraft(typeof(PlateHiroSode), 1011078, 1030187, 80.0, 130.0, typeof(IronIngot), 1044036, 16, 1044037); + SetNeededExpansion(index, Expansion.SE); - 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); + index = AddCraft(typeof(PlateSuneate), 1011078, 1030195, 65.0, 115.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.SE); - 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); - } + index = AddCraft(typeof(PlateHaidate), 1011078, 1030200, 65.0, 115.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.SE); + } - if (Core.AOS) - { - AddCraft(typeof(BoneHarvester), 1011081, 1029915, 33.0, 83.0, typeof(IronIngot), 1044036, 10, 1044037); - } + 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); - AddCraft(typeof(Broadsword), 1011081, 1023934, 35.4, 85.4, typeof(IronIngot), 1044036, 10, 1044037); + if (Core.SE) + { + index = AddCraft(typeof(ChainHatsuburi), 1011079, 1030175, 30.0, 80.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.SE); - if (Core.AOS) - { - AddCraft(typeof(CrescentBlade), 1011081, 1029921, 45.0, 95.0, typeof(IronIngot), 1044036, 14, 1044037); - } + index = AddCraft(typeof(PlateHatsuburi), 1011079, 1030176, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.SE); - 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); + index = AddCraft(typeof(HeavyPlateJingasa), 1011079, 1030178, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.SE); - 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); + index = AddCraft(typeof(LightPlateJingasa), 1011079, 1030188, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 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(SmallPlateJingasa), 1011079, 1030191, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.SE); - index = AddCraft(typeof(WarCleaver), 1011081, 1031567, 70.0, 120.0, typeof(IronIngot), 1044036, 18, 1044037); - SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(DecorativePlateKabuto), 1011079, 1030179, 90.0, 140.0, typeof(IronIngot), 1044036, 25, 1044037); + SetNeededExpansion(index, Expansion.SE); - index = AddCraft(typeof(ElvenSpellblade), 1011081, 1031564, 70.0, 120.0, typeof(IronIngot), 1044036, 14, 1044037); - SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(PlateBattleKabuto), 1011079, 1030192, 90.0, 140.0, typeof(IronIngot), 1044036, 25, 1044037); + SetNeededExpansion(index, Expansion.SE); - index = AddCraft(typeof(AssassinSpike), 1011081, 1031565, 70.0, 120.0, typeof(IronIngot), 1044036, 9, 1044037); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(Leafblade), 1011081, 1031566, 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), 1032680, 10, 1053098); - AddRareRecipe(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); - AddRareRecipe(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); - AddRareRecipe(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); - AddRareRecipe(index, 3); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(TrueSpellblade), 1011081, 1073513, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); - AddRecipe(index, 4); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(IcySpellblade), 1011081, 1073514, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); - AddRecipe(index, 5); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(FierySpellblade), 1011081, 1073515, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(FireRuby), 1032695, 1, 1044240); - AddRecipe(index, 6); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(SpellbladeOfDefense), 1011081, 1073516, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); - AddRes(index, typeof(WhitePearl), 1032694, 1, 1044240); - AddRecipe(index, 7); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(TrueAssassinSpike), 1011081, 1073517, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); - AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); - AddRecipe(index, 8); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(ChargedAssassinSpike), 1011081, 1073518, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); - AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); - AddRecipe(index, 9); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(MagekillerAssassinSpike), 1011081, 1073519, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); - AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); - AddRecipe(index, 10); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(WoundingAssassinSpike), 1011081, 1073520, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); - AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); - AddRecipe(index, 11); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(TrueLeafblade), 1011081, 1073521, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); - AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); - AddRecipe(index, 12); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(Luckblade), 1011081, 1073522, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); - AddRes(index, typeof(WhitePearl), 1032694, 1, 1044240); - AddRecipe(index, 13); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(MagekillerLeafblade), 1011081, 1073523, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); - AddRes(index, typeof(FireRuby), 1032695, 1, 1044240); - AddRecipe(index, 14); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(LeafbladeOfEase), 1011081, 1073524, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); - AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); - AddRecipe(index, 15); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(KnightsWarCleaver), 1011081, 1073525, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); - AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); - AddRecipe(index, 16); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(ButchersWarCleaver), 1011081, 1073526, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); - AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); - AddRecipe(index, 17); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(SerratedWarCleaver), 1011081, 1073527, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); - AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); - AddRecipe(index, 18); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(TrueWarCleaver), 1011081, 1073528, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); - AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); - AddRecipe(index, 19); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(AdventurersMachete), 1011081, 1073533, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(WhitePearl), 1032694, 1, 1044240); - AddRecipe(index, 20); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(OrcishMachete), 1011081, 1073534, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(Scourge), 1072136, 1, 1042081); - AddRecipe(index, 21); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(MacheteOfDefense), 1011081, 1073535, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); - AddRecipe(index, 22); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(DiseasedMachete), 1011081, 1073536, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); - AddRes(index, typeof(Blight), 1072134, 1, 1042081); - AddRecipe(index, 23); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(Runesabre), 1011081, 1073537, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); - AddRecipe(index, 24); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(MagesRuneBlade), 1011081, 1073538, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); - AddRecipe(index, 25); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(RuneBladeOfKnowledge), 1011081, 1073539, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); - AddRecipe(index, 26); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(CorruptedRuneBlade), 1011081, 1073540, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(Corruption), 1072135, 1, 1042081); - AddRecipe(index, 27); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(TrueRadiantScimitar), 1011081, 1073541, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); - AddRecipe(index, 28); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(DarkglowScimitar), 1011081, 1073542, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); - AddRecipe(index, 29); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(IcyScimitar), 1011081, 1073543, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); - AddRecipe(index, 30); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(TwinklingScimitar), 1011081, 1073544, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); - AddRecipe(index, 31); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(BoneMachete), 1011081, 1020526, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); - AddRes(index, typeof(Bone), 1049064, 6, 1049063); - AddQuestRecipe(index, 32); - SetNeededExpansion(index, Expansion.ML); - } - } - - 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); + 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(OrnateAxe), 1011082, 1031572, 70.0, 120.0, typeof(IronIngot), 1044036, 18, 1044037); + index = AddCraft(typeof(Circlet), 1011079, 1032645, 62.1, 112.1, typeof(IronIngot), 1044036, 6, 1044037); SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(GuardianAxe), 1011082, 1073545, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + 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); - AddRecipe(index, 33); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(SingingAxe), 1011082, 1073546, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); - AddRecipe(index, 34); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(ThunderingAxe), 1011082, 1073547, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); - AddRecipe(index, 35); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(HeavyOrnateAxe), 1011082, 1073548, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); - AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); - AddRecipe(index, 36); SetNeededExpansion(index, Expansion.ML); } + } - AddCraft(typeof(Bardiche), 1011083, 1023917, 31.7, 81.7, typeof(IronIngot), 1044036, 18, 1044037); + 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(BladedStaff), 1011083, 1029917, 40.0, 90.0, typeof(IronIngot), 1044036, 12, 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); + } - if (Core.AOS) - { - AddCraft(typeof(DoubleBladedStaff), 1011083, 1029919, 45.0, 95.0, typeof(IronIngot), 1044036, 16, 1044037); - } + if (Core.AOS) + { + AddCraft(typeof(BoneHarvester), 1011081, 1029915, 33.0, 83.0, typeof(IronIngot), 1044036, 10, 1044037); + } - AddCraft(typeof(Halberd), 1011083, 1025183, 39.1, 89.1, typeof(IronIngot), 1044036, 20, 1044037); + AddCraft(typeof(Broadsword), 1011081, 1023934, 35.4, 85.4, typeof(IronIngot), 1044036, 10, 1044037); - if (Core.AOS) - { - AddCraft(typeof(Lance), 1011083, 1029920, 48.0, 98.0, typeof(IronIngot), 1044036, 20, 1044037); - } + if (Core.AOS) + { + AddCraft(typeof(CrescentBlade), 1011081, 1029921, 45.0, 95.0, typeof(IronIngot), 1044036, 14, 1044037); + } - if (Core.AOS) - { - AddCraft(typeof(Pike), 1011083, 1029918, 47.0, 97.0, typeof(IronIngot), 1044036, 12, 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); - 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 ); - - 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); - } + 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(DiamondMace), 1011084, 1031556, 70.0, 120.0, typeof(IronIngot), 1044036, 20, 1044037); + index = AddCraft(typeof(RadiantScimitar), 1011081, 1031571, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ShardThrasher), 1011084, 1072918, 70.0, 120.0, typeof(IronIngot), 1044036, 20, 1044037); - AddRes(index, typeof(EyeOfTheTravesty), 1073126, 1, 1042081); - AddRes(index, typeof(Muculent), 1072139, 10, 1042081); - AddRes(index, typeof(Corruption), 1072135, 10, 1042081); - AddRareRecipe(index, 37); + 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, 1031565, 70.0, 120.0, typeof(IronIngot), 1044036, 9, 1044037); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(Leafblade), 1011081, 1031566, 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), 1032680, 10, 1053098); + AddRareRecipe(index, 0); ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(RubyMace), 1011084, 1073529, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); - AddRes(index, typeof(FireRuby), 1032695, 1, 1044240); - AddRecipe(index, 38); + 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); + AddRareRecipe(index, 1); + ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(EmeraldMace), 1011084, 1073530, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); - AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); - AddRecipe(index, 39); + 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); + AddRareRecipe(index, 2); + ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(SapphireMace), 1011084, 1073531, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); - AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); - AddRecipe(index, 40); + 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); + AddRareRecipe(index, 3); + ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(SilverEtchedMace), 1011084, 1073532, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); + index = AddCraft(typeof(TrueSpellblade), 1011081, 1073513, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); - AddRecipe(index, 41); + AddRecipe(index, 4); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(IcySpellblade), 1011081, 1073514, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); + AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); + AddRecipe(index, 5); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(FierySpellblade), 1011081, 1073515, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); + AddRes(index, typeof(FireRuby), 1032695, 1, 1044240); + AddRecipe(index, 6); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(SpellbladeOfDefense), 1011081, 1073516, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); + AddRes(index, typeof(WhitePearl), 1032694, 1, 1044240); + AddRecipe(index, 7); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(TrueAssassinSpike), 1011081, 1073517, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); + AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); + AddRecipe(index, 8); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(ChargedAssassinSpike), 1011081, 1073518, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); + AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); + AddRecipe(index, 9); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(MagekillerAssassinSpike), 1011081, 1073519, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); + AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); + AddRecipe(index, 10); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(WoundingAssassinSpike), 1011081, 1073520, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); + AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); + AddRecipe(index, 11); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(TrueLeafblade), 1011081, 1073521, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); + AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); + AddRecipe(index, 12); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(Luckblade), 1011081, 1073522, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); + AddRes(index, typeof(WhitePearl), 1032694, 1, 1044240); + AddRecipe(index, 13); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(MagekillerLeafblade), 1011081, 1073523, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); + AddRes(index, typeof(FireRuby), 1032695, 1, 1044240); + AddRecipe(index, 14); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(LeafbladeOfEase), 1011081, 1073524, 75.0, 125.0, typeof(IronIngot), 1044036, 12, 1044037); + AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); + AddRecipe(index, 15); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(KnightsWarCleaver), 1011081, 1073525, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); + AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); + AddRecipe(index, 16); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(ButchersWarCleaver), 1011081, 1073526, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); + AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); + AddRecipe(index, 17); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(SerratedWarCleaver), 1011081, 1073527, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); + AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); + AddRecipe(index, 18); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(TrueWarCleaver), 1011081, 1073528, 75.0, 125.0, typeof(IronIngot), 1044036, 18, 1044037); + AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); + AddRecipe(index, 19); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(AdventurersMachete), 1011081, 1073533, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); + AddRes(index, typeof(WhitePearl), 1032694, 1, 1044240); + AddRecipe(index, 20); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(OrcishMachete), 1011081, 1073534, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); + AddRes(index, typeof(Scourge), 1072136, 1, 1042081); + AddRecipe(index, 21); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(MacheteOfDefense), 1011081, 1073535, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); + AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); + AddRecipe(index, 22); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(DiseasedMachete), 1011081, 1073536, 75.0, 125.0, typeof(IronIngot), 1044036, 14, 1044037); + AddRes(index, typeof(Blight), 1072134, 1, 1042081); + AddRecipe(index, 23); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(Runesabre), 1011081, 1073537, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); + AddRecipe(index, 24); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(MagesRuneBlade), 1011081, 1073538, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); + AddRecipe(index, 25); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(RuneBladeOfKnowledge), 1011081, 1073539, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); + AddRecipe(index, 26); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(CorruptedRuneBlade), 1011081, 1073540, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(Corruption), 1072135, 1, 1042081); + AddRecipe(index, 27); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(TrueRadiantScimitar), 1011081, 1073541, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); + AddRecipe(index, 28); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(DarkglowScimitar), 1011081, 1073542, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); + AddRecipe(index, 29); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(IcyScimitar), 1011081, 1073543, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); + AddRecipe(index, 30); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(TwinklingScimitar), 1011081, 1073544, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); + AddRecipe(index, 31); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(BoneMachete), 1011081, 1020526, 45.0, 95.0, typeof(IronIngot), 1044036, 20, 1044037); + AddRes(index, typeof(Bone), 1049064, 6, 1049063); + AddQuestRecipe(index, 32); SetNeededExpansion(index, Expansion.ML); } - - 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); - - // 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; } - } - [AttributeUsage(AttributeTargets.Class)] - public class ForgeAttribute : Attribute - { - } + 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); - [AttributeUsage(AttributeTargets.Class)] - public class AnvilAttribute : Attribute - { + if (Core.ML) + { + index = AddCraft(typeof(OrnateAxe), 1011082, 1031572, 70.0, 120.0, typeof(IronIngot), 1044036, 18, 1044037); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(GuardianAxe), 1011082, 1073545, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); + AddRecipe(index, 33); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(SingingAxe), 1011082, 1073546, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(BrilliantAmber), 1032697, 1, 1044240); + AddRecipe(index, 34); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(ThunderingAxe), 1011082, 1073547, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(EcruCitrine), 1032693, 1, 1044240); + AddRecipe(index, 35); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(HeavyOrnateAxe), 1011082, 1073548, 75.0, 125.0, typeof(IronIngot), 1044036, 15, 1044037); + AddRes(index, typeof(Turquoise), 1032691, 1, 1044240); + AddRecipe(index, 36); + SetNeededExpansion(index, Expansion.ML); + } + + 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 ); + + 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); + } + + if (Core.ML) + { + index = AddCraft(typeof(DiamondMace), 1011084, 1031556, 70.0, 120.0, typeof(IronIngot), 1044036, 20, 1044037); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(ShardThrasher), 1011084, 1072918, 70.0, 120.0, typeof(IronIngot), 1044036, 20, 1044037); + AddRes(index, typeof(EyeOfTheTravesty), 1073126, 1, 1042081); + AddRes(index, typeof(Muculent), 1072139, 10, 1042081); + AddRes(index, typeof(Corruption), 1072135, 10, 1042081); + AddRareRecipe(index, 37); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(RubyMace), 1011084, 1073529, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); + AddRes(index, typeof(FireRuby), 1032695, 1, 1044240); + AddRecipe(index, 38); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(EmeraldMace), 1011084, 1073530, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); + AddRes(index, typeof(PerfectEmerald), 1032692, 1, 1044240); + AddRecipe(index, 39); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(SapphireMace), 1011084, 1073531, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); + AddRes(index, typeof(DarkSapphire), 1032690, 1, 1044240); + AddRecipe(index, 40); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(SilverEtchedMace), 1011084, 1073532, 75.0, 125.0, typeof(IronIngot), 1044036, 20, 1044037); + AddRes(index, typeof(BlueDiamond), 1032696, 1, 1044240); + AddRecipe(index, 41); + SetNeededExpansion(index, Expansion.ML); + } + + 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); + + // 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; } } + +[AttributeUsage(AttributeTargets.Class)] +public class ForgeAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.Class)] +public class AnvilAttribute : Attribute +{ +} diff --git a/Projects/UOContent/Engines/Craft/DefBowFletching.cs b/Projects/UOContent/Engines/Craft/DefBowFletching.cs index ac042499c..4d71341ce 100644 --- a/Projects/UOContent/Engines/Craft/DefBowFletching.cs +++ b/Projects/UOContent/Engines/Craft/DefBowFletching.cs @@ -1,260 +1,262 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefBowFletching : CraftSystem { - public class DefBowFletching : CraftSystem + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefBowFletching(); + } - private DefBowFletching() : base(1, 1, 1.25) // base( 1, 2, 1.7 ) + private DefBowFletching() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Fletching; + + public override TextDefinition GumpTitle => 1044006; + + public static CraftSystem CraftSystem { get; private set; } + + public override CraftECA ECA => CraftECA.FiftyPercentChanceMinusTenPercent; + + public override double GetChanceAtMin(CraftItem item) => 0.5; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Fletching; - - public override int GumpTitleNumber => 1044006; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefBowFletching(); - - public override CraftECA ECA => CraftECA.FiftyPercentChanceMinusTenPercent; - - public override double GetChanceAtMin(CraftItem item) => 0.5; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckAccessible(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + return 1044263; // The tool must be on your person to use. } - public override void PlayCraftEffect(Mobile from) - { - // no animation - // if (from.Body.Type == BodyType.Human && !from.Mounted) - // from.Animate( 33, 5, 1, true, false, 0 ); + return 0; + } - from.PlaySound(0x55); + 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 } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (failed) { - if (toolBroken) + if (lostMaterial) { - from.SendLocalizedMessage(1044038); // You have worn out your tool + return 1044043; // You failed to create the item, and some of your materials are lost. } - if (failed) - { - if (lostMaterial) - { - return 1044043; // You failed to create the item, and some of your materials are lost. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 1044157; // You failed to create the item, but no materials were lost. } - public override void InitCraftList() + if (quality == 0) { - int index; + return 502785; // You were barely able to make this item. It's quality is below average. + } - // Materials - AddCraft(typeof(Kindling), 1044457, 1023553, 0.0, 00.0, typeof(Log), 1044041, 1, 1044351); + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } - index = AddCraft(typeof(Shaft), 1044457, 1027124, 0.0, 40.0, typeof(Log), 1044041, 1, 1044351); + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + return 1044154; // You create the item. + } + + public override void InitCraftList() + { + int index; + + // 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); - - // 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); - } - - if (Core.ML) - { - index = AddCraft( - typeof(BlightGrippedLongbow), - 1044566, - 1072907, - 75.0, - 125.0, - typeof(Log), - 1044041, - 20, - 1044351 - ); - AddRes(index, typeof(LardOfParoxysmus), 1032681, 1, 1053098); - AddRes(index, typeof(Blight), 1032675, 10, 1053098); - AddRes(index, typeof(Corruption), 1032676, 10, 1053098); - AddRareRecipe(index, 200); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - /* TODO - index = AddCraft( typeof( FaerieFire ), 1044566, 1072908, 75.0, 125.0, typeof( Log ), 1044041, 20, 1044351 ); - AddRes( index, typeof( LardOfParoxysmus ), 1032681, 1, 1053098 ); - AddRes( index, typeof( Putrefication ), 1032678, 10, 1053098 ); - AddRes( index, typeof( Taint ), 1032679, 10, 1053098 ); - AddRareRecipe( index, 201 ); - ForceNonExceptional( index ); - SetNeededExpansion( index, Expansion.ML ); - */ - - index = AddCraft( - typeof(SilvanisFeywoodBow), - 1044566, - 1072955, - 75.0, - 125.0, - typeof(Log), - 1044041, - 20, - 1044351 - ); - AddRes(index, typeof(LardOfParoxysmus), 1032681, 1, 1053098); - AddRes(index, typeof(Scourge), 1032677, 10, 1053098); - AddRes(index, typeof(Muculent), 1032680, 10, 1053098); - AddRareRecipe(index, 202); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - /* TODO - index = AddCraft( typeof( MischiefMaker ), 1044566, 1072910, 75.0, 125.0, typeof( Log ), 1044041, 15, 1044351 ); - AddRes( index, typeof( DreadHornMane ), 1032682, 1, 1053098 ); - AddRes( index, typeof( Corruption ), 1032676, 10, 1053098 ); - AddRes( index, typeof( Putrefication ), 1032678, 10, 1053098 ); - AddRareRecipe( index, 203 ); - ForceNonExceptional( index ); - SetNeededExpansion( index, Expansion.ML ); - */ - - index = AddCraft(typeof(TheNightReaper), 1044566, 1072912, 75.0, 125.0, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(DreadHornMane), 1032682, 1, 1053098); - AddRes(index, typeof(Blight), 1032675, 10, 1053098); - AddRes(index, typeof(Scourge), 1032677, 10, 1053098); - AddRareRecipe(index, 204); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(BarbedLongbow), 1044566, 1073505, 75.0, 125.0, typeof(Log), 1044041, 20, 1044351); - AddRes(index, typeof(FireRuby), 1026254, 1, 1053098); - AddRecipe(index, 205); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(SlayerLongbow), 1044566, 1073506, 75.0, 125.0, typeof(Log), 1044041, 20, 1044351); - AddRes(index, typeof(BrilliantAmber), 1026256, 1, 1053098); - AddRecipe(index, 206); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(FrozenLongbow), 1044566, 1073507, 75.0, 125.0, typeof(Log), 1044041, 20, 1044351); - AddRes(index, typeof(Turquoise), 1026250, 1, 1053098); - AddRecipe(index, 207); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(LongbowOfMight), 1044566, 1073508, 75.0, 125.0, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(BlueDiamond), 1026255, 1, 1053098); - AddRecipe(index, 208); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(RangersShortbow), 1044566, 1073509, 75.0, 125.0, typeof(Log), 1044041, 15, 1044351); - AddRes(index, typeof(PerfectEmerald), 1026251, 1, 1053098); - AddRecipe(index, 209); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(LightweightShortbow), - 1044566, - 1073510, - 75.0, - 125.0, - typeof(Log), - 1044041, - 15, - 1044351 - ); - AddRes(index, typeof(WhitePearl), 1026253, 1, 1053098); - AddRecipe(index, 210); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(MysticalShortbow), 1044566, 1073511, 75.0, 125.0, typeof(Log), 1044041, 15, 1044351); - AddRes(index, typeof(EcruCitrine), 1026252, 1, 1053098); - AddRecipe(index, 211); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(AssassinsShortbow), - 1044566, - 1073512, - 75.0, - 125.0, - typeof(Log), - 1044041, - 15, - 1044351 - ); - AddRes(index, typeof(DarkSapphire), 1026249, 1, 1053098); - AddRecipe(index, 212); - SetNeededExpansion(index, Expansion.ML); - } - - MarkOption = true; - Repair = Core.AOS; + 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); + } + + if (Core.ML) + { + index = AddCraft( + typeof(BlightGrippedLongbow), + 1044566, + 1072907, + 75.0, + 125.0, + typeof(Log), + 1044041, + 20, + 1044351 + ); + AddRes(index, typeof(LardOfParoxysmus), 1032681, 1, 1053098); + AddRes(index, typeof(Blight), 1032675, 10, 1053098); + AddRes(index, typeof(Corruption), 1032676, 10, 1053098); + AddRareRecipe(index, 200); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + /* TODO + index = AddCraft( typeof( FaerieFire ), 1044566, 1072908, 75.0, 125.0, typeof( Log ), 1044041, 20, 1044351 ); + AddRes( index, typeof( LardOfParoxysmus ), 1032681, 1, 1053098 ); + AddRes( index, typeof( Putrefication ), 1032678, 10, 1053098 ); + AddRes( index, typeof( Taint ), 1032679, 10, 1053098 ); + AddRareRecipe( index, 201 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + */ + + index = AddCraft( + typeof(SilvanisFeywoodBow), + 1044566, + 1072955, + 75.0, + 125.0, + typeof(Log), + 1044041, + 20, + 1044351 + ); + AddRes(index, typeof(LardOfParoxysmus), 1032681, 1, 1053098); + AddRes(index, typeof(Scourge), 1032677, 10, 1053098); + AddRes(index, typeof(Muculent), 1032680, 10, 1053098); + AddRareRecipe(index, 202); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + /* TODO + index = AddCraft( typeof( MischiefMaker ), 1044566, 1072910, 75.0, 125.0, typeof( Log ), 1044041, 15, 1044351 ); + AddRes( index, typeof( DreadHornMane ), 1032682, 1, 1053098 ); + AddRes( index, typeof( Corruption ), 1032676, 10, 1053098 ); + AddRes( index, typeof( Putrefication ), 1032678, 10, 1053098 ); + AddRareRecipe( index, 203 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + */ + + index = AddCraft(typeof(TheNightReaper), 1044566, 1072912, 75.0, 125.0, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(DreadHornMane), 1032682, 1, 1053098); + AddRes(index, typeof(Blight), 1032675, 10, 1053098); + AddRes(index, typeof(Scourge), 1032677, 10, 1053098); + AddRareRecipe(index, 204); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(BarbedLongbow), 1044566, 1073505, 75.0, 125.0, typeof(Log), 1044041, 20, 1044351); + AddRes(index, typeof(FireRuby), 1026254, 1, 1053098); + AddRecipe(index, 205); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(SlayerLongbow), 1044566, 1073506, 75.0, 125.0, typeof(Log), 1044041, 20, 1044351); + AddRes(index, typeof(BrilliantAmber), 1026256, 1, 1053098); + AddRecipe(index, 206); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(FrozenLongbow), 1044566, 1073507, 75.0, 125.0, typeof(Log), 1044041, 20, 1044351); + AddRes(index, typeof(Turquoise), 1026250, 1, 1053098); + AddRecipe(index, 207); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(LongbowOfMight), 1044566, 1073508, 75.0, 125.0, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(BlueDiamond), 1026255, 1, 1053098); + AddRecipe(index, 208); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(RangersShortbow), 1044566, 1073509, 75.0, 125.0, typeof(Log), 1044041, 15, 1044351); + AddRes(index, typeof(PerfectEmerald), 1026251, 1, 1053098); + AddRecipe(index, 209); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(LightweightShortbow), + 1044566, + 1073510, + 75.0, + 125.0, + typeof(Log), + 1044041, + 15, + 1044351 + ); + AddRes(index, typeof(WhitePearl), 1026253, 1, 1053098); + AddRecipe(index, 210); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(MysticalShortbow), 1044566, 1073511, 75.0, 125.0, typeof(Log), 1044041, 15, 1044351); + AddRes(index, typeof(EcruCitrine), 1026252, 1, 1053098); + AddRecipe(index, 211); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(AssassinsShortbow), + 1044566, + 1073512, + 75.0, + 125.0, + typeof(Log), + 1044041, + 15, + 1044351 + ); + AddRes(index, typeof(DarkSapphire), 1026249, 1, 1053098); + AddRecipe(index, 212); + SetNeededExpansion(index, Expansion.ML); + } + + MarkOption = true; + Repair = Core.AOS; } } diff --git a/Projects/UOContent/Engines/Craft/DefCarpentry.cs b/Projects/UOContent/Engines/Craft/DefCarpentry.cs index 95e1a76f6..fcae26682 100644 --- a/Projects/UOContent/Engines/Craft/DefCarpentry.cs +++ b/Projects/UOContent/Engines/Craft/DefCarpentry.cs @@ -1,607 +1,609 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefCarpentry : CraftSystem { - public class DefCarpentry : CraftSystem + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefCarpentry(); + } - private DefCarpentry() : base(1, 1, 1.25) // base( 1, 1, 3.0 ) + private DefCarpentry() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Carpentry; + + public override TextDefinition GumpTitle => 1044004; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) => 0.5; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Carpentry; - - public override int GumpTitleNumber => 1044004; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefCarpentry(); - - public override double GetChanceAtMin(CraftItem item) => 0.5; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckAccessible(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + return 1044263; // The tool must be on your person to use. } - public override void PlayCraftEffect(Mobile from) - { - // no animation - // if (from.Body.Type == BodyType.Human && !from.Mounted) - // from.Animate( 9, 5, 1, true, false, 0 ); - // 0.7 second delay + return 0; + } - from.PlaySound(0x23D); + public override void PlayCraftEffect(Mobile from) + { + // no animation + // if (from.Body.Type == BodyType.Human && !from.Mounted) + // from.Animate( 9, 5, 1, true, false, 0 ); + // 0.7 second delay + + 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 } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (failed) { - if (toolBroken) + if (lostMaterial) { - from.SendLocalizedMessage(1044038); // You have worn out your tool + return 1044043; // You failed to create the item, and some of your materials are lost. } - if (failed) - { - if (lostMaterial) - { - return 1044043; // You failed to create the item, and some of your materials are lost. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 1044157; // You failed to create the item, but no materials were lost. } - public override void InitCraftList() + if (quality == 0) { - int index; + return 502785; // You were barely able to make this item. It's quality is below average. + } - // Other Items - if (Core.Expansion is Expansion.AOS or Expansion.SE) - { - index = AddCraft(typeof(Board), 1044294, 1027127, 0.0, 0.0, typeof(Log), 1044466, 1, 1044465); - SetUseAllRes(index, true); - } + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } - 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(Easel), 1044294, 1044317, 86.8, 111.8, typeof(Log), 1044041, 20, 1044351); + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } - 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); - } + return 1044154; // You create the item. + } - if (Core.AOS) // Duplicate Entries to preserve ordering depending on era - { - index = AddCraft(typeof(FishingPole), 1044294, 1023519, 68.4, 93.4, typeof(Log), 1044041, 5, 1044351 ); - AddSkill(index, SkillName.Tailoring, 40.0, 45.0); - AddRes(index, typeof(Cloth), 1044286, 5, 1044287); - } + public override void InitCraftList() + { + int index; - if (Core.ML) - { - index = AddCraft(typeof(RunedSwitch), 1044294, 1072896, 70.0, 120.0, typeof(Log), 1044041, 2, 1044351); - AddRes(index, typeof(EnchantedSwitch), 1072893, 1, 1053098); - AddRes(index, typeof(RunedPrism), 1073465, 1, 1053098); - AddRes(index, typeof(JeweledFiligree), 1072894, 1, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ArcanistStatueSouthDeed), 1044294, 1072885, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ArcanistStatueEastDeed), 1044294, 1072886, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WarriorStatueSouthDeed), 1044294, 1072887, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); - AddRecipe(index, 300); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WarriorStatueEastDeed), 1044294, 1072888, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); - AddRecipe(index, 301); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(SquirrelStatueSouthDeed), 1044294, 1072884, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); - AddRecipe(index, 302); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(SquirrelStatueEastDeed), 1044294, 1073398, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); - AddRecipe(index, 303); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(GiantReplicaAcorn), 1044294, 1072889, 80.0, 105.0, typeof(Log), 1044041, 35, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(MountedDreadHorn), 1044294, 1032632, 90.0, 115.0, typeof(Log), 1044041, 50, 1044351); - AddRes(index, typeof(PristineDreadHorn), 1032634, 1, 1053098); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(AcidProofRope), 1044294, 1074886, 80, 130.0, typeof(GreaterStrengthPotion), 1073466, 2, 1044253); - AddRes(index, typeof(ProtectionScroll), 1044395, 1, 1053098); - AddRes(index, typeof(SwitchItem), 1032127, 1, 1053098); - AddRareRecipe(index, 304); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } + // Other Items + if (Core.Expansion is Expansion.AOS or Expansion.SE) + { + index = AddCraft(typeof(Board), 1044294, 1027127, 0.0, 0.0, typeof(Log), 1044466, 1, 1044465); + SetUseAllRes(index, true); + } - // 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); + 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(Easel), 1044294, 1044317, 86.8, 111.8, typeof(Log), 1044041, 20, 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); - } + 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); + } - if (Core.ML) - { - index = AddCraft(typeof(OrnateElvenTableSouthDeed), 1044291, 1072869, 85.0, 110.0, typeof(Log), 1044041, 60, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(OrnateElvenTableEastDeed), 1044291, 1073384, 85.0, 110.0, typeof(Log), 1044041, 60, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(FancyElvenTableSouthDeed), 1044291, 1073385, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(FancyElvenTableEastDeed), 1044291, 1073386, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenPodium), 1044291, 1073399, 80.0, 105.0, typeof(Log), 1044041, 20, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(OrnateElvenChair), 1044291, 1072870, 80.0, 105.0, typeof(Log), 1044041, 30, 1044351); - AddRecipe(index, 305); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(BigElvenChair), 1044291, 1072872, 85.0, 110.0, typeof(Log), 1044041, 40, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenReadingChair), 1044291, 1072873, 80.0, 105.0, typeof(Log), 1044041, 30, 1044351); - SetNeededExpansion(index, Expansion.ML); - } + if (Core.AOS) // Duplicate Entries to preserve ordering depending on era + { + index = AddCraft(typeof(FishingPole), 1044294, 1023519, 68.4, 93.4, typeof(Log), 1044041, 5, 1044351 ); + AddSkill(index, SkillName.Tailoring, 40.0, 45.0); + AddRes(index, typeof(Cloth), 1044286, 5, 1044287); + } - // 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.ML) + { + index = AddCraft(typeof(RunedSwitch), 1044294, 1072896, 70.0, 120.0, typeof(Log), 1044041, 2, 1044351); + AddRes(index, typeof(EnchantedSwitch), 1072893, 1, 1053098); + AddRes(index, typeof(RunedPrism), 1073465, 1, 1053098); + AddRes(index, typeof(JeweledFiligree), 1072894, 1, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ArcanistStatueSouthDeed), 1044294, 1072885, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ArcanistStatueEastDeed), 1044294, 1072886, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WarriorStatueSouthDeed), 1044294, 1072887, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); + AddRecipe(index, 300); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WarriorStatueEastDeed), 1044294, 1072888, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); + AddRecipe(index, 301); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(SquirrelStatueSouthDeed), 1044294, 1072884, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); + AddRecipe(index, 302); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(SquirrelStatueEastDeed), 1044294, 1073398, 0.0, 35.0, typeof(Log), 1044041, 250, 1044351); + AddRecipe(index, 303); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(GiantReplicaAcorn), 1044294, 1072889, 80.0, 105.0, typeof(Log), 1044041, 35, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(MountedDreadHorn), 1044294, 1032632, 90.0, 115.0, typeof(Log), 1044041, 50, 1044351); + AddRes(index, typeof(PristineDreadHorn), 1032634, 1, 1053098); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(AcidProofRope), 1044294, 1074886, 80, 130.0, typeof(GreaterStrengthPotion), 1073466, 2, 1044253); + AddRes(index, typeof(ProtectionScroll), 1044395, 1, 1053098); + AddRes(index, typeof(SwitchItem), 1032127, 1, 1053098); + AddRareRecipe(index, 304); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + } - 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, 1030332, 90.0, 115.0, typeof(Log), 1044041, 40, 1044351); - SetNeededExpansion(index, Expansion.SE); - index = AddCraft(typeof(CherryArmoire), 1044292, 1030334, 90.0, 115.0, typeof(Log), 1044041, 40, 1044351); - 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); - 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); + 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); + } - if (Core.ML) - { - index = AddCraft(typeof(ArcaneBookshelfSouthDeed), 1044292, 1072871, 94.7, 119.7, typeof(Log), 1044041, 80, 1044351); - AddRecipe(index, 306); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ArcaneBookshelfEastDeed), 1044292, 1073371, 94.7, 119.7, typeof(Log), 1044041, 80, 1044351); - AddRecipe(index, 307); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft( typeof(OrnateElvenChestSouthDeed), 1044292, 1072862, 94.7, 119.7, typeof(Log), 1044041, 40, 1044351); - AddRecipe(index, 308); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft( typeof(OrnateElvenChestEastDeed), 1044292, 1073383, 94.7, 119.7, typeof(Log), 1044041, 40, 1044351); - AddRecipe(index, 309); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenWashBasinSouthDeed), 1044292, 1072865, 70.0, 95.0, typeof(Log), 1044041, 40, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenWashBasinEastDeed), 1044292, 1072865, 70.0, 95.0, typeof(Log), 1044041, 40, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenDresserSouthDeed), 1044292, 1072864, 75.0, 100.0, typeof(Log), 1044041, 45, 1044351); - AddRecipe(index, 310); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenDresserEastDeed), 1044292, 1073388, 75.0, 100.0, typeof(Log), 1044041, 45, 1044351); - AddRecipe(index, 311); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft( typeof(FancyElvenArmoire), 1044292, 1072866, 80.0, 105.0, typeof(Log), 1044041, 60, 1044351); - AddRecipe(index, 312); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(SimpleElvenArmoire), 1044292, 1073401, 80.0, 105.0, typeof(Log), 1044041, 60, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(RarewoodChest), 1044292, 1073402, 80.0, 105.0, typeof(Log), 1044041, 30, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(DecorativeBox), 1044292, 1073403, 80.0, 105.0, typeof(Log), 1044041, 25, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(FullBookcase), 1044292, 1071213, 60.0, 85.0, typeof(Log), 1044041, 25, 1044351); - AddRes(index, typeof(AcademicBooksArtifact), 1071202, 1, 1044253); - SetNeededExpansion(index, Expansion.ML); - } + if (Core.ML) + { + index = AddCraft(typeof(OrnateElvenTableSouthDeed), 1044291, 1072869, 85.0, 110.0, typeof(Log), 1044041, 60, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(OrnateElvenTableEastDeed), 1044291, 1073384, 85.0, 110.0, typeof(Log), 1044041, 60, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(FancyElvenTableSouthDeed), 1044291, 1073385, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(FancyElvenTableEastDeed), 1044291, 1073386, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenPodium), 1044291, 1073399, 80.0, 105.0, typeof(Log), 1044041, 20, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(OrnateElvenChair), 1044291, 1072870, 80.0, 105.0, typeof(Log), 1044041, 30, 1044351); + AddRecipe(index, 305); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(BigElvenChair), 1044291, 1072872, 85.0, 110.0, typeof(Log), 1044041, 40, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenReadingChair), 1044291, 1072873, 80.0, 105.0, typeof(Log), 1044041, 30, 1044351); + SetNeededExpansion(index, Expansion.ML); + } - // Staves and Shields - AddCraft(typeof(ShepherdsCrook), Core.ML ? 1044566 : 1044295, 1023713, 78.9, 103.9, typeof(Log), 1044041, 7, 1044351); - AddCraft(typeof(QuarterStaff), Core.ML ? 1044566 : 1044295, 1023721, 73.6, 98.6, typeof(Log), 1044041, 6, 1044351); - AddCraft(typeof(GnarledStaff), Core.ML ? 1044566 : 1044295, 1025112, 78.9, 103.9, typeof(Log), 1044041, 7, 1044351); + // 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.AOS) // Duplicate Entries to preserve ordering depending on era - { - index = AddCraft(typeof(FishingPole), Core.ML ? 1044294 : 1044295, 1023519, 68.4, 93.4, typeof(Log), 1044041, 5, 1044351); - AddSkill(index, SkillName.Tailoring, 40.0, 45.0); - AddRes(index, typeof(Cloth), 1044286, 5, 1044287); - } + 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, 1030332, 90.0, 115.0, typeof(Log), 1044041, 40, 1044351); + SetNeededExpansion(index, Expansion.SE); + index = AddCraft(typeof(CherryArmoire), 1044292, 1030334, 90.0, 115.0, typeof(Log), 1044041, 40, 1044351); + SetNeededExpansion(index, Expansion.SE); + } - if (Core.SE) - { - index = AddCraft(typeof(Bokuto), Core.ML ? 1044566 : 1044295, 1030227, 70.0, 95.0, typeof(Log), 1044041, 6, 1044351); - SetNeededExpansion(index, Expansion.SE); - index = AddCraft(typeof(Fukiya), Core.ML ? 1044566 : 1044295, 1030229, 60.0, 85.0, typeof(Log), 1044041, 6, 1044351); - SetNeededExpansion(index, Expansion.SE); - index = AddCraft(typeof(Tetsubo), Core.ML ? 1044566 : 1044295, 1030225, 80.0, 140.3, typeof(Log), 1044041, 10, 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); - if (Core.ML) - { - index = AddCraft(typeof(WildStaff), 1044566, 1031557, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(PhantomStaff), 1044566, 1072919, 90.0, 130.0, typeof(Log), 1044041, 16, 1044351); - AddRes(index, typeof(DiseasedBark), 1032683, 1, 1053098); - AddRes(index, typeof(Putrefication), 1032678, 10, 1053098); - AddRes(index, typeof(Taint), 1032679, 10, 1053098); - AddRareRecipe(index, 313); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ArcanistsWildStaff), 1044566, 1073549, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); - AddRes(index, typeof(WhitePearl), 1026253, 1, 1053098); - AddRecipe(index, 314); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(AncientWildStaff), 1044566, 1073550, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); - AddRes(index, typeof(PerfectEmerald), 1026251, 1, 1053098); - AddRecipe(index, 315); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ThornedWildStaff), 1044566, 1073551, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); - AddRes(index, typeof(FireRuby), 1026254, 1, 1053098); - AddRecipe(index, 316); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(HardenedWildStaff), 1044566, 1073552, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); - AddRes(index, typeof(Turquoise), 1026250, 1, 1053098); - AddRecipe(index, 317); - SetNeededExpansion(index, Expansion.ML); - } + if (Core.ML) + { + index = AddCraft(typeof(ArcaneBookshelfSouthDeed), 1044292, 1072871, 94.7, 119.7, typeof(Log), 1044041, 80, 1044351); + AddRecipe(index, 306); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ArcaneBookshelfEastDeed), 1044292, 1073371, 94.7, 119.7, typeof(Log), 1044041, 80, 1044351); + AddRecipe(index, 307); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft( typeof(OrnateElvenChestSouthDeed), 1044292, 1072862, 94.7, 119.7, typeof(Log), 1044041, 40, 1044351); + AddRecipe(index, 308); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft( typeof(OrnateElvenChestEastDeed), 1044292, 1073383, 94.7, 119.7, typeof(Log), 1044041, 40, 1044351); + AddRecipe(index, 309); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenWashBasinSouthDeed), 1044292, 1072865, 70.0, 95.0, typeof(Log), 1044041, 40, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenWashBasinEastDeed), 1044292, 1072865, 70.0, 95.0, typeof(Log), 1044041, 40, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenDresserSouthDeed), 1044292, 1072864, 75.0, 100.0, typeof(Log), 1044041, 45, 1044351); + AddRecipe(index, 310); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenDresserEastDeed), 1044292, 1073388, 75.0, 100.0, typeof(Log), 1044041, 45, 1044351); + AddRecipe(index, 311); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft( typeof(FancyElvenArmoire), 1044292, 1072866, 80.0, 105.0, typeof(Log), 1044041, 60, 1044351); + AddRecipe(index, 312); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(SimpleElvenArmoire), 1044292, 1073401, 80.0, 105.0, typeof(Log), 1044041, 60, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(RarewoodChest), 1044292, 1073402, 80.0, 105.0, typeof(Log), 1044041, 30, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(DecorativeBox), 1044292, 1073403, 80.0, 105.0, typeof(Log), 1044041, 25, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(FullBookcase), 1044292, 1071213, 60.0, 85.0, typeof(Log), 1044041, 25, 1044351); + AddRes(index, typeof(AcademicBooksArtifact), 1071202, 1, 1044253); + SetNeededExpansion(index, Expansion.ML); + } - // Armor - AddCraft(typeof(WoodenShield), Core.ML ? 1062760 : 1044295, 1027034, 52.6, 77.6, typeof(Log), 1044041, 9, 1044351); + // Staves and Shields + AddCraft(typeof(ShepherdsCrook), Core.ML ? 1044566 : 1044295, 1023713, 78.9, 103.9, typeof(Log), 1044041, 7, 1044351); + AddCraft(typeof(QuarterStaff), Core.ML ? 1044566 : 1044295, 1023721, 73.6, 98.6, typeof(Log), 1044041, 6, 1044351); + AddCraft(typeof(GnarledStaff), Core.ML ? 1044566 : 1044295, 1025112, 78.9, 103.9, typeof(Log), 1044041, 7, 1044351); - if (Core.ML) - { - index = AddCraft(typeof(WoodlandChest), 1062760, 1031111, 90.0, 115.0, typeof(Log), 1044041, 20, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 6, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WoodlandArms), 1062760, 1031116, 80.0, 105.0, typeof(Log), 1044041, 15, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WoodlandGloves), 1062760, 1031114, 85.0, 110.0, typeof(Log), 1044041, 15, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WoodlandLegs), 1062760, 1031115, 85.0, 110.0, typeof(Log), 1044041, 15, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WoodlandGorget), 1062760, 1031113, 85.0, 110.0, typeof(Log), 1044041, 15, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(RavenHelm), 1062760, 1031121, 65.0, 115.0, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - AddRes(index, typeof(Feather), 1027123, 25, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(VultureHelm), 1062760, 1031122, 63.9, 113.9, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - AddRes(index, typeof(Feather), 1027123, 25, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(WingedHelm), 1062760, 1031123, 58.4, 108.4, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); - AddRes(index, typeof(Feather), 1027123, 60, 1053098); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(IronwoodCrown), 1062760, 1072924, 85.0, 120.0, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(DiseasedBark), 1032683, 1, 1053098); - AddRes(index, typeof(Corruption), 1032676, 10, 1053098); - AddRes(index, typeof(Putrefication), 1032678, 10, 1053098); - AddRareRecipe(index, 318); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(BrambleCoat), 1062760, 1072925, 85.0, 120.0, typeof(Log), 1044041, 10, 1044351); - AddRes(index, typeof(DiseasedBark), 1032683, 1, 1053098); - AddRes(index, typeof(Taint), 1032679, 10, 1053098); - AddRes(index, typeof(Scourge), 1032677, 10, 1053098); - AddRareRecipe(index, 319); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } + if (!Core.AOS) // Duplicate Entries to preserve ordering depending on era + { + index = AddCraft(typeof(FishingPole), Core.ML ? 1044294 : 1044295, 1023519, 68.4, 93.4, typeof(Log), 1044041, 5, 1044351); + AddSkill(index, SkillName.Tailoring, 40.0, 45.0); + AddRes(index, typeof(Cloth), 1044286, 5, 1044287); + } - // Instruments - index = AddCraft(typeof(LapHarp), 1044293, 1023762, 63.1, 88.1, typeof(Log), 1044041, 20, 1044351); + if (Core.SE) + { + index = AddCraft(typeof(Bokuto), Core.ML ? 1044566 : 1044295, 1030227, 70.0, 95.0, typeof(Log), 1044041, 6, 1044351); + SetNeededExpansion(index, Expansion.SE); + index = AddCraft(typeof(Fukiya), Core.ML ? 1044566 : 1044295, 1030229, 60.0, 85.0, typeof(Log), 1044041, 6, 1044351); + SetNeededExpansion(index, Expansion.SE); + index = AddCraft(typeof(Tetsubo), Core.ML ? 1044566 : 1044295, 1030225, 80.0, 140.3, typeof(Log), 1044041, 10, 1044351); + SetNeededExpansion(index, Expansion.SE); + } + + if (Core.ML) + { + index = AddCraft(typeof(WildStaff), 1044566, 1031557, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(PhantomStaff), 1044566, 1072919, 90.0, 130.0, typeof(Log), 1044041, 16, 1044351); + AddRes(index, typeof(DiseasedBark), 1032683, 1, 1053098); + AddRes(index, typeof(Putrefication), 1032678, 10, 1053098); + AddRes(index, typeof(Taint), 1032679, 10, 1053098); + AddRareRecipe(index, 313); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ArcanistsWildStaff), 1044566, 1073549, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); + AddRes(index, typeof(WhitePearl), 1026253, 1, 1053098); + AddRecipe(index, 314); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(AncientWildStaff), 1044566, 1073550, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); + AddRes(index, typeof(PerfectEmerald), 1026251, 1, 1053098); + AddRecipe(index, 315); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ThornedWildStaff), 1044566, 1073551, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); + AddRes(index, typeof(FireRuby), 1026254, 1, 1053098); + AddRecipe(index, 316); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(HardenedWildStaff), 1044566, 1073552, 63.8, 113.8, typeof(Log), 1044041, 16, 1044351); + AddRes(index, typeof(Turquoise), 1026250, 1, 1053098); + AddRecipe(index, 317); + SetNeededExpansion(index, Expansion.ML); + } + + // Armor + AddCraft(typeof(WoodenShield), Core.ML ? 1062760 : 1044295, 1027034, 52.6, 77.6, typeof(Log), 1044041, 9, 1044351); + + if (Core.ML) + { + index = AddCraft(typeof(WoodlandChest), 1062760, 1031111, 90.0, 115.0, typeof(Log), 1044041, 20, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 6, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WoodlandArms), 1062760, 1031116, 80.0, 105.0, typeof(Log), 1044041, 15, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WoodlandGloves), 1062760, 1031114, 85.0, 110.0, typeof(Log), 1044041, 15, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WoodlandLegs), 1062760, 1031115, 85.0, 110.0, typeof(Log), 1044041, 15, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WoodlandGorget), 1062760, 1031113, 85.0, 110.0, typeof(Log), 1044041, 15, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(RavenHelm), 1062760, 1031121, 65.0, 115.0, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + AddRes(index, typeof(Feather), 1027123, 25, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(VultureHelm), 1062760, 1031122, 63.9, 113.9, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + AddRes(index, typeof(Feather), 1027123, 25, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(WingedHelm), 1062760, 1031123, 58.4, 108.4, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(BarkFragment), 1032687, 4, 1053098); + AddRes(index, typeof(Feather), 1027123, 60, 1053098); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(IronwoodCrown), 1062760, 1072924, 85.0, 120.0, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(DiseasedBark), 1032683, 1, 1053098); + AddRes(index, typeof(Corruption), 1032676, 10, 1053098); + AddRes(index, typeof(Putrefication), 1032678, 10, 1053098); + AddRareRecipe(index, 318); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(BrambleCoat), 1062760, 1072925, 85.0, 120.0, typeof(Log), 1044041, 10, 1044351); + AddRes(index, typeof(DiseasedBark), 1032683, 1, 1053098); + AddRes(index, typeof(Taint), 1032679, 10, 1053098); + AddRes(index, typeof(Scourge), 1032677, 10, 1053098); + AddRareRecipe(index, 319); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + } + + // 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); - 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); + SetNeededExpansion(index, Expansion.SE); + } - 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 + 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); + } - // Misc - 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); - } - - if (Core.ML) - { - index = AddCraft(typeof(ParrotPerchDeed), 1044290, 1072617, 50.0, 85.0, typeof(Log), 1044041, 100, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ArcaneCircleDeed), 1044290, 1072703, 94.7, 119.7, typeof(Log), 1044041, 100, 1044351); - AddRes(index, typeof(BlueDiamond), 1026255, 2, 1053098); - AddRes(index, typeof(PerfectEmerald), 1026251, 2, 1053098); - AddRes(index, typeof(FireRuby), 1026254, 2, 1053098); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(TallElvenBedSouthDeed), 1044290, 1072858, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); - AddSkill(index, SkillName.Tailoring, 75.0, 80.0); - AddRes(index, typeof(Cloth), 1044286, 100, 1044287); - AddRecipe(index, 320); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(TallElvenBedEastDeed), 1044290, 1072859, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); - AddSkill(index, SkillName.Tailoring, 75.0, 80.0); - AddRes(index, typeof(Cloth), 1044286, 100, 1044287); - AddRecipe(index, 321); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenBedSouthDeed), 1044290, 1072860, 94.7, 119.7, typeof(Log), 1044041, 100, 1044351); - AddRes(index, typeof(Cloth), 1044286, 100, 1044287); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenBedEastDeed), 1044290, 1072861, 94.7, 119.7, typeof(Log), 1044041, 100, 1044351); - AddRes(index, typeof(Cloth), 1044286, 100, 1044287); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenLoveseatSouthDeed), 1044290, 1072867, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenLoveseatEastDeed), 1044290, 1073372, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(AlchemistTableSouthDeed), 1044290, 1073396, 85.0, 110.0, typeof(Log), 1044041, 70, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(AlchemistTableEastDeed), 1044290, 1073397, 85.0, 110.0, typeof(Log), 1044041, 70, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } - - index = AddCraft(typeof(SmallBedSouthDeed), 1044290, 1044321, 94.7, 119.8, typeof(Log), 1044041, 100, 1044351); + if (Core.ML) + { + index = AddCraft(typeof(ParrotPerchDeed), 1044290, 1072617, 50.0, 85.0, typeof(Log), 1044041, 100, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ArcaneCircleDeed), 1044290, 1072703, 94.7, 119.7, typeof(Log), 1044041, 100, 1044351); + AddRes(index, typeof(BlueDiamond), 1026255, 2, 1053098); + AddRes(index, typeof(PerfectEmerald), 1026251, 2, 1053098); + AddRes(index, typeof(FireRuby), 1026254, 2, 1053098); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(TallElvenBedSouthDeed), 1044290, 1072858, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); AddSkill(index, SkillName.Tailoring, 75.0, 80.0); AddRes(index, typeof(Cloth), 1044286, 100, 1044287); - index = AddCraft(typeof(SmallBedEastDeed), 1044290, 1044322, 94.7, 119.8, typeof(Log), 1044041, 100, 1044351); + AddRecipe(index, 320); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(TallElvenBedEastDeed), 1044290, 1072859, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); AddSkill(index, SkillName.Tailoring, 75.0, 80.0); AddRes(index, typeof(Cloth), 1044286, 100, 1044287); - index = AddCraft(typeof(LargeBedSouthDeed), 1044290, 1044323, 94.7, 119.8, 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, 119.8, 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); - - // Blacksmithy - This changed to Anvils and Forges (1111809) for SA - if (Core.ML) - { - index = AddCraft(typeof(ElvenForgeDeed), 1044296, 1072875, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } - - 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); - - if (Core.ML) - { - index = AddCraft(typeof(ElvenSpinningWheelEastDeed), 1044298, 1073393, 75.0, 100.0, typeof(Log), 1044041, 60, 1044351); - AddSkill(index, SkillName.Tailoring, 65.0, 85.0); - AddRes(index, typeof(Cloth), 1044286, 40, 1044287); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenSpinningWheelSouthDeed), 1044298, 1072878, 75.0, 100.0, typeof(Log), 1044041, 60, 1044351); - AddSkill(index, SkillName.Tailoring, 65.0, 85.0); - AddRes(index, typeof(Cloth), 1044286, 40, 1044287); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenStoveSouthDeed), 1044298, 1073394, 85.0, 110.0, typeof(Log), 1044041, 80, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - index = AddCraft(typeof(ElvenStoveEastDeed), 1044298, 1073395, 85.0, 110.0, typeof(Log), 1044041, 80, 1044351); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } - - 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); - index = AddCraft(typeof(StoneOvenEastDeed), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 1044299, 1044349, 94.7, 119.7, typeof(Log), 1044041, 150, 1044351); - AddCraft(typeof(WaterTroughSouthDeed), Core.ML ? 1044298 : 1044299, 1044350, 94.7, 119.7, typeof(Log), 1044041, 150, 1044351); - - MarkOption = true; - Repair = Core.AOS; - - SetSubRes(typeof(Log), 1072643); - - // Add every material you want the player to be able to choose from - // This will override the overridable material TODO: Verify the required skill amount - AddSubRes(typeof(Log), 1072643, 00.0, 1044041, 1072652); - AddSubRes(typeof(OakLog), 1072644, 65.0, 1044041, 1072652); - AddSubRes(typeof(AshLog), 1072645, 80.0, 1044041, 1072652); - AddSubRes(typeof(YewLog), 1072646, 95.0, 1044041, 1072652); - AddSubRes(typeof(HeartwoodLog), 1072647, 100.0, 1044041, 1072652); - AddSubRes(typeof(BloodwoodLog), 1072648, 100.0, 1044041, 1072652); - AddSubRes(typeof(FrostwoodLog), 1072649, 100.0, 1044041, 1072652); + AddRecipe(index, 321); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenBedSouthDeed), 1044290, 1072860, 94.7, 119.7, typeof(Log), 1044041, 100, 1044351); + AddRes(index, typeof(Cloth), 1044286, 100, 1044287); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenBedEastDeed), 1044290, 1072861, 94.7, 119.7, typeof(Log), 1044041, 100, 1044351); + AddRes(index, typeof(Cloth), 1044286, 100, 1044287); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenLoveseatSouthDeed), 1044290, 1072867, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenLoveseatEastDeed), 1044290, 1073372, 80.0, 105.0, typeof(Log), 1044041, 50, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(AlchemistTableSouthDeed), 1044290, 1073396, 85.0, 110.0, typeof(Log), 1044041, 70, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(AlchemistTableEastDeed), 1044290, 1073397, 85.0, 110.0, typeof(Log), 1044041, 70, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); } + + index = AddCraft(typeof(SmallBedSouthDeed), 1044290, 1044321, 94.7, 119.8, 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, 119.8, 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, 119.8, 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, 119.8, 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); + + // Blacksmithy - This changed to Anvils and Forges (1111809) for SA + if (Core.ML) + { + index = AddCraft(typeof(ElvenForgeDeed), 1044296, 1072875, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + } + + 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); + + if (Core.ML) + { + index = AddCraft(typeof(ElvenSpinningWheelEastDeed), 1044298, 1073393, 75.0, 100.0, typeof(Log), 1044041, 60, 1044351); + AddSkill(index, SkillName.Tailoring, 65.0, 85.0); + AddRes(index, typeof(Cloth), 1044286, 40, 1044287); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenSpinningWheelSouthDeed), 1044298, 1072878, 75.0, 100.0, typeof(Log), 1044041, 60, 1044351); + AddSkill(index, SkillName.Tailoring, 65.0, 85.0); + AddRes(index, typeof(Cloth), 1044286, 40, 1044287); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenStoveSouthDeed), 1044298, 1073394, 85.0, 110.0, typeof(Log), 1044041, 80, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + index = AddCraft(typeof(ElvenStoveEastDeed), 1044298, 1073395, 85.0, 110.0, typeof(Log), 1044041, 80, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + } + + 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); + index = AddCraft(typeof(StoneOvenEastDeed), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 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), Core.ML ? 1044298 : 1044299, 1044349, 94.7, 119.7, typeof(Log), 1044041, 150, 1044351); + AddCraft(typeof(WaterTroughSouthDeed), Core.ML ? 1044298 : 1044299, 1044350, 94.7, 119.7, typeof(Log), 1044041, 150, 1044351); + + MarkOption = true; + Repair = Core.AOS; + + SetSubRes(typeof(Log), 1072643); + + // Add every material you want the player to be able to choose from + // This will override the overridable material TODO: Verify the required skill amount + AddSubRes(typeof(Log), 1072643, 00.0, 1044041, 1072652); + AddSubRes(typeof(OakLog), 1072644, 65.0, 1044041, 1072652); + AddSubRes(typeof(AshLog), 1072645, 80.0, 1044041, 1072652); + AddSubRes(typeof(YewLog), 1072646, 95.0, 1044041, 1072652); + AddSubRes(typeof(HeartwoodLog), 1072647, 100.0, 1044041, 1072652); + AddSubRes(typeof(BloodwoodLog), 1072648, 100.0, 1044041, 1072652); + AddSubRes(typeof(FrostwoodLog), 1072649, 100.0, 1044041, 1072652); } } diff --git a/Projects/UOContent/Engines/Craft/DefCartography.cs b/Projects/UOContent/Engines/Craft/DefCartography.cs index 0c93b0fa6..305429956 100644 --- a/Projects/UOContent/Engines/Craft/DefCartography.cs +++ b/Projects/UOContent/Engines/Craft/DefCartography.cs @@ -1,88 +1,90 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefCartography : CraftSystem { - public class DefCartography : CraftSystem + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefCartography(); + } - private DefCartography() : base(1, 1, 1.25) // base( 1, 1, 3.0 ) + private DefCartography() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Cartography; + + public override TextDefinition GumpTitle => 1044008; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) => 0.0; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Cartography; - - public override int GumpTitleNumber => 1044008; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefCartography(); - - public override double GetChanceAtMin(CraftItem item) => 0.0; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckAccessible(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + return 1044263; // The tool must be on your person to use. } - public override void PlayCraftEffect(Mobile from) + 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.PlaySound(0x249); + from.SendLocalizedMessage(1044038); // You have worn out your tool } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (failed) { - if (toolBroken) + if (lostMaterial) { - from.SendLocalizedMessage(1044038); // You have worn out your tool + return 1044043; // You failed to create the item, and some of your materials are lost. } - if (failed) - { - if (lostMaterial) - { - return 1044043; // You failed to create the item, and some of your materials are lost. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 1044157; // You failed to create the item, but no materials were lost. } - public override void InitCraftList() + if (quality == 0) { - 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); + return 502785; // You were barely able to make this item. It's quality is below average. } + + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } + + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + 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); } } diff --git a/Projects/UOContent/Engines/Craft/DefCooking.cs b/Projects/UOContent/Engines/Craft/DefCooking.cs index 199c287cc..0938dbcae 100644 --- a/Projects/UOContent/Engines/Craft/DefCooking.cs +++ b/Projects/UOContent/Engines/Craft/DefCooking.cs @@ -1,439 +1,437 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefCooking : CraftSystem { - public class DefCooking : CraftSystem + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefCooking(); + } - private DefCooking() : base(1, 1, 1.25) // base( 1, 1, 1.5 ) + private DefCooking() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Cooking; + + public override TextDefinition GumpTitle => 1044003; + + public static CraftSystem CraftSystem { get; private set; } + + public override CraftECA ECA => CraftECA.ChanceMinusSixtyToFourtyFive; + + public override double GetChanceAtMin(CraftItem item) => 0.0; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Cooking; + // The tool must be on your person to use. + return !BaseTool.CheckAccessible(tool, from) ? 1044263 : 0; + } - public override int GumpTitleNumber => 1044003; + public override void PlayCraftEffect(Mobile from) + { + } - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefCooking(); - - public override CraftECA ECA => CraftECA.ChanceMinusSixtyToFourtyFive; - - public override double GetChanceAtMin(CraftItem item) => 0.0; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + public override int PlayEndingEffect( + Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, + bool makersMark, CraftItem item + ) + { + if (toolBroken) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + from.SendLocalizedMessage(1044038); // You have worn out your tool } - public override void PlayCraftEffect(Mobile from) + if (failed) { + if (lostMaterial) + { + return 1044043; // You failed to create the item, and some of your materials are lost. + } + + return 1044157; // You failed to create the item, but no materials were lost. } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (quality == 0) { - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 502785; // You were barely able to make this item. It's quality is below average. } - public override void InitCraftList() + if (makersMark && quality == 2) { - int index; + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } - /* Begin Ingredients */ - index = AddCraft(typeof(SackFlour), 1044495, 1024153, 0.0, 100.0, typeof(WheatSheaf), 1044489, 2, 1044490); - SetNeedMill(index, true); + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } - index = AddCraft(typeof(Dough), 1044495, 1024157, 0.0, 100.0, typeof(SackFlour), 1044468, 1, 1044253); - AddRes(index, typeof(BaseBeverage), 1046458, 1, 1044253); + return 1044154; // You create the item. + } - index = AddCraft(typeof(SweetDough), 1044495, 1041340, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(JarHoney), 1044472, 1, 1044253); + public override void InitCraftList() + { + int index; - index = AddCraft(typeof(CakeMix), 1044495, 1041002, 0.0, 100.0, typeof(SackFlour), 1044468, 1, 1044253); - AddRes(index, typeof(SweetDough), 1044475, 1, 1044253); + /* Begin Ingredients */ + index = AddCraft(typeof(SackFlour), 1044495, 1024153, 0.0, 100.0, typeof(WheatSheaf), 1044489, 2, 1044490); + SetNeedMill(index, true); - index = AddCraft(typeof(CookieMix), 1044495, 1024159, 0.0, 100.0, typeof(JarHoney), 1044472, 1, 1044253); - AddRes(index, typeof(SweetDough), 1044475, 1, 1044253); + index = AddCraft(typeof(Dough), 1044495, 1024157, 0.0, 100.0, typeof(SackFlour), 1044468, 1, 1044253); + AddRes(index, typeof(BaseBeverage), 1046458, 1, 1044253); - if (Core.ML) - { - index = AddCraft(typeof(CocoaButter), 1044495, 1079998, 0.0, 100.0, typeof(CocoaPulp), 1080530, 1, 1044253); - SetItemHue(index, 0x457); - SetNeededExpansion(index, Expansion.ML); - SetNeedOven(index, true); + 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(CocoaLiquor), 1044495, 1079999, 0.0, 100.0, typeof(CocoaPulp), 1080530, 1, 1044253); - AddRes(index, typeof(EmptyPewterBowl), 1025629, 1, 1044253); - SetItemHue(index, 0x46A); - SetNeededExpansion(index, Expansion.ML); - SetNeedOven(index, true); - } - /* End Ingredients */ + index = AddCraft(typeof(CakeMix), 1044495, 1041002, 0.0, 100.0, typeof(SackFlour), 1044468, 1, 1044253); + AddRes(index, typeof(SweetDough), 1044475, 1, 1044253); - /* Begin Preparations */ - index = AddCraft(typeof(UnbakedQuiche), 1044496, 1041339, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(Eggs), 1044477, 1, 1044253); + index = AddCraft(typeof(CookieMix), 1044495, 1024159, 0.0, 100.0, typeof(JarHoney), 1044472, 1, 1044253); + AddRes(index, typeof(SweetDough), 1044475, 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); + if (Core.ML) + { + index = AddCraft(typeof(CocoaButter), 1044495, 1079998, 0.0, 100.0, typeof(CocoaPulp), 1080530, 1, 1044253); + SetItemHue(index, 0x457); + SetNeededExpansion(index, Expansion.ML); + SetNeedOven(index, true); - 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(CocoaLiquor), 1044495, 1079999, 0.0, 100.0, typeof(CocoaPulp), 1080530, 1, 1044253); + AddRes(index, typeof(EmptyPewterBowl), 1025629, 1, 1044253); + SetItemHue(index, 0x46A); + SetNeededExpansion(index, Expansion.ML); + SetNeedOven(index, true); + } + /* End Ingredients */ - index = AddCraft(typeof(UncookedCheesePizza), 1044496, 1041341, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(CheeseWheel), 1044486, 1, 1044253); + /* Begin Preparations */ + index = AddCraft(typeof(UnbakedQuiche), 1044496, 1041339, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); + AddRes(index, typeof(Eggs), 1044477, 1, 1044253); - index = AddCraft(typeof(UnbakedFruitPie), 1044496, 1041334, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(Pear), 1044481, 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(UnbakedPeachCobbler), 1044496, 1041335, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(Peach), 1044480, 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(UnbakedApplePie), 1044496, 1041336, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(Apple), 1044479, 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(UnbakedPumpkinPie), 1044496, 1041342, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); - AddRes(index, typeof(Pumpkin), 1044484, 1, 1044253); + index = AddCraft(typeof(UnbakedFruitPie), 1044496, 1041334, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); + AddRes(index, typeof(Pear), 1044481, 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(UnbakedPeachCobbler), 1044496, 1041335, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); + AddRes(index, typeof(Peach), 1044480, 1, 1044253); - 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(UnbakedApplePie), 1044496, 1041336, 0.0, 100.0, typeof(Dough), 1044469, 1, 1044253); + AddRes(index, typeof(Apple), 1044479, 1, 1044253); - 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(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(TribalPaint), + typeof(GreenTea), 1044496, - 1040000, - Core.ML ? 55.0 : 80.0, - Core.ML ? 105.0 : 80.0, - typeof(SackFlour), - 1044468, + 1030315, + 80.0, + 130.0, + typeof(GreenTeaBasket), + 1030316, 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); + AddRes(index, typeof(BaseBeverage), 1046458, 1, 1044253); + SetNeededExpansion(index, Expansion.SE); SetNeedOven(index, true); index = AddCraft( - typeof(SausagePizza), - 1044497, - 1044517, - 0.0, - 100.0, - typeof(UncookedSausagePizza), - 1044520, + typeof(WasabiClumps), + 1044496, + 1029451, + 70.0, + 120.0, + typeof(BaseBeverage), + 1046458, 1, 1044253 ); - SetNeedOven(index, true); + AddRes(index, typeof(WoodenBowlOfPeas), 1025633, 3, 1044253); + SetNeededExpansion(index, Expansion.SE); index = AddCraft( - typeof(CheesePizza), - 1044497, - 1044516, - 0.0, - 100.0, - typeof(UncookedCheesePizza), - 1044521, + typeof(SushiRolls), + 1044496, + 1030303, + 90.0, + 120.0, + typeof(BaseBeverage), + 1046458, 1, 1044253 ); - SetNeedOven(index, true); - - index = AddCraft(typeof(FruitPie), 1044497, 1041346, 0.0, 100.0, typeof(UnbakedFruitPie), 1044522, 1, 1044253); - SetNeedOven(index, true); + AddRes(index, typeof(RawFishSteak), 1044476, 10, 1044253); + SetNeededExpansion(index, Expansion.SE); index = AddCraft( - typeof(PeachCobbler), - 1044497, - 1041344, - 0.0, - 100.0, - typeof(UnbakedPeachCobbler), - 1044523, + typeof(SushiPlatter), + 1044496, + 1030305, + 90.0, + 120.0, + typeof(BaseBeverage), + 1046458, 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 */ - - /* Begin Chocolatiering */ - if (Core.ML) - { - index = AddCraft( - typeof(DarkChocolate), - 1080001, - 1079994, - 15.0, - 100.0, - typeof(SackOfSugar), - 1079997, - 1, - 1044253 - ); - AddRes(index, typeof(CocoaButter), 1079998, 1, 1044253); - AddRes(index, typeof(CocoaLiquor), 1079999, 1, 1044253); - SetItemHue(index, 0x465); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(MilkChocolate), - 1080001, - 1079995, - 32.5, - 107.5, - typeof(SackOfSugar), - 1079997, - 1, - 1044253 - ); - AddRes(index, typeof(CocoaButter), 1079998, 1, 1044253); - AddRes(index, typeof(CocoaLiquor), 1079999, 1, 1044253); - AddRes(index, typeof(BaseBeverage), 1022544, 1, 1044253); - SetBeverageType(index, BeverageType.Milk); - SetItemHue(index, 0x461); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(WhiteChocolate), - 1080001, - 1079996, - 52.5, - 127.5, - typeof(SackOfSugar), - 1079997, - 1, - 1044253 - ); - AddRes(index, typeof(CocoaButter), 1079998, 1, 1044253); - AddRes(index, typeof(Vanilla), 1080000, 1, 1044253); - AddRes(index, typeof(BaseBeverage), 1022544, 1, 1044253); - SetBeverageType(index, BeverageType.Milk); - SetItemHue(index, 0x47E); - SetNeededExpansion(index, Expansion.ML); - } - - /* End Chocolatiering */ + AddRes(index, typeof(RawFishSteak), 1044476, 10, 1044253); + SetNeededExpansion(index, Expansion.SE); } + + index = AddCraft( + typeof(TribalPaint), + 1044496, + 1040000, + Core.ML ? 55.0 : 80.0, + Core.ML ? 105.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 */ + + /* Begin Chocolatiering */ + if (Core.ML) + { + index = AddCraft( + typeof(DarkChocolate), + 1080001, + 1079994, + 15.0, + 100.0, + typeof(SackOfSugar), + 1079997, + 1, + 1044253 + ); + AddRes(index, typeof(CocoaButter), 1079998, 1, 1044253); + AddRes(index, typeof(CocoaLiquor), 1079999, 1, 1044253); + SetItemHue(index, 0x465); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(MilkChocolate), + 1080001, + 1079995, + 32.5, + 107.5, + typeof(SackOfSugar), + 1079997, + 1, + 1044253 + ); + AddRes(index, typeof(CocoaButter), 1079998, 1, 1044253); + AddRes(index, typeof(CocoaLiquor), 1079999, 1, 1044253); + AddRes(index, typeof(BaseBeverage), 1022544, 1, 1044253); + SetBeverageType(index, BeverageType.Milk); + SetItemHue(index, 0x461); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(WhiteChocolate), + 1080001, + 1079996, + 52.5, + 127.5, + typeof(SackOfSugar), + 1079997, + 1, + 1044253 + ); + AddRes(index, typeof(CocoaButter), 1079998, 1, 1044253); + AddRes(index, typeof(Vanilla), 1080000, 1, 1044253); + AddRes(index, typeof(BaseBeverage), 1022544, 1, 1044253); + SetBeverageType(index, BeverageType.Milk); + SetItemHue(index, 0x47E); + SetNeededExpansion(index, Expansion.ML); + } + + /* End Chocolatiering */ } } diff --git a/Projects/UOContent/Engines/Craft/DefGlassblowing.cs b/Projects/UOContent/Engines/Craft/DefGlassblowing.cs index bf4793f13..0157f734a 100644 --- a/Projects/UOContent/Engines/Craft/DefGlassblowing.cs +++ b/Projects/UOContent/Engines/Craft/DefGlassblowing.cs @@ -2,123 +2,125 @@ using System; using Server.Items; using Server.Mobiles; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefGlassblowing : CraftSystem { - public class DefGlassblowing : CraftSystem + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefGlassblowing(); + } - private DefGlassblowing() : base(1, 1, 1.25) // base( 1, 2, 1.7 ) + private DefGlassblowing() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Alchemy; + + public override TextDefinition GumpTitle => 1044622; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) => item.ItemType == typeof(HollowPrism) ? 0.5 : 0.0; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Alchemy; - - public override int GumpTitleNumber => 1044622; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefGlassblowing(); - - public override double GetChanceAtMin(CraftItem item) => item.ItemType == typeof(HollowPrism) ? 0.5 : 0.0; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckTool(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckTool(tool, from)) - { - return 1048146; // If you have a tool equipped, you must use that tool. - } - - if (!(from is PlayerMobile mobile && mobile.Glassblowing && mobile.Skills.Alchemy.Base >= 100.0)) - { - return 1044634; // You havent learned glassblowing. - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - DefBlacksmithy.CheckAnvilAndForge(from, 2, out _, out var forge); - - return forge ? 0 : 1044628; // You must be near a forge to blow glass. + return 1048146; // If you have a tool equipped, you must use that tool. } - public override void PlayCraftEffect(Mobile from) + if (!(from is PlayerMobile mobile && mobile.Glassblowing && mobile.Skills.Alchemy.Base >= 100.0)) { - from.PlaySound(0x2B); // bellows - - // if (from.Body.Type == BodyType.Human && !from.Mounted) - // from.Animate( 9, 5, 1, true, false, 0 ); - - // new InternalTimer( from ).Start(); + return 1044634; // You havent learned glassblowing. } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (!BaseTool.CheckAccessible(tool, from)) { - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - from.PlaySound(0x41); // glass breaking - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 1044263; // The tool must be on your person to use. } - public override void InitCraftList() + DefBlacksmithy.CheckAnvilAndForge(from, 2, out _, out var forge); + + return forge ? 0 : 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(); + } + + public override int PlayEndingEffect( + Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, + bool makersMark, CraftItem item + ) + { + if (toolBroken) { - var index = AddCraft(typeof(Bottle), 1044050, 1023854, 52.5, 102.5, typeof(Sand), 1044625, 1, 1044627); - SetUseAllRes(index, true); + from.SendLocalizedMessage(1044038); // You have worn out your tool + } - 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); - - if (Core.ML) + if (failed) + { + if (lostMaterial) { - index = AddCraft(typeof(HollowPrism), 1044050, 1072895, 100.0, 150.0, typeof(Sand), 1044625, 8, 1044627); - SetNeededExpansion(index, Expansion.ML); + return 1044043; // You failed to create the item, and some of your materials are lost. } + + return 1044157; // You failed to create the item, but no materials were lost. + } + + from.PlaySound(0x41); // glass breaking + + if (quality == 0) + { + return 502785; // You were barely able to make this item. It's quality is below average. + } + + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } + + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + return 1044154; // You create the item. + } + + public override void InitCraftList() + { + var 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); + + if (Core.ML) + { + index = AddCraft(typeof(HollowPrism), 1044050, 1072895, 100.0, 150.0, typeof(Sand), 1044625, 8, 1044627); + SetNeededExpansion(index, Expansion.ML); } } } diff --git a/Projects/UOContent/Engines/Craft/DefInscription.cs b/Projects/UOContent/Engines/Craft/DefInscription.cs index df75d4cb4..e6c33bb8c 100644 --- a/Projects/UOContent/Engines/Craft/DefInscription.cs +++ b/Projects/UOContent/Engines/Craft/DefInscription.cs @@ -4,622 +4,638 @@ using Server.Items; using Server.Spells; using Server.Utilities; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefInscription : CraftSystem { - public class DefInscription : CraftSystem + private static readonly Type typeofSpellScroll = typeof(SpellScroll); + + private static readonly Type[] regTypes = { - private static CraftSystem m_CraftSystem; + typeof(BlackPearl), + typeof(Bloodmoss), + typeof(Garlic), + typeof(Ginseng), + typeof(MandrakeRoot), + typeof(Nightshade), + typeof(SulfurousAsh), + typeof(SpidersSilk) + }; - private static readonly Type typeofSpellScroll = typeof(SpellScroll); + private int _circle, _mana; + private int _index; - private readonly Type[] m_RegTypes = - { - typeof(BlackPearl), - typeof(Bloodmoss), - typeof(Garlic), - typeof(Ginseng), - typeof(MandrakeRoot), - typeof(Nightshade), - typeof(SulfurousAsh), - typeof(SpidersSilk) - }; - - private int m_Circle, m_Mana; - - private int m_Index; - - private DefInscription() - : base(1, 1, 1.25) // base( 1, 1, 3.0 ) + public static void Initialize() + { + CraftSystem = new DefInscription(); + } + + private DefInscription() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Inscribe; + + public override TextDefinition GumpTitle => 1044009; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) => 0.0; + + public override int CanCraft(Mobile from, BaseTool tool, Type typeItem) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Inscribe; - - public override int GumpTitleNumber => 1044009; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefInscription(); - - public override double GetChanceAtMin(CraftItem item) => 0.0; - - public override int CanCraft(Mobile from, BaseTool tool, Type typeItem) + if (!BaseTool.CheckAccessible(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - if (typeItem != null) - { - var scroll = typeItem.CreateEntityInstance(); - - if (scroll != null) - { - var hasSpell = Spellbook.Find(from, scroll.SpellID)?.HasSpell(scroll.SpellID) == true; - - scroll.Delete(); - - return hasSpell ? 0 : 1042404; // null : You don't have that spell! - } - } - - return 0; + return 1044263; // The tool must be on your person to use. } - public override void PlayCraftEffect(Mobile from) + if (typeItem != null) { - from.PlaySound(0x249); + var scroll = typeItem.CreateEntityInstance(); + + if (scroll != null) + { + var hasSpell = Spellbook.Find(from, scroll.SpellID)?.HasSpell(scroll.SpellID) == true; + + scroll.Delete(); + + return hasSpell ? 0 : 1042404; // null : You don't have that spell! + } } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + 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) { - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. - } + from.SendLocalizedMessage(1044038); // You have worn out your tool + } + if (!typeofSpellScroll.IsAssignableFrom(item.ItemType)) // not a scroll + { if (failed) { - return 501630; // You fail to inscribe the scroll, and the scroll is ruined. + if (lostMaterial) + { + return 1044043; // You failed to create the item, and some of your materials are lost. + } + + return 1044157; // You failed to create the item, but no materials were lost. } - return 501629; // You inscribe the spell and put the scroll in your backpack. + if (quality == 0) + { + return 502785; // You were barely able to make this item. It's quality is below average. + } + + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } + + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + return 1044154; // You create the item. } - private void AddSpell(Type type, params Reg[] regs) + if (failed) { - double minSkill, maxSkill; + return 501630; // You fail to inscribe the scroll, and the scroll is ruined. + } - switch (m_Circle) - { - default: + return 501629; // You inscribe the spell and put the scroll in your backpack. + } + + private void AddSpell(Type type, params Reg[] regs) + { + double minSkill, maxSkill; + + switch (_circle) + { + default: + { minSkill = -25.0; maxSkill = 25.0; break; - case 1: + } + case 1: + { minSkill = -10.8; maxSkill = 39.2; break; - case 2: + } + case 2: + { minSkill = 03.5; maxSkill = 53.5; break; - case 3: + } + case 3: + { minSkill = 17.8; maxSkill = 67.8; break; - case 4: + } + case 4: + { minSkill = 32.1; maxSkill = 82.1; break; - case 5: + } + case 5: + { minSkill = 46.4; maxSkill = 96.4; break; - case 6: + } + case 6: + { minSkill = 60.7; maxSkill = 110.7; break; - case 7: + } + case 7: + { minSkill = 75.0; maxSkill = 125.0; break; - } + } + } - var 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] + var index = AddCraft( + type, + 1044369 + _circle, + 1044381 + _index++, + minSkill, + maxSkill, + regTypes[(int)regs[0]], + 1044353 + (int)regs[0], + 1, + 1044361 + (int)regs[0] + ); + + for (var i = 1; i < regs.Length; ++i) + { + AddRes(index, regTypes[(int)regs[i]], 1044353 + (int)regs[i], 1, 1044361 + (int)regs[i]); + } + + AddRes(index, typeof(BlankScroll), 1044377, 1, 1044378); + + SetManaReq(index, _mana); + } + + private void AddNecroSpell(int spell, int mana, double minSkill, Type type, params Type[] regs) + { + var index = AddCraft( + type, + 1061677, + 1060509 + spell, + minSkill, + minSkill + 1.0, // Yes, on OSI it's only 1.0 skill diff'. Don't blame me, blame OSI. + regs[0], + CraftItem.LabelNumber(regs[0]), + 1, + 501627 + ); + + for (var i = 1; i < regs.Length; ++i) + { + AddRes(index, regs[i], CraftItem.LabelNumber(regs[0]), 1, 501627); + } + + AddRes(index, typeof(BlankScroll), 1044377, 1, 1044378); + SetManaReq(index, mana); + } + + private void AddMysticismSpell(int spell, int mana, double minSkill, double maxSkill, Type type, params Type[] regs) + { + var index = AddCraft( + type, + 1111671, + 1031678 + spell, + minSkill, + maxSkill, + regs[0], + CraftItem.LabelNumber(regs[0]), + 1, + 501627 + ); + + for (var i = 1; i < regs.Length; ++i) + { + AddRes(index, regs[i], CraftItem.LabelNumber(regs[i]), 1, 501627); + } + + AddRes(index, typeof(BlankScroll), 1044377, 1, 1044378); + SetManaReq(index, mana); + } + + public override void InitCraftList() + { + _circle = 0; + _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); + + _circle = 1; + _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); + + _circle = 2; + _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(TelekinesisScroll), Reg.Bloodmoss, Reg.MandrakeRoot); + AddSpell(typeof(TeleportScroll), Reg.Bloodmoss, Reg.MandrakeRoot); + AddSpell(typeof(UnlockScroll), Reg.Bloodmoss, Reg.SulfurousAsh); + AddSpell(typeof(WallOfStoneScroll), Reg.Bloodmoss, Reg.Garlic); + + _circle = 3; + _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); + + _circle = 4; + _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); + + _circle = 5; + _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); + + _circle = 6; + _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); + + _circle = 7; + _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 ); - - for (var 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) - { - var index = AddCraft( - type, - 1061677, - 1060509 + spell, - minSkill, - minSkill + 1.0, // Yes, on OSI it's only 1.0 skill diff'. Don't blame me, blame OSI. - regs[0], - CraftItem.LabelNumber(regs[0]), - 1, - 501627 + 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 ); - - for (var i = 1; i < regs.Length; ++i) - { - AddRes(index, regs[i], CraftItem.LabelNumber(regs[0]), 1, 501627); - } - - AddRes(index, typeof(BlankScroll), 1044377, 1, 1044378); - SetManaReq(index, mana); - } - - private void AddMysticismSpell(int spell, int mana, double minSkill, double maxSkill, Type type, params Type[] regs) - { - var index = AddCraft( - type, - 1111671, - 1031678 + spell, - minSkill, - maxSkill, - regs[0], - CraftItem.LabelNumber(regs[0]), - 1, - 501627 + AddNecroSpell( + 12, + 23, + 98.6, + typeof(VampiricEmbraceScroll), + Reagent.BatWing, + Reagent.NoxCrystal, + Reagent.PigIron ); - - for (var i = 1; i < regs.Length; ++i) - { - AddRes(index, regs[i], CraftItem.LabelNumber(regs[i]), 1, 501627); - } - - AddRes(index, typeof(BlankScroll), 1044377, 1, 1044378); - SetManaReq(index, mana); + 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); } - public override void InitCraftList() + int index; + + if (Core.ML) { - m_Circle = 0; - m_Mana = 4; + index = AddCraft( + typeof(EnchantedSwitch), + 1044294, + 1072893, + 45.0, + 95.0, + typeof(BlankScroll), + 1044377, + 1, + 1044378 + ); + AddRes(index, typeof(SpidersSilk), 1044360, 1, 1044253); + AddRes(index, typeof(BlackPearl), 1044353, 1, 1044253); + AddRes(index, typeof(SwitchItem), 1073464, 1, 1044253); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); - 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(TelekinesisScroll), Reg.Bloodmoss, Reg.MandrakeRoot); - 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); - } - - int index; - - if (Core.ML) - { - index = AddCraft( - typeof(EnchantedSwitch), - 1044294, - 1072893, - 45.0, - 95.0, - typeof(BlankScroll), - 1044377, - 1, - 1044378 - ); - AddRes(index, typeof(SpidersSilk), 1044360, 1, 1044253); - AddRes(index, typeof(BlackPearl), 1044353, 1, 1044253); - AddRes(index, typeof(SwitchItem), 1073464, 1, 1044253); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(RunedPrism), 1044294, 1073465, 45.0, 95.0, typeof(BlankScroll), 1044377, 1, 1044378); - AddRes(index, typeof(SpidersSilk), 1044360, 1, 1044253); - AddRes(index, typeof(BlackPearl), 1044353, 1, 1044253); - AddRes(index, typeof(HollowPrism), 1072895, 1, 1044253); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } - - // Runebook - 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) - { - AddCraft(typeof(BulkOrderBook), 1044294, 1028793, 65.0, 115.0, typeof(BlankScroll), 1044377, 10, 1044378); - } - - if (Core.SE) - { - AddCraft(typeof(Spellbook), 1044294, 1023834, 50.0, 126, typeof(BlankScroll), 1044377, 10, 1044378); - } - - /* TODO - if (Core.ML) - { - index = AddCraft( typeof( ScrappersCompendium ), 1044294, 1072940, 75.0, 125.0, typeof( BlankScroll ), 1044377, 100, 1044378 ); - AddRes( index, typeof( DreadHornMane ), 1032682, 1, 1044253 ); - AddRes( index, typeof( Taint ), 1032679, 10, 1044253 ); - AddRes( index, typeof( Corruption ), 1032676, 10, 1044253 ); - AddRareRecipe( index, 400 ); - ForceNonExceptional( index ); - SetNeededExpansion( index, Expansion.ML ); - } - */ - - if (Core.SA) - { - AddCraft(typeof(MysticSpellbook), 1044294, 1031677, 50.0, 150.0, typeof(BlankScroll), 1044377, 10, 1044378); - - AddMysticismSpell(0, 4, -25.0, 25.0, typeof(NetherBoltScroll), Reagent.BlackPearl, Reagent.SulfurousAsh); - AddMysticismSpell( - 1, - 4, - -25.0, - 25.0, - typeof(HealingStoneScroll), - Reagent.Bone, - Reagent.Garlic, - Reagent.Ginseng, - Reagent.SpidersSilk - ); - AddMysticismSpell( - 2, - 6, - -10.8, - 39.2, - typeof(PurgeMagicScroll), - Reagent.FertileDirt, - Reagent.Garlic, - Reagent.MandrakeRoot, - Reagent.SulfurousAsh - ); - AddMysticismSpell( - 3, - 6, - -10.8, - 39.2, - typeof(EnchantScroll), - Reagent.SpidersSilk, - Reagent.MandrakeRoot, - Reagent.SulfurousAsh - ); - AddMysticismSpell( - 4, - 9, - 3.5, - 53.5, - typeof(SleepScroll), - Reagent.Nightshade, - Reagent.SpidersSilk, - Reagent.BlackPearl - ); - AddMysticismSpell( - 5, - 9, - 3.5, - 53.5, - typeof(EagleStrikeScroll), - Reagent.Bloodmoss, - Reagent.Bone, - Reagent.SpidersSilk, - Reagent.MandrakeRoot - ); - AddMysticismSpell( - 6, - 11, - 17.8, - 67.8, - typeof(AnimatedWeaponScroll), - Reagent.Bone, - Reagent.BlackPearl, - Reagent.MandrakeRoot, - Reagent.Nightshade - ); - AddMysticismSpell( - 7, - 11, - 17.8, - 67.8, - typeof(StoneFormScroll), - Reagent.Bloodmoss, - Reagent.FertileDirt, - Reagent.Garlic - ); - AddMysticismSpell( - 8, - 14, - 32.1, - 82.1, - typeof(SpellTriggerScroll), - Reagent.DragonsBlood, - Reagent.Garlic, - Reagent.MandrakeRoot, - Reagent.SpidersSilk - ); - AddMysticismSpell( - 9, - 14, - 32.1, - 82.1, - typeof(MassSleepScroll), - Reagent.Ginseng, - Reagent.Nightshade, - Reagent.SpidersSilk - ); - AddMysticismSpell( - 10, - 20, - 46.4, - 96.4, - typeof(CleansingWindsScroll), - Reagent.DragonsBlood, - Reagent.Garlic, - Reagent.Ginseng, - Reagent.MandrakeRoot - ); - AddMysticismSpell( - 11, - 20, - 46.4, - 96.4, - typeof(BombardScroll), - Reagent.Bloodmoss, - Reagent.DragonsBlood, - Reagent.Garlic, - Reagent.SulfurousAsh - ); - AddMysticismSpell( - 12, - 40, - 60.7, - 110.7, - typeof(SpellPlagueScroll), - Reagent.DaemonBone, - Reagent.DragonsBlood, - Reagent.Nightshade, - Reagent.SulfurousAsh - ); - AddMysticismSpell( - 13, - 40, - 60.7, - 110.7, - typeof(HailStormScroll), - Reagent.DragonsBlood, - Reagent.Bloodmoss, - Reagent.BlackPearl, - Reagent.MandrakeRoot - ); - AddMysticismSpell( - 14, - 50, - 75.0, - 125.0, - typeof(NetherCycloneScroll), - Reagent.MandrakeRoot, - Reagent.Nightshade, - Reagent.SulfurousAsh, - Reagent.Bloodmoss - ); - AddMysticismSpell( - 15, - 50, - 75.0, - 125.0, - typeof(RisingColossusScroll), - Reagent.DaemonBone, - Reagent.DragonsBlood, - Reagent.FertileDirt, - Reagent.Nightshade - ); - } - - MarkOption = true; + index = AddCraft(typeof(RunedPrism), 1044294, 1073465, 45.0, 95.0, typeof(BlankScroll), 1044377, 1, 1044378); + AddRes(index, typeof(SpidersSilk), 1044360, 1, 1044253); + AddRes(index, typeof(BlackPearl), 1044353, 1, 1044253); + AddRes(index, typeof(HollowPrism), 1072895, 1, 1044253); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); } - private enum Reg + // Runebook + 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) { - BlackPearl, - Bloodmoss, - Garlic, - Ginseng, - MandrakeRoot, - Nightshade, - SulfurousAsh, - SpidersSilk + AddCraft(typeof(BulkOrderBook), 1044294, 1028793, 65.0, 115.0, typeof(BlankScroll), 1044377, 10, 1044378); } + + if (Core.SE) + { + AddCraft(typeof(Spellbook), 1044294, 1023834, 50.0, 126, typeof(BlankScroll), 1044377, 10, 1044378); + } + + /* TODO + if (Core.ML) + { + index = AddCraft( typeof( ScrappersCompendium ), 1044294, 1072940, 75.0, 125.0, typeof( BlankScroll ), 1044377, 100, 1044378 ); + AddRes( index, typeof( DreadHornMane ), 1032682, 1, 1044253 ); + AddRes( index, typeof( Taint ), 1032679, 10, 1044253 ); + AddRes( index, typeof( Corruption ), 1032676, 10, 1044253 ); + AddRareRecipe( index, 400 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + } + */ + + if (Core.SA) + { + AddCraft(typeof(MysticSpellbook), 1044294, 1031677, 50.0, 150.0, typeof(BlankScroll), 1044377, 10, 1044378); + + AddMysticismSpell(0, 4, -25.0, 25.0, typeof(NetherBoltScroll), Reagent.BlackPearl, Reagent.SulfurousAsh); + AddMysticismSpell( + 1, + 4, + -25.0, + 25.0, + typeof(HealingStoneScroll), + Reagent.Bone, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.SpidersSilk + ); + AddMysticismSpell( + 2, + 6, + -10.8, + 39.2, + typeof(PurgeMagicScroll), + Reagent.FertileDirt, + Reagent.Garlic, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + AddMysticismSpell( + 3, + 6, + -10.8, + 39.2, + typeof(EnchantScroll), + Reagent.SpidersSilk, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + AddMysticismSpell( + 4, + 9, + 3.5, + 53.5, + typeof(SleepScroll), + Reagent.Nightshade, + Reagent.SpidersSilk, + Reagent.BlackPearl + ); + AddMysticismSpell( + 5, + 9, + 3.5, + 53.5, + typeof(EagleStrikeScroll), + Reagent.Bloodmoss, + Reagent.Bone, + Reagent.SpidersSilk, + Reagent.MandrakeRoot + ); + AddMysticismSpell( + 6, + 11, + 17.8, + 67.8, + typeof(AnimatedWeaponScroll), + Reagent.Bone, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.Nightshade + ); + AddMysticismSpell( + 7, + 11, + 17.8, + 67.8, + typeof(StoneFormScroll), + Reagent.Bloodmoss, + Reagent.FertileDirt, + Reagent.Garlic + ); + AddMysticismSpell( + 8, + 14, + 32.1, + 82.1, + typeof(SpellTriggerScroll), + Reagent.DragonsBlood, + Reagent.Garlic, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + AddMysticismSpell( + 9, + 14, + 32.1, + 82.1, + typeof(MassSleepScroll), + Reagent.Ginseng, + Reagent.Nightshade, + Reagent.SpidersSilk + ); + AddMysticismSpell( + 10, + 20, + 46.4, + 96.4, + typeof(CleansingWindsScroll), + Reagent.DragonsBlood, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.MandrakeRoot + ); + AddMysticismSpell( + 11, + 20, + 46.4, + 96.4, + typeof(BombardScroll), + Reagent.Bloodmoss, + Reagent.DragonsBlood, + Reagent.Garlic, + Reagent.SulfurousAsh + ); + AddMysticismSpell( + 12, + 40, + 60.7, + 110.7, + typeof(SpellPlagueScroll), + Reagent.DaemonBone, + Reagent.DragonsBlood, + Reagent.Nightshade, + Reagent.SulfurousAsh + ); + AddMysticismSpell( + 13, + 40, + 60.7, + 110.7, + typeof(HailStormScroll), + Reagent.DragonsBlood, + Reagent.Bloodmoss, + Reagent.BlackPearl, + Reagent.MandrakeRoot + ); + AddMysticismSpell( + 14, + 50, + 75.0, + 125.0, + typeof(NetherCycloneScroll), + Reagent.MandrakeRoot, + Reagent.Nightshade, + Reagent.SulfurousAsh, + Reagent.Bloodmoss + ); + AddMysticismSpell( + 15, + 50, + 75.0, + 125.0, + typeof(RisingColossusScroll), + Reagent.DaemonBone, + Reagent.DragonsBlood, + Reagent.FertileDirt, + Reagent.Nightshade + ); + } + + MarkOption = true; + } + + private enum Reg + { + BlackPearl, + Bloodmoss, + Garlic, + Ginseng, + MandrakeRoot, + Nightshade, + SulfurousAsh, + SpidersSilk } } diff --git a/Projects/UOContent/Engines/Craft/DefMasonry.cs b/Projects/UOContent/Engines/Craft/DefMasonry.cs index cd2d6cdea..b0dbf78b3 100644 --- a/Projects/UOContent/Engines/Craft/DefMasonry.cs +++ b/Projects/UOContent/Engines/Craft/DefMasonry.cs @@ -2,146 +2,148 @@ using System; using Server.Items; using Server.Mobiles; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefMasonry : CraftSystem { - public class DefMasonry : CraftSystem + public static void Initialize() { - private static CraftSystem m_CraftSystem; + CraftSystem = new DefMasonry(); + } - private DefMasonry() : base(1, 1, 1.25) // base( 1, 2, 1.7 ) + private DefMasonry() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Carpentry; + + public override TextDefinition GumpTitle => 1044500; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) => 0.0; + + public override bool RetainsColorFrom(CraftItem item, Type type) => true; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Carpentry; - - public override int GumpTitleNumber => 1044500; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefMasonry(); - - public override double GetChanceAtMin(CraftItem item) => 0.0; - - public override bool RetainsColorFrom(CraftItem item, Type type) => true; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckTool(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckTool(tool, from)) - { - return 1048146; // If you have a tool equipped, you must use that tool. - } - - if (!(from is PlayerMobile mobile && mobile.Masonry && mobile.Skills.Carpentry.Base >= 100.0)) - { - return 1044633; // You havent learned stonecraft. - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + return 1048146; // If you have a tool equipped, you must use that tool. } - public override void PlayCraftEffect(Mobile from) + if (!(from is PlayerMobile mobile && mobile.Masonry && mobile.Skills.Carpentry.Base >= 100.0)) { - // no effects - // if (from.Body.Type == BodyType.Human && !from.Mounted) - // from.Animate( 9, 5, 1, true, false, 0 ); - // new InternalTimer( from ).Start(); + return 1044633; // You haven't learned stonecraft. } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + if (!BaseTool.CheckAccessible(tool, from)) { - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + return 1044263; // The tool must be on your person to use. } - public override void InitCraftList() + 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(); + } + + public override int PlayEndingEffect( + Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, + bool makersMark, CraftItem item + ) + { + if (toolBroken) { - // 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); + from.SendLocalizedMessage(1044038); // You have worn out your tool + } - if (Core.SE) + if (failed) + { + if (lostMaterial) { - var 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); + return 1044043; // You failed to create the item, and some of your materials are lost. } - // 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); + return 1044157; // You failed to create the item, but no materials were lost. } + + if (quality == 0) + { + return 502785; // You were barely able to make this item. It's quality is below average. + } + + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } + + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + 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) + { + var 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); } } diff --git a/Projects/UOContent/Engines/Craft/DefTailoring.cs b/Projects/UOContent/Engines/Craft/DefTailoring.cs index fba543763..e18fb0856 100644 --- a/Projects/UOContent/Engines/Craft/DefTailoring.cs +++ b/Projects/UOContent/Engines/Craft/DefTailoring.cs @@ -1,669 +1,671 @@ using System; using Server.Items; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefTailoring : CraftSystem { - public class DefTailoring : CraftSystem + private static readonly Type[] tailorColorables = { - private static CraftSystem m_CraftSystem; + typeof(GozaMatEastDeed), typeof(GozaMatSouthDeed), + typeof(SquareGozaMatEastDeed), typeof(SquareGozaMatSouthDeed), + typeof(BrocadeGozaMatEastDeed), typeof(BrocadeGozaMatSouthDeed), + typeof(BrocadeSquareGozaMatEastDeed), typeof(BrocadeSquareGozaMatSouthDeed) + }; - private static readonly Type[] m_TailorColorables = - { - typeof(GozaMatEastDeed), typeof(GozaMatSouthDeed), - typeof(SquareGozaMatEastDeed), typeof(SquareGozaMatSouthDeed), - typeof(BrocadeGozaMatEastDeed), typeof(BrocadeGozaMatSouthDeed), - typeof(BrocadeSquareGozaMatEastDeed), typeof(BrocadeSquareGozaMatSouthDeed) - }; + public static void Initialize() + { + CraftSystem = new DefTailoring(); + } - private DefTailoring() : base(1, 1, 1.25) // base( 1, 1, 4.5 ) + private DefTailoring() : base(1, 1, 1.25) + { + } + + public override SkillName MainSkill => SkillName.Tailoring; + + public override TextDefinition GumpTitle => 1044005; // Tailoring Menu + + public static CraftSystem CraftSystem { get; private set; } + + public override CraftECA ECA => CraftECA.ChanceMinusSixtyToFourtyFive; + + public override double GetChanceAtMin(CraftItem item) => 0.5; + + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + { + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override SkillName MainSkill => SkillName.Tailoring; - - public override int GumpTitleNumber => 1044005; // Tailoring Menu - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefTailoring(); - - public override CraftECA ECA => CraftECA.ChanceMinusSixtyToFourtyFive; - - public override double GetChanceAtMin(CraftItem item) => 0.5; - - public override int CanCraft(Mobile from, BaseTool tool, Type itemType) + if (!BaseTool.CheckAccessible(tool, from)) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - return 0; + return 1044263; // The tool must be on your person to use. } - public override bool RetainsColorFrom(CraftItem item, Type type) + return 0; + } + + public override bool RetainsColorFrom(CraftItem item, Type type) + { + if (type != typeof(Cloth) && type != typeof(UncutCloth)) { - if (type != typeof(Cloth) && type != typeof(UncutCloth)) - { - return false; - } - - type = item.ItemType; - - var contains = false; - - for (var i = 0; !contains && i < m_TailorColorables.Length; ++i) - { - contains = m_TailorColorables[i] == type; - } - - return contains; + return false; } - public override void PlayCraftEffect(Mobile from) + type = item.ItemType; + + var contains = false; + + for (var i = 0; !contains && i < tailorColorables.Length; ++i) { - from.PlaySound(0x248); + contains = tailorColorables[i] == type; } - public override int PlayEndingEffect( - Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, - bool makersMark, CraftItem item - ) + return contains; + } + + 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) { - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - return 1044154; // You create the item. + from.SendLocalizedMessage(1044038); // You have worn out your tool } - public override void InitCraftList() + if (failed) { - int index; - - 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, 6.2, 31.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) + if (lostMaterial) { - AddCraft(typeof(FlowerGarland), 1011375, 1028965, 10.0, 35.0, typeof(Cloth), 1044286, 5, 1044287); + return 1044043; // You failed to create the item, and some of your materials are lost. } - 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); - } - - 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); - } - - 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); - } - - 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); - } - - if (Core.ML) - { - index = AddCraft(typeof(ElvenQuiver), 1015283, 1032657, 65.0, 115.0, typeof(Leather), 1044462, 28, 1044463); - AddRecipe(index, 501); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(QuiverOfFire), 1015283, 1073109, 65.0, 115.0, typeof(Leather), 1044462, 28, 1044463); - AddRes(index, typeof(FireRuby), 1032695, 15, 1042081); - AddRecipe(index, 502); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft(typeof(QuiverOfIce), 1015283, 1073110, 65.0, 115.0, typeof(Leather), 1044462, 28, 1044463); - AddRes(index, typeof(WhitePearl), 1032694, 15, 1042081); - AddRecipe(index, 503); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(QuiverOfBlight), - 1015283, - 1073111, - 65.0, - 115.0, - typeof(Leather), - 1044462, - 28, - 1044463 - ); - AddRes(index, typeof(Blight), 1032675, 10, 1042081); - AddRecipe(index, 504); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(QuiverOfLightning), - 1015283, - 1073112, - 65.0, - 115.0, - typeof(Leather), - 1044462, - 28, - 1044463 - ); - AddRes(index, typeof(Corruption), 1032676, 10, 1042081); - AddRecipe(index, 505); - SetNeededExpansion(index, Expansion.ML); - } - - 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); - } - - 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); - - if (Core.ML) - { - index = AddCraft( - typeof(SpellWovenBritches), - 1015293, - 1072929, - 92.5, - 117.5, - typeof(Leather), - 1044462, - 15, - 1044463 - ); - AddRes(index, typeof(EyeOfTheTravesty), 1032685, 1, 1044253); - AddRes(index, typeof(Putrefication), 1032678, 10, 1044253); - AddRes(index, typeof(Scourge), 1032677, 10, 1044253); - AddRareRecipe(index, 506); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(SongWovenMantle), - 1015293, - 1072931, - 92.5, - 117.5, - typeof(Leather), - 1044462, - 15, - 1044463 - ); - AddRes(index, typeof(EyeOfTheTravesty), 1032685, 1, 1044253); - AddRes(index, typeof(Blight), 1032675, 10, 1044253); - AddRes(index, typeof(Muculent), 1032680, 10, 1044253); - AddRareRecipe(index, 507); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(StitchersMittens), - 1015293, - 1072932, - 92.5, - 117.5, - typeof(Leather), - 1044462, - 15, - 1044463 - ); - AddRes(index, typeof(CapturedEssence), 1032686, 1, 1044253); - AddRes(index, typeof(Corruption), 1032676, 10, 1044253); - AddRes(index, typeof(Taint), 1032679, 10, 1044253); - AddRareRecipe(index, 508); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } - - 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, 4, 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, - 5, - 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); - } - if (Core.SA) - { - index = AddCraft(typeof(GargishLeatherArmsType1), 1015293, 1020769, 53.9, 78.9, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherArmsType2), 1015293, 1020770, 53.9, 78.9, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherKiltType1), 1015293, 1020784, 58.0, 83.0, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherKiltType2), 1015293, 1020785, 58.0, 83.0, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherLegsType1), 1015293, 1020773, 66.3, 91.3, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherLegsType2), 1015293, 1020774, 66.3, 91.3, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherChestType1), 1015293, 1020772, 70.5, 95.5, typeof(Leather), 1044462, 12, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherChestType2), 1015293, 1020771, 70.5, 95.5, typeof(Leather), 1044462, 12, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishLeatherWingArmor), 1015293, 1096662, 65.3, 90.3, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - } - - 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); - } - if (Core.SA) - { - index = AddCraft(typeof(GargishStuddedArmsType1), 1015300, 1020643, 87.1, 112.1, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedArmsType2), 1015300, 1020644, 87.1, 112.1, typeof(Leather), 1044462, 8, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedKiltType1), 1015300, 1020647, 89.5, 114.5, typeof(Leather), 1044462, 10, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedKiltType2), 1015300, 1020648, 89.5, 114.5, typeof(Leather), 1044462, 10, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedLegsType1), 1015300, 1020649, 91.2, 116.2, typeof(Leather), 1044462, 12, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedLegsType2), 1015300, 1020650, 91.2, 116.2, typeof(Leather), 1044462, 12, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedChestType1), 1015300, 1020645, 94.0, 119.0, typeof(Leather), 1044462, 14, 1044463); - SetNeededExpansion(index, Expansion.SA); - index = AddCraft(typeof(GargishStuddedChestType2), 1015300, 1020646, 94.0, 119.0, typeof(Leather), 1044462, 14, 1044463); - SetNeededExpansion(index, Expansion.SA); - } - - 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); - - 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); - - index = AddCraft(typeof(OrcHelm), 1049149, 1027947, 90.0, 115.0, typeof(Leather), 1044462, 6, 1044463); - AddRes(index, typeof(Bone), 1049064, 4, 1049063); - - // 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; + return 1044157; // You failed to create the item, but no materials were lost. } + + if (quality == 0) + { + return 502785; // You were barely able to make this item. It's quality is below average. + } + + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } + + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + return 1044154; // You create the item. + } + + public override void InitCraftList() + { + int index; + + 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, 6.2, 31.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); + } + + 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); + } + + 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); + } + + 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); + } + + if (Core.ML) + { + index = AddCraft(typeof(ElvenQuiver), 1015283, 1032657, 65.0, 115.0, typeof(Leather), 1044462, 28, 1044463); + AddRecipe(index, 501); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(QuiverOfFire), 1015283, 1073109, 65.0, 115.0, typeof(Leather), 1044462, 28, 1044463); + AddRes(index, typeof(FireRuby), 1032695, 15, 1042081); + AddRecipe(index, 502); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft(typeof(QuiverOfIce), 1015283, 1073110, 65.0, 115.0, typeof(Leather), 1044462, 28, 1044463); + AddRes(index, typeof(WhitePearl), 1032694, 15, 1042081); + AddRecipe(index, 503); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(QuiverOfBlight), + 1015283, + 1073111, + 65.0, + 115.0, + typeof(Leather), + 1044462, + 28, + 1044463 + ); + AddRes(index, typeof(Blight), 1032675, 10, 1042081); + AddRecipe(index, 504); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(QuiverOfLightning), + 1015283, + 1073112, + 65.0, + 115.0, + typeof(Leather), + 1044462, + 28, + 1044463 + ); + AddRes(index, typeof(Corruption), 1032676, 10, 1042081); + AddRecipe(index, 505); + SetNeededExpansion(index, Expansion.ML); + } + + 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); + } + + 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); + + if (Core.ML) + { + index = AddCraft( + typeof(SpellWovenBritches), + 1015293, + 1072929, + 92.5, + 117.5, + typeof(Leather), + 1044462, + 15, + 1044463 + ); + AddRes(index, typeof(EyeOfTheTravesty), 1032685, 1, 1044253); + AddRes(index, typeof(Putrefication), 1032678, 10, 1044253); + AddRes(index, typeof(Scourge), 1032677, 10, 1044253); + AddRareRecipe(index, 506); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(SongWovenMantle), + 1015293, + 1072931, + 92.5, + 117.5, + typeof(Leather), + 1044462, + 15, + 1044463 + ); + AddRes(index, typeof(EyeOfTheTravesty), 1032685, 1, 1044253); + AddRes(index, typeof(Blight), 1032675, 10, 1044253); + AddRes(index, typeof(Muculent), 1032680, 10, 1044253); + AddRareRecipe(index, 507); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(StitchersMittens), + 1015293, + 1072932, + 92.5, + 117.5, + typeof(Leather), + 1044462, + 15, + 1044463 + ); + AddRes(index, typeof(CapturedEssence), 1032686, 1, 1044253); + AddRes(index, typeof(Corruption), 1032676, 10, 1044253); + AddRes(index, typeof(Taint), 1032679, 10, 1044253); + AddRareRecipe(index, 508); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + } + + 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, 4, 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, + 5, + 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); + } + if (Core.SA) + { + index = AddCraft(typeof(GargishLeatherArmsType1), 1015293, 1020769, 53.9, 78.9, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherArmsType2), 1015293, 1020770, 53.9, 78.9, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherKiltType1), 1015293, 1020784, 58.0, 83.0, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherKiltType2), 1015293, 1020785, 58.0, 83.0, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherLegsType1), 1015293, 1020773, 66.3, 91.3, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherLegsType2), 1015293, 1020774, 66.3, 91.3, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherChestType1), 1015293, 1020772, 70.5, 95.5, typeof(Leather), 1044462, 12, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherChestType2), 1015293, 1020771, 70.5, 95.5, typeof(Leather), 1044462, 12, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishLeatherWingArmor), 1015293, 1096662, 65.3, 90.3, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + } + + 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); + } + if (Core.SA) + { + index = AddCraft(typeof(GargishStuddedArmsType1), 1015300, 1020643, 87.1, 112.1, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedArmsType2), 1015300, 1020644, 87.1, 112.1, typeof(Leather), 1044462, 8, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedKiltType1), 1015300, 1020647, 89.5, 114.5, typeof(Leather), 1044462, 10, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedKiltType2), 1015300, 1020648, 89.5, 114.5, typeof(Leather), 1044462, 10, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedLegsType1), 1015300, 1020649, 91.2, 116.2, typeof(Leather), 1044462, 12, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedLegsType2), 1015300, 1020650, 91.2, 116.2, typeof(Leather), 1044462, 12, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedChestType1), 1015300, 1020645, 94.0, 119.0, typeof(Leather), 1044462, 14, 1044463); + SetNeededExpansion(index, Expansion.SA); + index = AddCraft(typeof(GargishStuddedChestType2), 1015300, 1020646, 94.0, 119.0, typeof(Leather), 1044462, 14, 1044463); + SetNeededExpansion(index, Expansion.SA); + } + + 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); + + 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); + + index = AddCraft(typeof(OrcHelm), 1049149, 1027947, 90.0, 115.0, typeof(Leather), 1044462, 6, 1044463); + AddRes(index, typeof(Bone), 1049064, 4, 1049063); + + // 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; } } diff --git a/Projects/UOContent/Engines/Craft/DefTinkering.cs b/Projects/UOContent/Engines/Craft/DefTinkering.cs index dbe6f497a..6f594cb6f 100644 --- a/Projects/UOContent/Engines/Craft/DefTinkering.cs +++ b/Projects/UOContent/Engines/Craft/DefTinkering.cs @@ -3,759 +3,761 @@ using Server.Factions; using Server.Items; using Server.Targeting; -namespace Server.Engines.Craft +namespace Server.Engines.Craft; + +public class DefTinkering : CraftSystem { - public class DefTinkering : CraftSystem + private static readonly Type[] _tinkerColorables = { - private static CraftSystem m_CraftSystem; - - private static readonly Type[] m_TinkerColorables = - { - 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) - }; - - private DefTinkering() : base(1, 1, 1.25) // base( 1, 1, 3.0 ) - { - } - - public override SkillName MainSkill => SkillName.Tinkering; - - public override int GumpTitleNumber => 1044007; - - public static CraftSystem CraftSystem => m_CraftSystem ??= new DefTinkering(); - - public override double GetChanceAtMin(CraftItem item) - { - if (item.NameNumber is 1044258 or 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?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } - - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } - - 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 bool RetainsColorFrom(CraftItem item, Type type) - { - if (!type.IsSubclassOf(typeof(BaseIngot))) - { - return false; - } - - type = item.ItemType; - - var contains = false; - - for (var i = 0; !contains && i < m_TinkerColorables.Length; ++i) - { - contains = m_TinkerColorables[i] == type; - } - - return contains; - } - - public override void PlayCraftEffect(Mobile from) - { - // no sound - // from.PlaySound( 0x241 ); - } - - 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. - } - - return 1044157; // You failed to create the item, but no materials were lost. - } - - if (quality == 0) - { - return 502785; // You were barely able to make this item. It's quality is below average. - } - - if (makersMark && quality == 2) - { - return 1044156; // You create an exceptional quality item and affix your maker's mark. - } - - if (quality == 2) - { - return 1044155; // You create an exceptional quality item. - } - - 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) - { - var offset = (int)gemType - 1; - - var 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; - - 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); - } - - 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); - - 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); - - if (Core.ML) - { - index = AddCraft( - typeof(JeweledFiligree), - 1044047, - 1072894, - 70.0, - 110.0, - typeof(IronIngot), - 1044036, - 2, - 1044037 - ); - AddRes(index, typeof(StarSapphire), 1044231, 1, 1044253); - AddRes(index, typeof(Ruby), 1044234, 1, 1044253); - SetNeededExpansion(index, Expansion.ML); - } - - 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); - - 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); - } - - 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)); - - 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); - - // 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); - - // Magic Jewelry - if (Core.ML) - { - index = AddCraft( - typeof(ResilientBracer), - 1073107, - 1072933, - 100.0, - 125.0, - typeof(IronIngot), - 1044036, - 2, - 1044037 - ); - AddRes(index, typeof(CapturedEssence), 1032686, 1, 1044253); - AddRes(index, typeof(BlueDiamond), 1032696, 10, 1044253); - AddRes(index, typeof(Diamond), 1062608, 50, 1044253); - AddRareRecipe(index, 600); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(EssenceOfBattle), - 1073107, - 1072935, - 100.0, - 125.0, - typeof(IronIngot), - 1044036, - 2, - 1044037 - ); - AddRes(index, typeof(CapturedEssence), 1032686, 1, 1044253); - AddRes(index, typeof(FireRuby), 1032695, 10, 1044253); - AddRes(index, typeof(Ruby), 1062603, 50, 1044253); - AddRareRecipe(index, 601); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(PendantOfTheMagi), - 1073107, - 1072937, - 100.0, - 125.0, - typeof(IronIngot), - 1044036, - 2, - 1044037 - ); - AddRes(index, typeof(EyeOfTheTravesty), 1032685, 1, 1044253); - AddRes(index, typeof(WhitePearl), 1032694, 10, 1044253); - AddRes(index, typeof(StarSapphire), 1062600, 50, 1044253); - AddRareRecipe(index, 602); - ForceNonExceptional(index); - SetNeededExpansion(index, Expansion.ML); - } - - // 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; - } + 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 static void Initialize() + { + CraftSystem = new DefTinkering(); } - public abstract class TrapCraft : CustomCraft + private DefTinkering() : base(1, 1, 1.25) { - public TrapCraft(Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality) - : base(from, craftItem, craftSystem, typeRes, tool, quality) - { - } - - public LockableContainer Container { get; private set; } - - public abstract TrapType TrapType { get; } - - 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) - { - var container = target as LockableContainer; - - message = Verify(container); - - if (message > 0) - { - return false; - } - - 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); - } - - public override Item CompleteCraft(out int message) - { - message = Verify(Container); - - if (message == 0) - { - var trapLevel = (int)(From.Skills.Tinkering.Value / 10); - - Container.TrapType = TrapType; - Container.TrapPower = trapLevel * 9; - Container.TrapLevel = trapLevel; - Container.TrapOnLockpick = true; - - message = 1005639; // Trap is disabled until you lock the chest. - } - - return null; - } - - private class ContainerTarget : Target - { - private readonly TrapCraft m_TrapCraft; - - public ContainerTarget(TrapCraft trapCraft) : base(-1, false, TargetFlags.None) => m_TrapCraft = trapCraft; - - protected override void OnTarget(Mobile from, object targeted) - { - if (m_TrapCraft.Acquire(targeted, out var 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) - { - var from = m_TrapCraft.From; - var tool = m_TrapCraft.Tool; - - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - from.SendGump(new CraftGump(from, m_TrapCraft.CraftSystem, tool, message)); - } - else if (message > 0) - { - from.SendLocalizedMessage(message); - } - } - } } - [CraftItemID(0x1BFC)] - public class DartTrapCraft : TrapCraft + public override SkillName MainSkill => SkillName.Tinkering; + + public override TextDefinition GumpTitle => 1044007; + + public static CraftSystem CraftSystem { get; private set; } + + public override double GetChanceAtMin(CraftItem item) { - public DartTrapCraft( - Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, - int quality - ) : base(from, craftItem, craftSystem, typeRes, tool, quality) + if (item.NameNumber is 1044258 or 1046445) // potion keg and faction trap removal kit { + return 0.5; // 50% } - public override TrapType TrapType => TrapType.DartTrap; + return 0.0; // 0% } - [CraftItemID(0x113E)] - public class PoisonTrapCraft : TrapCraft + public override int CanCraft(Mobile from, BaseTool tool, Type itemType) { - public PoisonTrapCraft( - Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, - int quality - ) : base(from, craftItem, craftSystem, typeRes, tool, quality) + if (tool?.Deleted != false || tool.UsesRemaining < 0) { + return 1044038; // You have worn out your tool! } - public override TrapType TrapType => TrapType.PoisonTrap; + if (!BaseTool.CheckAccessible(tool, from)) + { + return 1044263; // The tool must be on your person to use. + } + + 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; } - [CraftItemID(0x370C)] - public class ExplosionTrapCraft : TrapCraft + public override bool RetainsColorFrom(CraftItem item, Type type) { - public ExplosionTrapCraft( - Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, - int quality - ) : base(from, craftItem, craftSystem, typeRes, tool, quality) + if (!type.IsSubclassOf(typeof(BaseIngot))) { + return false; } - public override TrapType TrapType => TrapType.ExplosionTrap; + type = item.ItemType; + + var contains = false; + + for (var i = 0; !contains && i < _tinkerColorables.Length; ++i) + { + contains = _tinkerColorables[i] == type; + } + + return contains; + } + + public override void PlayCraftEffect(Mobile from) + { + // no sound + // from.PlaySound( 0x241 ); + } + + 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. + } + + return 1044157; // You failed to create the item, but no materials were lost. + } + + if (quality == 0) + { + return 502785; // You were barely able to make this item. It's quality is below average. + } + + if (makersMark && quality == 2) + { + return 1044156; // You create an exceptional quality item and affix your maker's mark. + } + + if (quality == 2) + { + return 1044155; // You create an exceptional quality item. + } + + 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) + { + var offset = (int)gemType - 1; + + var 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; + + 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); + } + + 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); + + 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); + + if (Core.ML) + { + index = AddCraft( + typeof(JeweledFiligree), + 1044047, + 1072894, + 70.0, + 110.0, + typeof(IronIngot), + 1044036, + 2, + 1044037 + ); + AddRes(index, typeof(StarSapphire), 1044231, 1, 1044253); + AddRes(index, typeof(Ruby), 1044234, 1, 1044253); + SetNeededExpansion(index, Expansion.ML); + } + + 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); + + 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); + } + + 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)); + + 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); + + // 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); + + // Magic Jewelry + if (Core.ML) + { + index = AddCraft( + typeof(ResilientBracer), + 1073107, + 1072933, + 100.0, + 125.0, + typeof(IronIngot), + 1044036, + 2, + 1044037 + ); + AddRes(index, typeof(CapturedEssence), 1032686, 1, 1044253); + AddRes(index, typeof(BlueDiamond), 1032696, 10, 1044253); + AddRes(index, typeof(Diamond), 1062608, 50, 1044253); + AddRareRecipe(index, 600); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(EssenceOfBattle), + 1073107, + 1072935, + 100.0, + 125.0, + typeof(IronIngot), + 1044036, + 2, + 1044037 + ); + AddRes(index, typeof(CapturedEssence), 1032686, 1, 1044253); + AddRes(index, typeof(FireRuby), 1032695, 10, 1044253); + AddRes(index, typeof(Ruby), 1062603, 50, 1044253); + AddRareRecipe(index, 601); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + + index = AddCraft( + typeof(PendantOfTheMagi), + 1073107, + 1072937, + 100.0, + 125.0, + typeof(IronIngot), + 1044036, + 2, + 1044037 + ); + AddRes(index, typeof(EyeOfTheTravesty), 1032685, 1, 1044253); + AddRes(index, typeof(WhitePearl), 1032694, 10, 1044253); + AddRes(index, typeof(StarSapphire), 1062600, 50, 1044253); + AddRareRecipe(index, 602); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); + } + + // 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 +{ + public TrapCraft(Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality) + : base(from, craftItem, craftSystem, typeRes, tool, quality) + { + } + + public LockableContainer Container { get; private set; } + + public abstract TrapType TrapType { get; } + + 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) + { + var container = target as LockableContainer; + + message = Verify(container); + + if (message > 0) + { + return false; + } + + 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); + } + + public override Item CompleteCraft(out int message) + { + message = Verify(Container); + + if (message == 0) + { + var trapLevel = (int)(From.Skills.Tinkering.Value / 10); + + Container.TrapType = TrapType; + Container.TrapPower = trapLevel * 9; + Container.TrapLevel = trapLevel; + Container.TrapOnLockpick = true; + + message = 1005639; // Trap is disabled until you lock the chest. + } + + return null; + } + + private class ContainerTarget : Target + { + private readonly TrapCraft m_TrapCraft; + + public ContainerTarget(TrapCraft trapCraft) : base(-1, false, TargetFlags.None) => m_TrapCraft = trapCraft; + + protected override void OnTarget(Mobile from, object targeted) + { + if (m_TrapCraft.Acquire(targeted, out var 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) + { + var from = m_TrapCraft.From; + var tool = m_TrapCraft.Tool; + + if (tool?.Deleted == false && tool.UsesRemaining > 0) + { + from.SendGump(new CraftGump(from, m_TrapCraft.CraftSystem, tool, message)); + } + else if (message > 0) + { + from.SendLocalizedMessage(message); + } + } + } +} + +[CraftItemID(0x1BFC)] +public class DartTrapCraft : TrapCraft +{ + public DartTrapCraft( + Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, + int quality + ) : base(from, craftItem, craftSystem, typeRes, tool, quality) + { + } + + public override TrapType TrapType => TrapType.DartTrap; +} + +[CraftItemID(0x113E)] +public class PoisonTrapCraft : TrapCraft +{ + public PoisonTrapCraft( + Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, + int quality + ) : base(from, craftItem, craftSystem, typeRes, tool, quality) + { + } + + public override TrapType TrapType => TrapType.PoisonTrap; +} + +[CraftItemID(0x370C)] +public class ExplosionTrapCraft : TrapCraft +{ + public ExplosionTrapCraft( + Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, + int quality + ) : base(from, craftItem, craftSystem, typeRes, tool, quality) + { + } + + public override TrapType TrapType => TrapType.ExplosionTrap; +} diff --git a/Projects/UOContent/Engines/Doom/GenGauntlet.cs b/Projects/UOContent/Engines/Doom/GenGauntlet.cs index ebc5979f9..881546cb6 100644 --- a/Projects/UOContent/Engines/Doom/GenGauntlet.cs +++ b/Projects/UOContent/Engines/Doom/GenGauntlet.cs @@ -118,7 +118,7 @@ namespace Server.Engines.Doom gate.GumpHeight = 280; gate.MessageColor = 0x7F00; - gate.MessageNumber = 1062109; // You are about to exit Dungeon Doom. Do you wish to continue? + gate.Message = 1062109; // You are about to exit Dungeon Doom. Do you wish to continue? gate.TitleColor = 0x7800; gate.TitleNumber = 1062108; // Please verify... diff --git a/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs b/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs index 91d82e580..2b05632e9 100644 --- a/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs +++ b/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs @@ -5,8 +5,7 @@ namespace Server.Mobiles public class UnholyFamiliar : BaseCreature { [Constructible] - public UnholyFamiliar() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public UnholyFamiliar() : base(AIType.AI_Melee) { Body = 99; BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs b/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs index 5baa0466b..ec13266a9 100644 --- a/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs +++ b/Projects/UOContent/Engines/Ethics/Evil/Mobiles/UnholySteed.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles { [Constructible] public UnholySteed() - : base("a dark steed", 0x74, 0x3EA7, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + : base("a dark steed", 0x74, 0x3EA7, AIType.AI_Melee, FightMode.Aggressor) { SetStr(496, 525); SetDex(86, 105); diff --git a/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs b/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs index 6494204b1..51b451ded 100644 --- a/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs +++ b/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs @@ -46,7 +46,7 @@ namespace Server.Ethics.Evil ++enemyCount; } - using var sb = new ValueStringBuilder(); + using var sb = ValueStringBuilder.Create(); sb.Append("You sense "); sb.Append(enemyCount == 0 ? "no" : enemyCount.ToString()); diff --git a/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs b/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs index b07570a89..7f67f8f06 100644 --- a/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs +++ b/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles { [Constructible] public HolyFamiliar() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Melee) { Body = 100; BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs b/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs index a3f088fd4..da52600f8 100644 --- a/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs +++ b/Projects/UOContent/Engines/Ethics/Hero/Mobiles/HolySteed.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles { [Constructible] public HolySteed() - : base("a silver steed", 0x75, 0x3EA8, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + : base("a silver steed", 0x75, 0x3EA8, AIType.AI_Melee, FightMode.Aggressor) { SetStr(496, 525); SetDex(86, 105); diff --git a/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs b/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs index d49597a32..12123d7e9 100644 --- a/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs +++ b/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs @@ -46,7 +46,7 @@ namespace Server.Ethics.Hero ++enemyCount; } - using var sb = new ValueStringBuilder(); + using var sb = ValueStringBuilder.Create(); sb.Append("You sense "); sb.Append(enemyCount == 0 ? "no" : enemyCount.ToString()); diff --git a/Projects/UOContent/Engines/Factions/Core/Faction.cs b/Projects/UOContent/Engines/Factions/Core/Faction.cs index 9345a7328..f4f126922 100644 --- a/Projects/UOContent/Engines/Factions/Core/Faction.cs +++ b/Projects/UOContent/Engines/Factions/Core/Faction.cs @@ -1320,7 +1320,7 @@ namespace Server.Factions var context = new SkillLossContext(); m_SkillLoss[mob] = context; - var mods = context.m_Mods = new List(); + var mods = context.m_Mods = new HashSet(); for (var i = 0; i < mob.Skills.Length; ++i) { @@ -1352,11 +1352,12 @@ namespace Server.Factions var mods = context.m_Mods; - for (var i = 0; i < mods.Count; ++i) + foreach (var mod in mods) { - mob.RemoveSkillMod(mods[i]); + mod.Remove(); } + context.m_Mods = null; context._timerToken.Cancel(); return true; @@ -1376,7 +1377,7 @@ namespace Server.Factions private class SkillLossContext { - public List m_Mods; + public HashSet m_Mods; public TimerExecutionToken _timerToken; } } diff --git a/Projects/UOContent/Engines/Factions/Core/FactionSystem.cs b/Projects/UOContent/Engines/Factions/Core/FactionSystem.cs new file mode 100644 index 000000000..13a8a6df4 --- /dev/null +++ b/Projects/UOContent/Engines/Factions/Core/FactionSystem.cs @@ -0,0 +1,118 @@ +using System; + +namespace Server.Factions; + +public static class FactionSystem +{ + public static bool Enabled { get; private set; } + + public static void Configure() + { + Enabled = ServerConfiguration.GetSetting("factions.enabled", false); + + if (Enabled) + { + GenericPersistence.Register("Factions", Serialize, Deserialize); + } + } + + // This does not do the actual work of removing faction stuff, only turns off the persistence. + public static void Disable() + { + if (!Enabled) + { + return; + } + + Persistence.Unregister("Factions"); + Enabled = false; + ServerConfiguration.SetSetting("factions.enabled", false); + } + + // This does not do the actual work of creating faction stuff, only turns on the persistence. + public static void Enable() + { + if (Enabled) + { + return; + } + + GenericPersistence.Register("Factions", Serialize, Deserialize); + Enabled = true; + ServerConfiguration.SetSetting("factions.enabled", true); + } + + private static void Serialize(IGenericWriter writer) + { + writer.WriteEncodedInt(0); // version + + var factions = Faction.Factions; + for (var i = 0; i < factions.Count; i++) + { + factions[i].State.Serialize(writer); + } + + var towns = Town.Towns; + for (var i = 0; i < towns.Count; i++) + { + towns[i].State.Serialize(writer); + } + } + + private static void Deserialize(IGenericReader reader) + { + var version = reader.ReadEncodedInt(); + + var count = Faction.Factions.Count; + for (var i = 0; i < count; i++) + { + new FactionState(reader); + } + + count = Town.Towns.Count; + for (var i = 0; i < count; i++) + { + new TownState(reader); + } + } +} + +[ManualDirtyChecking] +[TypeAlias("Server.Factions.FactionPersistance")] +[Obsolete("Deprecated in favor of the static system. Only used for legacy deserialization")] +public class FactionPersistence : Item +{ + public FactionPersistence() + { + Delete(); + } + + public FactionPersistence(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + int type; + + while ((type = reader.ReadEncodedInt()) != 0) + { + if (type == 1) + { + new FactionState(reader); + } + else if (type == 2) + { + new TownState(reader); + } + } + } +} diff --git a/Projects/UOContent/Engines/Factions/Core/Generator.cs b/Projects/UOContent/Engines/Factions/Core/Generator.cs index 13e255343..de45b99a8 100644 --- a/Projects/UOContent/Engines/Factions/Core/Generator.cs +++ b/Projects/UOContent/Engines/Factions/Core/Generator.cs @@ -11,7 +11,7 @@ namespace Server.Factions public static void GenerateFactions_OnCommand(CommandEventArgs e) { - new FactionPersistance(); + FactionSystem.Enable(); var factions = Faction.Factions; diff --git a/Projects/UOContent/Engines/Factions/Core/Persistance.cs b/Projects/UOContent/Engines/Factions/Core/Persistance.cs deleted file mode 100644 index 882ac72d0..000000000 --- a/Projects/UOContent/Engines/Factions/Core/Persistance.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace Server.Factions -{ - public class FactionPersistance : Item - { - public FactionPersistance() : base(1) - { - Movable = false; - - if (Instance?.Deleted == true) - { - Instance = this; - } - else - { - base.Delete(); - } - } - - public FactionPersistance(Serial serial) : base(serial) => Instance = this; - - public static FactionPersistance Instance { get; private set; } - - public override string DefaultName => "Faction Persistance - Internal"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - var factions = Faction.Factions; - - for (var i = 0; i < factions.Count; ++i) - { - writer.WriteEncodedInt((int)PersistedType.Faction); - factions[i].State.Serialize(writer); - } - - var towns = Town.Towns; - - for (var 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(IGenericReader reader) - { - base.Deserialize(reader); - - var 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() - { - } - - private enum PersistedType - { - Terminator, - Faction, - Town - } - } -} diff --git a/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs b/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs index 3f9c5df00..e8ffee06d 100644 --- a/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs +++ b/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs @@ -13,11 +13,11 @@ namespace Server.Factions private readonly Faction m_Faction; private readonly Item m_Item; private readonly Mobile m_Mobile; - private readonly object m_Notice; + private readonly TextDefinition m_Notice; private readonly BaseTool m_Tool; public FactionImbueGump( - int quality, Item item, Mobile from, CraftSystem craftSystem, BaseTool tool, object notice, + int quality, Item item, Mobile from, CraftSystem craftSystem, BaseTool tool, TextDefinition notice, int availableSilver, Faction faction, FactionItemDefinition def ) : base(100, 200) { @@ -98,13 +98,16 @@ namespace Server.Factions { m_Mobile.SendGump(new CraftGump(m_Mobile, m_CraftSystem, m_Tool, m_Notice)); } - else if (m_Notice is string s) + else if (m_Notice != null) { - m_Mobile.SendMessage(s); - } - else if (m_Notice is int i && i > 0) - { - m_Mobile.SendLocalizedMessage(i); + if (m_Notice.Number > 0) + { + m_Mobile.SendLocalizedMessage(m_Notice.Number); + } + else + { + m_Mobile.SendMessage(m_Notice.String); + } } } } diff --git a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs index 5d6d2729b..dcddde70e 100644 --- a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs +++ b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs @@ -95,7 +95,7 @@ namespace Server foreach (var m in eable) { if (from.CanBeHarmful(m, false) && - m.InLOS(new Point3D(origin, origin.Z + 1)) && + m.InLOS(new Point3D(origin.X, origin.Y, origin.Z + 1)) && Faction.Find(m) != null) { targets.Add(from); @@ -118,7 +118,7 @@ namespace Server } Effects.SendMovingEffect( - new Entity(Serial.Zero, new Point3D(origin, origin.Z + 4), facet), + new Entity(Serial.Zero, new Point3D(origin.X, origin.Y, origin.Z + 4), facet), mob, 14068, 1, diff --git a/Projects/UOContent/Engines/Factions/Items/Sigil.cs b/Projects/UOContent/Engines/Factions/Items/Sigil.cs index 66606eef3..8fab6abd9 100644 --- a/Projects/UOContent/Engines/Factions/Items/Sigil.cs +++ b/Projects/UOContent/Engines/Factions/Items/Sigil.cs @@ -128,7 +128,7 @@ namespace Server.Factions InvalidateProperties(); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs b/Projects/UOContent/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs index 78dd3c488..0ee8d8ebb 100644 --- a/Projects/UOContent/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs +++ b/Projects/UOContent/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs @@ -30,12 +30,12 @@ namespace Server.Factions } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); // NOTE: OSI does not list uses remaining; intentional difference - list.Add(1060584, Charges.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, Charges); // uses remaining: ~1_val~ } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Engines/Factions/Mobiles/FactionWarHorse.cs b/Projects/UOContent/Engines/Factions/Mobiles/FactionWarHorse.cs index a90e44678..e0aa13256 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/FactionWarHorse.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/FactionWarHorse.cs @@ -10,7 +10,7 @@ namespace Server.Factions [Constructible] public FactionWarHorse(Faction faction = null) - : base("a war horse", 0xE2, 0x3EA0, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + : base("a war horse", 0xE2, 0x3EA0, AIType.AI_Melee, FightMode.Aggressor) { BaseSoundID = 0xA8; diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs index aa7716781..bffd70992 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs @@ -35,7 +35,7 @@ namespace Server.Factions private DateTime m_OrdersEnd; private Town m_Town; - public BaseFactionGuard(string title) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BaseFactionGuard(string title) : base(AIType.AI_Melee) { Orders = new Orders(this); Title = title; @@ -308,13 +308,13 @@ namespace Server.Factions } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (m_Faction != null && Map == Faction.Facet) { - list.Add(1060846, m_Faction.Definition.PropName); // Guard: ~1_val~ + list.Add(1060846, $"{m_Faction.Definition.PropName}"); // Guard: ~1_val~ } } diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index 83ff2454b..833b56681 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -209,17 +209,8 @@ namespace Server.Factions return true; } - public int GetStatMod(Mobile mob, StatType type) - { - var mod = mob.GetStatMod($"[Magic] {type} Offset"); - - if (mod == null) - { - return 0; - } - - return mod.Offset; - } + public static int GetStatMod(Mobile mob, StatType type) => + mob.GetStatMod($"[Magic] {type} Curse")?.Offset ?? 0; public Spell RandomOffenseSpell() { @@ -411,13 +402,13 @@ namespace Server.Factions { if (m_Mobile.Debug) { - m_Mobile.DebugSay("My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name); + m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); } m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } - else + else if (m_Mobile.Debug) { m_Mobile.DebugSay("I am stuck"); } @@ -815,17 +806,14 @@ namespace Server.Factions 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); - } - } - else + if (spell is not GreaterHealSpell) { spell = null; } + else if (m_Guard.Hits + 30 > m_Guard.HitsMax && m_Guard.Hits + 10 < m_Guard.HitsMax) + { + spell = new HealSpell(m_Guard); + } } } else if (spell == null && m_Guard.Stam < m_Guard.StamMax / 3 && IsAllowed(GuardAI.Melee)) diff --git a/Projects/UOContent/Engines/Harvest/Core/HarvestResource.cs b/Projects/UOContent/Engines/Harvest/Core/HarvestResource.cs index 4fdb5b1b4..72ad34b8f 100644 --- a/Projects/UOContent/Engines/Harvest/Core/HarvestResource.cs +++ b/Projects/UOContent/Engines/Harvest/Core/HarvestResource.cs @@ -4,7 +4,7 @@ namespace Server.Engines.Harvest { public class HarvestResource { - public HarvestResource(double reqSkill, double minSkill, double maxSkill, object message, params Type[] types) + public HarvestResource(double reqSkill, double minSkill, double maxSkill, TextDefinition message, params Type[] types) { ReqSkill = reqSkill; MinSkill = minSkill; @@ -21,17 +21,20 @@ namespace Server.Engines.Harvest public double MaxSkill { get; set; } - public object SuccessMessage { get; } + public TextDefinition SuccessMessage { get; } public void SendSuccessTo(Mobile m) { - if (SuccessMessage is int messageInt) + if (SuccessMessage != null) { - m.SendLocalizedMessage(messageInt); - } - else - { - m.SendMessage(SuccessMessage.ToString()); + if (SuccessMessage.Number > 0) + { + m.SendLocalizedMessage(SuccessMessage.Number); + } + else + { + m.SendMessage(SuccessMessage.String); + } } } } diff --git a/Projects/UOContent/Engines/Harvest/Fishing.cs b/Projects/UOContent/Engines/Harvest/Fishing.cs index 035f21e4d..7e8cb9009 100644 --- a/Projects/UOContent/Engines/Harvest/Fishing.cs +++ b/Projects/UOContent/Engines/Harvest/Fishing.cs @@ -464,12 +464,7 @@ namespace Server.Engines.Harvest var ns = from.NetState; - if (ns == null) - { - return; - } - - if (number == 1043297 || ns.HighSeas) + if (number == 1043297 || ns?.HighSeas == true) { from.SendLocalizedMessage(number, name); } diff --git a/Projects/UOContent/Engines/Help/HelpTopic.cs b/Projects/UOContent/Engines/Help/HelpTopic.cs index 0fc242891..1b9179169 100644 --- a/Projects/UOContent/Engines/Help/HelpTopic.cs +++ b/Projects/UOContent/Engines/Help/HelpTopic.cs @@ -35,7 +35,7 @@ namespace Server.Engines.Help public static void SendDisplayHelpTopic(this NetState ns, int topicID, bool display = true) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/Khaldun/Mobiles/GrimmochDrummel.cs b/Projects/UOContent/Engines/Khaldun/Mobiles/GrimmochDrummel.cs index 7adde3926..d18e54dc8 100644 --- a/Projects/UOContent/Engines/Khaldun/Mobiles/GrimmochDrummel.cs +++ b/Projects/UOContent/Engines/Khaldun/Mobiles/GrimmochDrummel.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GrimmochDrummel : BaseCreature { [Constructible] - public GrimmochDrummel() : base(AIType.AI_Archer, FightMode.Closest, 10, 1, 0.2, 0.4) + public GrimmochDrummel() : base(AIType.AI_Archer) { Title = "the Cursed"; diff --git a/Projects/UOContent/Engines/Khaldun/Mobiles/LysanderGathenwale.cs b/Projects/UOContent/Engines/Khaldun/Mobiles/LysanderGathenwale.cs index c35736293..8d7f2cb3d 100644 --- a/Projects/UOContent/Engines/Khaldun/Mobiles/LysanderGathenwale.cs +++ b/Projects/UOContent/Engines/Khaldun/Mobiles/LysanderGathenwale.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class LysanderGathenwale : BaseCreature { [Constructible] - public LysanderGathenwale() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public LysanderGathenwale() : base(AIType.AI_Mage) { Title = "the Cursed"; diff --git a/Projects/UOContent/Engines/Khaldun/Mobiles/MorgBergen.cs b/Projects/UOContent/Engines/Khaldun/Mobiles/MorgBergen.cs index 6c6bf8f46..fa1a61a20 100644 --- a/Projects/UOContent/Engines/Khaldun/Mobiles/MorgBergen.cs +++ b/Projects/UOContent/Engines/Khaldun/Mobiles/MorgBergen.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class MorgBergen : BaseCreature { [Constructible] - public MorgBergen() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public MorgBergen() : base(AIType.AI_Melee) { Title = "the Cursed"; diff --git a/Projects/UOContent/Engines/Khaldun/Mobiles/TavaraSewel.cs b/Projects/UOContent/Engines/Khaldun/Mobiles/TavaraSewel.cs index c568c64f8..4e01e38b8 100644 --- a/Projects/UOContent/Engines/Khaldun/Mobiles/TavaraSewel.cs +++ b/Projects/UOContent/Engines/Khaldun/Mobiles/TavaraSewel.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class TavaraSewel : BaseCreature { [Constructible] - public TavaraSewel() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public TavaraSewel() : base(AIType.AI_Melee) { Title = "the Cursed"; diff --git a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs index 216706930..2bc0bee22 100644 --- a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs +++ b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs @@ -267,7 +267,7 @@ namespace Server.Items protected override void SetLockLevel() { - LockLevel = 0; // Can't be unlocked + LockLevel = ILockpickable.CannotPick; // Can't be unlocked } public override bool CheckLocked(Mobile from) diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/AGhostOfCovetous.cs b/Projects/UOContent/Engines/ML Quests/Definitions/AGhostOfCovetous.cs index 754d1d8f0..d8e567687 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/AGhostOfCovetous.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/AGhostOfCovetous.cs @@ -96,8 +96,7 @@ namespace Server.Engines.MLQuests.Definitions public class Ben : BaseCreature { [Constructible] - public Ben() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Ben() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Apprentice Necromancer"; Body = 0x190; @@ -107,6 +106,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x204C; FacialHairHue = 0x463; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); AddItem(new Backpack()); @@ -142,13 +142,13 @@ namespace Server.Engines.MLQuests.Definitions public class Frederic : BaseCreature { [Constructible] - public Frederic() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Frederic() : base(AIType.AI_Vendor, FightMode.None, 2) { Body = 0x1A; Hue = 0x455; Frozen = true; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); } @@ -178,14 +178,15 @@ namespace Server.Engines.MLQuests.Definitions public class Leon : BaseCreature { [Constructible] - public Leon() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Leon() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Alchemist"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -221,8 +222,7 @@ namespace Server.Engines.MLQuests.Definitions public class Andros : BaseCreature { [Constructible] - public Andros() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Andros() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Blacksmith"; Body = 0x190; @@ -232,6 +232,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x2049; HairHue = 0x45E; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); AddItem(new Backpack()); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Bedlam.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Bedlam.cs index f0d7b5daf..fe4fbdcec 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Bedlam.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Bedlam.cs @@ -69,14 +69,15 @@ namespace Server.Engines.MLQuests.Definitions public class Kia : BaseCreature { [Constructible] - public Kia() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Kia() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the student"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -113,14 +114,15 @@ namespace Server.Engines.MLQuests.Definitions public class Emerillo : BaseCreature { [Constructible] - public Emerillo() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Emerillo() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the cook"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -165,14 +167,15 @@ namespace Server.Engines.MLQuests.Definitions public class Nythalia : BaseCreature { [Constructible] - public Nythalia() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Nythalia() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the student"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/BlightedGrove.cs b/Projects/UOContent/Engines/ML Quests/Definitions/BlightedGrove.cs index 31bf2a6de..26da2dce7 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/BlightedGrove.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/BlightedGrove.cs @@ -180,7 +180,7 @@ namespace Server.Engines.MLQuests.Definitions { [Constructible] public Jamal() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.2, 0.4) + : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Fisherman"; Body = 400; @@ -224,7 +224,7 @@ namespace Server.Engines.MLQuests.Definitions { [Constructible] public Iosep() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.2, 0.4) + : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Exporter"; Body = 400; diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Britannia.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Britannia.cs index 7226809c2..df01d3119 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Britannia.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Britannia.cs @@ -108,14 +108,15 @@ namespace Server.Engines.MLQuests.Definitions public class Aurelia : BaseCreature { [Constructible] - public Aurelia() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Aurelia() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Architect's Daughter"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -162,12 +163,13 @@ namespace Server.Engines.MLQuests.Definitions public class SkeletonOfSzandor : BaseCreature { [Constructible] - public SkeletonOfSzandor() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public SkeletonOfSzandor() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Late Architect"; Hue = 0x83F2; // TODO: Random human hue? Why??? Body = 0x32; + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); } diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs index 20e0216f3..6594c71fb 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs @@ -2004,14 +2004,15 @@ namespace Server.Engines.MLQuests.Definitions public class Saril : BaseCreature { [Constructible] - public Saril() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Saril() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the guard"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2070,14 +2071,15 @@ namespace Server.Engines.MLQuests.Definitions public class Cailla : BaseCreature { [Constructible] - public Cailla() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Cailla() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the guard"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2137,14 +2139,15 @@ namespace Server.Engines.MLQuests.Definitions public class Tamm : BaseCreature { [Constructible] - public Tamm() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Tamm() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the guard"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2202,14 +2205,15 @@ namespace Server.Engines.MLQuests.Definitions public class Landy : BaseCreature { [Constructible] - public Landy() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Landy() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the soil nurturer"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2268,14 +2272,15 @@ namespace Server.Engines.MLQuests.Definitions public class Alejaha : BaseCreature { [Constructible] - public Alejaha() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Alejaha() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2333,14 +2338,15 @@ namespace Server.Engines.MLQuests.Definitions public class Mielan : BaseCreature { [Constructible] - public Mielan() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Mielan() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arcanist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2396,14 +2402,15 @@ namespace Server.Engines.MLQuests.Definitions public class Ciala : BaseCreature { [Constructible] - public Ciala() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Ciala() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arborist"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2467,14 +2474,15 @@ namespace Server.Engines.MLQuests.Definitions public class Aniel : BaseCreature { [Constructible] - public Aniel() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Aniel() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arborist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2530,14 +2538,15 @@ namespace Server.Engines.MLQuests.Definitions public class Aulan : BaseCreature { [Constructible] - public Aulan() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Aulan() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the expeditionist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2607,14 +2616,15 @@ namespace Server.Engines.MLQuests.Definitions public class Brinnae : BaseCreature { [Constructible] - public Brinnae() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Brinnae() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2666,14 +2676,15 @@ namespace Server.Engines.MLQuests.Definitions public class Caelas : BaseCreature { [Constructible] - public Caelas() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Caelas() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2729,14 +2740,15 @@ namespace Server.Engines.MLQuests.Definitions public class Clehin : BaseCreature { [Constructible] - public Clehin() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Clehin() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the soil nurturer"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2790,14 +2802,15 @@ namespace Server.Engines.MLQuests.Definitions public class Cloorne : BaseCreature { [Constructible] - public Cloorne() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Cloorne() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the expeditionist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2866,14 +2879,15 @@ namespace Server.Engines.MLQuests.Definitions public class Salaenih : BaseCreature { [Constructible] - public Salaenih() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Salaenih() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the expeditionist"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -2949,14 +2963,15 @@ namespace Server.Engines.MLQuests.Definitions public class Vilo : BaseCreature { [Constructible] - public Vilo() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Vilo() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the guard"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3016,14 +3031,15 @@ namespace Server.Engines.MLQuests.Definitions public class Tholef : BaseCreature { [Constructible] - public Tholef() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Tholef() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the grape tender"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3085,14 +3101,15 @@ namespace Server.Engines.MLQuests.Definitions public class Tillanil : BaseCreature { [Constructible] - public Tillanil() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Tillanil() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the grape tender"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3147,14 +3164,15 @@ namespace Server.Engines.MLQuests.Definitions public class Waelian : BaseCreature { [Constructible] - public Waelian() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Waelian() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the trinket weaver"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3216,14 +3234,15 @@ namespace Server.Engines.MLQuests.Definitions public class Sleen : BaseCreature { [Constructible] - public Sleen() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Sleen() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the trinket weaver"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3279,14 +3298,15 @@ namespace Server.Engines.MLQuests.Definitions public class Unoelil : BaseCreature { [Constructible] - public Unoelil() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Unoelil() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the bark weaver"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3341,14 +3361,15 @@ namespace Server.Engines.MLQuests.Definitions public class Anolly : BaseCreature { [Constructible] - public Anolly() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Anolly() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the bark weaver"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3390,14 +3411,15 @@ namespace Server.Engines.MLQuests.Definitions public class Jusae : BaseCreature { [Constructible] - public Jusae() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Jusae() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the bowcrafter"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3462,14 +3484,15 @@ namespace Server.Engines.MLQuests.Definitions public class Cillitha : BaseCreature { [Constructible] - public Cillitha() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Cillitha() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the bowcrafter"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3524,14 +3547,15 @@ namespace Server.Engines.MLQuests.Definitions public class Lohn : BaseCreature { [Constructible] - public Lohn() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Lohn() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the metal weaver"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3593,14 +3617,15 @@ namespace Server.Engines.MLQuests.Definitions public class Olla : BaseCreature { [Constructible] - public Olla() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Olla() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the metal weaver"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3657,14 +3682,15 @@ namespace Server.Engines.MLQuests.Definitions public class Thallary : BaseCreature { [Constructible] - public Thallary() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Thallary() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the cloth weaver"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3720,14 +3746,15 @@ namespace Server.Engines.MLQuests.Definitions public class Ahie : BaseCreature { [Constructible] - public Ahie() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Ahie() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the cloth weaver"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3782,14 +3809,15 @@ namespace Server.Engines.MLQuests.Definitions public class Tyeelor : BaseCreature { [Constructible] - public Tyeelor() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Tyeelor() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the expeditionist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3845,14 +3873,15 @@ namespace Server.Engines.MLQuests.Definitions public class Athailon : BaseCreature { [Constructible] - public Athailon() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Athailon() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the expeditionist"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3906,14 +3935,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderTaellia : BaseCreature { [Constructible] - public ElderTaellia() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderTaellia() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -3950,14 +3980,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderMallew : BaseCreature { [Constructible] - public ElderMallew() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderMallew() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4007,14 +4038,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderAbbein : BaseCreature { [Constructible] - public ElderAbbein() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderAbbein() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4050,14 +4082,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderVicaie : BaseCreature { [Constructible] - public ElderVicaie() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderVicaie() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4098,14 +4131,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderJothan : BaseCreature { [Constructible] - public ElderJothan() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderJothan() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4143,14 +4177,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderAlethanian : BaseCreature { [Constructible] - public ElderAlethanian() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderAlethanian() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4188,14 +4223,15 @@ namespace Server.Engines.MLQuests.Definitions public class Rebinil : BaseCreature { [Constructible] - public Rebinil() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Rebinil() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the healer"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4231,14 +4267,15 @@ namespace Server.Engines.MLQuests.Definitions public class Aluniol : BaseCreature { [Constructible] - public Aluniol() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Aluniol() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the healer"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4274,14 +4311,15 @@ namespace Server.Engines.MLQuests.Definitions public class Olaeni : BaseCreature { [Constructible] - public Olaeni() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Olaeni() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the thaumaturgist"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4318,14 +4356,15 @@ namespace Server.Engines.MLQuests.Definitions public class Bolaevin : BaseCreature { [Constructible] - public Bolaevin() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Bolaevin() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arcanist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4368,14 +4407,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperAneen : BaseCreature { [Constructible] - public LorekeeperAneen() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperAneen() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4411,14 +4451,15 @@ namespace Server.Engines.MLQuests.Definitions public class Daelas : BaseCreature { [Constructible] - public Daelas() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Daelas() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arborist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4463,14 +4504,15 @@ namespace Server.Engines.MLQuests.Definitions public class Alelle : BaseCreature { [Constructible] - public Alelle() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Alelle() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arborist"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4519,14 +4561,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperNillaen : BaseCreature { [Constructible] - public LorekeeperNillaen() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperNillaen() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4570,14 +4613,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperRyal : BaseCreature { [Constructible] - public LorekeeperRyal() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperRyal() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4634,14 +4678,15 @@ namespace Server.Engines.MLQuests.Definitions public class Braen : BaseCreature { [Constructible] - public Braen() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Braen() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the thaumaturgist"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4689,14 +4734,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderAcob : BaseCreature { [Constructible] - public ElderAcob() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderAcob() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4743,14 +4789,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperCalendor : BaseCreature { [Constructible] - public LorekeeperCalendor() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperCalendor() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -4798,14 +4845,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperSiarra : BaseCreature { [Constructible] - public LorekeeperSiarra() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperSiarra() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs index 4284e0906..d705f351e 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs @@ -278,12 +278,12 @@ namespace Server.Engines.MLQuests.Definitions public class Enigma : BaseCreature { [Constructible] - public Enigma() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Enigma() : base(AIType.AI_Vendor, FightMode.None, 2) { Body = 788; BaseSoundID = 0x3EE; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); } @@ -616,12 +616,13 @@ namespace Server.Engines.MLQuests.Definitions public class Sledge : BaseCreature { [Constructible] - public Sledge() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Sledge() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Versatile"; Body = 400; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); AddItem(new Tunic(Utility.RandomNeutralHue())); @@ -671,12 +672,13 @@ namespace Server.Engines.MLQuests.Definitions public class Patricus : BaseCreature { [Constructible] - public Patricus() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Patricus() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Trader"; Body = 400; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); AddItem(new FancyShirt(Utility.RandomNeutralHue())); @@ -713,13 +715,14 @@ namespace Server.Engines.MLQuests.Definitions public class Belulah : BaseCreature { [Constructible] - public Belulah() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Belulah() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the scorned"; Female = true; Body = 401; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/HonestBeggar.cs b/Projects/UOContent/Engines/ML Quests/Definitions/HonestBeggar.cs index 3173d8179..2b54401c5 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/HonestBeggar.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/HonestBeggar.cs @@ -55,14 +55,15 @@ namespace Server.Engines.MLQuests.Definitions public class Evan : BaseCreature { [Constructible] - public Evan() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Evan() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Beggar"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -98,14 +99,15 @@ namespace Server.Engines.MLQuests.Definitions public class Regina : BaseCreature { [Constructible] - public Regina() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Regina() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Noble"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs index 394010258..7887057f4 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs @@ -174,12 +174,13 @@ namespace Server.Engines.MLQuests.Definitions public class GrandpaCharley : BaseCreature { [Constructible] - public GrandpaCharley() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public GrandpaCharley() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the farmer"; Body = 400; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); var hairHue = 0x3B2 + Utility.Random(2); @@ -226,14 +227,15 @@ namespace Server.Engines.MLQuests.Definitions public class Jelrice : BaseCreature { [Constructible] - public Jelrice() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Jelrice() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the trader"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -276,14 +278,15 @@ namespace Server.Engines.MLQuests.Definitions public class Yorus : BaseCreature { [Constructible] - public Yorus() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Yorus() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the tinker"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Malas.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Malas.cs index 5e3d974ea..56a595140 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Malas.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Malas.cs @@ -27,14 +27,15 @@ namespace Server.Engines.MLQuests.Definitions public class Drithen : BaseCreature { [Constructible] - public Drithen() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Drithen() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Fierce"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); AddItem(new Backpack()); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/MistakenIdentity.cs b/Projects/UOContent/Engines/ML Quests/Definitions/MistakenIdentity.cs index 057188b8d..42b2eed73 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/MistakenIdentity.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/MistakenIdentity.cs @@ -195,14 +195,15 @@ namespace Server.Engines.MLQuests.Definitions public class Aernya : BaseCreature { [Constructible] - public Aernya() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Aernya() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Mistress of Admissions"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -241,14 +242,15 @@ namespace Server.Engines.MLQuests.Definitions public class Gorrow : BaseCreature { [Constructible] - public Gorrow() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Gorrow() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Mayor"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); AddItem(new Backpack()); @@ -298,14 +300,15 @@ namespace Server.Engines.MLQuests.Definitions public class MasterGnosos : BaseCreature { [Constructible] - public MasterGnosos() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public MasterGnosos() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the necromancer"; Race = Race.Human; Body = 0x190; Female = false; Hue = 0x83E8; + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); HairItemID = 0x2049; diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs index d0d38411c..bdcca750f 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs @@ -654,6 +654,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x204D; FacialHairHue = 0x47D; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -708,8 +709,7 @@ namespace Server.Engines.MLQuests.Definitions public class Dimethro : BaseCreature { [Constructible] - public Dimethro() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Dimethro() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Wrestling Instructor"; Body = 0x190; @@ -719,6 +719,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x204D; FacialHairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.EvalInt, 120.0); @@ -767,8 +768,7 @@ namespace Server.Engines.MLQuests.Definitions public class Churchill : BaseCreature { [Constructible] - public Churchill() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Churchill() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Mace Fighting Instructor"; Body = 0x190; @@ -776,6 +776,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203C; HairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -854,6 +855,7 @@ namespace Server.Engines.MLQuests.Definitions HairHue = 0x47D; Female = true; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -927,8 +929,7 @@ namespace Server.Engines.MLQuests.Definitions public class Recaro : BaseCreature { [Constructible] - public Recaro() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Recaro() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Fencer Instructor"; Body = 0x190; @@ -938,6 +939,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x204D; FacialHairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1011,8 +1013,7 @@ namespace Server.Engines.MLQuests.Definitions public class AldenArmstrong : BaseCreature { [Constructible] - public AldenArmstrong() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public AldenArmstrong() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Tactics Instructor"; Body = 0x190; @@ -1020,6 +1021,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203B; HairHue = 0x44E; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1076,8 +1078,7 @@ namespace Server.Engines.MLQuests.Definitions public class Jockles : BaseCreature { [Constructible] - public Jockles() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Jockles() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Swordsmanship Instructor"; Body = 0x190; @@ -1085,6 +1086,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203C; HairHue = 0x8A7; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1137,14 +1139,14 @@ namespace Server.Engines.MLQuests.Definitions public class TylAriadne : BaseCreature { [Constructible] - public TylAriadne() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public TylAriadne() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Parrying Instructor"; Body = 0x190; Hue = 0x8374; HairItemID = 0; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1218,8 +1220,7 @@ namespace Server.Engines.MLQuests.Definitions public class Alefian : BaseCreature { [Constructible] - public Alefian() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Alefian() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Resisting Spells Instructor"; Body = 0x190; @@ -1227,6 +1228,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203D; HairHue = 0x457; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.EvalInt, 120.0); @@ -1274,8 +1276,7 @@ namespace Server.Engines.MLQuests.Definitions public class Gustar : BaseCreature { [Constructible] - public Gustar() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Gustar() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Meditation Instructor"; Body = 0x190; @@ -1283,6 +1284,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203B; HairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.EvalInt, 120.0); @@ -1356,8 +1358,7 @@ namespace Server.Engines.MLQuests.Definitions public class Jillian : BaseCreature { [Constructible] - public Jillian() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Jillian() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Inscription Instructor"; Body = 0x191; @@ -1366,6 +1367,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203D; HairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.EvalInt, 120.0); @@ -1413,8 +1415,7 @@ namespace Server.Engines.MLQuests.Definitions public class Kaelynna : BaseCreature { [Constructible] - public Kaelynna() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Kaelynna() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Magery Instructor"; Body = 0x191; @@ -1423,6 +1424,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203C; HairHue = 0x47D; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.EvalInt, 120.0); @@ -1470,8 +1472,7 @@ namespace Server.Engines.MLQuests.Definitions public class Mithneral : BaseCreature { [Constructible] - public Mithneral() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Mithneral() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Evaluating Intelligence Instructor"; Body = 0x190; @@ -1479,6 +1480,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203C; HairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.EvalInt, 120.0); @@ -1540,6 +1542,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203D; HairHue = 0x46C; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.ArmsLore, 120.0); @@ -1591,8 +1594,7 @@ namespace Server.Engines.MLQuests.Definitions public class AndreasVesalius : BaseCreature { [Constructible] - public AndreasVesalius() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public AndreasVesalius() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Anatomy Instructor"; Body = 0x190; @@ -1602,6 +1604,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x203E; FacialHairHue = 0x477; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1651,8 +1654,7 @@ namespace Server.Engines.MLQuests.Definitions public class Avicenna : BaseCreature { [Constructible] - public Avicenna() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Avicenna() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Healing Instructor"; Body = 0x190; @@ -1660,6 +1662,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203B; HairHue = 0x477; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1708,8 +1711,7 @@ namespace Server.Engines.MLQuests.Definitions public class SarsmeaSmythe : BaseCreature { [Constructible] - public SarsmeaSmythe() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public SarsmeaSmythe() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Focus Instructor"; Body = 0x191; @@ -1718,6 +1720,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203C; HairHue = 0x456; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Anatomy, 120.0); @@ -1840,12 +1843,12 @@ namespace Server.Engines.MLQuests.Definitions public class Chiyo : BaseCreature { [Constructible] - public Chiyo() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Chiyo() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Hiding Instructor"; Body = 0xF7; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Hiding, 120.0); @@ -1889,8 +1892,7 @@ namespace Server.Engines.MLQuests.Definitions public class Jun : BaseCreature { [Constructible] - public Jun() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Jun() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Stealth Instructor"; Body = 0x190; @@ -1898,6 +1900,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203B; HairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Hiding, 120.0); @@ -1949,8 +1952,7 @@ namespace Server.Engines.MLQuests.Definitions public class Walker : BaseCreature { [Constructible] - public Walker() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Walker() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Tracking Instructor"; Body = 0x190; @@ -1960,6 +1962,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x204B; FacialHairHue = 0x47D; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Hiding, 120.0); @@ -2097,6 +2100,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203D; HairHue = 0x457; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Magery, 120.0); @@ -2176,6 +2180,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203C; HairHue = 0x455; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.Magery, 120.0); @@ -2225,8 +2230,7 @@ namespace Server.Engines.MLQuests.Definitions public class JacobWaltz : BaseCreature { [Constructible] - public JacobWaltz() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public JacobWaltz() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Miner Instructor"; Body = 0x190; @@ -2236,6 +2240,7 @@ namespace Server.Engines.MLQuests.Definitions FacialHairItemID = 0x204D; FacialHairHue = 0x44E; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.ArmsLore, 120.0); @@ -2296,6 +2301,7 @@ namespace Server.Engines.MLQuests.Definitions HairItemID = 0x203B; HairHue = 0x47B; + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); SetSkill(SkillName.ArmsLore, 120.0); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs index 3eeccc7b0..f3d9b8ba9 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs @@ -262,14 +262,15 @@ namespace Server.Engines.MLQuests.Definitions public class Andric : BaseCreature { [Constructible] - public Andric() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Andric() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the archer trainer"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -340,14 +341,15 @@ namespace Server.Engines.MLQuests.Definitions public class Kashiel : BaseCreature { [Constructible] - public Kashiel() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Kashiel() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the archer"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -402,14 +404,15 @@ namespace Server.Engines.MLQuests.Definitions public class Asandos : BaseCreature { [Constructible] - public Asandos() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Asandos() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the chef"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -462,14 +465,15 @@ namespace Server.Engines.MLQuests.Definitions public class Clairesse : BaseCreature { [Constructible] - public Clairesse() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Clairesse() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the servant"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -518,14 +522,15 @@ namespace Server.Engines.MLQuests.Definitions public class Gervis : BaseCreature { [Constructible] - public Gervis() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Gervis() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the blacksmith trainer"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -586,14 +591,15 @@ namespace Server.Engines.MLQuests.Definitions public class Mugg : BaseCreature { [Constructible] - public Mugg() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Mugg() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the miner"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -639,14 +645,15 @@ namespace Server.Engines.MLQuests.Definitions public class Lowel : BaseCreature { [Constructible] - public Lowel() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Lowel() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the carpenter"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -697,14 +704,15 @@ namespace Server.Engines.MLQuests.Definitions public class Lyle : BaseCreature { [Constructible] - public Lyle() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Lyle() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the mage"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -753,14 +761,15 @@ namespace Server.Engines.MLQuests.Definitions public class Nibbet : BaseCreature { [Constructible] - public Nibbet() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Nibbet() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the tinker"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -798,14 +807,15 @@ namespace Server.Engines.MLQuests.Definitions public class Norton : BaseCreature { [Constructible] - public Norton() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Norton() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the fisher"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -856,14 +866,15 @@ namespace Server.Engines.MLQuests.Definitions public class Sadrah : BaseCreature { [Constructible] - public Sadrah() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Sadrah() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the courier"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -916,14 +927,15 @@ namespace Server.Engines.MLQuests.Definitions public class Hargrove : BaseCreature { [Constructible] - public Hargrove() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Hargrove() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Lumberjack"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs index 97a363595..5424438d0 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs @@ -762,13 +762,15 @@ namespace Server.Engines.MLQuests.Definitions { [Constructible] public Beotham() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the bowcrafter"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -828,14 +830,15 @@ namespace Server.Engines.MLQuests.Definitions public class Danoel : BaseCreature { [Constructible] - public Danoel() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Danoel() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the metal weaver"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -886,14 +889,15 @@ namespace Server.Engines.MLQuests.Definitions public class Tallinin : BaseCreature { [Constructible] - public Tallinin() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Tallinin() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the cloth weaver"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -947,14 +951,15 @@ namespace Server.Engines.MLQuests.Definitions public class Tiana : BaseCreature { [Constructible] - public Tiana() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Tiana() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the guard"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -1018,14 +1023,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperOolua : BaseCreature { [Constructible] - public LorekeeperOolua() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperOolua() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -1075,14 +1081,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperRollarn : BaseCreature { [Constructible] - public LorekeeperRollarn() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperRollarn() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -1143,14 +1150,15 @@ namespace Server.Engines.MLQuests.Definitions public class Dallid : BaseCreature { [Constructible] - public Dallid() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Dallid() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the cook"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -1206,14 +1214,15 @@ namespace Server.Engines.MLQuests.Definitions public class Canir : BaseCreature { [Constructible] - public Canir() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Canir() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the thaumaturgist"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -1267,14 +1276,15 @@ namespace Server.Engines.MLQuests.Definitions public class Yellienir : BaseCreature { [Constructible] - public Yellienir() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Yellienir() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the bark weaver"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -1313,14 +1323,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderOnallan : BaseCreature { [Constructible] - public ElderOnallan() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public ElderOnallan() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Body = 0x25D; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs index 38104cf18..edfbfa3f7 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs @@ -493,14 +493,15 @@ namespace Server.Engines.MLQuests.Definitions public class Aeluva : BaseCreature { [Constructible] - public Aeluva() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2.0) + public Aeluva() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arcanist"; Race = Race.Elf; Female = true; Body = 606; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -552,13 +553,14 @@ namespace Server.Engines.MLQuests.Definitions public class Koole : BaseCreature { [Constructible] - public Koole() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2.0) + public Koole() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arcanist"; Race = Race.Elf; Body = 605; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -622,14 +624,15 @@ namespace Server.Engines.MLQuests.Definitions public class Synaeva : BaseCreature { [Constructible] - public Synaeva() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2.0) + public Synaeva() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the arcanist"; Race = Race.Elf; Female = true; Body = 606; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -682,14 +685,15 @@ namespace Server.Engines.MLQuests.Definitions public class ElderBrae : BaseCreature { [Constructible] - public ElderBrae() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2.0) + public ElderBrae() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the wise"; Race = Race.Elf; Female = true; Body = 606; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/TheAncientWorld.cs b/Projects/UOContent/Engines/ML Quests/Definitions/TheAncientWorld.cs index 61a66f811..37d732311 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/TheAncientWorld.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/TheAncientWorld.cs @@ -98,14 +98,15 @@ namespace Server.Engines.MLQuests.Definitions public class LorekeeperBroolol : BaseCreature { [Constructible] - public LorekeeperBroolol() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public LorekeeperBroolol() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the keeper of tradition"; Race = Race.Elf; Body = 0x25E; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/UnfadingMemories.cs b/Projects/UOContent/Engines/ML Quests/Definitions/UnfadingMemories.cs index cfa189062..fec2e21be 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/UnfadingMemories.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/UnfadingMemories.cs @@ -83,14 +83,15 @@ namespace Server.Engines.MLQuests.Definitions public class Emilio : BaseCreature { [Constructible] - public Emilio() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Emilio() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Tortured Artist"; Race = Race.Human; Body = 0x190; Female = false; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); @@ -130,14 +131,15 @@ namespace Server.Engines.MLQuests.Definitions public class Thalia : BaseCreature { [Constructible] - public Thalia() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public Thalia() : base(AIType.AI_Vendor, FightMode.None, 2) { Title = "the Bride"; Race = Race.Human; Body = 0x191; Female = true; Hue = Race.RandomSkinHue(); + + SetSpeed(0.5, 2.0); InitStats(100, 100, 25); Utility.AssignRandomHair(this); diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs index d5b6a0555..abc739166 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs @@ -76,11 +76,11 @@ namespace Server.Engines.MLQuests.Gumps } } - public static void Initialize() + public static unsafe void Initialize() { m_Pending = new Dictionary(); - IncomingExtendedCommandPackets.RegisterExtended(0x2A, true, RaceChangeReply); + IncomingExtendedCommandPackets.RegisterExtended(0x2A, true, &RaceChangeReply); } public static bool IsPending(NetState state) => state != null && m_Pending.ContainsKey(state); @@ -193,7 +193,7 @@ namespace Server.Engines.MLQuests.Gumps return false; } - private static void RaceChangeReply(NetState state, CircularBufferReader reader, ref int packetLength) + private static void RaceChangeReply(NetState state, CircularBufferReader reader, int packetLength) { if (!m_Pending.TryGetValue(state, out var raceChangeState)) { diff --git a/Projects/UOContent/Engines/ML Quests/Items/APersonalLetterAddressedToAhie.cs b/Projects/UOContent/Engines/ML Quests/Items/APersonalLetterAddressedToAhie.cs index 5dccf5f17..fbb09aab1 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/APersonalLetterAddressedToAhie.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/APersonalLetterAddressedToAhie.cs @@ -15,7 +15,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/AlchemistsBandage.cs b/Projects/UOContent/Engines/ML Quests/Items/AlchemistsBandage.cs index 116550434..03526c475 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/AlchemistsBandage.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/AlchemistsBandage.cs @@ -17,7 +17,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/BasinOfCrystalClearWater.cs b/Projects/UOContent/Engines/ML Quests/Items/BasinOfCrystalClearWater.cs index b4f725563..0f2068339 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/BasinOfCrystalClearWater.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/BasinOfCrystalClearWater.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/BridesLetter.cs b/Projects/UOContent/Engines/ML Quests/Items/BridesLetter.cs index 83f043636..12f897720 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/BridesLetter.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/BridesLetter.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/CompletedTuitionReimbursementForm.cs b/Projects/UOContent/Engines/ML Quests/Items/CompletedTuitionReimbursementForm.cs index 04f706bf7..efc18c8db 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/CompletedTuitionReimbursementForm.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/CompletedTuitionReimbursementForm.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/CrateForSledge.cs b/Projects/UOContent/Engines/ML Quests/Items/CrateForSledge.cs index 033da30ef..5127ee830 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/CrateForSledge.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/CrateForSledge.cs @@ -15,7 +15,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/DreadSpiderSilk.cs b/Projects/UOContent/Engines/ML Quests/Items/DreadSpiderSilk.cs index 8583a7ce3..29788c71f 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/DreadSpiderSilk.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/DreadSpiderSilk.cs @@ -17,7 +17,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/FragmentOfAMapDelivery.cs b/Projects/UOContent/Engines/ML Quests/Items/FragmentOfAMapDelivery.cs index 18b52b92f..54a62d187 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/FragmentOfAMapDelivery.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/FragmentOfAMapDelivery.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/FriendsOfTheLibraryApplication.cs b/Projects/UOContent/Engines/ML Quests/Items/FriendsOfTheLibraryApplication.cs index 8bde0ff1f..b2eeb94d8 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/FriendsOfTheLibraryApplication.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/FriendsOfTheLibraryApplication.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/GiftForArielle.cs b/Projects/UOContent/Engines/ML Quests/Items/GiftForArielle.cs index a3fe7a704..414d24622 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/GiftForArielle.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/GiftForArielle.cs @@ -14,7 +14,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/NotarizedApplication.cs b/Projects/UOContent/Engines/ML Quests/Items/NotarizedApplication.cs index 06d7d5b6e..c27d26f5a 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/NotarizedApplication.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/NotarizedApplication.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/OfficialSealingWax.cs b/Projects/UOContent/Engines/ML Quests/Items/OfficialSealingWax.cs index e540a50d5..9378ebea6 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/OfficialSealingWax.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/OfficialSealingWax.cs @@ -17,7 +17,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/PortraitOfTheBride.cs b/Projects/UOContent/Engines/ML Quests/Items/PortraitOfTheBride.cs index 9f2ca6e75..f95740802 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/PortraitOfTheBride.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/PortraitOfTheBride.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/PrismaticAmber.cs b/Projects/UOContent/Engines/ML Quests/Items/PrismaticAmber.cs index dcf074ddd..cae5ea63a 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/PrismaticAmber.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/PrismaticAmber.cs @@ -13,7 +13,7 @@ namespace Server.Items public override int LabelNumber => 1075299; // Prismatic Amber - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/QuestGiverItem.cs b/Projects/UOContent/Engines/ML Quests/Items/QuestGiverItem.cs index 4389efdaa..652de737e 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/QuestGiverItem.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/QuestGiverItem.cs @@ -27,7 +27,7 @@ namespace Server.Engines.MLQuests.Items public List MLQuests => m_MLQuests ?? (m_MLQuests = MLQuestSystem.FindQuestList(GetType()) ?? MLQuestSystem.EmptyList); - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); @@ -105,7 +105,7 @@ namespace Server.Engines.MLQuests.Items { } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/ReginasLetter.cs b/Projects/UOContent/Engines/ML Quests/Items/ReginasLetter.cs index aaf62201b..1759d5b0e 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/ReginasLetter.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/ReginasLetter.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/ReginasRing.cs b/Projects/UOContent/Engines/ML Quests/Items/ReginasRing.cs index d7a40fb3c..2af14cacd 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/ReginasRing.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/ReginasRing.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/SealedNotesForJamal.cs b/Projects/UOContent/Engines/ML Quests/Items/SealedNotesForJamal.cs index 6cdf61405..488be1a84 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/SealedNotesForJamal.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/SealedNotesForJamal.cs @@ -14,7 +14,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/SealingWaxOrderAddressedToPetrus.cs b/Projects/UOContent/Engines/ML Quests/Items/SealingWaxOrderAddressedToPetrus.cs index 59b4eb59a..e1783bdd6 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/SealingWaxOrderAddressedToPetrus.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/SealingWaxOrderAddressedToPetrus.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/SignedTuitionReimbursementForm.cs b/Projects/UOContent/Engines/ML Quests/Items/SignedTuitionReimbursementForm.cs index 3094e5769..66c86bbef 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/SignedTuitionReimbursementForm.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/SignedTuitionReimbursementForm.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/SpiritBottle.cs b/Projects/UOContent/Engines/ML Quests/Items/SpiritBottle.cs index 86767928b..e24768807 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/SpiritBottle.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/SpiritBottle.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/TaintedTreeSample.cs b/Projects/UOContent/Engines/ML Quests/Items/TaintedTreeSample.cs index 545aa65ff..f18a0bb99 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/TaintedTreeSample.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/TaintedTreeSample.cs @@ -17,7 +17,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/Teleporters.cs b/Projects/UOContent/Engines/ML Quests/Items/Teleporters.cs index dc7262333..e7abdc648 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/Teleporters.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/Teleporters.cs @@ -71,7 +71,7 @@ namespace Server.Engines.MLQuests.Items return false; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -179,7 +179,7 @@ namespace Server.Engines.MLQuests.Items return true; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Engines/ML Quests/Items/TuitionReimbursementForm.cs b/Projects/UOContent/Engines/ML Quests/Items/TuitionReimbursementForm.cs index 7daaa8b72..16740b154 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/TuitionReimbursementForm.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/TuitionReimbursementForm.cs @@ -13,7 +13,7 @@ namespace Server.Items public override bool Nontransferable => true; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); AddQuestItemProperty(list); diff --git a/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs b/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs index 9ab500594..b6c00bcc4 100644 --- a/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs +++ b/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs @@ -12,9 +12,9 @@ namespace Server.Engines.MLQuests.Mobiles { private InternalTimer m_Timer; - public DoneQuestCollector() - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public DoneQuestCollector() : base(AIType.AI_Vendor, FightMode.None, 2) { + SetSpeed(0.5, 2.0); } public DoneQuestCollector(Serial serial) diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs index 25e9ef729..87344e480 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs @@ -26,7 +26,7 @@ namespace Server.Engines.MLQuests.Objectives if (itemid is <= 0 or > 0x4000) { - logger.Warning("Cliloc {0} is likely giving the wrong item ID", name.Number); + logger.Warning("Cliloc {Number} is likely giving the wrong item ID", name.Number); } } } diff --git a/Projects/UOContent/Engines/Party/PartyPackets.cs b/Projects/UOContent/Engines/Party/PartyPackets.cs index 627f1811f..97c2bb952 100644 --- a/Projects/UOContent/Engines/Party/PartyPackets.cs +++ b/Projects/UOContent/Engines/Party/PartyPackets.cs @@ -48,7 +48,7 @@ namespace Server.Engines.PartySystem public static void SendPartyMemberList(this NetState ns, Party p) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -64,7 +64,7 @@ namespace Server.Engines.PartySystem public static void SendPartyRemoveMember(this NetState ns, Serial m, Party p = null) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -104,7 +104,7 @@ namespace Server.Engines.PartySystem public static void SendPartyTextMessage(this NetState ns, Serial m, string text, bool toAll) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -134,7 +134,7 @@ namespace Server.Engines.PartySystem public static void SendPartyInvitation(this NetState ns, Serial leader) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/Pathing/Movement.cs b/Projects/UOContent/Engines/Pathing/Movement.cs index 1b733bea3..3a4a5acec 100644 --- a/Projects/UOContent/Engines/Pathing/Movement.cs +++ b/Projects/UOContent/Engines/Pathing/Movement.cs @@ -406,7 +406,15 @@ namespace Server.Movement var notWater = !itemData.Wet; - if (!itemData.Surface && itemData.Impassable && (!canSwim || notWater) || cantWalk && notWater) + /* + * To move we must satisfy the following: + * 1. Item is a _passable_ surface and Mob can walk -or- + * 2. Item is water and Mob can swim + */ + if ( + (!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) || + cantWalk && notWater + ) { continue; } @@ -477,7 +485,17 @@ namespace Server.Movement var notWater = !itemData.Wet; - if (item.Movable || !itemData.Surface && itemData.Impassable && (!canSwim || notWater) || cantWalk && notWater) + /* + * To move we must satisfy the following: + * 1. Item is not movable + * 2. Item is a _passable_ surface and Mob can walk -or- + * Item is water and Mob can swim + */ + if ( + item.Movable || + (!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) || + cantWalk && notWater + ) { continue; } @@ -581,7 +599,7 @@ namespace Server.Movement return moveIsOk; } - private bool CanMoveOver(Mobile m, Mobile t) => + private static bool CanMoveOver(Mobile m, Mobile t) => !t.Alive || !m.Alive || t.IsDeadBondedPet || m.IsDeadBondedPet || t.Hidden && t.AccessLevel > AccessLevel.Player; private void GetStartZ(Mobile m, Map map, Point3D loc, List itemList, out int zLow, out int zTop) diff --git a/Projects/UOContent/Engines/Plants/MiscMobiles/GiantIceWorm.cs b/Projects/UOContent/Engines/Plants/MiscMobiles/GiantIceWorm.cs index a882b772a..8ba3969f7 100644 --- a/Projects/UOContent/Engines/Plants/MiscMobiles/GiantIceWorm.cs +++ b/Projects/UOContent/Engines/Plants/MiscMobiles/GiantIceWorm.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class GiantIceWorm : BaseCreature { [Constructible] - public GiantIceWorm() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GiantIceWorm() : base(AIType.AI_Melee) { Body = 89; BaseSoundID = 0xDC; diff --git a/Projects/UOContent/Engines/Plants/PlantItem.cs b/Projects/UOContent/Engines/Plants/PlantItem.cs index b99a0f7c7..21aaabc1a 100644 --- a/Projects/UOContent/Engines/Plants/PlantItem.cs +++ b/Projects/UOContent/Engines/Plants/PlantItem.cs @@ -30,17 +30,14 @@ namespace Server.Engines.Plants public class PlantItem : Item, ISecurable { - /* - * Clients 7.0.12.0+ expect a container type in the plant label. - * To support older (and only older) clients, change this to false. - */ - private static readonly bool ShowContainerType = true; private PlantHue m_PlantHue; - private PlantStatus m_PlantStatus; private PlantType m_PlantType; private bool m_ShowType; + // For clients older than 7.0.12.0 + private ObjectPropertyList _oldClientPropertyList; + [Constructible] public PlantItem(bool fertileDirt = false) : base(0x1602) { @@ -59,6 +56,9 @@ namespace Server.Engines.Plants public PlantSystem PlantSystem { get; private set; } + public ObjectPropertyList OldClientPropertyList => + _oldClientPropertyList ??= InitializePropertyList(_oldClientPropertyList); + public override bool ForceShowProperties => ObjectPropertyList.Enabled; [CommandProperty(AccessLevel.GameMaster)] @@ -248,80 +248,189 @@ namespace Server.Engines.Plants InvalidateProperties(); } - public override void AddNameProperty(ObjectPropertyList list) + private ObjectPropertyList InitializePropertyList(ObjectPropertyList list) { - if (m_PlantStatus >= PlantStatus.DeadTwigs) - { - base.AddNameProperty(list); - } - else if (m_PlantStatus < PlantStatus.Seed) - { - string args; + GetProperties(list); + AppendChildProperties(list); + list.Terminate(); + return list; + } - if (ShowContainerType) + // Overridden to support new and old client localization + public override void SendOPLPacketTo(NetState ns) + { + if (!ObjectPropertyList.Enabled) + { + return; + } + + if (ns.Version < ClientVersion.Version70120) + { + ns.SendOPLInfo(Serial, OldClientPropertyList.Hash); + return; + } + + ns.SendOPLInfo(this); + } + + public override void SendPropertiesTo(NetState ns) + { + if (ns?.Version < ClientVersion.Version70120) + { + ns?.Send(OldClientPropertyList.Buffer); + return; + } + + ns?.Send(PropertyList.Buffer); + } + + public override void ClearProperties() + { + base.ClearProperties(); + _oldClientPropertyList = null; + } + + public override void InvalidateProperties() + { + if (!ObjectPropertyList.Enabled) + { + return; + } + + base.InvalidateProperties(); + + if (Map != null && Map != Map.Internal && !World.Loading) + { + int? oldHash = _oldClientPropertyList?.Hash; + + if (oldHash != null) { - args = $"#{GetLocalizedContainerType()}\t#{PlantSystem.GetLocalizedDirtStatus()}"; + _oldClientPropertyList.Reset(); } else { - args = $"#{PlantSystem.GetLocalizedDirtStatus()}"; + _oldClientPropertyList = new ObjectPropertyList(this); } - list.Add(1060830, args); // a ~1_val~ of ~2_val~ dirt + InitializePropertyList(_oldClientPropertyList); + + if (oldHash != _oldClientPropertyList.Hash) + { + Delta(ItemDelta.Properties); + } } else { - var typeInfo = PlantTypeInfo.GetInfo(m_PlantType); - var hueInfo = PlantHueInfo.GetInfo(m_PlantHue); + ClearProperties(); + } + } - if (m_PlantStatus >= PlantStatus.DecorativePlant) + public override void OnAosSingleClick(Mobile from) + { + var ns = from?.NetState; + + if (ns == null) + { + return; + } + + var opl = ns.Version < ClientVersion.Version70120 ? OldClientPropertyList : PropertyList; + + if (opl.Header > 0) + { + from.NetState.SendMessageLocalized( + Serial, + ItemID, + MessageType.Label, + 0x3B2, + 3, + opl.Header, + Name, + opl.HeaderArgs + ); + } + } + + public override void GetProperties(IPropertyList list) + { + if (m_PlantStatus >= PlantStatus.DeadTwigs) + { + base.GetProperties(list); + return; + } + + var container = GetLocalizedContainerType(); + var dirt = PlantSystem.GetLocalizedDirtStatus(); + var health = PlantSystem.GetLocalizedHealth(); + var plantStatus = GetLocalizedPlantStatus(); + + if (m_PlantStatus < PlantStatus.Seed) + { + // Clients above 7.0.12.0 use the regular PropertyList + if (list != _oldClientPropertyList) { - list.Add(typeInfo.GetPlantLabelDecorative(hueInfo), $"#{hueInfo.Name}\t#{typeInfo.Name}"); + // a ~1_val~ of ~2_val~ dirt + list.Add(1060830, $"{container:#}\t{dirt:#}"); } - else if (m_PlantStatus >= PlantStatus.FullGrownPlant) + else + { + // a ~1_val~ of ~2_val~ dirt + list.Add(1060830, $"{dirt:#}"); + } + + return; + } + + var typeInfo = PlantTypeInfo.GetInfo(m_PlantType); + var hueInfo = PlantHueInfo.GetInfo(m_PlantHue); + + if (m_PlantStatus >= PlantStatus.DecorativePlant) + { + list.Add(typeInfo.GetPlantLabelDecorative(hueInfo), $"{hueInfo.Name:#}\t{typeInfo.Name:#}"); + return; + } + + if (m_PlantStatus >= PlantStatus.FullGrownPlant) + { + list.Add( + typeInfo.GetPlantLabelFullGrown(hueInfo), + $"{health:#}\t{hueInfo.Name:#}\t{typeInfo.Name:#}" + ); + return; + } + + if (m_ShowType) + { + var plantNumber = m_PlantStatus == PlantStatus.Plant + ? typeInfo.GetPlantLabelPlant(hueInfo) + : typeInfo.GetPlantLabelSeed(hueInfo); + + if (list != _oldClientPropertyList) { list.Add( - typeInfo.GetPlantLabelFullGrown(hueInfo), - $"#{PlantSystem.GetLocalizedHealth()}\t#{hueInfo.Name}\t#{typeInfo.Name}" + plantNumber, + $"{container:#}\t{dirt:#}\t{health:#}\t{hueInfo.Name:#}\t{typeInfo.Name:#}\t{plantStatus:#}" ); } else { - string args; - - if (ShowContainerType) - { - args = - $"#{GetLocalizedContainerType()}\t#{PlantSystem.GetLocalizedDirtStatus()}\t#{PlantSystem.GetLocalizedHealth()}"; - } - else - { - args = $"#{PlantSystem.GetLocalizedDirtStatus()}\t#{PlantSystem.GetLocalizedHealth()}"; - } - - if (m_ShowType) - { - args += $"\t#{hueInfo.Name}\t#{typeInfo.Name}\t#{GetLocalizedPlantStatus()}"; - - if (m_PlantStatus == PlantStatus.Plant) - { - list.Add(typeInfo.GetPlantLabelPlant(hueInfo), args); - } - else - { - list.Add(typeInfo.GetPlantLabelSeed(hueInfo), args); - } - } - else - { - args += - $"\t#{(typeInfo.PlantCategory == PlantCategory.Default ? hueInfo.Name : (int)typeInfo.PlantCategory)}\t#{GetLocalizedPlantStatus()}"; - - list.Add( - hueInfo.IsBright() ? 1060832 : 1060831, - args - ); // a ~1_val~ of ~2_val~ dirt with a ~3_val~ [bright] ~4_val~ ~5_val~ - } + list.Add( + plantNumber, + $"{dirt:#}\t{health:#}\t{hueInfo.Name:#}\t{typeInfo.Name:#}\t{plantStatus:#}" + ); + } + } + else + { + var category = typeInfo.PlantCategory == PlantCategory.Default ? hueInfo.Name : (int)typeInfo.PlantCategory; + var plantNumber = hueInfo.IsBright() ? 1060832 : 1060831; + if (list != _oldClientPropertyList) + { + list.Add(plantNumber, $"{container:#}\t{dirt:#}\t{health:#}\t{category:#}\t{plantStatus:#}"); + } + else + { + list.Add(plantNumber,$"{dirt:#}\t{health:#}\t{category:#}\t{plantStatus:#}"); } } } diff --git a/Projects/UOContent/Engines/Plants/Seed.cs b/Projects/UOContent/Engines/Plants/Seed.cs index bb67f9e4b..bee1228a8 100644 --- a/Projects/UOContent/Engines/Plants/Seed.cs +++ b/Projects/UOContent/Engines/Plants/Seed.cs @@ -124,9 +124,41 @@ namespace Server.Engines.Plants return hueInfo.IsBright() ? 1113491 : 1113490; // ~1_amount~ [bright] ~2_val~ seeds } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { - list.Add(GetLabel(out var args), args); + var typeInfo = PlantTypeInfo.GetInfo(m_PlantType); + var hueInfo = PlantHueInfo.GetInfo(m_PlantHue); + + int title; + + if (m_ShowType || typeInfo.PlantCategory == PlantCategory.Default) + { + title = hueInfo.Name; + } + else + { + title = (int)typeInfo.PlantCategory; + } + + if (Amount == 1) + { + if (m_ShowType) + { + list.Add(typeInfo.GetSeedLabel(hueInfo), $"{title:#}\t{typeInfo.Name:#}"); + return; + } + + list.Add(hueInfo.IsBright() ? 1060839 : 1060838, $"{title:#}"); + return; + } + + if (m_ShowType) + { + list.Add(typeInfo.GetSeedLabelPlural(hueInfo), $"{Amount}\t{title:#}\t{typeInfo.Name:#}"); + return; + } + + list.Add(hueInfo.IsBright() ? 1113491 : 1113490, $"{Amount}\t{title:#}"); } public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Quests/Collector/Items/Obsidian.cs b/Projects/UOContent/Engines/Quests/Collector/Items/Obsidian.cs index 7806d403d..d70818d0e 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Items/Obsidian.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Items/Obsidian.cs @@ -141,7 +141,7 @@ namespace Server.Engines.Quests.Collector public static string RandomName(Mobile from) => m_Names.RandomElement() ?? from.Name; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (m_Quantity < m_Partial) { diff --git a/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs b/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs index afd44311f..e1180f01c 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs @@ -31,10 +31,10 @@ namespace Server.Engines.Quests.Collector } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var info = ImageTypeInfo.Get(m_Image); - list.Add(1060847, $"#1055126\t#{info.Name}"); // a painted image of: + list.Add(1060847, $"{1055126:#}\t{info.Name:#}"); // a painted image of: } public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs index 97774aaa1..bb4249616 100644 --- a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs +++ b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs @@ -43,11 +43,11 @@ namespace Server.Engines.Quests public virtual bool ValidateUse(Mobile from) => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); // charges: ~1_val~ + list.Add(1060741, m_Charges); // charges: ~1_val~ } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs index a4f020cb7..a01569975 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs @@ -9,7 +9,7 @@ namespace Server.Engines.Quests.Necro private PlayerMobile m_Necromancer; private bool m_ToDelete; - public SummonedPaladin(PlayerMobile necromancer) : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public SummonedPaladin(PlayerMobile necromancer) : base(AIType.AI_Melee, FightMode.Aggressor) { m_Necromancer = necromancer; diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs index 1d10745b6..d4e24e217 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs @@ -6,7 +6,7 @@ namespace Server.Engines.Quests.Ninja public class Henchman : BaseCreature { [Constructible] - public Henchman() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Henchman() : base(AIType.AI_Melee, FightMode.Aggressor) { InitStats(45, 30, 5); diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs index 5defbf092..151f85901 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs @@ -6,7 +6,7 @@ namespace Server.Engines.Quests.Samurai public class CursedSoul : BaseCreature { [Constructible] - public CursedSoul() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public CursedSoul() : base(AIType.AI_Melee, FightMode.Aggressor) { Body = 3; BaseSoundID = 471; diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs index bc5cd2092..7d9085567 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs @@ -5,7 +5,7 @@ namespace Server.Engines.Quests.Samurai public class DeadlyImp : BaseCreature { [Constructible] - public DeadlyImp() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public DeadlyImp() : base(AIType.AI_Melee, FightMode.Aggressor) { Body = 74; BaseSoundID = 422; diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs index 3b135a242..c73bbc558 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs @@ -5,7 +5,7 @@ namespace Server.Engines.Quests.Samurai public class DiseasedCat : BaseCreature { [Constructible] - public DiseasedCat() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public DiseasedCat() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xC9; Hue = Utility.RandomAnimalHue(); diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs index 8e2016a15..914aec85e 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs @@ -5,7 +5,7 @@ namespace Server.Engines.Quests.Samurai public class FierceDragon : BaseCreature { [Constructible] - public FierceDragon() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public FierceDragon() : base(AIType.AI_Melee, FightMode.Aggressor) { Body = 103; BaseSoundID = 362; diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs index 5bcf2fedf..fd7ab7078 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs @@ -5,7 +5,7 @@ namespace Server.Engines.Quests.Samurai public class InjuredWolf : BaseCreature { [Constructible] - public InjuredWolf() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public InjuredWolf() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xE1; BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs index 130567bd9..f3b63e1b2 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs @@ -6,7 +6,7 @@ namespace Server.Engines.Quests.Samurai public class YoungNinja : BaseCreature { [Constructible] - public YoungNinja() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public YoungNinja() : base(AIType.AI_Melee, FightMode.Aggressor) { InitStats(45, 30, 5); SetHits(20, 30); diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs index 98ddf1f45..c2f1aed70 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs @@ -6,7 +6,7 @@ namespace Server.Engines.Quests.Samurai public class YoungRonin : BaseCreature { [Constructible] - public YoungRonin() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public YoungRonin() : base(AIType.AI_Melee, FightMode.Aggressor) { InitStats(45, 30, 5); SetHits(10, 20); diff --git a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs index bc93f6289..2a14b34db 100644 --- a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs +++ b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs @@ -1,5 +1,3 @@ -using System.Linq; - namespace Server.Engines.Quests.Naturalist { public class NestArea @@ -38,7 +36,22 @@ namespace Server.Engines.Quests.Naturalist m_Rects = rects; } - public static int NonSpecialCount => m_Areas.Count(area => !area.Special); + public static int NonSpecialCount + { + get + { + int count = 0; + foreach (var area in m_Areas) + { + if (!area.Special) + { + count++; + } + } + + return count; + } + } public bool Special { get; } @@ -60,7 +73,15 @@ namespace Server.Engines.Quests.Naturalist public static NestArea Find(Point3D p) { - return m_Areas.FirstOrDefault(area => area.Contains(p)); + foreach (var area in m_Areas) + { + if (area.Contains(p)) + { + return area; + } + } + + return null; } public static NestArea GetByID(int id) diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs index 9dfd386d6..1731abbee 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs @@ -70,7 +70,7 @@ namespace Server.Engines.Quests.Haven return new FacialHairInfo(Race.Human.RandomFacialHair(false), m_HairHue); } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (ItemID == 0x2006) // Corpse form { diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs index ee0a3795c..cf30f30b9 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs @@ -9,7 +9,7 @@ namespace Server.Engines.Quests.Haven public class MilitiaFighter : BaseCreature { [Constructible] - public MilitiaFighter() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public MilitiaFighter() : base(AIType.AI_Melee) { InitStats(40, 30, 5); Title = "the Militia Fighter"; @@ -108,7 +108,7 @@ namespace Server.Engines.Quests.Haven { } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (ItemID == 0x2006) // Corpse form { diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs index dfaa2e6ad..acc83cd2d 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs @@ -39,7 +39,7 @@ namespace Server.Engines.Quests.Hag private static List GetEquipment() => new(); - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add("a charred corpse"); } diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs index 59858d6fe..1da04cee4 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs @@ -391,13 +391,10 @@ namespace Server.Engines.Quests.Hag if (creature.GetType() == type) { - System.From.SendLocalizedMessage( - 1055043, - $"#{info.Name}" - ); // You gather a ~1_INGREDIENT_NAME~ from the corpse. + // You gather a ~1_INGREDIENT_NAME~ from the corpse. + System.From.SendLocalizedMessage(1055043, $"#{info.Name}"); CurProgress++; - break; } } diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs index 239b34072..e4faa6332 100644 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs @@ -12,971 +12,1055 @@ using CPA = Server.CommandPropertyAttribute; using static Server.Attributes; -namespace Server.Engines.Spawners +namespace Server.Engines.Spawners; + +public abstract class BaseSpawner : Item, ISpawner { - public abstract class BaseSpawner : Item, ISpawner + private static WarnTimer m_WarnTimer; + + private Guid _guid; + private int m_Count; + private bool m_Group; + private int m_HomeRange; + private TimeSpan m_MaxDelay; + private TimeSpan m_MinDelay; + private bool m_Running; + private int m_Team; + + private InternalTimer m_Timer; + private int m_WalkingRange = -1; + + public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4) { - private static WarnTimer m_WarnTimer; + } - private Guid _guid; - private int m_Count; - private bool m_Group; - private int m_HomeRange; - private TimeSpan m_MaxDelay; - private TimeSpan m_MinDelay; - private bool m_Running; - private int m_Team; + public BaseSpawner(string spawnedName) : this( + 1, + TimeSpan.FromMinutes(5), + TimeSpan.FromMinutes(10), + 0, + 4, + spawnedName + ) + { + } - private InternalTimer m_Timer; - private int m_WalkingRange = -1; + public BaseSpawner( + int amount, int minDelay, int maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : this( + amount, + TimeSpan.FromMinutes(minDelay), + TimeSpan.FromMinutes(maxDelay), + team, + homeRange, + spawnedNames + ) + { + } - public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4) + public BaseSpawner( + int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : base(0x1f13) + { + _guid = Guid.NewGuid(); + InitSpawn(amount, minDelay, maxDelay, team, homeRange); + for (var i = 0; i < spawnedNames.Length; i++) { + AddEntry(spawnedNames[i], 100, amount, false); } + } - public BaseSpawner(string spawnedName) : this( - 1, - TimeSpan.FromMinutes(5), - TimeSpan.FromMinutes(10), - 0, - 4, - spawnedName - ) - { - } - - public BaseSpawner( - int amount, int minDelay, int maxDelay, int team, int homeRange, - params string[] spawnedNames - ) : this( - amount, - TimeSpan.FromMinutes(minDelay), - TimeSpan.FromMinutes(maxDelay), - team, - homeRange, - spawnedNames - ) - { - } - - public BaseSpawner( - int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, - params string[] spawnedNames - ) : base(0x1f13) + public BaseSpawner(DynamicJson json, JsonSerializerOptions options) : base(0x1f13) + { + if (!json.GetProperty("guid", options, out _guid)) { _guid = Guid.NewGuid(); - InitSpawn(amount, minDelay, maxDelay, team, homeRange); - for (var i = 0; i < spawnedNames.Length; i++) - { - AddEntry(spawnedNames[i], 100, amount, false); - } } - public BaseSpawner(DynamicJson json, JsonSerializerOptions options) : base(0x1f13) + if (json.GetProperty("name", options, out string name)) { - if (!json.GetProperty("guid", options, out _guid)) - { - _guid = Guid.NewGuid(); - } - - if (json.GetProperty("name", options, out string name)) - { - Name = name; - } - - json.GetProperty("count", options, out int amount); - json.GetProperty("minDelay", options, out TimeSpan minDelay); - json.GetProperty("maxDelay", options, out TimeSpan maxDelay); - json.GetProperty("team", options, out int team); - json.GetProperty("homeRange", options, out int homeRange); - json.GetProperty("walkingRange", options, out int walkingRange); - m_WalkingRange = walkingRange; - - InitSpawn(amount, minDelay, maxDelay, team, homeRange); - - json.GetProperty("entries", options, out List entries); - - foreach (var entry in entries) - { - AddEntry(entry.SpawnedName, entry.SpawnedProbability, entry.SpawnedMaxCount, false); - } + Name = name; } - public BaseSpawner(Serial serial) : base(serial) + json.GetProperty("count", options, out int amount); + json.GetProperty("minDelay", options, out TimeSpan minDelay); + json.GetProperty("maxDelay", options, out TimeSpan maxDelay); + json.GetProperty("team", options, out int team); + json.GetProperty("homeRange", options, out int homeRange); + json.GetProperty("walkingRange", options, out int walkingRange); + m_WalkingRange = walkingRange; + + InitSpawn(amount, minDelay, maxDelay, team, homeRange); + + json.GetProperty("entries", options, out List entries); + + foreach (var entry in entries) { + AddEntry(entry.SpawnedName, entry.SpawnedProbability, entry.SpawnedMaxCount, false); } + } - public override string DefaultName => "Spawner"; - public bool IsFull => Spawned?.Count >= m_Count; - public bool IsEmpty => Spawned?.Count == 0; - public DateTime End { get; set; } + public BaseSpawner(Serial serial) : base(serial) + { + } - public List Entries { get; private set; } + public override string DefaultName => "Spawner"; + public bool IsFull => Spawned?.Count >= m_Count; + public bool IsEmpty => Spawned?.Count == 0; + public DateTime End { get; set; } - public Dictionary Spawned { get; private set; } + public List Entries { get; private set; } - [CommandProperty(AccessLevel.Developer)] - public Guid Guid + public Dictionary Spawned { get; private set; } + + [CommandProperty(AccessLevel.Developer)] + public Guid Guid + { + get => _guid; + set => _guid = value; + } + + [CommandProperty(AccessLevel.Developer)] + public int Count + { + get => m_Count; + set { - get => _guid; - set => _guid = value; - } + m_Count = value; - [CommandProperty(AccessLevel.Developer)] - public int Count - { - get => m_Count; - set - { - m_Count = value; - - if (IsFull) - { - m_Timer?.Stop(); - } - else if (m_Timer?.Running != true) - { - DoTimer(); - } - - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public virtual WayPoint WayPoint { get; set; } - - [CommandProperty(AccessLevel.Developer)] - public bool Running - { - get => m_Running; - set - { - if (value) - { - Start(); - } - else - { - Stop(); - } - - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public int WalkingRange - { - get => m_WalkingRange; - set - { - m_WalkingRange = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public int Team - { - get => m_Team; - set - { - m_Team = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public TimeSpan MinDelay - { - get => m_MinDelay; - set - { - m_MinDelay = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public TimeSpan MaxDelay - { - get => m_MaxDelay; - set - { - m_MaxDelay = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public TimeSpan NextSpawn - { - get => m_Running && m_Timer?.Running == true ? End - Core.Now : TimeSpan.Zero; - set - { - Start(); - DoTimer(value); - } - } - - [CommandProperty(AccessLevel.Developer)] - public bool Group - { - get => m_Group; - set - { - m_Group = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.Developer)] - public bool ReturnOnDeactivate { get; set; } - - public virtual Point3D HomeLocation => Location; - public bool UnlinkOnTaming => true; - - [CommandProperty(AccessLevel.Developer)] - public int HomeRange - { - get => m_HomeRange; - set - { - m_HomeRange = value; - InvalidateProperties(); - } - } - - Region ISpawner.Region => Region.Find(Location, Map); - - public void Remove(ISpawnable spawn) - { - Defrag(); - - if (spawn != null) - { - Spawned.TryGetValue(spawn, out var entry); - - entry?.Spawned.Remove(spawn); - - Spawned.Remove(spawn); - } - - if (m_Running && !IsFull && m_Timer?.Running == false) - { - DoTimer(); - } - } - - public virtual void Respawn() - { - RemoveSpawns(); - - for (var i = 0; i < m_Count; i++) - { - Spawn(); - } - - DoTimer(); // Turn off the timer! - } - - public virtual void ToJson(DynamicJson json, JsonSerializerOptions options) - { - json.Type = GetType().Name; - json.SetProperty("name", options, Name); - json.SetProperty("guid", options, _guid); - json.SetProperty("location", options, Location); - json.SetProperty("map", options, Map); - json.SetProperty("count", options, Count); - json.SetProperty("minDelay", options, MinDelay); - json.SetProperty("maxDelay", options, MaxDelay); - json.SetProperty("team", options, Team); - json.SetProperty("homeRange", options, HomeRange); - json.SetProperty("walkingRange", options, WalkingRange); - json.SetProperty("entries", options, Entries); - } - - public abstract Point3D GetSpawnPosition(ISpawnable spawned, Map map); - - public override void OnAfterDuped(Item newItem) - { - if (newItem is BaseSpawner newSpawner) - { - for (var i = 0; i < Entries.Count; i++) - { - newSpawner.AddEntry( - Entries[i].SpawnedName, - Entries[i].SpawnedProbability, - Entries[i].SpawnedMaxCount, - false - ); - } - } - } - - public SpawnerEntry AddEntry(string creaturename, int probability = 100, int amount = 1, bool dotimer = true) - { - var entry = new SpawnerEntry(creaturename, probability, amount); - Entries.Add(entry); - if (dotimer) - { - DoTimer(TimeSpan.FromSeconds(1)); - } - - return entry; - } - - public void InitSpawn(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange) - { - 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; - Entries = new List(); - Spawned = new Dictionary(); - - DoTimer(TimeSpan.FromSeconds(1)); - } - - public override bool CanSeeStaffOnly(Mobile from) => from.AccessLevel >= AccessLevel.Developer; - - public override bool IsAccessibleTo(Mobile from) => from.AccessLevel >= AccessLevel.Developer; - - public override void OnDoubleClick(Mobile from) - { - from.SendGump(new SpawnerGump(this)); - } - - public virtual void GetSpawnerProperties(ObjectPropertyList list) - { - } - - 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(1050039, "walking range:\t{0}", m_WalkingRange); // ~1_NUMBER~ ~2_ITEMNAME~ - list.Add(1053099, "group:\t{0}", m_Group); // ~1_oretype~: ~2_armortype~ - list.Add(1060847, "team:\t{0}", m_Team); // ~1_val~ ~2_val~ - list.Add(1063483, "delay:\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_MATERIAL~: ~2_ITEMNAME~ - - GetSpawnerProperties(list); - - for (var i = 0; i < 6 && i < Entries.Count; ++i) - { - list.Add(1060658 + i, "\t{0}\t{1}", Entries[i].SpawnedName, CountSpawns(Entries[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 (Entries.Count > 0) - { - m_Running = true; - DoTimer(); - } - } - } - - public void Stop() - { - m_Timer?.Stop(); - m_Running = false; - } - - public void Defrag() - { - Entries ??= new List(); - - for (var i = 0; i < Entries.Count; ++i) - { - Entries[i].Defrag(this); - } - } - - public virtual bool OnDefragSpawn(ISpawnable spawned, bool remove) - { - if (!remove) // Override could have set it to true already - { - remove = spawned.Deleted || spawned.Spawner == null || spawned switch - { - Item item => item.RootParent is Mobile || item.IsLockedDown || item.IsSecure, - Mobile m => m is BaseCreature c && (c.Controlled || c.IsStabled), - _ => true - }; - } - - if (remove) - { - Spawned.Remove(spawned); - } - - return remove; - } - - public void OnTick() - { - if (m_Group) - { - Defrag(); - - if (Spawned.Count > 0) - { - return; - } - - Respawn(); - } - else - { - Spawn(); - } - - DoTimer(); - } - - public virtual void Spawn() - { - Defrag(); - - if (Entries.Count <= 0 || IsFull) - { - return; - } - - var probsum = 0; - - foreach (var spawnerEntry in Entries) - { - if (!spawnerEntry.IsFull) - { - probsum += spawnerEntry.SpawnedProbability; - } - } - - if (probsum <= 0) - { - return; - } - - var rand = Utility.RandomMinMax(1, probsum); - - for (var i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; - if (entry.IsFull) - { - continue; - } - - if (rand <= entry.SpawnedProbability) - { - Spawn(entry, out var flags); - entry.Valid = flags; - return; - } - - rand -= entry.SpawnedProbability; - } - } - - private static string[,] FormatProperties(string[] args) - { - string[,] props; - - var remains = args.Length; - - if (remains >= 2) - { - props = new string[remains / 2, 2]; - - remains /= 2; - - for (var j = 0; j < remains; ++j) - { - props[j, 0] = args[j * 2]; - props[j, 1] = args[j * 2 + 1]; - } - } - else - { - props = new string[0, 0]; - } - - return props; - } - - private static PropertyInfo[] GetTypeProperties(Type type, string[,] props) - { - PropertyInfo[] realProps = null; - - if (props != null) - { - realProps = new PropertyInfo[props.GetLength(0)]; - - var allProps = - type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); - - for (var i = 0; i < realProps.Length; ++i) - { - PropertyInfo thisProp = null; - - var propName = props[i, 0]; - - for (var j = 0; thisProp == null && j < allProps.Length; ++j) - { - if (propName.InsensitiveEquals(allProps[j].Name)) - { - thisProp = allProps[j]; - } - } - - if (thisProp == null) - { - return null; - } - - var attr = GetCPA(thisProp); - - if (attr == null || attr.WriteLevel > AccessLevel.Developer || !thisProp.CanWrite || attr.ReadOnly) - { - return null; - } - - realProps[i] = thisProp; - } - } - - return realProps; - } - - public bool Spawn(int index, out EntryFlags flags) - { - if (index >= 0 && index < Entries.Count) - { - return Spawn(Entries[index], out flags); - } - - flags = EntryFlags.InvalidEntry; - return false; - } - - public bool Spawn(SpawnerEntry entry, out EntryFlags flags) - { - var map = GetSpawnMap(); - flags = EntryFlags.None; - - if (map == null || map == Map.Internal || Parent != null) - { - return false; - } - - // Defrag taken care of in Spawn(), beforehand - // Count check taken care of in Spawn(), beforehand - - var type = AssemblyHandler.FindTypeByName(entry.SpawnedName); - - if (type == null) - { - flags = EntryFlags.InvalidType; - return false; - } - - try - { - IEntity entity = null; - - var propargs = string.IsNullOrEmpty(entry.Properties) - ? Array.Empty() - : CommandSystem.Split(entry.Properties.Trim()); - - var props = FormatProperties(propargs); - - var realProps = GetTypeProperties(type, props); - - if (realProps == null) - { - flags = EntryFlags.InvalidProps; - return false; - } - - var paramargs = string.IsNullOrEmpty(entry.Parameters) - ? Array.Empty() - : entry.Parameters.Trim().Split(' '); - - if (paramargs.Length == 0) - { - entity = type.CreateInstance( - ci => IsConstructible(ci, AccessLevel.Developer) - ); - } - else - { - var ctors = type.GetConstructors(); - - for (var i = 0; i < ctors.Length; ++i) - { - var ctor = ctors[i]; - - if (IsConstructible(ctor, AccessLevel.Developer)) - { - var paramList = ctor.GetParameters(); - - if (paramargs.Length == paramList.Length) - { - var paramValues = Add.ParseValues(paramList, paramargs); - - if (paramValues != null) - { - entity = ctor.Invoke(paramValues) as IEntity; - break; - } - } - } - } - } - - if (entity == null) - { - flags = EntryFlags.InvalidType | EntryFlags.InvalidParams; - return false; - } - - for (var i = 0; i < realProps.Length; i++) - { - if (realProps[i] != null) - { - var result = Types.TryParse( - realProps[i].PropertyType, - props[i, 1], - out var toSet - ); - - if (result == null) - { - realProps[i].SetValue(entity, toSet, null); - } - else - { - flags = EntryFlags.InvalidProps; - - (entity as ISpawnable)?.Delete(); - - return false; - } - } - } - - if (entity is Mobile m) - { - Spawned.Add(m, entry); - entry.Spawned.Add(m); - - var loc = m is BaseVendor ? Location : GetSpawnPosition(m, map); - - m.OnBeforeSpawn(loc, map); - m.MoveToWorld(loc, map); - - if (m is BaseCreature c) - { - var walkrange = GetWalkingRange(); - - c.RangeHome = walkrange >= 0 ? walkrange : m_HomeRange; - c.CurrentWayPoint = WayPoint; - - if (m_Team > 0) - { - c.Team = m_Team; - } - - c.Home = Location; - c.HomeMap = Map; - } - - m.Spawner = this; - m.OnAfterSpawn(); - } - else if (entity is Item item) - { - Spawned.Add(item, entry); - entry.Spawned.Add(item); - - var loc = GetSpawnPosition(item, map); - - item.OnBeforeSpawn(loc, map); - - item.MoveToWorld(loc, map); - - item.Spawner = this; - item.OnAfterSpawn(); - } - else - { - // Other IEntity types that might get created are simply not supported - flags = EntryFlags.InvalidType | EntryFlags.InvalidParams; - return false; - } - } - catch (Exception e) - { - Console.WriteLine($"EXCEPTION CAUGHT: {Serial}"); - Console.WriteLine(e); - return false; - } - - ClearProperties(); - return true; - } - - public virtual int GetWalkingRange() => m_WalkingRange; - - public virtual Map GetSpawnMap() => Map; - - public void DoTimer() - { - if (!m_Running) - { - return; - } - - var minSeconds = (int)m_MinDelay.TotalSeconds; - var maxSeconds = (int)m_MaxDelay.TotalSeconds; - - var delay = TimeSpan.FromSeconds(Utility.RandomMinMax(minSeconds, maxSeconds)); - DoTimer(delay); - } - - public virtual void DoTimer(TimeSpan delay) - { - if (!m_Running) - { - return; - } - - End = Core.Now + delay; - - if (m_Timer == null) - { - m_Timer = new InternalTimer(this, delay); - } - else + if (IsFull) { m_Timer?.Stop(); - m_Timer.Delay = delay; } - - if (!IsFull) - { - m_Timer.Start(); - } - } - - public int CountSpawns(SpawnerEntry entry) - { - Defrag(); - - return entry.Spawned.Count; - } - - public void RemoveEntry(SpawnerEntry entry) - { - Defrag(); - - for (var i = entry.Spawned.Count - 1; i >= 0; i--) - { - var e = entry.Spawned[i]; - entry.Spawned.RemoveAt(i); - e?.Delete(); - } - - Entries.Remove(entry); - - if (m_Running && !IsFull && m_Timer?.Running == false) + else if (m_Timer?.Running != true) { DoTimer(); } InvalidateProperties(); } + } - public void RemoveSpawn(int index) // Entry + [CommandProperty(AccessLevel.Developer)] + public virtual WayPoint WayPoint { get; set; } + + [CommandProperty(AccessLevel.Developer)] + public bool Running + { + get => m_Running; + set { - if (index >= 0 && index < Entries.Count) + if (value) { - RemoveSpawn(Entries[index]); + Start(); + } + else + { + Stop(); + } + + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.Developer)] + public int WalkingRange + { + get => m_WalkingRange; + set + { + m_WalkingRange = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.Developer)] + public int Team + { + get => m_Team; + set + { + m_Team = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.Developer)] + public TimeSpan MinDelay + { + get => m_MinDelay; + set + { + m_MinDelay = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.Developer)] + public TimeSpan MaxDelay + { + get => m_MaxDelay; + set + { + m_MaxDelay = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.Developer)] + public TimeSpan NextSpawn + { + get => m_Running && m_Timer?.Running == true ? End - Core.Now : TimeSpan.Zero; + set + { + Start(); + DoTimer(value); + } + } + + [CommandProperty(AccessLevel.Developer)] + public bool Group + { + get => m_Group; + set + { + m_Group = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.Developer)] + public bool ReturnOnDeactivate { get; set; } + + public virtual Point3D HomeLocation => Location; + public bool UnlinkOnTaming => true; + + [CommandProperty(AccessLevel.Developer)] + public int HomeRange + { + get => m_HomeRange; + set + { + m_HomeRange = value; + InvalidateProperties(); + } + } + + Region ISpawner.Region => Region.Find(Location, Map); + + public void UpdateEntries(List entries) + { + Entries = entries; + } + + public void Remove(ISpawnable spawn) + { + Defrag(); + + if (spawn != null) + { + Spawned.TryGetValue(spawn, out var entry); + + entry?.Spawned.Remove(spawn); + + Spawned.Remove(spawn); + } + + if (m_Running && !IsFull && m_Timer?.Running == false) + { + DoTimer(); + } + } + + public virtual void Respawn() + { + RemoveSpawns(); + + for (var i = 0; i < m_Count; i++) + { + Spawn(); + } + + DoTimer(); // Turn off the timer! + } + + public virtual void Reset() + { + Stop(); + RemoveSpawns(); + } + + public virtual void ToJson(DynamicJson json, JsonSerializerOptions options) + { + json.Type = GetType().Name; + json.SetProperty("name", options, Name); + json.SetProperty("guid", options, _guid); + json.SetProperty("location", options, Location); + json.SetProperty("map", options, Map); + json.SetProperty("count", options, Count); + json.SetProperty("minDelay", options, MinDelay); + json.SetProperty("maxDelay", options, MaxDelay); + json.SetProperty("team", options, Team); + json.SetProperty("homeRange", options, HomeRange); + json.SetProperty("walkingRange", options, WalkingRange); + json.SetProperty("entries", options, Entries); + } + + public abstract Point3D GetSpawnPosition(ISpawnable spawned, Map map); + + public override void OnAfterDuped(Item newItem) + { + if (newItem is BaseSpawner newSpawner) + { + for (var i = 0; i < Entries.Count; i++) + { + newSpawner.AddEntry( + Entries[i].SpawnedName, + Entries[i].SpawnedProbability, + Entries[i].SpawnedMaxCount, + false + ); + } + } + } + + public SpawnerEntry AddEntry(string creaturename, int probability = 100, int amount = 1, bool dotimer = true) + { + var entry = new SpawnerEntry(creaturename, probability, amount); + Entries.Add(entry); + if (dotimer) + { + DoTimer(TimeSpan.FromSeconds(1)); + } + + return entry; + } + + public void InitSpawn(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange) + { + 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; + Entries = new List(); + Spawned = new Dictionary(); + + DoTimer(TimeSpan.FromSeconds(1)); + } + + public override bool CanSeeStaffOnly(Mobile from) => from.AccessLevel >= AccessLevel.Developer; + + public override bool IsAccessibleTo(Mobile from) => from.AccessLevel >= AccessLevel.Developer; + + public override void OnDoubleClick(Mobile from) + { + from.SendGump(new SpawnerGump(this)); + } + + public virtual void GetSpawnerProperties(IPropertyList list) + { + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (m_Running) + { + list.Add(1060742); // active + + list.Add(1060656, m_Count); // amount to make: ~1_val~ + list.Add(1061169, m_HomeRange); // range ~1_val~ + list.Add(1050039, $"{"walking range:"}\t{m_WalkingRange}"); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1053099, $"{"group:"}\t{m_Group}"); // ~1_oretype~: ~2_armortype~ + list.Add(1060847, $"{"team:"}\t{m_Team}"); // ~1_val~ ~2_val~ + list.Add(1063483, $"{"delay:"}\t{m_MinDelay} to {m_MaxDelay}"); // ~1_MATERIAL~: ~2_ITEMNAME~ + + GetSpawnerProperties(list); + + for (var i = 0; i < 6 && i < Entries.Count; ++i) + { + var entry = Entries[i]; + list.Add(1060658 + i, $"\t{entry.SpawnedName}\t{CountSpawns(entry)}"); + } + } + 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 (Entries.Count > 0) + { + m_Running = true; + DoTimer(); + } + } + } + + public void Stop() + { + m_Timer?.Stop(); + m_Running = false; + } + + public void Defrag() + { + Entries ??= new List(); + + for (var i = 0; i < Entries.Count; ++i) + { + Entries[i].Defrag(this); + } + } + + public virtual bool OnDefragSpawn(ISpawnable spawned, bool remove) + { + if (!remove) // Override could have set it to true already + { + remove = spawned.Deleted || spawned.Spawner == null || spawned switch + { + Item item => item.RootParent is Mobile || item.IsLockedDown || item.IsSecure, + Mobile m => m is BaseCreature c && (c.Controlled || c.IsStabled), + _ => true + }; + } + + if (remove) + { + Spawned.Remove(spawned); + } + + return remove; + } + + public void OnTick() + { + if (m_Group) + { + Defrag(); + + if (Spawned.Count > 0) + { + return; + } + + Respawn(); + } + else + { + Spawn(); + } + + DoTimer(); + } + + public virtual void Spawn() + { + Defrag(); + + if (Entries.Count <= 0 || IsFull) + { + return; + } + + var probsum = 0; + + foreach (var spawnerEntry in Entries) + { + if (!spawnerEntry.IsFull) + { + probsum += spawnerEntry.SpawnedProbability; } } - public void RemoveSpawn(SpawnerEntry entry) + if (probsum <= 0) { - for (var i = entry.Spawned.Count - 1; i >= 0; i--) + return; + } + + var rand = Utility.RandomMinMax(1, probsum); + + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + if (entry.IsFull) { - var e = entry.Spawned[i]; + continue; + } + + if (rand <= entry.SpawnedProbability) + { + Spawn(entry, out var flags); + entry.Valid = flags; + return; + } + + rand -= entry.SpawnedProbability; + } + } + + private static string[,] FormatProperties(string[] args) + { + string[,] props; + + var remains = args.Length; + + if (remains >= 2) + { + props = new string[remains / 2, 2]; + + remains /= 2; + + for (var j = 0; j < remains; ++j) + { + props[j, 0] = args[j * 2]; + props[j, 1] = args[j * 2 + 1]; + } + } + else + { + props = new string[0, 0]; + } + + return props; + } + + private static PropertyInfo[] GetTypeProperties(Type type, string[,] props) + { + PropertyInfo[] realProps = null; + + if (props != null) + { + realProps = new PropertyInfo[props.GetLength(0)]; + + var allProps = + type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); + + for (var i = 0; i < realProps.Length; ++i) + { + PropertyInfo thisProp = null; + + var propName = props[i, 0]; + + for (var j = 0; thisProp == null && j < allProps.Length; ++j) + { + if (propName.InsensitiveEquals(allProps[j].Name)) + { + thisProp = allProps[j]; + } + } + + if (thisProp == null) + { + return null; + } + + var attr = GetCPA(thisProp); + + if (attr == null || attr.WriteLevel > AccessLevel.Developer || !thisProp.CanWrite || attr.ReadOnly) + { + return null; + } + + realProps[i] = thisProp; + } + } + + return realProps; + } + + public bool Spawn(int index, out EntryFlags flags) + { + if (index >= 0 && index < Entries.Count) + { + return Spawn(Entries[index], out flags); + } + + flags = EntryFlags.InvalidEntry; + return false; + } + + public bool Spawn(SpawnerEntry entry, out EntryFlags flags) + { + var map = GetSpawnMap(); + flags = EntryFlags.None; + + if (map == null || map == Map.Internal || Parent != null) + { + return false; + } + + // Defrag taken care of in Spawn(), beforehand + // Count check taken care of in Spawn(), beforehand + + var type = AssemblyHandler.FindTypeByName(entry.SpawnedName); + + if (type == null) + { + flags = EntryFlags.InvalidType; + return false; + } + + try + { + IEntity entity = null; + + var propargs = string.IsNullOrEmpty(entry.Properties) + ? Array.Empty() + : CommandSystem.Split(entry.Properties.Trim()); + + var props = FormatProperties(propargs); + + var realProps = GetTypeProperties(type, props); + + if (realProps == null) + { + flags = EntryFlags.InvalidProps; + return false; + } + + var paramargs = string.IsNullOrEmpty(entry.Parameters) + ? Array.Empty() + : entry.Parameters.Trim().Split(' '); + + if (paramargs.Length == 0) + { + entity = type.CreateInstance( + ci => IsConstructible(ci, AccessLevel.Developer) + ); + } + else + { + var ctors = type.GetConstructors(); + + for (var i = 0; i < ctors.Length; ++i) + { + var ctor = ctors[i]; + + if (IsConstructible(ctor, AccessLevel.Developer)) + { + var paramList = ctor.GetParameters(); + + if (paramargs.Length == paramList.Length) + { + var paramValues = Add.ParseValues(paramList, paramargs); + + if (paramValues != null) + { + entity = ctor.Invoke(paramValues) as IEntity; + break; + } + } + } + } + } + + if (entity == null) + { + flags = EntryFlags.InvalidType | EntryFlags.InvalidParams; + return false; + } + + for (var i = 0; i < realProps.Length; i++) + { + if (realProps[i] != null) + { + var result = Types.TryParse( + realProps[i].PropertyType, + props[i, 1], + out var toSet + ); + + if (result == null) + { + realProps[i].SetValue(entity, toSet, null); + } + else + { + flags = EntryFlags.InvalidProps; + + (entity as ISpawnable)?.Delete(); + + return false; + } + } + } + + if (entity is Mobile m) + { + Spawned.Add(m, entry); + entry.Spawned.Add(m); + + var loc = m is BaseVendor ? Location : GetSpawnPosition(m, map); + + m.OnBeforeSpawn(loc, map); + m.MoveToWorld(loc, map); + + if (m is BaseCreature c) + { + var walkrange = GetWalkingRange(); + + c.RangeHome = walkrange >= 0 ? walkrange : m_HomeRange; + c.CurrentWayPoint = WayPoint; + + if (m_Team > 0) + { + c.Team = m_Team; + } + + c.Home = Location; + c.HomeMap = Map; + } + + m.Spawner = this; + m.OnAfterSpawn(); + } + else if (entity is Item item) + { + Spawned.Add(item, entry); + entry.Spawned.Add(item); + + var loc = GetSpawnPosition(item, map); + + item.OnBeforeSpawn(loc, map); + + item.MoveToWorld(loc, map); + + item.Spawner = this; + item.OnAfterSpawn(); + } + else + { + // Other IEntity types that might get created are simply not supported + flags = EntryFlags.InvalidType | EntryFlags.InvalidParams; + return false; + } + } + catch (Exception e) + { + Console.WriteLine($"EXCEPTION CAUGHT: {Serial}"); + Console.WriteLine(e); + return false; + } + + ClearProperties(); + return true; + } + + public virtual int GetWalkingRange() => m_WalkingRange; + + public virtual Map GetSpawnMap() => Map; + + public void DoTimer() + { + if (!m_Running) + { + return; + } + + var minSeconds = (int)m_MinDelay.TotalSeconds; + var maxSeconds = (int)m_MaxDelay.TotalSeconds; + + var delay = TimeSpan.FromSeconds(Utility.RandomMinMax(minSeconds, maxSeconds)); + DoTimer(delay); + } + + public virtual void DoTimer(TimeSpan delay) + { + if (!m_Running) + { + return; + } + + End = Core.Now + delay; + + if (m_Timer == null) + { + m_Timer = new InternalTimer(this, delay); + } + else + { + m_Timer.Stop(); + m_Timer.Delay = delay; + } + + if (!IsFull) + { + m_Timer.Start(); + } + } + + public int CountSpawns(SpawnerEntry entry) + { + Defrag(); + + return entry.Spawned.Count; + } + + public void RemoveEntry(SpawnerEntry entry) + { + Defrag(); + + for (var i = entry.Spawned.Count - 1; i >= 0; i--) + { + var e = entry.Spawned[i]; + entry.Spawned.RemoveAt(i); + e?.Delete(); + } + + Entries.Remove(entry); + + if (m_Running && !IsFull && m_Timer?.Running == false) + { + DoTimer(); + } + + InvalidateProperties(); + } + + public void RemoveSpawn(int index) // Entry + { + if (index >= 0 && index < Entries.Count) + { + RemoveSpawn(Entries[index]); + } + } + + public void RemoveSpawn(SpawnerEntry entry) + { + for (var i = entry.Spawned.Count - 1; i >= 0; i--) + { + var e = entry.Spawned[i]; + + if (e != null) + { + entry.Spawned.RemoveAt(i); + Spawned.Remove(e); + e.Delete(); + } + } + } + + public void RemoveSpawns() + { + Defrag(); + + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + + for (var j = entry.Spawned.Count - 1; j >= 0; j--) + { + var e = entry.Spawned[j]; if (e != null) { - entry.Spawned.RemoveAt(i); Spawned.Remove(e); + entry.Spawned.RemoveAt(j); e.Delete(); } } } - public void RemoveSpawns() + if (m_Running && !IsFull && m_Timer?.Running == false) { - Defrag(); + DoTimer(); + } - for (var i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; + InvalidateProperties(); + } - for (var j = entry.Spawned.Count - 1; j >= 0; j--) + public void BringToHome() + { + Defrag(); + + foreach (var e in Spawned.Keys) + { + e?.MoveToWorld(Location, Map); + } + } + + public override void OnDelete() + { + base.OnDelete(); + + Stop(); + RemoveSpawns(); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(9); // version + + writer.Write(_guid); + + writer.Write(ReturnOnDeactivate); + + writer.Write(Entries.Count); + + for (var i = 0; i < Entries.Count; ++i) + { + Entries[i].Serialize(writer); + } + + writer.Write(m_WalkingRange); + + writer.Write(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(End); + } + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Spawned = new Dictionary(); + + if (version < 7) + { + Entries = new List(); + } + + switch (version) + { + case 9: { - var e = entry.Spawned[j]; - - if (e != null) - { - Spawned.Remove(e); - entry.Spawned.RemoveAt(j); - e.Delete(); - } + _guid = reader.ReadGuid(); + goto case 8; } - } + case 8: + { + ReturnOnDeactivate = reader.ReadBool(); + goto case 7; + } + case 7: + { + var size = reader.ReadInt(); - if (m_Running && !IsFull && m_Timer?.Running == false) - { - DoTimer(); - } + Entries = new List(size); - InvalidateProperties(); - } - - public void BringToHome() - { - Defrag(); - - foreach (var e in Spawned.Keys) - { - e?.MoveToWorld(Location, Map); - } - } - - public override void OnDelete() - { - base.OnDelete(); - - Stop(); - RemoveSpawns(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(9); // version - - writer.Write(_guid); - - writer.Write(ReturnOnDeactivate); - - writer.Write(Entries.Count); - - for (var i = 0; i < Entries.Count; ++i) - { - Entries[i].Serialize(writer); - } - - writer.Write(m_WalkingRange); - - writer.Write(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(End); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - Spawned = new Dictionary(); - - if (version < 7) - { - Entries = new List(); - } - - switch (version) - { - case 9: + for (var i = 0; i < size; ++i) { - _guid = reader.ReadGuid(); - goto case 8; + Entries.Add(new SpawnerEntry(this, reader)); } - case 8: - { - ReturnOnDeactivate = reader.ReadBool(); - goto case 7; - } - case 7: - { - var size = reader.ReadInt(); - Entries = new List(size); + goto case 4; // Skip the other crap + } + case 6: + { + var size = reader.ReadInt(); - for (var i = 0; i < size; ++i) + var addentries = Entries.Count == 0; + + for (var i = 0; i < size; ++i) + { + if (addentries) { - Entries.Add(new SpawnerEntry(this, reader)); + Entries.Add(new SpawnerEntry(string.Empty, 100, reader.ReadInt())); + } + else + { + Entries[i].SpawnedMaxCount = reader.ReadInt(); } - - goto case 4; // Skip the other crap } - case 6: + + goto case 5; + } + case 5: + { + var size = reader.ReadInt(); + + var addentries = Entries.Count == 0; + + for (var i = 0; i < size; ++i) + { + if (addentries) + { + Entries.Add(new SpawnerEntry(string.Empty, reader.ReadInt(), 1)); + } + else + { + Entries[i].SpawnedProbability = reader.ReadInt(); + } + } + + goto case 4; + } + case 4: + { + m_WalkingRange = reader.ReadInt(); + + goto case 3; + } + case 3: + case 2: + { + WayPoint = reader.ReadEntity(); + + 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(); + + var ts = m_Running ? reader.ReadDeltaTime() - Core.Now : TimeSpan.Zero; + + if (version < 7) { var size = reader.ReadInt(); @@ -984,224 +1068,151 @@ namespace Server.Engines.Spawners for (var i = 0; i < size; ++i) { + var typeName = reader.ReadString(); + if (addentries) { - Entries.Add(new SpawnerEntry(string.Empty, 100, reader.ReadInt())); + Entries.Add(new SpawnerEntry(typeName, 100, 1)); } else { - Entries[i].SpawnedMaxCount = reader.ReadInt(); + Entries[i].SpawnedName = typeName; + } + + if (AssemblyHandler.FindTypeByName(typeName) == null) + { + m_WarnTimer ??= new WarnTimer(); + + m_WarnTimer.Add(Location, Map, typeName); } } - goto case 5; - } - case 5: - { - var size = reader.ReadInt(); + var count = reader.ReadInt(); - var addentries = Entries.Count == 0; - - for (var i = 0; i < size; ++i) + for (var i = 0; i < count; ++i) { - if (addentries) + var e = reader.ReadEntity(); + + if (e == null) { - Entries.Add(new SpawnerEntry(string.Empty, reader.ReadInt(), 1)); - } - else - { - Entries[i].SpawnedProbability = reader.ReadInt(); - } - } - - goto case 4; - } - case 4: - { - m_WalkingRange = reader.ReadInt(); - - goto case 3; - } - case 3: - case 2: - { - WayPoint = reader.ReadEntity(); - - 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(); - - var ts = m_Running ? reader.ReadDeltaTime() - Core.Now : TimeSpan.Zero; - - if (version < 7) - { - var size = reader.ReadInt(); - - var addentries = Entries.Count == 0; - - for (var i = 0; i < size; ++i) - { - var typeName = reader.ReadString(); - - if (addentries) - { - Entries.Add(new SpawnerEntry(typeName, 100, 1)); - } - else - { - Entries[i].SpawnedName = typeName; - } - - if (AssemblyHandler.FindTypeByName(typeName) == null) - { - m_WarnTimer ??= new WarnTimer(); - - m_WarnTimer.Add(Location, Map, typeName); - } + continue; } - var count = reader.ReadInt(); - - for (var i = 0; i < count; ++i) + if (e is BaseCreature creature) { - var e = reader.ReadEntity(); + creature.RemoveIfUntamed = true; + } - if (e == null) + e.Spawner = this; + + for (var j = 0; j < Entries.Count; j++) + { + if (AssemblyHandler.FindTypeByName(Entries[j].SpawnedName) == e.GetType()) { - continue; - } - - if (e is BaseCreature creature) - { - creature.RemoveIfUntamed = true; - } - - e.Spawner = this; - - for (var j = 0; j < Entries.Count; j++) - { - if (AssemblyHandler.FindTypeByName(Entries[j].SpawnedName) == e.GetType()) - { - Entries[j].Spawned.Add(e); - Spawned.Add(e, Entries[j]); - break; - } + Entries[j].Spawned.Add(e); + Spawned.Add(e, Entries[j]); + break; } } } - - DoTimer(ts); - - break; } - } - if (version < 4) - { - m_WalkingRange = m_HomeRange; - } + DoTimer(ts); + + break; + } } - private class InternalTimer : Timer + if (version < 4) { - private readonly BaseSpawner m_Spawner; - - public InternalTimer(BaseSpawner spawner, TimeSpan delay) : base(delay) => m_Spawner = spawner; - - protected override void OnTick() - { - if (m_Spawner?.Deleted == false) - { - m_Spawner.OnTick(); - } - } + m_WalkingRange = m_HomeRange; } + } - private class WarnTimer : Timer + private class InternalTimer : Timer + { + private readonly BaseSpawner m_Spawner; + + public InternalTimer(BaseSpawner spawner, TimeSpan delay) : base(delay) => m_Spawner = spawner; + + protected override void OnTick() { - private readonly List m_List; - - public WarnTimer() : base(TimeSpan.FromSeconds(1.0)) + if (m_Spawner?.Deleted == false) { - m_List = new List(); - 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 var op = new StreamWriter("badspawn.log", true); - op.WriteLine("# Bad spawns : {0}", DateTime.Now); - op.WriteLine("# Format: X Y Z F Name"); - op.WriteLine(); - - foreach (var 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 - { - // ignored - } - } - - private class WarnEntry - { - public readonly Map m_Map; - public readonly string m_Name; - public Point3D m_Point; - - public WarnEntry(Point3D p, Map map, string name) - { - m_Point = p; - m_Map = map; - m_Name = name; - } + m_Spawner.OnTick(); } } } - [Flags] - public enum EntryFlags + private class WarnTimer : Timer { - None = 0x000, - InvalidType = 0x001, - InvalidParams = 0x002, - InvalidProps = 0x004, - InvalidEntry = 0x008 + private readonly List m_List; + + public WarnTimer() : base(TimeSpan.FromSeconds(1.0)) + { + m_List = new List(); + 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 var op = new StreamWriter("badspawn.log", true); + op.WriteLine("# Bad spawns : {0}", DateTime.Now); + op.WriteLine("# Format: X Y Z F Name"); + op.WriteLine(); + + foreach (var 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 + { + // ignored + } + } + + private class WarnEntry + { + public readonly Map m_Map; + public readonly string m_Name; + public Point3D m_Point; + + public WarnEntry(Point3D p, Map map, string name) + { + m_Point = p; + m_Map = map; + m_Name = name; + } + } } } + +[Flags] +public enum EntryFlags +{ + None = 0x000, + InvalidType = 0x001, + InvalidParams = 0x002, + InvalidProps = 0x004, + InvalidEntry = 0x008 +} diff --git a/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs index 4e24ef087..e3092ebe3 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs @@ -13,81 +13,88 @@ * along with this program. If not, see . * *************************************************************************/ +using System; using System.Collections.Generic; using System.IO; using Server.Commands.Generic; using Server.Json; using Server.Network; -namespace Server.Engines.Spawners +namespace Server.Engines.Spawners; + +public class ExportSpawnersCommand : BaseCommand { - public class ExportSpawnersCommand : BaseCommand + public static void Initialize() { - public static void Initialize() + TargetCommands.Register(new ExportSpawnersCommand()); + } + + public ExportSpawnersCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllItems & ~CommandSupport.Contained; + Commands = new[] { "ExportSpawners" }; + ObjectTypes = ObjectTypes.Items; + Usage = "ExportSpawners"; + Description = "Exports the given spawners to a file"; + ListOptimized = true; + } + + public override void ExecuteList(CommandEventArgs e, List list) + { + string path = e.Arguments.Length == 0 ? string.Empty : e.Arguments[0].Trim(); + string condition = e.Arguments.Length == 2 ? e.Arguments[1].Trim() : string.Empty; + + if (string.IsNullOrEmpty(path)) { - TargetCommands.Register(new ExportSpawnersCommand()); + path = Path.Combine(Core.BaseDirectory, $"Data/Spawns/{Utility.GetTimeStamp()}.json"); + } + else + { + var directory = Path.GetDirectoryName(Path.GetFullPath(path!))!; + if (!Path.IsPathRooted(path)) + { + path = Path.Combine(Core.BaseDirectory, path); + PathUtility.EnsureDirectory(directory); + } + else if (!Directory.Exists(directory)) + { + LogFailure("Directory doesn't exist."); + return; + } } - public ExportSpawnersCommand() + NetState.FlushAll(); + + var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory()); + + var spawnRecords = new List(list.Count); + for (var i = 0; i < list.Count; i++) { - AccessLevel = AccessLevel.GameMaster; - Supports = CommandSupport.AllItems & ~CommandSupport.Contained; - Commands = new[] { "ExportSpawners" }; - ObjectTypes = ObjectTypes.Items; - Usage = "ExportSpawners"; - Description = "Exports the given the spawners to the a file"; - ListOptimized = true; - } - - public override void ExecuteList(CommandEventArgs e, List list) - { - var path = e.Arguments.Length == 0 ? null : e.Arguments[0].Trim(); - - if (string.IsNullOrEmpty(path)) + if (list[i] is BaseSpawner spawner) { - path = Path.Combine(Core.BaseDirectory, $"Data/Spawns/{Utility.GetTimeStamp()}.json"); - } - else - { - var directory = Path.GetDirectoryName(Path.GetFullPath(path!))!; - if (!Path.IsPathRooted(path)) - { - path = Path.Combine(Core.BaseDirectory, path); - PathUtility.EnsureDirectory(directory); - } - else if (!Directory.Exists(directory)) - { - LogFailure("Directory doesn't exist."); - return; - } - } - - NetState.FlushAll(); - - var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory()); - - var spawnRecords = new List(list.Count); - for (var i = 0; i < list.Count; i++) - { - if (list[i] is BaseSpawner spawner) + if (!string.IsNullOrEmpty(spawner.Name) && spawner.Name.StartsWith( + condition, + StringComparison.OrdinalIgnoreCase + )) { var dynamicJson = DynamicJson.Create(spawner.GetType()); spawner.ToJson(dynamicJson, options); spawnRecords.Add(dynamicJson); } } - - if (spawnRecords.Count == 0) - { - LogFailure("No matching spawners found."); - return; - } - - e.Mobile.SendMessage("Exporting spawners..."); - - JsonConfig.Serialize(path, spawnRecords, options); - - e.Mobile.SendMessage($"Spawners exported to {path}"); } + + if (spawnRecords.Count == 0) + { + LogFailure("No matching spawners found."); + return; + } + + e.Mobile.SendMessage("Exporting spawners..."); + + JsonConfig.Serialize(path, spawnRecords, options); + + e.Mobile.SendMessage($"Spawners exported to {path}"); } } diff --git a/Projects/UOContent/Engines/Spawners/Commands/GenerateSpawnersCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/GenerateSpawnersCommand.cs index 2292ce482..4c17ea231 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/GenerateSpawnersCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/GenerateSpawnersCommand.cs @@ -107,13 +107,16 @@ namespace Server.Engines.Spawners watch.Stop(); - logger.Information("Generated {0} spawners ({1:F2} seconds, {2} failures)"); - from.SendMessage( - "GenerateSpawners: Generated {0} spawners ({1:F2} seconds, {2} failures)", + logger.Information( + "Generated {Count} spawners ({Duration:F2} seconds, {Failures} failures)", totalGenerated, watch.Elapsed.TotalSeconds, totalFailures ); + + from.SendMessage( + $"GenerateSpawners: Generated {totalGenerated} spawners ({watch.Elapsed.TotalSeconds:F2} seconds, {totalFailures} failures)" + ); } private static void ParseSpawnerList( diff --git a/Projects/UOContent/Engines/Spawners/RegionSpawner.cs b/Projects/UOContent/Engines/Spawners/RegionSpawner.cs index a7296cee9..d3446e22a 100644 --- a/Projects/UOContent/Engines/Spawners/RegionSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/RegionSpawner.cs @@ -88,13 +88,13 @@ namespace Server.Engines.Spawners json.SetProperty("region", options, SpawnRegion.Name); } - public override void GetSpawnerProperties(ObjectPropertyList list) + public override void GetSpawnerProperties(IPropertyList list) { base.GetSpawnerProperties(list); if (Running && m_SpawnRegion != null) { - list.Add(1076228, "region:\t{0}", m_SpawnRegion.Name); // ~1_DUMMY~ ~2_DUMMY~ + list.Add(1076228, $"{"region:"}\t{m_SpawnRegion.Name}"); // ~1_DUMMY~ ~2_DUMMY~ } } diff --git a/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs b/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs index 2b1583603..684b3d477 100644 --- a/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs +++ b/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs @@ -96,7 +96,7 @@ namespace Server.Gumps } case 3: // Apply { - using var propsBuilder = new ValueStringBuilder(64); + using var propsBuilder = ValueStringBuilder.Create(); bool first = true; foreach (var attr in MobileAttributes) { diff --git a/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs b/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs new file mode 100644 index 000000000..2437c125d --- /dev/null +++ b/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs @@ -0,0 +1,591 @@ +using System; +using Server.Collections; +using Server.Network; +using Server.Engines.Spawners; + +namespace Server.Gumps; + +public enum SpawnSearchType +{ + Creature = 0, + Coords, + Props, + Name +} +public class SpawnSearch +{ + public SpawnSearchType Type { get; set; } = SpawnSearchType.Creature; + public string SearchPattern { get; set; } = ""; +} + +public class SpawnerControllerGump : GumpGrid +{ + private BaseSpawner _copy; + private int _page; + private const int EntryCount = 5; + private Mobile _mobile; + private SpawnSearch _search; + private int _lineCount; + private BaseSpawner[] _spawners = Array.Empty(); + private Grid _main; + private const int TypeCount = 15; + + public static int GetButtonID(int type, int index) => 1 + type + index * TypeCount; + + public static void Initialize() + { + CommandSystem.Register("Spawn", AccessLevel.GameMaster, OpenSpawnGump_OnCommand); + } + + public static void OpenSpawnGump_OnCommand(CommandEventArgs e) + { + e.Mobile.SendGump(new SpawnerControllerGump(e.Mobile)); + } + + public void AddBlackAlpha(int x, int y, int width, int height) + { + AddImageTiled(x, y, width, height, 2624); + AddAlphaRegion(x, y, width, height); + } + + public void DrawBorder(ListView list) + { + var row1Y = _main.Rows[1].Y; + AddImageTiled(0, row1Y, _main.Width, 3, 9357); + AddAlphaRegion(0, row1Y, _main.Width, 3); + + AddImageTiled(0, row1Y + 21, _main.Width, 3, 9357); + AddAlphaRegion(0, row1Y + 21, _main.Width, 3); + + for (int i = 0; i < list.ColsCount - 1; i++) + { + var x = list.Header.Cols[i].HEnd - 8; + var y = list.Header.Y + 10; + var height = 20; + AddImageTiled(x, y, 3, height, 9355); + AddAlphaRegion(x, y, 3, height); + } + + AddImageTiled(0, _main.Rows[2].Y - 11, _main.Width, 6, 9357); + AddAlphaRegion(0, _main.Rows[2].Y - 11, _main.Width, 6); + AddImageTiled(0, _main.Rows[2].Y + 55, _main.Width, 6, 9357); + AddAlphaRegion(0, _main.Rows[2].Y + 55, _main.Width, 6); + } + + private static string ExtractCoords(Point3D cord) => $"X {cord.X} Y {cord.Y}"; + + public void DrawSearch() + { + const int deltaX = 140; + var search = SubGrid("search", _main.Name, 0, 0, 2, 1); + + var col0X = search.Columns[0].X; + var col1X = search.Columns[1].X; + var col1HCenter = search.Columns[1].HCenter; + var row0Y = search.Rows[0].Y; + var row0VCenter = search.Rows[0].VCenter; + + AddLabelHtml(col0X, row0Y + 8, col1X, 30, "Search type", GridColors.Gold); + AddLabelHtml(col1X, row0Y + 8, col1X, 30, "Search Match", GridColors.Gold); + + AddImageTiled(0, row0Y + 30, _main.Width, 3, 9357); + AddAlphaRegion(0, row0Y + 30, _main.Width, 3); + + AddImageTiled(col1X, row0Y, 3, search.Height, 9355); + AddAlphaRegion(col1X, row0Y, 3, search.Height); + + var creature = _search.Type == SpawnSearchType.Creature ? 9021 : 9020; + var cords = _search.Type == SpawnSearchType.Coords ? 9021 : 9020; + var props = _search.Type == SpawnSearchType.Props ? 9021 : 9020; + var args = _search.Type == SpawnSearchType.Name ? 9021 : 9020; + + AddButton(col0X + 10, row0Y + 45, creature, creature, GetButtonID(1, 0)); + AddLabelHtml(col0X + 15, row0Y + 48, 100, 30, "Creature", GridColors.White); + + + AddButton(col0X + 10 + deltaX - 40, row0Y + 45, cords, cords, GetButtonID(1, 1)); + AddLabelHtml(col0X + 40 + deltaX - 40, row0Y + 48, 150, 30, "Range", GridColors.White, 4, false); + + AddButton(col0X + 10 + deltaX * 2 - 100, row0Y + 45, props, props, GetButtonID(1, 2)); + AddLabelHtml(col0X + 40 + deltaX * 2 - 100, row0Y + 48, 150, 30, "Creature Props", GridColors.White, 4, false); + + AddButton(col0X + 10 + deltaX * 3 - 100, row0Y + 45, args, args, GetButtonID(1, 3)); + AddLabelHtml(col0X + 40 + deltaX * 3 - 100, row0Y + 48, 150, 30, "Spawner Name", GridColors.White, 4, false); + + AddImageTiled(col1HCenter - 100, row0VCenter + 16, 200, 1, 9357); + AddTextEntry(col1HCenter - 100, row0VCenter, 200, 20, (int)GridHues.White, 0xFFFF, _search.SearchPattern, GetButtonID(1, 4)); + AddButton(col1HCenter + 120, row0VCenter, 4023, 4024, GetButtonID(1, 4)); + + var type = (int)_search.Type; + if (type == 0) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by creature name", GridColors.White); + } + else if (type == 1) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by range (number)", GridColors.White); + } + else if (type == 2) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by entry property field", GridColors.White); + } + else if (type == 3) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by spawner name", GridColors.White); + } + } + + private void SearchSpawner() + { + if (_search.SearchPattern.Length < 1) + { + return; + } + + using var queue = PooledRefQueue.Create(); + + if (!(_search.Type == SpawnSearchType.Coords && int.TryParse(_search.SearchPattern, out var range))) + { + range = -1; + } + + foreach (var item in World.Items.Values) + { + if (item is not BaseSpawner spawner) + { + continue; + } + + bool enqueue = _search.Type switch + { + SpawnSearchType.Creature => SearchSpawnerCreatures(spawner, _search.SearchPattern), + SpawnSearchType.Coords => _mobile.InRange(spawner.Location, range), + SpawnSearchType.Props => SearchSpawnerProperties(spawner, _search.SearchPattern), + SpawnSearchType.Name => spawner.Name?.InsensitiveContains(_search.SearchPattern) == true, + _ => false + }; + + if (enqueue) + { + queue.Enqueue(spawner); + } + } + + _spawners = queue.ToArray(); + } + + private static bool SearchSpawnerCreatures(BaseSpawner spawner, string searchPattern) + { + foreach (var entry in spawner.Entries) + { + if (entry.SpawnedName?.InsensitiveContains(searchPattern) == true) + { + return true; + } + } + + return false; + } + + private static bool SearchSpawnerProperties(BaseSpawner spawner, string searchPattern) + { + foreach (var entry in spawner.Entries) + { + if (entry.Properties?.InsensitiveContains(searchPattern) == true) + { + return true; + } + } + + return false; + } + + private void DrawFooter(ListView list) + { + if (list.CanNext) + { + AddButton(_main.Columns[0].HCenter + 60, _main.Rows[2].VCenter + 16, 4005, 4006, GetButtonID(10, 0)); + } + + if (list.CanBack) + { + AddButton(_main.Columns[0].HCenter + -60, _main.Rows[2].VCenter + 16, 4014, 4015, GetButtonID(11, 0)); + } + + var col0X = _main.Columns[0].X; + var col0Width = _main.Columns[0].Width; + var row2Y = _main.Rows[2].Y; + + //past to all spawners + AddButton(col0X + 15, row2Y, 4029, 4031, GetButtonID(1, 5)); + AddLabelHtml(col0X + 55, row2Y + 4, col0Width, 20, $"Paste props for {_spawners.Length} spawners", GridColors.Orange, 4, false); + + //paste target + AddButton(col0X + 15, row2Y + 28, 4029, 4031, GetButtonID(1, 6)); + AddLabelHtml(col0X + 55, row2Y + 32, col0Width, 20, $"Paste entry for {_spawners.Length} spawners", GridColors.Orange, 4, false); + + //paste ground + AddButton(col0X + 15 + 260, row2Y, 4029, 4031, GetButtonID(1, 7)); + AddLabelHtml(col0X + 55 + 260, row2Y + 4, col0Width, 20, "Paste copy to the ground", GridColors.Yellow, 4, false); + + AddLabelHtml(col0X + 16, _main.Rows[2].VCenter + 17, col0Width, 20, $"Pages {list.Page + 1}/{list.TotalPages + 1}", GridColors.White); + AddLabelHtml(col0X + 20, _main.Rows[2].VCenter + 17, col0Width, 20, $"Total Spawners {_spawners.Length}", GridColors.White, 4, false); + } + + + public void DrawSpawner(ListView list) + { + for (int i = 0; i < list.Items.Length; i++) + { + const int vCenter = 15; + const int perItem = 45; + + var spawner = _spawners[list.Items[i].Index]; + var item = list.Items[i]; + + AddButton(item.Cols[0].X + 15, list.Items[i].Y + 5, 4011, 4012, GetButtonID(2, item.Index)); + AddLabelHtml(item.Cols[0].X, list.Items[i].Y + 26, item.Cols[0].Width, 30, "copy", GridColors.White, 3); + + AddButton(item.Cols[1].X + 2, list.Items[i].Y + 5, 4029, 4031, GetButtonID(3, item.Index)); + AddLabelHtml(item.Cols[1].X + 3, list.Items[i].Y + 26, item.Cols[1].Width, 30, "props", GridColors.White, 3, false); + + AddButton(item.Cols[1].HCenter - 2, list.Items[i].Y + 5, 4029, 4031, GetButtonID(4, item.Index)); + AddLabelHtml(item.Cols[1].HCenter - 3, list.Items[i].Y + 26, 55, 30, "entry", GridColors.White, 3, false); + + //props + AddLabelHtml(item.Cols[2].X, list.Items[i].Y + vCenter + 10, list.Header.Cols[2].Width, 30, spawner.Serial.ToString(), GridColors.White); + + //map + AddLabelHtml(item.Cols[3].X, list.Items[i].Y + vCenter, list.Header.Cols[3].Width, 30, spawner.Map.ToString(), GridColors.White); + + //teleport button + AddButton(item.Cols[4].X - 5, list.Items[i].Y + vCenter, 2062, 2062, GetButtonID(5, item.Index)); + AddImage(item.Cols[4].X - 5, list.Items[i].Y + vCenter, 2062, 936); + AddLabelHtml(item.Cols[4].X - 7, list.Items[i].Y + vCenter, list.Header.Cols[4].Width, 30, ExtractCoords(spawner.Location), GridColors.White); + + //open entry button + AddButton(item.Cols[5].X + 6, list.Items[i].Y + vCenter - 4, 0x2635, 0x2635, GetButtonID(7, item.Index)); + AddImage(item.Cols[5].X + 6, list.Items[i].Y + vCenter - 7, 0x2635, 827); + AddLabelHtml(item.Cols[5].X, list.Items[i].Y + vCenter, list.Header.Cols[5].Width, 30, spawner.Entries?.Count.ToString(), GridColors.White); + + + //entry + var entry = i * EntryCount; + + AddImageTiled(item.Cols[2].X - 2, list.Items[i].Y + 18, item.Cols[2].Width, 1, 9357); + AddTextEntry(item.Cols[2].X, list.Items[i].Y + 2, list.Header.Cols[2].Width, 80, (int)GridHues.White, entry, spawner.Name, 20); + AddButton(item.Cols[2].X, list.Items[i].Y + vCenter + 9, 5837, 5838, GetButtonID(6, item.Index)); + + AddImageTiled(item.Cols[6].X + 8, list.Items[i].Y + 29, item.Cols[6].Width / 2, 1, 9357); + AddTextEntry(item.Cols[6].X + 12, list.Items[i].Y + 13, list.Header.Cols[6].Width, 80, (int)GridHues.White, entry + 1, spawner.WalkingRange.ToString(), 2); + + AddImageTiled(item.Cols[7].X + 8, list.Items[i].Y + 29, item.Cols[7].Width / 2, 1, 9357); + AddTextEntry(item.Cols[7].X + 12, list.Items[i].Y + 13, list.Header.Cols[7].Width, 80, (int)GridHues.White, entry + 2, spawner.HomeRange.ToString(), 2); + + AddImageTiled(item.Cols[8].X + 6, list.Items[i].Y + 29, item.Cols[8].Width - 20, 1, 9357); + AddTextEntry(item.Cols[8].X + 8, list.Items[i].Y + 13, list.Header.Cols[8].Width, 80, (int)GridHues.White, entry + 3, spawner.MinDelay.ToString(), 8); + + AddImageTiled(item.Cols[9].X + 6, list.Items[i].Y + 29, item.Cols[9].Width - 20, 1, 9357); + AddTextEntry(item.Cols[9].X + 8, list.Items[i].Y + 13, list.Header.Cols[9].Width, 80, (int)GridHues.White, entry + 4, spawner.MaxDelay.ToString(), 8); + //end entry + + AddLabelHtml(item.Cols[10].X, list.Items[i].Y + vCenter, list.Header.Cols[10].Width, 30, spawner.NextSpawn.ToString(@"hh\:mm\:ss"), GridColors.White); + + AddButton(item.Cols[11].X, list.Items[i].Y + 5, 4023, 4025, GetButtonID(8, item.Index)); + AddLabelHtml(item.Cols[11].X + 4, list.Items[i].Y + 26, 55, 30, "save", GridColors.White, 3, false); + + AddButton(item.Cols[11].X + perItem, list.Items[i].Y + 5, 4017, 4018, GetButtonID(9, item.Index)); + AddLabelHtml(item.Cols[11].X + perItem, list.Items[i].Y + 26, 55, 30, "delete", GridColors.White, 3, false); + + AddImageTiled(0, list.Items[i].Y + list.ColHeight, _main.Width, 1, 9357); + AddAlphaRegion(0, list.Items[i].Y + list.ColHeight, _main.Width, 1); + } + } + + public void DrawColumnName(ListView list) + { + AddLabelHtml(list.Header.Cols[0].X, list.Header.Y + 10, list.Header.Cols[0].Width, 30, "Copy", GridColors.Gold); + AddLabelHtml(list.Header.Cols[1].X, list.Header.Y + 10, list.Header.Cols[1].Width, 30, "Paste", GridColors.Gold); + AddLabelHtml(list.Header.Cols[2].X, list.Header.Y + 10, list.Header.Cols[2].Width, 30, "Name/Serial", GridColors.Gold); + AddLabelHtml(list.Header.Cols[3].X, list.Header.Y + 10, list.Header.Cols[3].Width, 30, "Map", GridColors.Gold); + AddLabelHtml(list.Header.Cols[4].X - 4, list.Header.Y + 10, list.Header.Cols[4].Width, 30, "Coords", GridColors.Gold); + AddLabelHtml(list.Header.Cols[5].X - 4, list.Header.Y + 10, list.Header.Cols[5].Width, 30, "Entry", GridColors.Gold); + AddLabelHtml(list.Header.Cols[6].X - 4, list.Header.Y + 10, list.Header.Cols[6].Width, 30, "Walk", GridColors.Gold); + AddLabelHtml(list.Header.Cols[7].X - 4, list.Header.Y + 10, list.Header.Cols[7].Width, 30, "Home", GridColors.Gold); + AddLabelHtml(list.Header.Cols[8].X - 4, list.Header.Y + 10, list.Header.Cols[8].Width, 30, "Min Delay", GridColors.Gold); + AddLabelHtml(list.Header.Cols[9].X - 4, list.Header.Y + 10, list.Header.Cols[9].Width, 30, "Max Delay", GridColors.Gold); + AddLabelHtml(list.Header.Cols[10].X - 4, list.Header.Y + 10, list.Header.Cols[10].Width, 30, "Next Spawn", GridColors.Gold); + AddLabelHtml(list.Header.Cols[11].X, list.Header.Y + 10, list.Header.Cols[11].Width, 30, "Actions", GridColors.Gold); + } + + public SpawnerControllerGump(Mobile mobile, int page = 0, BaseSpawner copy = null, SpawnSearch search = null) : base(20, 30) + { + _main = Grid("main", 1000, 800, 1, 3, rowSize: "10* * 100"); + + AddBackground(0, 0, 1000, 800, 5054); + AddBlackAlpha(0, 0, 1000, 800); + + _mobile = mobile; + _page = page; + _copy = copy; + _search = search ?? new SpawnSearch(); + + SearchSpawner(); + + var _list = AddListView( + _main.Name, + 0, + 1, + _spawners.Length, + page, + 45, + 12, + colSize: "6* 8* 18* 6* 12* 6* 5* 5* 8* 8* 9* *", + headerHeight: 30, + marginY: -7 + ); + + _lineCount = _list.LineCount; + DrawBorder(_list); + DrawSearch(); + DrawSpawner(_list); + DrawColumnName(_list); + DrawFooter(_list); + + } + public void CopyProperty(BaseSpawner spawner, BaseSpawner target) + { + target.Name = spawner.Name; + target.MinDelay = spawner.MinDelay; + target.MaxDelay = spawner.MaxDelay; + target.WalkingRange = spawner.WalkingRange; + target.HomeRange = spawner.HomeRange; + target.Group = spawner.Group; + target.Count = spawner.Count; + } + public void CopyEntry(BaseSpawner spawner, BaseSpawner target) + { + if (spawner.Entries?.Count > 0) + { + target.Entries?.Clear(); + + for (int i = 0; i < spawner.Entries.Count; i++) + { + var item = spawner.Entries[i]; + var targetEntry = target.AddEntry(item.SpawnedName, item.SpawnedProbability, item.SpawnedMaxCount); + targetEntry.Properties = item.Properties; + targetEntry.Parameters = item.Parameters; + } + + } + } + public void FullCopy(BaseSpawner spawner, BaseSpawner target) + { + CopyProperty(spawner, target); + CopyEntry(spawner, target); + } + public void Refresh(bool update = true) + { + if (update) + { + _mobile.SendGump(new SpawnerControllerGump(_mobile, _page, _copy, _search)); + } + else + { + _mobile.SendGump(this); + } + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + var buttonID = info.ButtonID - 1; + var type = buttonID % TypeCount; + var index = buttonID / TypeCount; + + switch (type) + { + case -1: + { + return; + } + case 1: + { + _page = 0; + if (index < 4) + { + _search.Type = (SpawnSearchType)index; + var isNumeric = int.TryParse(_search.SearchPattern, out _); + if (index == 1 && !isNumeric || index != 1 && isNumeric) + { + _search.SearchPattern = ""; + } + } + else if (index == 4) + { + var entry = info.GetTextEntry(0xFFFF); + + if (entry?.Text?.Length > 0) + { + _search.SearchPattern = entry.Text; + } + } + else if (index == 5 && _copy != null && _spawners?.Length > 0) + { + foreach (var spawner in _spawners) + { + CopyProperty(_copy, spawner); + } + } + else if (index == 6 && _copy != null && _spawners?.Length > 0) + { + foreach (var spawner in _spawners) + { + CopyEntry(_copy, spawner); + } + } + else if (index == 7 && _copy != null) + { + var newSpawner = new Spawner(); + + FullCopy(_copy, newSpawner); + newSpawner.Map = _mobile.Map; + newSpawner.Location = _mobile.Location; + newSpawner.Stop(); + newSpawner.Start(); + newSpawner.Respawn(); + } + + break; + } + //copy + case 2: + { + if (index < _spawners.Length) + { + _copy = _spawners[index]; + } + + break; + } + //paste props + case 3: + { + if (index < _spawners.Length && _copy != null) + { + CopyProperty(_copy, _spawners[index]); + } + + break; + } + //paste entry + case 4: + { + if (index < _spawners.Length && _copy != null) + { + CopyEntry(_copy, _spawners[index]); + } + + break; + } + //save + case 8: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + + var indexEntry = index >= _lineCount ? (index - _lineCount * _page) * EntryCount : index * EntryCount; + + var name = info.GetTextEntry(indexEntry); + if (name?.Text.Length > 0) + { + spawner.Name = name.Text; + } + + if (int.TryParse(info.GetTextEntry(indexEntry + 1)?.Text, out var walkRange)) + { + spawner.WalkingRange = walkRange; + } + + if (int.TryParse(info.GetTextEntry(indexEntry + 2)?.Text, out var homeHange)) + { + spawner.HomeRange = homeHange; + } + + if (TimeSpan.TryParse(info.GetTextEntry(indexEntry + 3)?.Text, out var minDelay)) + { + spawner.MinDelay = minDelay; + } + + if (TimeSpan.TryParse(info.GetTextEntry(indexEntry + 4)?.Text, out var maxDelay)) + { + spawner.MaxDelay = maxDelay; + } + } + + break; + } + //go + case 5: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + _mobile.Map = spawner.Map; + _mobile.Location = spawner.Location; + } + Refresh(false); + return; + } + //open entry + case 6: + case 7: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + Refresh(); + + if (type == 7) + { + _mobile.SendGump(new SpawnerGump(spawner)); + } + else + { + _mobile.SendGump(new PropertiesGump(_mobile, spawner)); + } + + return; + } + + break; + } + //delete + case 9: + { + if (index < _spawners.Length) + { + _spawners[index].Delete(); + } + + break; + } + case 10: + { + _page++; + break; + } + case 11: + { + if (_page > 0) + { + _page--; + } + + break; + } + } + + Refresh(); + } +} diff --git a/Projects/UOContent/Engines/Spawners/SpawnerGump.cs b/Projects/UOContent/Engines/Spawners/SpawnerGump.cs index c1d075037..012899230 100644 --- a/Projects/UOContent/Engines/Spawners/SpawnerGump.cs +++ b/Projects/UOContent/Engines/Spawners/SpawnerGump.cs @@ -1,369 +1,447 @@ -using System.Collections.Generic; +using Server.Collections; using Server.Gumps; using Server.Network; -namespace Server.Engines.Spawners +namespace Server.Engines.Spawners; + +public class SpawnerGump : Gump { - public class SpawnerGump : Gump + private readonly BaseSpawner _spawner; + private SpawnerEntry _entry; + private int _page; + + public SpawnerGump(BaseSpawner spawner, SpawnerEntry focusentry = null, int page = 0) : base(50, 50) { - private readonly BaseSpawner m_Spawner; - private SpawnerEntry m_Entry; - private int m_Page; + _spawner = spawner; + _entry = focusentry; + _page = page; - public SpawnerGump(BaseSpawner spawner, SpawnerEntry focusentry = null, int page = 0) : base(50, 50) + AddPage(0); + + AddBackground(0, 0, 346, 400 + (_entry != null ? 44 : 0), 5054); + AddAlphaRegion(0, 0, 346, 400 + (_entry != null ? 44 : 0)); + + AddHtml(240, 1, 250, 20, "#"); + AddHtml(271, 1, 250, 20, "Max"); + AddHtml(311, 1, 250, 20, "Prb"); + + // AddLabel( 95, 1, 0, "Creatures List" ); + + var offset = 0; + + for (var i = 0; i < 13; i++) { - m_Spawner = spawner; - m_Entry = focusentry; - m_Page = page; + var textIndex = i * 5; + var entryIndex = _page * 13 + i; - AddPage(0); + SpawnerEntry entry = null; - AddBackground(0, 0, 343, 371 + (m_Entry != null ? 44 : 0), 5054); - - AddHtml(95, 1, 250, 20, "Creatures List"); - AddHtml(245, 1, 250, 20, "#"); - AddHtml(282, 1, 250, 20, "Prb"); - - // AddLabel( 95, 1, 0, "Creatures List" ); - - var offset = 0; - - for (var i = 0; i < 13; i++) + if (entryIndex < spawner.Entries.Count) { - var textindex = i * 5; - var entryindex = m_Page * 13 + i; + entry = _spawner.Entries[entryIndex]; + } - SpawnerEntry entry = null; + if (entry == null || _entry != entry) + { + AddButton( + 5, + 22 * i + 21 + offset, + entry != null ? 0xFBA : 0xFA5, + entry != null ? 0xFBC : 0xFA7, + GetButtonID(2, i * 2) + ); // Expand + } + else + { + AddButton( + 5, + 22 * i + 21 + offset, + 0xFBB, + 0xFBC, + GetButtonID(2, i * 2) + ); // Unexpand + } - if (entryindex < spawner.Entries.Count) + AddButton(38, 22 * i + 21 + offset, 0xFA2, 0xFA4, GetButtonID(2, 1 + i * 2)); // Delete + + AddImageTiled(71, 22 * i + 20 + offset, 161, 23, 0xA40); // creature text box + AddImageTiled(72, 22 * i + 21 + offset, 159, 21, 0xBBC); // creature text box + + AddImageTiled(235, 22 * i + 20 + offset, 35, 23, 0xA40); // count html label + AddImageTiled(236, 22 * i + 21 + offset, 33, 21, 0xE14); // count html label + + AddImageTiled(267, 22 * i + 20 + offset, 35, 23, 0xA40); // maxcount text box + AddImageTiled(268, 22 * i + 21 + offset, 33, 21, 0xBBC); // maxcount text box + + AddImageTiled(305, 22 * i + 20 + offset, 35, 23, 0xA40); // probability text box + AddImageTiled(306, 22 * i + 21 + offset, 33, 21, 0xBBC); // probability text box + + string name; + string probability; + string maxCount; + var flags = EntryFlags.None; + + if (entry != null) + { + name = entry.SpawnedName; + probability = entry.SpawnedProbability.ToString(); + maxCount = entry.SpawnedMaxCount.ToString(); + flags = entry.Valid; + + var count = spawner.CountSpawns(entry); + + AddHtml(235, 22 * i + 20 + offset + 1, 35, 15, $"
{count}/
"); + } + else + { + name = ""; + probability = ""; + maxCount = ""; + } + + // creature + AddTextEntry( + 75, + 22 * i + 21 + offset, + 156, + 21, + (flags & EntryFlags.InvalidType) != 0 ? 33 : 0, + textIndex, + name + ); + AddTextEntry(270, 22 * i + 21 + offset, 30, 21, 0, textIndex + 1, maxCount); // max count + AddTextEntry(308, 22 * i + 21 + offset, 30, 21, 0, textIndex + 2, probability); // probability + + if (entry != null && _entry == entry) + { + AddLabel(5, 22 * i + 42, 0x384, "Params"); + AddImageTiled(55, 22 * i + 42, 253, 23, 0xA40); // Parameters + AddImageTiled(56, 22 * i + 43, 251, 21, 0xBBC); // Parameters + + AddLabel(5, 22 * i + 64, 0x384, "Props"); + AddImageTiled(55, 22 * i + 64, 253, 23, 0xA40); // Properties + AddImageTiled(56, 22 * i + 65, 251, 21, 0xBBC); // Properties + + AddTextEntry( + 59, + 22 * i + 42, + 248, + 21, + (flags & EntryFlags.InvalidParams) != 0 ? 33 : 0, + textIndex + 3, + entry.Parameters + ); // parameters + AddTextEntry( + 59, + 22 * i + 62, + 248, + 21, + (flags & EntryFlags.InvalidProps) != 0 ? 33 : 0, + textIndex + 4, + entry.Properties + ); // properties + + offset += 44; + } + } + + if (spawner.Running) + { + AddButton(5, 312 + offset, 0x2A4E, 0x2A3A, GetButtonID(1, 6)); + AddLabel(38, 317 + offset, 0x384, "On"); + } + else + { + AddButton(5, 312 + offset, 0x2A62, 0x2A3A, GetButtonID(1, 7)); + AddLabel(38, 317 + offset, 0x384, "Off"); + } + + var totalSpawned = 0; + var totalSpawns = 0; + var totalWeight = 0; + + foreach (SpawnerEntry spawnerEntry in _spawner.Entries) + { + totalSpawns += spawnerEntry.SpawnedMaxCount; + totalSpawned += spawner.CountSpawns(spawnerEntry); + totalWeight += spawnerEntry.SpawnedProbability; + } + + AddHtml(270, 308 + offset, 35, 20, $"
{totalSpawns}
"); + AddHtml(308, 308 + offset, 35, 20, $"
{totalWeight}
"); + + AddHtml(5, 1, 161, 20, $"{spawner.Name} ({totalSpawned}/{spawner.Count})"); + + AddButton(5, 347 + offset, 0xFAB, 0xFAD, GetButtonID(1, 2)); + AddLabel(38, 347 + offset, 0x384, "Props"); + + AddButton(5, 369 + offset, 0xFAE, 0xFAF, GetButtonID(1, 8)); + AddLabel(38, 369 + offset, 0x384, "Goto"); + + AddButton(90, 325 + offset, 0xFA2, 0xFA3, GetButtonID(1, 9)); + AddLabel(123, 325 + offset, 0x384, "Reset"); + + AddButton(90, 347 + offset, 0xFB4, 0xFB6, GetButtonID(1, 3)); + AddLabel(123, 347 + offset, 0x384, "Bring Home"); + + AddButton(90, 369 + offset, 0xFA8, 0xFAA, GetButtonID(1, 4)); + AddLabel(123, 369 + offset, 0x384, "Total Respawn"); + + AddButton(260, 347 + offset, 0xFB7, 0xFB9, GetButtonID(1, 5)); + AddLabel(293, 347 + offset, 0x384, "Save"); + + AddButton(260, 369 + offset, 0xFB1, 0xFB3, 0); + AddLabel(293, 369 + offset, 0x384, "Cancel"); + + if (_page > 0) + { + AddButton(200, 308 + offset, 0x15E3, 0x15E7, GetButtonID(1, 0)); + } + else + { + AddImage(200, 308 + offset, 0x25EA); + } + + if ((_page + 1) * 13 <= _spawner.Entries.Count) + { + AddButton(217, 308 + offset, 0x15E1, 0x15E5, GetButtonID(1, 1)); + } + else + { + AddImage(217, 308 + offset, 0x25E6); + } + } + + public int GetButtonID(int type, int index) => 1 + index * 10 + type; + + private static string GetCountColor(int count, int maxCount) => + ((double) count / maxCount) switch + { + <= 0.25 => "#DE3163", // red + <= 0.50 => "#FF7F50", // orange + <= 0.75 => "#DFFF00", // yellow + <= 1 => "#00FF00", // green + _ => "#F4F4F4" // white + }; + + public void CreateArray(RelayInfo info, Mobile from, BaseSpawner spawner) + { + var ocount = spawner.Entries.Count; + + using var queue = PooledRefQueue.Create(); + + for (var i = 0; i < 13; i++) + { + var index = i * 5; + var entryindex = _page * 13 + i; + + var cte = info.GetTextEntry(index); + var mte = info.GetTextEntry(index + 1); + var poste = info.GetTextEntry(index + 2); + var parmte = info.GetTextEntry(index + 3); + var propte = info.GetTextEntry(index + 4); + + if (cte == null) + { + continue; + } + + var str = cte.Text.Trim().ToLower(); + + if (str.Length > 0) + { + var type = AssemblyHandler.FindTypeByName(str); + + if (type == null) { - entry = m_Spawner.Entries[entryindex]; + from.SendMessage("{0} is not a valid type name for entry #{1}.", str, i); + return; } - if (entry == null || m_Entry != entry) + SpawnerEntry entry; + + if (entryindex < ocount) { - AddButton( - 5, - 22 * i + 21 + offset, - entry != null ? 0xFBA : 0xFA5, - entry != null ? 0xFBC : 0xFA7, - GetButtonID(2, i * 2) - ); // Expand + entry = spawner.Entries[entryindex]; + entry.SpawnedName = str; + + if (mte != null) + { + entry.SpawnedMaxCount = Utility.ToInt32(mte.Text.Trim()); + } + + if (poste != null) + { + entry.SpawnedProbability = Utility.ToInt32(poste.Text.Trim()); + } } else { - AddButton( - 5, - 22 * i + 21 + offset, - 0xFBB, - 0xFBC, - GetButtonID(2, i * 2) - ); // Unexpand + var maxcount = 1; + var probcount = 100; + + if (mte != null) + { + maxcount = Utility.ToInt32(mte.Text.Trim()); + } + + if (poste != null) + { + probcount = Utility.ToInt32(poste.Text.Trim()); + } + + entry = spawner.AddEntry(str, probcount, maxcount); } - AddButton(38, 22 * i + 21 + offset, 0xFA2, 0xFA4, GetButtonID(2, 1 + i * 2)); // Delete - - AddImageTiled(71, 22 * i + 20 + offset, 161, 23, 0xA40); // creature text box - AddImageTiled(72, 22 * i + 21 + offset, 159, 21, 0xBBC); // creature text box - - AddImageTiled(235, 22 * i + 20 + offset, 35, 23, 0xA40); // maxcount text box - AddImageTiled(236, 22 * i + 21 + offset, 33, 21, 0xBBC); // maxcount text box - - AddImageTiled(273, 22 * i + 20 + offset, 35, 23, 0xA40); // probability text box - AddImageTiled(274, 22 * i + 21 + offset, 33, 21, 0xBBC); // probability text box - - var name = ""; - var probability = ""; - var maxcount = ""; - var flags = EntryFlags.None; - - if (entry != null) + if (parmte != null) { - name = entry.SpawnedName; - probability = entry.SpawnedProbability.ToString(); - maxcount = entry.SpawnedMaxCount.ToString(); - flags = entry.Valid; - - AddLabel(315, 22 * i + 20 + offset, 0, spawner.CountSpawns(entry).ToString()); + entry.Parameters = parmte.Text.Trim(); } - AddTextEntry( - 75, - 22 * i + 21 + offset, - 156, - 21, - (flags & EntryFlags.InvalidType) != 0 ? 33 : 0, - textindex, - name - ); // creature - AddTextEntry(239, 22 * i + 21 + offset, 30, 21, 0, textindex + 1, maxcount); // max count - AddTextEntry(277, 22 * i + 21 + offset, 30, 21, 0, textindex + 2, probability); // probability - - if (entry != null && m_Entry == entry) + if (propte != null) { - AddLabel(5, 22 * i + 42, 0x384, "Params"); - AddImageTiled(55, 22 * i + 42, 253, 23, 0xA40); // Parameters - AddImageTiled(56, 22 * i + 43, 251, 21, 0xBBC); // Parameters - - AddLabel(5, 22 * i + 64, 0x384, "Props"); - AddImageTiled(55, 22 * i + 64, 253, 23, 0xA40); // Properties - AddImageTiled(56, 22 * i + 65, 251, 21, 0xBBC); // Properties - - AddTextEntry( - 59, - 22 * i + 42, - 248, - 21, - (flags & EntryFlags.InvalidParams) != 0 ? 33 : 0, - textindex + 3, - entry.Parameters - ); // parameters - AddTextEntry( - 59, - 22 * i + 62, - 248, - 21, - (flags & EntryFlags.InvalidProps) != 0 ? 33 : 0, - textindex + 4, - entry.Properties - ); // properties - - offset += 44; + entry.Properties = propte.Text.Trim(); } } - - AddButton(5, 347 + offset, 0xFB1, 0xFB3, 0); - AddLabel(38, 347 + offset, 0x384, "Cancel"); - - AddButton(5, 325 + offset, 0xFB7, 0xFB9, GetButtonID(1, 2)); - AddLabel(38, 325 + offset, 0x384, "Okay"); - - AddButton(110, 325 + offset, 0xFB4, 0xFB6, GetButtonID(1, 3)); - AddLabel(143, 325 + offset, 0x384, "Bring to Home"); - - AddButton(110, 347 + offset, 0xFA8, 0xFAA, GetButtonID(1, 4)); - AddLabel(143, 347 + offset, 0x384, "Total Respawn"); - - AddButton(253, 325 + offset, 0xFB7, 0xFB9, GetButtonID(1, 5)); - AddLabel(286, 325 + offset, 0x384, "Apply"); - - if (m_Page > 0) + else if (entryindex < ocount && spawner.Entries[entryindex] != null) { - AddButton(276, 308 + offset, 0x15E3, 0x15E7, GetButtonID(1, 0)); - } - else - { - AddImage(276, 308 + offset, 0x25EA); - } - - if ((m_Page + 1) * 13 <= m_Spawner.Entries.Count) - { - AddButton(293, 308 + offset, 0x15E1, 0x15E5, GetButtonID(1, 1)); - } - else - { - AddImage(293, 308 + offset, 0x25E6); + queue.Enqueue(spawner.Entries[entryindex]); } } - public int GetButtonID(int type, int index) => 1 + index * 10 + type; - - public void CreateArray(RelayInfo info, Mobile from, BaseSpawner spawner) + while (queue.Count > 0) { - var ocount = spawner.Entries.Count; - - var rementries = new List(); - - for (var i = 0; i < 13; i++) - { - var index = i * 5; - var entryindex = m_Page * 13 + i; - - var cte = info.GetTextEntry(index); - var mte = info.GetTextEntry(index + 1); - var poste = info.GetTextEntry(index + 2); - var parmte = info.GetTextEntry(index + 3); - var propte = info.GetTextEntry(index + 4); - - if (cte == null) - { - continue; - } - - var str = cte.Text.Trim().ToLower(); - - if (str.Length > 0) - { - var type = AssemblyHandler.FindTypeByName(str); - - if (type == null) - { - from.SendMessage("{0} is not a valid type name for entry #{1}.", str, i); - return; - } - - SpawnerEntry entry; - - if (entryindex < ocount) - { - entry = spawner.Entries[entryindex]; - entry.SpawnedName = str; - - if (mte != null) - { - entry.SpawnedMaxCount = Utility.ToInt32(mte.Text.Trim()); - } - - if (poste != null) - { - entry.SpawnedProbability = Utility.ToInt32(poste.Text.Trim()); - } - } - else - { - var maxcount = 1; - var probcount = 100; - - if (mte != null) - { - maxcount = Utility.ToInt32(mte.Text.Trim()); - } - - if (poste != null) - { - probcount = Utility.ToInt32(poste.Text.Trim()); - } - - entry = spawner.AddEntry(str, probcount, maxcount); - } - - if (parmte != null) - { - entry.Parameters = parmte.Text.Trim(); - } - - if (propte != null) - { - entry.Properties = propte.Text.Trim(); - } - } - else if (entryindex < ocount && spawner.Entries[entryindex] != null) - { - rementries.Add(spawner.Entries[entryindex]); - } - } - - for (var i = 0; i < rementries.Count; i++) - { - spawner.RemoveEntry(rementries[i]); - } - - if (ocount == 0 && spawner.Entries.Count > 0) - { - spawner.Start(); - } + spawner.RemoveEntry(queue.Dequeue()); } - public override void OnResponse(NetState state, RelayInfo info) + if (ocount == 0 && spawner.Entries.Count > 0) { - if (m_Spawner.Deleted) - { - return; - } + spawner.Start(); + } + } - var val = info.ButtonID - 1; + public override void OnResponse(NetState state, RelayInfo info) + { + if (_spawner.Deleted) + { + return; + } - if (val < 0) - { - return; - } + var val = info.ButtonID - 1; - var type = val % 10; - var index = val / 10; + if (val < 0) + { + return; + } - switch (type) - { - case 0: // Cancel + var type = val % 10; + var index = val / 10; + + switch (type) + { + case 0: // Cancel + { return; - case 1: + } + case 1: + { + switch (index) { - switch (index) - { - case 0: + case 0: + { + if (_spawner.Entries != null && _page > 0) { - if (m_Spawner.Entries != null && m_Page > 0) - { - m_Page--; - m_Entry = null; - } + _page--; + _entry = null; + } - break; - } - case 1: + break; + } + case 1: + { + if ((_page + 1) * 13 <= _spawner.Entries?.Count) { - if ((m_Page + 1) * 13 <= m_Spawner.Entries?.Count) - { - m_Page++; - m_Entry = null; - } + _page++; + _entry = null; + } - break; - } - case 2: // Okay - { - CreateArray(info, state.Mobile, m_Spawner); - return; - } - case 3: - { - m_Spawner.BringToHome(); - break; - } - case 4: // Complete respawn - { - m_Spawner.Respawn(); - break; - } - case 5: - { - CreateArray(info, state.Mobile, m_Spawner); - break; - } - } + break; + } + case 2: // Props + { + state.Mobile.SendGump(new PropertiesGump(state.Mobile, _spawner)); + break; + } + case 3: // Bring Home + { + _spawner.BringToHome(); + break; + } + case 4: // Complete respawn + { + _spawner.Respawn(); + break; + } + case 5: // Save + { + CreateArray(info, state.Mobile, _spawner); + break; + } + case 6: // On button + { + _spawner.Running = false; - break; + break; + } + case 7: // Off button + { + _spawner.Running = true; + break; + } + case 8: // Goto + { + state.Mobile.MoveToWorld(_spawner.Location, _spawner.Map); + break; + } + case 9: // Reset + { + _spawner.Reset(); + break; + } } - case 2: + + break; + } + case 2: + { + var entryIndex = index / 2 + _page * 13; + var buttonType = index % 2; + + if (entryIndex >= 0 && entryIndex < _spawner.Entries.Count) { - var entryindex = index / 2 + m_Page * 13; - var buttontype = index % 2; - - if (entryindex >= 0 && entryindex < m_Spawner.Entries.Count) + var entry = _spawner.Entries[entryIndex]; + if (buttonType == 0) // Spawn creature { - var entry = m_Spawner.Entries[entryindex]; - if (buttontype == 0) // Spawn creature - { - m_Entry = m_Entry != entry ? entry : null; - } - else // Remove creatures - { - m_Spawner.RemoveSpawn(entryindex); - } + _entry = _entry != entry ? entry : null; + } + else // Remove creatures + { + _spawner.RemoveSpawn(entryIndex); } - - CreateArray(info, state.Mobile, m_Spawner); - break; } - } - if (m_Entry != null && m_Spawner.Entries?.Contains(m_Entry) == true) - { - state.Mobile.SendGump(new SpawnerGump(m_Spawner, m_Entry, m_Page)); - } - else - { - state.Mobile.SendGump(new SpawnerGump(m_Spawner, null, m_Page)); - } + CreateArray(info, state.Mobile, _spawner); + break; + } + } + + if (_entry != null && _spawner.Entries?.Contains(_entry) == true) + { + state.Mobile.SendGump(new SpawnerGump(_spawner, _entry, _page)); + } + else + { + state.Mobile.SendGump(new SpawnerGump(_spawner, null, _page)); } } } diff --git a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs new file mode 100644 index 000000000..5b2112386 --- /dev/null +++ b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using Server.Items; +using Server.Logging; +using Server.Utilities; + +namespace Server.Engines.Stealables; + +public static class StealableArtifacts +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(StealableArtifacts)); + + private static bool _enabled; + private static Type[] _typesOfEntries; + private static StealableInstance[] _artifacts; + + private static Timer _respawnTimer; + private static Dictionary _table; + + public static void Configure() + { + GenericPersistence.Register("StealableArtifacts", Serialize, Deserialize); + } + + private static void RemoveStealableArtifacts() + { + _enabled = false; + _respawnTimer?.Stop(); + _respawnTimer = null; + + if (_artifacts?.Length > 0) + { + foreach (var si in _artifacts) + { + var item = si.Item; + if (item.Deleted == false && !item.Movable && item.Parent == null) + { + item.Delete(); + } + } + } + + _artifacts = null; + } + + private static void CreateStealableArtifacts() + { + _enabled = true; + + _artifacts = new StealableInstance[Entries.Length]; + _table ??= new Dictionary(Entries.Length); + + for (var i = 0; i < Entries.Length; i++) + { + _artifacts[i] = new StealableInstance(Entries[i]); + } + + _respawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); + } + + public static StealableEntry[] Entries { get; } = + { + // Doom - Artifact rarity 1 + new(Map.Malas, new Point3D(317, 56, -1), 72, 108, typeof(RockArtifact)), + new(Map.Malas, new Point3D(360, 31, 8), 72, 108, typeof(SkullCandleArtifact)), + new(Map.Malas, new Point3D(369, 372, -1), 72, 108, typeof(BottleArtifact)), + new(Map.Malas, new Point3D(378, 372, 0), 72, 108, typeof(DamagedBooksArtifact)), + // Doom - Artifact rarity 2 + new(Map.Malas, new Point3D(432, 16, -1), 144, 216, typeof(StretchedHideArtifact)), + new(Map.Malas, new Point3D(489, 9, 0), 144, 216, typeof(BrazierArtifact)), + // Doom - Artifact rarity 3 + new(Map.Malas, new Point3D(471, 96, -1), 288, 432, typeof(LampPostArtifact), GetLampPostHue()), + new(Map.Malas, new Point3D(421, 198, 2), 288, 432, typeof(BooksNorthArtifact)), + new(Map.Malas, new Point3D(431, 189, -1), 288, 432, typeof(BooksWestArtifact)), + new(Map.Malas, new Point3D(435, 196, -1), 288, 432, typeof(BooksFaceDownArtifact)), + // Doom - Artifact rarity 5 + new(Map.Malas, new Point3D(447, 9, 8), 1152, 1728, typeof(StuddedLeggingsArtifact)), + new(Map.Malas, new Point3D(423, 28, 0), 1152, 1728, typeof(EggCaseArtifact)), + new(Map.Malas, new Point3D(347, 44, 4), 1152, 1728, typeof(SkinnedGoatArtifact)), + new(Map.Malas, new Point3D(497, 57, -1), 1152, 1728, typeof(GruesomeStandardArtifact)), + new(Map.Malas, new Point3D(381, 375, 11), 1152, 1728, typeof(BloodyWaterArtifact)), + new(Map.Malas, new Point3D(489, 369, 2), 1152, 1728, typeof(TarotCardsArtifact)), + new(Map.Malas, new Point3D(497, 369, 5), 1152, 1728, typeof(BackpackArtifact)), + // Doom - Artifact rarity 7 + new(Map.Malas, new Point3D(475, 23, 4), 4608, 6912, typeof(StuddedTunicArtifact)), + new(Map.Malas, new Point3D(423, 28, 0), 4608, 6912, typeof(CocoonArtifact)), + // Doom - Artifact rarity 8 + new(Map.Malas, new Point3D(354, 36, -1), 9216, 13824, typeof(SkinnedDeerArtifact)), + // Doom - Artifact rarity 9 + new(Map.Malas, new Point3D(433, 11, -1), 18432, 27648, typeof(SaddleArtifact)), + new(Map.Malas, new Point3D(403, 31, 4), 18432, 27648, typeof(LeatherTunicArtifact)), + // Doom - Artifact rarity 10 + new(Map.Malas, new Point3D(257, 70, -2), 36864, 55296, typeof(ZyronicClaw)), + new(Map.Malas, new Point3D(354, 176, 7), 36864, 55296, typeof(TitansHammer)), + new(Map.Malas, new Point3D(369, 389, -1), 36864, 55296, typeof(BladeOfTheRighteous)), + new(Map.Malas, new Point3D(467, 92, 4), 36864, 55296, typeof(InquisitorsResolution)), + // Doom - Artifact rarity 12 + new(Map.Malas, new Point3D(487, 364, -1), 147456, 221184, typeof(RuinedPaintingArtifact)), + + // Yomotsu Mines - Artifact rarity 1 + new(Map.Malas, new Point3D(18, 110, -1), 72, 108, typeof(Basket1Artifact)), + new(Map.Malas, new Point3D(66, 114, -1), 72, 108, typeof(Basket2Artifact)), + // Yomotsu Mines - Artifact rarity 2 + new(Map.Malas, new Point3D(63, 12, 11), 144, 216, typeof(Basket4Artifact)), + new(Map.Malas, new Point3D(5, 29, -1), 144, 216, typeof(Basket5NorthArtifact)), + new(Map.Malas, new Point3D(30, 81, 3), 144, 216, typeof(Basket5WestArtifact)), + // Yomotsu Mines - Artifact rarity 3 + new(Map.Malas, new Point3D(115, 7, -1), 288, 432, typeof(Urn1Artifact)), + new(Map.Malas, new Point3D(85, 13, -1), 288, 432, typeof(Urn2Artifact)), + new(Map.Malas, new Point3D(110, 53, -1), 288, 432, typeof(Sculpture1Artifact)), + new(Map.Malas, new Point3D(108, 37, -1), 288, 432, typeof(Sculpture2Artifact)), + new(Map.Malas, new Point3D(121, 14, -1), 288, 432, typeof(TeapotNorthArtifact)), + new(Map.Malas, new Point3D(121, 115, -1), 288, 432, typeof(TeapotWestArtifact)), + new(Map.Malas, new Point3D(84, 40, -1), 288, 432, typeof(TowerLanternArtifact)), + // Yomotsu Mines - Artifact rarity 9 + new(Map.Malas, new Point3D(94, 7, -1), 18432, 27648, typeof(ManStatuetteSouthArtifact)), + + // Fan Dancer's Dojo - Artifact rarity 1 + new(Map.Malas, new Point3D(113, 640, -2), 72, 108, typeof(Basket3NorthArtifact)), + new(Map.Malas, new Point3D(102, 355, -1), 72, 108, typeof(Basket3WestArtifact)), + // Fan Dancer's Dojo - Artifact rarity 2 + new(Map.Malas, new Point3D(99, 370, -1), 144, 216, typeof(Basket6Artifact)), + new(Map.Malas, new Point3D(100, 357, -1), 144, 216, typeof(ZenRock1Artifact)), + // Fan Dancer's Dojo - Artifact rarity 3 + new(Map.Malas, new Point3D(73, 473, -1), 288, 432, typeof(FanNorthArtifact)), + new(Map.Malas, new Point3D(99, 372, -1), 288, 432, typeof(FanWestArtifact)), + new(Map.Malas, new Point3D(92, 326, -1), 288, 432, typeof(BowlsVerticalArtifact)), + new(Map.Malas, new Point3D(97, 470, -1), 288, 432, typeof(ZenRock2Artifact)), + new(Map.Malas, new Point3D(103, 691, -1), 288, 432, typeof(ZenRock3Artifact)), + // Fan Dancer's Dojo - Artifact rarity 4 + new(Map.Malas, new Point3D(103, 336, 4), 576, 864, typeof(Painting1NorthArtifact)), + new(Map.Malas, new Point3D(59, 381, 4), 576, 864, typeof(Painting1WestArtifact)), + new(Map.Malas, new Point3D(84, 401, 2), 576, 864, typeof(Painting2NorthArtifact)), + new(Map.Malas, new Point3D(59, 392, 2), 576, 864, typeof(Painting2WestArtifact)), + new(Map.Malas, new Point3D(107, 483, -1), 576, 864, typeof(TripleFanNorthArtifact)), + new(Map.Malas, new Point3D(50, 475, -1), 576, 864, typeof(TripleFanWestArtifact)), + new(Map.Malas, new Point3D(107, 460, -1), 576, 864, typeof(BowlArtifact)), + new(Map.Malas, new Point3D(90, 502, -1), 576, 864, typeof(CupsArtifact)), + new(Map.Malas, new Point3D(107, 688, -1), 576, 864, typeof(BowlsHorizontalArtifact)), + new(Map.Malas, new Point3D(112, 676, -1), 576, 864, typeof(SakeArtifact)), + // Fan Dancer's Dojo - Artifact rarity 5 + new(Map.Malas, new Point3D(135, 614, -1), 1152, 1728, typeof(SwordDisplay1NorthArtifact)), + new(Map.Malas, new Point3D(50, 482, -1), 1152, 1728, typeof(SwordDisplay1WestArtifact)), + new(Map.Malas, new Point3D(119, 672, -1), 1152, 1728, typeof(Painting3Artifact)), + // Fan Dancer's Dojo - Artifact rarity 6 + new(Map.Malas, new Point3D(90, 326, -1), 2304, 3456, typeof(Painting4NorthArtifact)), + new(Map.Malas, new Point3D(99, 354, -1), 2304, 3456, typeof(Painting4WestArtifact)), + new(Map.Malas, new Point3D(179, 652, -1), 2304, 3456, typeof(SwordDisplay2NorthArtifact)), + new(Map.Malas, new Point3D(118, 627, -1), 2304, 3456, typeof(SwordDisplay2WestArtifact)), + // Fan Dancer's Dojo - Artifact rarity 7 + new(Map.Malas, new Point3D(90, 483, -1), 4608, 6912, typeof(FlowersArtifact)), + // Fan Dancer's Dojo - Artifact rarity 8 + new(Map.Malas, new Point3D(71, 562, -1), 9216, 13824, typeof(DolphinLeftArtifact)), + new(Map.Malas, new Point3D(102, 677, -1), 9216, 13824, typeof(DolphinRightArtifact)), + new(Map.Malas, new Point3D(61, 499, 0), 9216, 13824, typeof(SwordDisplay3SouthArtifact)), + new(Map.Malas, new Point3D(182, 669, -1), 9216, 13824, typeof(SwordDisplay3EastArtifact)), + new(Map.Malas, new Point3D(162, 647, -1), 9216, 13824, typeof(SwordDisplay4WestArtifact)), + new(Map.Malas, new Point3D(124, 624, 0), 9216, 13824, typeof(Painting5NorthArtifact)), + new(Map.Malas, new Point3D(146, 649, 2), 9216, 13824, typeof(Painting5WestArtifact)), + // Fan Dancer's Dojo - Artifact rarity 9 + new(Map.Malas, new Point3D(100, 488, -1), 18432, 27648, typeof(SwordDisplay4NorthArtifact)), + new(Map.Malas, new Point3D(175, 606, 0), 18432, 27648, typeof(SwordDisplay5NorthArtifact)), + new(Map.Malas, new Point3D(157, 608, -1), 18432, 27648, typeof(SwordDisplay5WestArtifact)), + new(Map.Malas, new Point3D(187, 643, 1), 18432, 27648, typeof(Painting6NorthArtifact)), + new(Map.Malas, new Point3D(146, 623, 1), 18432, 27648, typeof(Painting6WestArtifact)), + new(Map.Malas, new Point3D(178, 629, -1), 18432, 27648, typeof(ManStatuetteEastArtifact)) + }; + + public static Type[] TypesOfEntries + { + get + { + if (_typesOfEntries == null) + { + _typesOfEntries = new Type[Entries.Length]; + + for (var i = 0; i < Entries.Length; i++) + { + _typesOfEntries[i] = Entries[i].Type; + } + } + + return _typesOfEntries; + } + } + + private static int GetLampPostHue() => + Utility.RandomDouble() < 0.9 ? 0 : Utility.RandomList(0x455, 0x47E, 0x482, 0x486, 0x48F, 0x4F2, 0x58C, 0x66C); + + public static void Initialize() + { + CommandSystem.Register("GenStealArties", AccessLevel.Administrator, GenStealArties_OnCommand); + CommandSystem.Register("RemoveStealArties", AccessLevel.Administrator, RemoveStealArties_OnCommand); + } + + [Usage("GenStealArties"), Description("Generates the stealable artifacts spawner.")] + private static void GenStealArties_OnCommand(CommandEventArgs args) + { + var from = args.Mobile; + + if (_enabled) + { + from.SendMessage("Stealable artifacts spawner already present."); + return; + } + + CreateStealableArtifacts(); + from.SendMessage("Stealable artifacts spawner generated."); + } + + [Usage("RemoveStealArties")] + [Description("Removes the stealable artifacts spawner and every not yet stolen stealable artifacts.")] + private static void RemoveStealArties_OnCommand(CommandEventArgs args) + { + var from = args.Mobile; + + if (!_enabled) + { + from.SendMessage("Stealable artifacts spawner not present."); + return; + } + + RemoveStealableArtifacts(); + from.SendMessage("Stealable artifacts spawner removed."); + } + + public static StealableInstance GetStealableInstance(Item item) + { + if (_enabled && _table.TryGetValue(item, out var value)) + { + return value; + } + + return null; + } + + public static void CheckRespawn() + { + foreach (var si in _artifacts) + { + si.CheckRespawn(); + } + } + + private static void Serialize(IGenericWriter writer) + { + writer.WriteEncodedInt(1); // version + + writer.Write(_enabled); + + if (_enabled) + { + writer.WriteEncodedInt(_artifacts.Length); + + for (var i = 0; i < _artifacts.Length; i++) + { + var si = _artifacts[i]; + + writer.Write(si.Item); + writer.WriteDeltaTime(si.NextRespawn); + } + } + } + + private static void Deserialize(IGenericReader reader) + { + var version = reader.ReadEncodedInt(); + + _enabled = version > 0 && reader.ReadBool(); + + if (_enabled) + { + _artifacts = new StealableInstance[Entries.Length]; + _table = new Dictionary(Entries.Length); + + var length = reader.ReadEncodedInt(); + + for (var i = 0; i < length; i++) + { + var item = reader.ReadEntity(); + var nextRespawn = reader.ReadDeltaTime(); + + if (i < _artifacts.Length) + { + var si = new StealableInstance(Entries[i], item, nextRespawn); + _artifacts[i] = si; + + if (si.Item != null) + { + _table[si.Item] = si; + } + } + } + + for (var i = length; i < Entries.Length; i++) + { + _artifacts[i] = new StealableInstance(Entries[i]); + } + + _enabled = _artifacts.Length > 0; + + if (_enabled) + { + _respawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); + } + else + { + _artifacts = null; + } + } + } + + public class StealableEntry + { + public StealableEntry(Map map, Point3D location, int minDelay, int maxDelay, Type type, int hue = 0) + { + Map = map; + Location = location; + MinDelay = minDelay; + MaxDelay = maxDelay; + Type = type; + Hue = hue; + } + + public Map Map { get; } + + public Point3D Location { get; } + + public int MinDelay { get; } + + public int MaxDelay { get; } + + public Type Type { get; } + + public int Hue { get; } + + public Item CreateInstance() + { + try + { + var item = Type.CreateInstance(); + + if (Hue > 0) + { + item.Hue = Hue; + } + + item.Movable = false; + item.MoveToWorld(Location, Map); + + return item; + } + catch (Exception e) + { + logger.Warning(e, $"Failed to construct stealable artifact: {Type.FullName}"); + return null; + } + } + } + + public class StealableInstance + { + private Item m_Item; + + public StealableInstance(StealableEntry entry) : this(entry, null, Core.Now) + { + } + + public StealableInstance(StealableEntry entry, Item item, DateTime nextRespawn) + { + m_Item = item; + NextRespawn = nextRespawn; + Entry = entry; + } + + public StealableEntry Entry { get; } + + public Item Item + { + get => m_Item; + set + { + if (m_Item != null && value == null) + { + var delay = Utility.RandomMinMax(Entry.MinDelay, Entry.MaxDelay); + NextRespawn = Core.Now + TimeSpan.FromMinutes(delay); + } + + if (_enabled) + { + if (m_Item != null) + { + _table.Remove(m_Item); + } + + if (value != null) + { + _table[value] = this; + } + } + + m_Item = value; + } + } + + public DateTime NextRespawn { get; set; } + + public void CheckRespawn() + { + if (Item != null && (Item.Deleted || Item.Movable || Item.Parent != null)) + { + Item = null; + } + + if (Item == null && Core.Now >= NextRespawn) + { + Item = Entry.CreateInstance(); + } + } + } + + [ManualDirtyChecking] + [TypeAlias("Server.Items.StealableArtifactsSpawner")] + [Obsolete("Deprecated in favor of the static system. Only used for legacy deserialization")] + public class StealableArtifactsSpawner : Item + { + private StealableArtifactsSpawner() + { + Delete(); + } + + public StealableArtifactsSpawner(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + StealableArtifacts.Deserialize(reader); + + Timer.DelayCall(Delete); + } + } +} diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs b/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs index 2051ab96c..f9745cb4c 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs @@ -1,4 +1,5 @@ using System; +using Server.Engines.Stealables; using Server.Misc; using Server.Mobiles; using Server.Targeting; @@ -112,7 +113,7 @@ namespace Server.Items set { } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -121,7 +122,7 @@ namespace Server.Items TextDefinition.AddTo(list, m_Label); } - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ } public override void OnDoubleClick(Mobile from) @@ -234,7 +235,7 @@ namespace Server.Items || IsInTypeList(t, DemonKnight.ArtifactRarity10) || IsInTypeList(t, DemonKnight.ArtifactRarity11) || IsInTypeList(t, MondainsLegacy.Artifacts) - || IsInTypeList(t, StealableArtifactsSpawner.TypesOfEntires) + || IsInTypeList(t, StealableArtifacts.TypesOfEntries) || IsInTypeList(t, Paragon.Artifacts) || IsInTypeList(t, Leviathan.Artifacts) || IsInTypeList(t, TreasureMapChest.Artifacts) diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs index e7412891c..9289fa0e7 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs @@ -713,7 +713,7 @@ namespace Server.Items Utility.Intern(ref m_UrnName); } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add(1070935, m_UrnName); // Ancient Urn of ~1_name~ } @@ -772,7 +772,7 @@ namespace Server.Items Utility.Intern(ref m_SwordsName); } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add(1070936, m_SwordsName); // Honorable Swords of ~1_name~ } diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs b/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs index a4d2ede01..4e1ff18fb 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs @@ -151,7 +151,7 @@ namespace Server.Misc pm.ToTTotalMonsterFame += (int)(bc.Fame * (1 + Math.Sqrt(pm.Luck) / 100)); - // This is the Exponentional regression with only 2 datapoints. + // This is the Exponential regression with only 2 data points. // A log. func would also work, but it didn't make as much sense. // This function isn't OSI exact being that I don't know OSI's func they used ;p var x = pm.ToTTotalMonsterFame; @@ -269,10 +269,8 @@ namespace Server.Mobiles { if (pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward) { - SayTo( - pm, - 1070980 - ); // Congratulations! You have turned in enough minor treasures to earn a greater reward. + // Congratulations! You have turned in enough minor treasures to earn a greater reward. + SayTo(pm, 1070980); pm.CloseGump(); // Sanity @@ -285,18 +283,16 @@ namespace Server.Mobiles { 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. + // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item. + SayTo(pm, 1071013); } else { SayTo( pm, - 1070981, + 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. $"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}" - ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + ); } var buttons = ToTTurnInGump.FindRedeemableItems(pm); @@ -342,34 +338,26 @@ namespace Server.Gumps { private readonly Mobile m_Collector; - public ToTTurnInGump(Mobile collector, List buttons) : base( - 1071012, - buttons.ToList() - ) // Click a minor artifact to give it to Ihara Soko. - => - m_Collector = collector; + // Click a minor artifact to give it to Ihara Soko. + public ToTTurnInGump(Mobile collector, List buttons) + : base(1071012, buttons) => m_Collector = collector; - public static List FindRedeemableItems(Mobile m) + public static List FindRedeemableItems(Mobile m) { var pack = m.Backpack; if (pack == null) { - return new List(); + return new List(); } - var buttons = new List(); + var buttons = new List(); var items = pack.FindItemsByType(TreasuresOfTokuno.LesserArtifactsTotal); for (var i = 0; i < items.Length; i++) { var item = items[i]; - if (item is ChestOfHeirlooms heirlooms && !heirlooms.Locked) - { - continue; - } - - if (item is ChestOfHeirlooms ofHeirlooms && ofHeirlooms.TrapLevel != 10) + if (item is ChestOfHeirlooms heirlooms && (!heirlooms.Locked || heirlooms.TrapLevel != 10)) { continue; } @@ -400,10 +388,8 @@ namespace Server.Gumps if (++pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward) { - m_Collector.SayTo( - pm, - 1070980 - ); // Congratulations! You have turned in enough minor treasures to earn a greater reward. + // Congratulations! You have turned in enough minor treasures to earn a greater reward. + m_Collector.SayTo(pm, 1070980); pm.CloseGump(); // Sanity @@ -416,9 +402,9 @@ namespace Server.Gumps { m_Collector.SayTo( pm, - 1070981, + 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. $"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}" - ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + ); var buttons = FindRedeemableItems(pm); @@ -440,19 +426,17 @@ namespace Server.Gumps 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. + // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item. + m_Collector.SayTo(pm, 1071013); } - else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward - ) // This case should ALWAYS be true with this gump, jsut a sanity check + // This case should ALWAYS be true with this gump, just a sanity check + else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward) { m_Collector.SayTo( pm, - 1070981, + 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. $"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}" - ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + ); } else { @@ -611,11 +595,9 @@ namespace Server.Gumps pm.ToTItemsTurnedIn -= TreasuresOfTokuno.ItemsPerReward; m_Collector.SayTo( pm, - 1070984, - item.Name == null || item.Name.Length <= 0 - ? $"#{item.LabelNumber}" - : item.Name - ); // You have earned the gratitude of the Empire. I have placed the ~1_OBJTYPE~ in your backpack. + 1070984, // You have earned the gratitude of the Empire. I have placed the ~1_OBJTYPE~ in your backpack. + item.Name?.Length > 0 ? item.Name : $"#{item.LabelNumber}" + ); } else { @@ -634,19 +616,17 @@ namespace Server.Gumps 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. + // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item. + m_Collector.SayTo(pm, 1071013); } - else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward - ) // This and above case should ALWAYS be FALSE with this gump, jsut a sanity check + // This and above case should ALWAYS be FALSE with this gump, jsut a sanity check + else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward) { m_Collector.SayTo( pm, - 1070981, + 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. $"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}" - ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + ); } else { diff --git a/Projects/UOContent/Engines/UltimaStore/UltimaStorePackets.cs b/Projects/UOContent/Engines/UltimaStore/UltimaStorePackets.cs index cb40edc26..bb4b1698b 100644 --- a/Projects/UOContent/Engines/UltimaStore/UltimaStorePackets.cs +++ b/Projects/UOContent/Engines/UltimaStore/UltimaStorePackets.cs @@ -4,12 +4,12 @@ namespace Server.Engines.UltimaStore { public static class UltimaStorePackets { - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0xFA, 1, true, UltimaStoreOpenRequest); + IncomingPackets.Register(0xFA, 1, true, &UltimaStoreOpenRequest); } - public static void UltimaStoreOpenRequest(NetState state, CircularBufferReader reader, ref int packetLength) + public static void UltimaStoreOpenRequest(NetState state, CircularBufferReader reader, int packetLength) { state.Mobile.SendMessage("Ultima Store is not currently available."); } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index 1ce3132cb..12a279ea2 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -128,7 +128,7 @@ namespace Server.Mobiles DisplayPaperdollTo(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -496,7 +496,7 @@ namespace Server.Mobiles } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs index d1f2ab359..b9e7315cd 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs @@ -71,7 +71,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs index b7a6a59e3..a66d08291 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs @@ -47,7 +47,7 @@ namespace Server.Engines.VeteranRewards public static void SendStatueAnimation(this NetState ns, Serial serial, int status, int anim, int frame) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/Virtues/Valor.cs b/Projects/UOContent/Engines/Virtues/Valor.cs index 70699b242..2db766cf1 100644 --- a/Projects/UOContent/Engines/Virtues/Valor.cs +++ b/Projects/UOContent/Engines/Virtues/Valor.cs @@ -3,146 +3,139 @@ using Server.Engines.CannedEvil; using Server.Mobiles; using Server.Targeting; -namespace Server -{ - public static class ValorVirtue - { - private const int LossAmount = 250; - private static readonly TimeSpan LossDelay = TimeSpan.FromDays(7.0); +namespace Server; - public static void Initialize() +public static class ValorVirtue +{ + private const int LossAmount = 250; + private static readonly TimeSpan LossDelay = TimeSpan.FromDays(7.0); + + public static void Initialize() + { + VirtueGump.Register(112, OnVirtueUsed); + } + + public static void OnVirtueUsed(Mobile from) + { + if (from.Alive) { - VirtueGump.Register(112, OnVirtueUsed); + from.SendLocalizedMessage(1054034); // Target the Champion Idol of the Champion you wish to challenge!. + from.Target = new InternalTarget(); + } + } + + public static void CheckAtrophy(Mobile from) + { + if (from is not PlayerMobile pm) + { + return; } - public static void OnVirtueUsed(Mobile from) + try { - if (from.Alive) + if (pm.LastValorLoss + LossDelay < Core.Now) { - from.SendLocalizedMessage(1054034); // Target the Champion Idol of the Champion you wish to challenge!. - from.Target = new InternalTarget(); + if (VirtueHelper.Atrophy(from, VirtueName.Valor, LossAmount)) + { + from.SendLocalizedMessage(1054040); // You have lost some Valor. + } + + pm.LastValorLoss = Core.Now; } } - - public static void CheckAtrophy(Mobile from) + catch { - if (from is not PlayerMobile pm) + // ignored + } + } + + public static void Valor(Mobile from, object targ) + { + if (targ is not IdolOfTheChampion idol || idol.Deleted || idol.Spawn?.Deleted != false) + { + 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 if (idol.Spawn.Active) + { + if (idol.Spawn.Champion != null) // TODO: Message? { return; } - try + int needed, consumed; + switch (idol.Spawn.GetSubLevel()) { - if (pm.LastValorLoss + LossDelay < Core.Now) - { - if (VirtueHelper.Atrophy(from, VirtueName.Valor, LossAmount)) + case 0: { - from.SendLocalizedMessage(1054040); // You have lost some Valor. + needed = consumed = 2500; + break; } + case 1: + { + needed = consumed = 5000; + break; + } + case 2: + { + needed = 10000; + consumed = 7500; + break; + } + default: + { + needed = 20000; + consumed = 10000; + break; + } + } - pm.LastValorLoss = Core.Now; - } - } - catch + if (from.Virtues.GetValue((int)VirtueName.Valor) >= needed) { - // ignored - } - } - - public static void Valor(Mobile from, object targ) - { - if (targ is not IdolOfTheChampion idol || idol.Deleted || idol.Spawn?.Deleted != false) - { - 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! + VirtueHelper.Atrophy(from, VirtueName.Valor, consumed); + // Your challenge is heard by the Champion of this region! Beware its wrath! + from.SendLocalizedMessage(1054037); + idol.Spawn.HasBeenAdvanced = true; + idol.Spawn.AdvanceLevel(); } else { - var 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); - // Your challenge is heard by the Champion of this region! Beware its wrath! - from.SendLocalizedMessage(1054037); - idol.Spawn.HasBeenAdvanced = true; - idol.Spawn.AdvanceLevel(); - } - else - { - // The Champion of this region ignores your challenge. You must further prove your valor. - from.SendLocalizedMessage(1054039); - } - } - else - { - if (vl == VirtueLevel.Knight) - { - VirtueHelper.Atrophy(from, VirtueName.Valor, 11000); - // Your challenge is heard by the Champion of this region! Beware its wrath! - from.SendLocalizedMessage(1054037); - idol.Spawn.EndRestart(); - idol.Spawn.HasBeenAdvanced = true; - } - else - { - // You must be a Knight of Valor to summon the champion's spawn in this manner! - from.SendLocalizedMessage(1054036); - } - } + // The Champion of this region ignores your challenge. You must further prove your valor. + from.SendLocalizedMessage(1054039); } } - - private class InternalTarget : Target + else if (VirtueHelper.GetLevel(from, VirtueName.Valor) == VirtueLevel.Knight) { - public InternalTarget() : base(14, false, TargetFlags.None) - { - } + VirtueHelper.Atrophy(from, VirtueName.Valor, 11000); + // Your challenge is heard by the Champion of this region! Beware its wrath! + from.SendLocalizedMessage(1054037); + idol.Spawn.Start(); + // Uncomment to not allow advancing level after starting with valor. + // idol.Spawn.HasBeenAdvanced = true; + } + else + { + // You must be a Knight of Valor to summon the champion's spawn in this manner! + from.SendLocalizedMessage(1054036); + } + } - protected override void OnTarget(Mobile from, object targeted) - { - Valor(from, targeted); - } + private class InternalTarget : Target + { + public InternalTarget() : base(14, false, TargetFlags.None) + { + } + + protected override void OnTarget(Mobile from, object targeted) + { + Valor(from, targeted); } } } diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index 7f1b1deb6..93d2d39e0 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -221,7 +221,7 @@ namespace Server.Gumps } case AdminGumpPage.Information_Perf: { - using var sb = new ValueStringBuilder(); + using var sb = ValueStringBuilder.Create(); ThreadPool.GetAvailableThreads(out var curUser, out var curIOCP); ThreadPool.GetMaxThreads(out var maxUser, out var maxIOCP); @@ -261,16 +261,6 @@ namespace Server.Gumps AddButtonLabeled(20, 225, GetButtonID(3, 104), "Doors"); AddButtonLabeled(220, 225, GetButtonID(3, 105), "Signs"); - AddHtml(20, 275, 400, 30, Color(Center("Statics"), LabelColor32)); - - 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: @@ -560,7 +550,15 @@ namespace Server.Gumps var v = ns.Version; AddLabel(20, y, LabelHue, "Version:"); - AddLabel(200, y, LabelHue, v == null ? "(null)" : v.ToString()); + if (ns.Assistant != null) + { + AddLabel(200, y, LabelHue, $"{v.SourceString ?? "(null)"} ({ns.Assistant})"); + } + else + { + AddLabel(200, y, LabelHue, v.SourceString ?? "(null)"); + } + y += 20; AddLabel(20, y, LabelHue, "Location:"); @@ -639,7 +637,7 @@ namespace Server.Gumps AddLabel(12, 140, LabelHue, "There are no accounts to display."); } - using var sb = new ValueStringBuilder(); + using var sb = ValueStringBuilder.Create(); for (int i = 0, index = listPage * 12; i < 12 && index >= 0 && index < sharedAccounts.Count; @@ -1179,7 +1177,7 @@ namespace Server.Gumps AddButtonLabeled(20, 150, GetButtonID(5, 4), "Add Comment"); - var sb = new ValueStringBuilder(); + var sb = ValueStringBuilder.Create(); if (a.Comments.Count == 0) { @@ -1218,7 +1216,7 @@ namespace Server.Gumps AddButtonLabeled(20, 150, GetButtonID(5, 5), "Add Tag"); - var sb = new ValueStringBuilder(); + var sb = ValueStringBuilder.Create(); if (a.Tags.Count == 0) { @@ -3064,7 +3062,7 @@ namespace Server.Gumps if (list.Count > 0) { - using var sb = new ValueStringBuilder(); + using var sb = ValueStringBuilder.Create(); sb.Append("You are about to ban "); sb.Append(list.Count); sb.Append(list.Count != 1 ? "accounts." : "account."); diff --git a/Projects/UOContent/Gumps/BaseGridGump.cs b/Projects/UOContent/Gumps/BaseGridGump.cs index c755cde62..c5d69ec15 100644 --- a/Projects/UOContent/Gumps/BaseGridGump.cs +++ b/Projects/UOContent/Gumps/BaseGridGump.cs @@ -1,3 +1,6 @@ +using System.Runtime.CompilerServices; +using Server.Utilities; + namespace Server.Gumps { public abstract class BaseGridGump : Gump @@ -37,9 +40,10 @@ namespace Server.Gumps public virtual int TextHue => 0; public virtual int TextOffsetX => 2; - public string Center(string text) => $"
{text}
"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string Center(string text) => text.Center(); - public string Color(string text, int color) => $"{text}"; + public string Color(string text, int color) => text.Color(color); public int GetButtonID(int typeCount, int type, int index) => 1 + index * typeCount + type; diff --git a/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs b/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs index ad2dbcb5c..9e15cb31c 100644 --- a/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs +++ b/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs @@ -53,8 +53,8 @@ namespace Server.Gumps { } - public BaseImageTileButtonsGump(TextDefinition header, ImageTileButtonInfo[] buttons) : - base(10, 10) // Coords are 0, o on OSI, intentional difference + // Coords are 0, 0 on OSI, intentional difference + public BaseImageTileButtonsGump(TextDefinition header, ImageTileButtonInfo[] buttons) : base(10, 10) { Buttons = buttons; AddPage(0); @@ -109,9 +109,9 @@ namespace Server.Gumps b.ItemID, b.Hue, 15, - 10, - b.LocalizedTooltip + 10 ); + AddTooltip(b.LocalizedTooltip); TextDefinition.AddHtmlText(this, innerX + 84, innerY, 250, 60, b.Label, false, false, 0x7FFF, 0xFFFFFF); } } diff --git a/Projects/UOContent/Gumps/ClientGump.cs b/Projects/UOContent/Gumps/ClientGump.cs index 9084b8baf..db71a451b 100644 --- a/Projects/UOContent/Gumps/ClientGump.cs +++ b/Projects/UOContent/Gumps/ClientGump.cs @@ -23,13 +23,13 @@ namespace Server.Gumps AddPage(0); - AddBackground(0, 0, 400, 274, 5054); + AddBackground(0, 0, 420, 314, 5054); - AddImageTiled(10, 10, 380, 19, 0xA40); - AddAlphaRegion(10, 10, 380, 19); + AddImageTiled(10, 10, 400, 19, 0xA40); + AddAlphaRegion(10, 10, 400, 19); - AddImageTiled(10, 32, 380, 232, 0xA40); - AddAlphaRegion(10, 32, 380, 232); + AddImageTiled(10, 32, 400, 272, 0xA40); + AddAlphaRegion(10, 32, 400, 272); AddHtml(10, 10, 380, 20, Color(Center("User Information"), LabelColor32)); @@ -40,11 +40,20 @@ namespace Server.Gumps AddHtml(14, 36 + line * 20, 200, 20, Color("Client:", LabelColor32)); AddHtml( - 70, + 72, 36 + line++ * 20, 200, 20, - Color(state.Version == null ? "(null)" : state.Version.ToString(), LabelColor32) + Color(state.Version?.SourceString ?? "(null)", LabelColor32) + ); + + AddHtml(14, 36 + line * 20, 200, 20, Color("Assistant:", LabelColor32)); + AddHtml( + 72, + 36 + line++ * 20, + 200, + 20, + Color(state.Assistant ?? "(-none-)", LabelColor32) ); AddHtml(14, 36 + line * 20, 200, 20, Color("Version:", LabelColor32)); @@ -52,7 +61,7 @@ namespace Server.Gumps var info = state.ExpansionInfo; var expansionName = info.Name; - AddHtml(70, 36 + line++ * 20, 200, 20, Color(expansionName, LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, Color(expansionName, LabelColor32)); var a = state.Account as Account; var m = state.Mobile; @@ -60,70 +69,70 @@ namespace Server.Gumps if (from.AccessLevel >= AccessLevel.GameMaster && a != null) { AddHtml(14, 36 + line * 20, 200, 20, Color("Account:", LabelColor32)); - AddHtml(70, 36 + line++ * 20, 200, 20, Color(a.Username, LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, Color(a.Username, LabelColor32)); } if (m != null) { AddHtml(14, 36 + line * 20, 200, 20, Color("Mobile:", LabelColor32)); - AddHtml(70, 36 + line++ * 20, 200, 20, Color($"{m.Name} (0x{m.Serial.Value:X})", LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, Color($"{m.Name} ({m.Serial})", LabelColor32)); AddHtml(14, 36 + line * 20, 200, 20, Color("Location:", LabelColor32)); - AddHtml(70, 36 + line++ * 20, 200, 20, Color($"{m.Location} [{m.Map}]", LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, Color($"{m.Location} [{m.Map}]", LabelColor32)); - AddButton(13, 157, 0xFAB, 0xFAD, 1); - AddHtml(48, 158, 200, 20, Color("Send Message", LabelColor32)); + AddButton(13, 197, 0xFAB, 0xFAD, 1); + AddHtml(48, 198, 200, 20, Color("Send Message", LabelColor32)); - AddImageTiled(12, 182, 376, 80, 0xA40); - AddImageTiled(13, 183, 374, 78, 0xBBC); - AddTextEntry(15, 183, 372, 78, 0x480, 0, ""); + AddImageTiled(12, 222, 376, 80, 0xA40); + AddImageTiled(13, 223, 374, 78, 0xBBC); + AddTextEntry(15, 223, 372, 78, 0x480, 0, initialText); - AddImageTiled(245, 35, 142, 144, 5058); + AddImageTiled(265, 35, 142, 144, 5058); - AddImageTiled(246, 36, 140, 142, 0xA40); - AddAlphaRegion(246, 36, 140, 142); + AddImageTiled(266, 36, 140, 142, 0xA40); + AddAlphaRegion(266, 36, 140, 142); line = 0; if (BaseCommand.IsAccessible(from, m)) { - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 4); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Properties", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 4); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Properties", LabelColor32)); } if (from != m) { - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 5); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Go to them", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 5); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Go to them", LabelColor32)); - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 6); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Bring them here", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 6); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Bring them here", LabelColor32)); } - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 7); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Move to target", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 7); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Move to target", LabelColor32)); if (from.AccessLevel >= AccessLevel.GameMaster && from.AccessLevel > m.AccessLevel) { - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 8); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Disconnect", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 8); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Disconnect", LabelColor32)); if (m.Alive) { - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 9); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Kill", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 9); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Kill", LabelColor32)); } else { - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 10); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Resurrect", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 10); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Resurrect", LabelColor32)); } } if (from.AccessLevel >= AccessLevel.Counselor && from.AccessLevel > m.AccessLevel) { - AddButton(246, 36 + line * 20, 0xFA5, 0xFA7, 11); - AddHtml(280, 38 + line++ * 20, 100, 20, Color("Skills browser", LabelColor32)); + AddButton(266, 36 + line * 20, 0xFA5, 0xFA7, 11); + AddHtml(300, 38 + line++ * 20, 100, 20, Color("Skills browser", LabelColor32)); } } } @@ -272,7 +281,7 @@ namespace Server.Gumps { focus.Say("I've been kicked!"); - m_State.Disconnect($"Kicked by ${from}."); + m_State.Disconnect($"Kicked by {from}."); CommandLogging.WriteLine( from, diff --git a/Projects/UOContent/Gumps/HouseDemolishGump.cs b/Projects/UOContent/Gumps/HouseDemolishGump.cs index ec667c541..eb2780c71 100644 --- a/Projects/UOContent/Gumps/HouseDemolishGump.cs +++ b/Projects/UOContent/Gumps/HouseDemolishGump.cs @@ -138,8 +138,8 @@ namespace Server.Gumps { check.Delete(); - m_Mobile.SendLocalizedMessage(1060397, worth.ToString("#,0")); // ~1_AMOUNT~ gold has been deposited into your bank box. + m_Mobile.SendLocalizedMessage(1060397, $"{worth:#,0}"); m_House.RemoveKeys(m_Mobile); m_House.Delete(); diff --git a/Projects/UOContent/Gumps/NoticeGump.cs b/Projects/UOContent/Gumps/NoticeGump.cs index 582e25c96..eb10a69a0 100644 --- a/Projects/UOContent/Gumps/NoticeGump.cs +++ b/Projects/UOContent/Gumps/NoticeGump.cs @@ -9,7 +9,7 @@ namespace Server.Gumps private readonly NoticeGumpCallback m_Callback; public NoticeGump( - int header, int headerColor, object content, int contentColor, int width, int height, + int header, int headerColor, TextDefinition content, int contentColor, int width, int height, NoticeGumpCallback callback = null ) : base((640 - width) / 2, (480 - height) / 2) { @@ -28,21 +28,24 @@ namespace Server.Gumps AddImageTiled(10, 40, width - 20, height - 80, 2624); AddAlphaRegion(10, 40, width - 20, height - 80); - if (content is int i) + if (content != null) { - AddHtmlLocalized(10, 40, width - 20, height - 80, i, contentColor, false, true); - } - else if (content is string) - { - AddHtml( - 10, - 40, - width - 20, - height - 80, - $"{content}", - false, - true - ); + if (content.Number > 0) + { + AddHtmlLocalized(10, 40, width - 20, height - 80, content.Number, contentColor, false, true); + } + else + { + AddHtml( + 10, + 40, + width - 20, + height - 80, + $"{content.String}", + false, + true + ); + } } AddImageTiled(10, height - 30, width - 20, 20, 2624); diff --git a/Projects/UOContent/Gumps/Props/PropsGump.cs b/Projects/UOContent/Gumps/Props/PropsGump.cs index e7b43f0fc..7eb76c502 100644 --- a/Projects/UOContent/Gumps/Props/PropsGump.cs +++ b/Projects/UOContent/Gumps/Props/PropsGump.cs @@ -511,16 +511,16 @@ namespace Server.Gumps { if (serial.IsItem) { - return $"(I) 0x{serial.Value:X}"; + return $"(I) {serial}"; } if (serial.IsMobile) { - return $"(M) 0x{serial.Value:X}"; + return $"(M) {serial}"; } } - return $"(?) 0x{serial.Value:X}"; + return $"(?) {serial}"; } if (o is byte or sbyte or short or ushort or int or uint or long or ulong) @@ -530,12 +530,12 @@ namespace Server.Gumps if (o is Mobile mobile) { - return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\""; + return $"(M) {mobile.Serial} \"{mobile.Name}\""; } if (o is Item item) { - return $"(I) 0x{item.Serial.Value:X}"; + return $"(I) {item.Serial}"; } if (o is Type type) diff --git a/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs b/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs index 0e82aedf4..04de9076e 100644 --- a/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs +++ b/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs @@ -115,7 +115,7 @@ namespace Server.Gumps { case 1: // Current location { - toSet = new Point2D(m_Mobile.Location); + toSet = new Point2D(m_Mobile.Location.X, m_Mobile.Location.Y); shouldSet = true; shouldSend = true; @@ -194,12 +194,13 @@ namespace Server.Gumps protected override void OnTarget(Mobile from, object targeted) { - if (targeted is IPoint3D p) + if (targeted is IPoint3D point3D) { try { - CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, new Point2D(p).ToString()); - m_Property.SetValue(m_Object, new Point2D(p), null); + var p = new Point2D(point3D.X, point3D.Y); + CommandLogging.LogChangeProperty(m_Mobile, m_Object, m_Property.Name, p.ToString()); + m_Property.SetValue(m_Object, p, null); m_PropertiesGump.OnValueChanged(m_Object, m_Property); } catch diff --git a/Projects/UOContent/Gumps/WarningGump.cs b/Projects/UOContent/Gumps/WarningGump.cs index 8b3acfd41..3217bcdf9 100644 --- a/Projects/UOContent/Gumps/WarningGump.cs +++ b/Projects/UOContent/Gumps/WarningGump.cs @@ -9,7 +9,7 @@ namespace Server.Gumps private readonly WarningGumpCallback m_Callback; public WarningGump( - int header, int headerColor, object content, int contentColor, int width, int height, + int header, int headerColor, TextDefinition content, int contentColor, int width, int height, WarningGumpCallback callback = null, bool cancelButton = true ) : base((640 - width) / 2, (480 - height) / 2) { @@ -28,21 +28,24 @@ namespace Server.Gumps AddImageTiled(10, 40, width - 20, height - 80, 2624); AddAlphaRegion(10, 40, width - 20, height - 80); - if (content is int i) + if (content != null) { - AddHtmlLocalized(10, 40, width - 20, height - 80, i, contentColor, false, true); - } - else if (content is string) - { - AddHtml( - 10, - 40, - width - 20, - height - 80, - $"{content}", - false, - true - ); + if (content.Number > 0) + { + AddHtmlLocalized(10, 40, width - 20, height - 80, content.Number, contentColor, false, true); + } + else + { + AddHtml( + 10, + 40, + width - 20, + height - 80, + $"{content.String}", + false, + true + ); + } } AddImageTiled(10, height - 30, width - 20, 20, 2624); @@ -71,7 +74,7 @@ namespace Server.Gumps } else { - m_Callback.Invoke(false); + m_Callback(false); } } } diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs index dce038cf1..2e88a0a9b 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs @@ -1,3 +1,4 @@ +using ModernUO.Serialization; using Server.Gumps; using Server.Multis; using Server.Network; @@ -5,7 +6,7 @@ using Server.Targeting; namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class Fireflies : Item, IAddon { [Constructible] @@ -59,7 +60,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class FirefliesDeed : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs index 2242706da..4b6d4969c 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.AngelDecoration"), Flippable(0x46FA, 0x46FB)] public partial class AngelDecoration : Item { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs index a33fbf70a..f2588cf46 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.RockingHorse"), Flippable(0x4214, 0x4215)] public partial class RockingHorse : Item { diff --git a/Projects/UOContent/Holiday Stuff/Easter/2011/Items/DragonEasterEgg.cs b/Projects/UOContent/Holiday Stuff/Easter/2011/Items/DragonEasterEgg.cs index 5d936e8ca..bd0212152 100644 --- a/Projects/UOContent/Holiday Stuff/Easter/2011/Items/DragonEasterEgg.cs +++ b/Projects/UOContent/Holiday Stuff/Easter/2011/Items/DragonEasterEgg.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DragonEasterEgg : Item, IDyable { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs index 3384eec0f..992214b14 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using ModernUO.Serialization; using Server.Events.Halloween; using Server.Items; using Server.Mobiles; @@ -232,7 +233,7 @@ namespace Server.Engines.Events } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NaughtyTwin : BaseCreature { private static readonly Point3D[] Felucca_Locations = @@ -275,8 +276,7 @@ namespace Server.Engines.Events private readonly Mobile m_From; - public NaughtyTwin(Mobile from) - : base(AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4) + public NaughtyTwin(Mobile from) : base(AIType.AI_Melee, FightMode.None) { if (TrickOrTreat.CheckMobile(from)) { diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/HalloweenPumpkin.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/HalloweenPumpkin.cs index 7197c531e..896af55d9 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/HalloweenPumpkin.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/HalloweenPumpkin.cs @@ -1,8 +1,9 @@ -using Server.Mobiles; +using ModernUO.Serialization; +using Server.Mobiles; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class HalloweenPumpkin : Item { private static readonly string[] m_Staff = diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/PumpkinScarecrow.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/PumpkinScarecrow.cs index 2d27e9e53..b6983203c 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/PumpkinScarecrow.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/PumpkinScarecrow.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PumpkinScarecrow : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/RuinedTapestry.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/RuinedTapestry.cs index af265221c..db86b8f0a 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/RuinedTapestry.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/RuinedTapestry.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RuinedTapestry : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/TwilightLantern.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/TwilightLantern.cs index 456804a67..039092fac 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/TwilightLantern.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Items/TwilightLantern.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TwilightLantern : Lantern { [Constructible] @@ -10,7 +12,7 @@ public override bool AllowEquippedCast(Mobile from) => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs index cf00181eb..93c797c3a 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Server.Events.Halloween; using Server.Items; @@ -51,22 +50,23 @@ namespace Server.Engines.Events var rect = m_PumpkinFields[i]; var spawncount = rect.Height * rect.Width / 20; - var pumpkins = map.GetItemsInBounds(rect).OfType().Count(); + var eable = map.GetItemsInBounds(rect); + var pumpkins = 0; + foreach (var p in eable) + { + if (pumpkins++ >= spawncount) + { + break; + } + } + + eable.Free(); if (spawncount > pumpkins) { - new HalloweenPumpkin().MoveToWorld(RandomPointIn(rect, map), map); + new HalloweenPumpkin().MoveToWorld(Utility.RandomPointIn(rect, map), map); } } } - - private static Point3D RandomPointIn(Rectangle2D rect, Map map) - { - var x = Utility.Random(rect.X, rect.Width); - var y = Utility.Random(rect.Y, rect.Height); - var z = map.GetAverageZ(x, y); - - return new Point3D(x, y, z); - } } } diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/CreepyCake.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/CreepyCake.cs index e644f119f..268c25ae0 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/CreepyCake.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/CreepyCake.cs @@ -1,14 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { /* first seen halloween 2009. subsequently in 2010, 2011 and 2012. GM Beggar-only Semi-Rare Treats */ - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CreepyCake : Food { [Constructible] - public CreepyCake() : base(0x9e9, 1) => Hue = 0x3E4; + public CreepyCake() : base(0x9e9) => Hue = 0x3E4; public override string DefaultName => "Creepy Cake"; } diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/HarvestWine.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/HarvestWine.cs index d657d6dcb..ca0513ac4 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/HarvestWine.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/HarvestWine.cs @@ -1,10 +1,12 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { /* first seen halloween 2009. subsequently in 2010, 2011 and 2012. GM Beggar-only Semi-Rare Treats */ - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HarvestWine : BeverageBottle { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs index 74f3cca82..9e2c2b670 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs @@ -1,10 +1,12 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MrPlainsCookies : Food { [Constructible] - public MrPlainsCookies() : base(0x160C, 1) + public MrPlainsCookies() : base(0x160C) { Weight = 1.0; FillFactor = 4; diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MurkyMilk.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MurkyMilk.cs index 1a93b89fb..eb5ab5b87 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MurkyMilk.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MurkyMilk.cs @@ -1,10 +1,12 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { /* first seen halloween 2009. subsequently in 2010, 2011 and 2012. GM Beggar-only Semi-Rare Treats */ - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MurkyMilk : Pitcher { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/PumpkinPizza.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/PumpkinPizza.cs index 29c02dcbc..ef277242f 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/PumpkinPizza.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/PumpkinPizza.cs @@ -1,10 +1,12 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { /* first seen halloween 2009. subsequently in 2010, 2011 and 2012. GM Beggar-only Semi-Rare Treats */ - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PumpkinPizza : CheesePizza { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/GrimWarning.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/GrimWarning.cs index 9376063eb..3a5eef292 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/GrimWarning.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/GrimWarning.cs @@ -1,10 +1,12 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { /* first seen halloween 2009. subsequently in 2010, 2011 and 2012. GM Beggar-only Semi-Rare Treats */ - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GrimWarning : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/SkullsOnPike.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/SkullsOnPike.cs index e391394fc..76d7bf978 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/SkullsOnPike.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Items/SkullsOnPike.cs @@ -1,10 +1,12 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { /* first seen halloween 2009. subsequently in 2010, 2011 and 2012. GM Beggar-only Semi-Rare Treats */ - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SkullsOnPike : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ChairInAGhostCostume.cs b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ChairInAGhostCostume.cs index 73a5981b1..7b981aa3d 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ChairInAGhostCostume.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ChairInAGhostCostume.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ChairInAGhostCostume : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ColoredSmallWebs.cs b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ColoredSmallWebs.cs index d4dec3a11..33f5c0891 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ColoredSmallWebs.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ColoredSmallWebs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ColoredSmallWebs : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ExcellentIronMaiden.cs b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ExcellentIronMaiden.cs index 502eb1755..5213054b3 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ExcellentIronMaiden.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/ExcellentIronMaiden.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ExcellentIronMaiden : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/HalloweenGuillotine.cs b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/HalloweenGuillotine.cs index 6effbfe54..8eb576c92 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/HalloweenGuillotine.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2010/Items/HalloweenGuillotine.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HalloweenGuillotine : Item { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/BasePaintedMask.cs b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/BasePaintedMask.cs index 4ea4e1800..8fc991ddc 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/BasePaintedMask.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/BasePaintedMask.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.ClownMask", "Server.Items.DaemonMask", "Server.Items.PlagueMask")] public partial class BasePaintedMask : Item { diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/ClownMask.cs b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/ClownMask.cs index 6d3819aea..735285b54 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/ClownMask.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/ClownMask.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PaintedEvilClownMask : BasePaintedMask { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/DaemonMask.cs b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/DaemonMask.cs index e94e9b598..9d38be46d 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/DaemonMask.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/DaemonMask.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PaintedDaemonMask : BasePaintedMask { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/PlagueMask.cs b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/PlagueMask.cs index ff03d1bcb..320ac732d 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/PlagueMask.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2011/Items/PlagueMask.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PaintedPlagueMask : BasePaintedMask { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs b/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs index a281b34f4..954bd991d 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs @@ -1,15 +1,16 @@ using System; +using ModernUO.Serialization; using Server.Items; using Server.Items.Holiday; namespace Server.Mobiles { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PumpkinHead : BaseCreature { [Constructible] public PumpkinHead() - : base(Utility.RandomBool() ? AIType.AI_Melee : AIType.AI_Mage, FightMode.Closest, 10, 1, 0.05, 0.1) + : base(Utility.RandomBool() ? AIType.AI_Melee : AIType.AI_Mage) { Body = 1246 + Utility.Random(2); diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs b/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs index 1bc77cf3b..30e41249e 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Events.Halloween; using Server.Items; using Server.Mobiles; @@ -115,8 +116,7 @@ namespace Server.Engines.Events } var map = Utility.RandomBool() ? Map.Trammel : Map.Felucca; - - var home = GetRandomPointInRect(m_Cemetaries.RandomElement(), map); + var home = Utility.RandomPointIn(m_Cemetaries.RandomElement(), map); if (map.CanSpawnMobile(home)) { @@ -131,22 +131,13 @@ namespace Server.Engines.Events _deathQueue.Remove(player); } } - - private static Point3D GetRandomPointInRect(Rectangle2D rect, Map map) - { - var x = Utility.Random(rect.X, rect.Width); - var y = Utility.Random(rect.Y, rect.Height); - - return new Point3D(x, y, map.GetAverageZ(x, y)); - } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlayerBones : BaseContainer { [Constructible] - public PlayerBones(string name) - : base(Utility.RandomMinMax(0x0ECA, 0x0ED2)) + public PlayerBones(string name) : base(Utility.RandomMinMax(0x0ECA, 0x0ED2)) { Name = $"{name}'s bones"; @@ -160,7 +151,7 @@ namespace Server.Engines.Events } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ZombieSkeleton : BaseCreature { [SerializableField(0, "private", "private")] @@ -168,8 +159,7 @@ namespace Server.Engines.Events public override string DefaultName => _deadPlayer != null ? $"{_deadPlayer.Name}'s Zombie Skeleton" : "Zombie Skeleton"; - public ZombieSkeleton(PlayerMobile player = null) - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ZombieSkeleton(PlayerMobile player = null) : base(AIType.AI_Melee) { _deadPlayer = player; diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/EvilJesterMask.cs b/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/EvilJesterMask.cs index ac7e9d8ea..7cd77753d 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/EvilJesterMask.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/EvilJesterMask.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PaintedEvilJesterMask : BasePaintedMask { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/PorcelainMask.cs b/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/PorcelainMask.cs index 630fa6aba..8a194e968 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/PorcelainMask.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2012/Items/PorcelainMask.cs @@ -1,6 +1,8 @@ -namespace Server.Items.Holiday +using ModernUO.Serialization; + +namespace Server.Items.Holiday { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PaintedPorcelainMask : BasePaintedMask { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/Treats/Jellybeans.cs b/Projects/UOContent/Holiday Stuff/Halloween/Treats/Jellybeans.cs index 5e6d763c8..5b7279389 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/Treats/Jellybeans.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/Treats/Jellybeans.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class JellyBeans : CandyCane { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/Treats/Lollipops.cs b/Projects/UOContent/Holiday Stuff/Halloween/Treats/Lollipops.cs index a768fff99..280be39c6 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/Treats/Lollipops.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/Treats/Lollipops.cs @@ -1,7 +1,9 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { [TypeAlias("Server.Items.Lollipop")] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Lollipops : CandyCane { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/Treats/NougatSwirl.cs b/Projects/UOContent/Holiday Stuff/Halloween/Treats/NougatSwirl.cs index eb9942868..750239324 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/Treats/NougatSwirl.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/Treats/NougatSwirl.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NougatSwirl : CandyCane { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/Treats/Taffy.cs b/Projects/UOContent/Holiday Stuff/Halloween/Treats/Taffy.cs index 2b2571604..814e4a822 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/Treats/Taffy.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/Treats/Taffy.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Taffy : CandyCane { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Halloween/Treats/WrappedCandy.cs b/Projects/UOContent/Holiday Stuff/Halloween/Treats/WrappedCandy.cs index eb86ac43d..8df86c688 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/Treats/WrappedCandy.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/Treats/WrappedCandy.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WrappedCandy : CandyCane { [Constructible] diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2010/Items/AnimatedHeartShapedBox.cs b/Projects/UOContent/Holiday Stuff/Valentine/2010/Items/AnimatedHeartShapedBox.cs index e9e320e0c..3429c0af4 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2010/Items/AnimatedHeartShapedBox.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2010/Items/AnimatedHeartShapedBox.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x49CC, 0x49D0)] public partial class AnimatedHeartShapedBox : HeartShapedBox { diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs b/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs index 119b167fa..ad60231bd 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs @@ -1,10 +1,11 @@ using System; +using ModernUO.Serialization; using Server.Gumps; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class StValentinesBear : Item { [InternString] @@ -46,7 +47,7 @@ namespace Server.Items public bool CanSign => !IsSigned || Core.Now <= EditLimit; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (_owner != null) { @@ -62,7 +63,7 @@ namespace Server.Items AddLine(list, 1150303, _line3); // [ ~1_LINE2~ ] } - private static void AddLine(ObjectPropertyList list, int cliloc, string line) + private static void AddLine(IPropertyList list, int cliloc, string line) { if (line != null) { @@ -190,7 +191,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x48E0, 0x48E1)] public partial class StValentinesPanda : StValentinesBear { @@ -200,7 +201,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x48E2, 0x48E3)] public partial class StValentinesPolarBear : StValentinesBear { diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidStatue.cs b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidStatue.cs index a86ba5a56..20a11413a 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidStatue.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidStatue.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x4F7C, 0x4F7D)] public partial class CupidStatue : Item { diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs index 184c9cec5..3c292ceda 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CupidsArrow : Item { [InternString] @@ -26,7 +27,7 @@ namespace Server.Items public bool IsSigned => _from != null && _to != null; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { base.AddNameProperty(list); diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/HeartShapedBox.cs b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/HeartShapedBox.cs index 5f749777f..5882a3780 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/HeartShapedBox.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/HeartShapedBox.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x49CA, 0x49CB)] public partial class HeartShapedBox : BaseContainer { diff --git a/Projects/UOContent/Items/Addons/AbbatoirAddon.cs b/Projects/UOContent/Items/Addons/AbbatoirAddon.cs index 19db8af81..a3c1979ac 100644 --- a/Projects/UOContent/Items/Addons/AbbatoirAddon.cs +++ b/Projects/UOContent/Items/Addons/AbbatoirAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AbbatoirAddon : BaseAddon { [Constructible] @@ -20,7 +22,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new AbbatoirDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AbbatoirDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/AddonComponent.cs b/Projects/UOContent/Items/Addons/AddonComponent.cs index 90d74dbc0..f07f3da2e 100644 --- a/Projects/UOContent/Items/Addons/AddonComponent.cs +++ b/Projects/UOContent/Items/Addons/AddonComponent.cs @@ -1,9 +1,10 @@ +using ModernUO.Serialization; using Server.Engines.Craft; namespace Server.Items { [Anvil] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnvilComponent : AddonComponent { [Constructible] @@ -13,7 +14,7 @@ namespace Server.Items } [Forge] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ForgeComponent : AddonComponent { [Constructible] @@ -22,7 +23,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LocalizedAddonComponent : AddonComponent { [InvalidateProperties] @@ -36,7 +37,7 @@ namespace Server.Items public override int LabelNumber => _number; } - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class AddonComponent : Item, IChoppable { private static readonly LightEntry[] m_Entries = @@ -159,7 +160,7 @@ namespace Server.Items } } /* - public override void GetProperties(ObjectPropertyList list) => _addon?.GetProperties(list); + public override void GetProperties(IPropertyList list) => _addon?.GetProperties(list); public override void GetContextMenuEntries(Mobile from, List list) => _addon?.GetContextMenuEntries(from, list); diff --git a/Projects/UOContent/Items/Addons/AddonContainerComponent.cs b/Projects/UOContent/Items/Addons/AddonContainerComponent.cs index 509d430ea..6e7bf4b08 100644 --- a/Projects/UOContent/Items/Addons/AddonContainerComponent.cs +++ b/Projects/UOContent/Items/Addons/AddonContainerComponent.cs @@ -1,12 +1,13 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AddonContainerComponent : Item, IChoppable { [Constructible] @@ -76,7 +77,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) => _addon?.GetProperties(list); + public override void GetProperties(IPropertyList list) => _addon?.GetProperties(list); public override void GetContextMenuEntries(Mobile from, List list) => _addon?.GetContextMenuEntries(from, list); @@ -107,7 +108,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LocalizedContainerComponent : AddonContainerComponent { [SerializableField(0, setter: "private")] diff --git a/Projects/UOContent/Items/Addons/AlchemistTableEastAddon.cs b/Projects/UOContent/Items/Addons/AlchemistTableEastAddon.cs index a530b0d33..30492ddae 100644 --- a/Projects/UOContent/Items/Addons/AlchemistTableEastAddon.cs +++ b/Projects/UOContent/Items/Addons/AlchemistTableEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class AlchemistTableEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new AlchemistTableEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class AlchemistTableEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/AlchemistTableSouthAddon.cs b/Projects/UOContent/Items/Addons/AlchemistTableSouthAddon.cs index 8dabc4335..4f667d664 100644 --- a/Projects/UOContent/Items/Addons/AlchemistTableSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/AlchemistTableSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class AlchemistTableSouthAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new AlchemistTableSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class AlchemistTableSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/AnvilEastAddon.cs b/Projects/UOContent/Items/Addons/AnvilEastAddon.cs index 22d9cd232..bad7b1e1f 100644 --- a/Projects/UOContent/Items/Addons/AnvilEastAddon.cs +++ b/Projects/UOContent/Items/Addons/AnvilEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnvilEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new AnvilEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnvilEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/AnvilSouthAddon.cs b/Projects/UOContent/Items/Addons/AnvilSouthAddon.cs index fd5383a7c..a1495d3f5 100644 --- a/Projects/UOContent/Items/Addons/AnvilSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/AnvilSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnvilSouthAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new AnvilSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnvilSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs b/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs index ba197ed84..224d919d5 100644 --- a/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcaneBookshelfEastAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x42; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcaneBookshelfEastDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs b/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs index 4a9569f0d..c034d992d 100644 --- a/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcaneBookshelfSouthAddon : BaseAddonContainer { [Constructible] @@ -16,7 +18,7 @@ namespace Server.Items public override int LabelNumber => 1032420; // arcane bookshelf } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcaneBookshelfSouthDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ArcaneCircleAddon.cs b/Projects/UOContent/Items/Addons/ArcaneCircleAddon.cs index dd5b27f0b..931b506ae 100644 --- a/Projects/UOContent/Items/Addons/ArcaneCircleAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcaneCircleAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(1)] + [SerializationGenerator(1)] public partial class ArcaneCircleAddon : BaseAddon { [Constructible] @@ -40,7 +42,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcaneCircleDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ArcanistStatueEastAddon.cs b/Projects/UOContent/Items/Addons/ArcanistStatueEastAddon.cs index a41083159..8c8c3c0f9 100644 --- a/Projects/UOContent/Items/Addons/ArcanistStatueEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcanistStatueEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcanistStatueEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ArcanistStatueEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcanistStatueEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ArcanistStatueSouthAddon.cs b/Projects/UOContent/Items/Addons/ArcanistStatueSouthAddon.cs index 27e19a4e2..1cfa9f277 100644 --- a/Projects/UOContent/Items/Addons/ArcanistStatueSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcanistStatueSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcanistStatueSouthAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ArcanistStatueSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcanistStatueSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ArcheryButteAddon.cs b/Projects/UOContent/Items/Addons/ArcheryButteAddon.cs index 6f417235e..5834f8bb7 100644 --- a/Projects/UOContent/Items/Addons/ArcheryButteAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcheryButteAddon.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [FlippableAttribute(0x100A /*East*/, 0x100B /*South*/)] public partial class ArcheryButte : AddonComponent { @@ -47,11 +48,12 @@ namespace Server.Items public override void OnDoubleClick(Mobile from) { - if ((_arrows > 0 || _bolts > 0) && from.InRange(GetWorldLocation(), 1)) + var inRange = from.InRange(GetWorldLocation(), 1); + if ((_arrows > 0 || _bolts > 0) && inRange) { Gather(from); } - else + else if (from.Weapon is not BaseThrown || inRange) { Fire(from); } @@ -293,7 +295,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ArcheryButteAddon : BaseAddon { [Constructible] @@ -305,7 +307,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ArcheryButteDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ArcheryButteDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/BallotBox.cs b/Projects/UOContent/Items/Addons/BallotBox.cs index a952c0dfe..cc9a6b99d 100644 --- a/Projects/UOContent/Items/Addons/BallotBox.cs +++ b/Projects/UOContent/Items/Addons/BallotBox.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Gumps; using Server.Multis; using Server.Network; @@ -7,7 +8,7 @@ using Server.Prompts; namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class BallotBox : AddonComponent { public static readonly int MaxTopicLines = 6; @@ -312,7 +313,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class BallotBoxAddon : BaseAddon { public BallotBoxAddon() @@ -321,7 +322,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class BallotBoxDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/BaseAddon.cs b/Projects/UOContent/Items/Addons/BaseAddon.cs index a0507e8cb..f7fbd61c7 100644 --- a/Projects/UOContent/Items/Addons/BaseAddon.cs +++ b/Projects/UOContent/Items/Addons/BaseAddon.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.Multis; namespace Server.Items @@ -20,7 +21,7 @@ namespace Server.Items bool CouldFit(IPoint3D p, Map map); } - [Serializable(3, false)] + [SerializationGenerator(3, false)] public abstract partial class BaseAddon : Item, IChoppable, IAddon { [SerializableField(1, "private", "private")] diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs index 976a6fc00..3cf06af23 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.Multis; namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] public abstract partial class BaseAddonContainer : BaseContainer, IChoppable, IAddon { [SerializableField(0, setter: "private")] @@ -167,7 +168,7 @@ namespace Server.Items base.OnDelete(); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs index dc751fe0a..17c9fba16 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs @@ -97,7 +97,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Addons/BaseAddonDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonDeed.cs index 3313140fa..49977289b 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonDeed.cs @@ -1,10 +1,11 @@ +using ModernUO.Serialization; using Server.Multis; using Server.Spells; using Server.Targeting; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x14F0, 0x14EF)] public abstract partial class BaseAddonDeed : Item { diff --git a/Projects/UOContent/Items/Addons/BearRugs.cs b/Projects/UOContent/Items/Addons/BearRugs.cs index 20adc3297..d259305cd 100644 --- a/Projects/UOContent/Items/Addons/BearRugs.cs +++ b/Projects/UOContent/Items/Addons/BearRugs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrownBearRugEastAddon : BaseAddon { [Constructible] @@ -20,7 +22,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new BrownBearRugEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrownBearRugEastDeed : BaseAddonDeed { [Constructible] @@ -32,7 +34,7 @@ namespace Server.Items public override int LabelNumber => 1049397; // a brown bear rug deed facing east } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrownBearRugSouthAddon : BaseAddon { [Constructible] @@ -52,7 +54,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new BrownBearRugSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrownBearRugSouthDeed : BaseAddonDeed { [Constructible] @@ -64,7 +66,7 @@ namespace Server.Items public override int LabelNumber => 1049398; // a brown bear rug deed facing south } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PolarBearRugEastAddon : BaseAddon { [Constructible] @@ -84,7 +86,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new PolarBearRugEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PolarBearRugEastDeed : BaseAddonDeed { [Constructible] @@ -96,7 +98,7 @@ namespace Server.Items public override int LabelNumber => 1049399; // a polar bear rug deed facing east } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PolarBearRugSouthAddon : BaseAddon { [Constructible] @@ -116,7 +118,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new PolarBearRugSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PolarBearRugSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/BloodPentagram.cs b/Projects/UOContent/Items/Addons/BloodPentagram.cs index cbe6f1857..e6e6d2f04 100644 --- a/Projects/UOContent/Items/Addons/BloodPentagram.cs +++ b/Projects/UOContent/Items/Addons/BloodPentagram.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BloodPentagram : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/DartBoard.cs b/Projects/UOContent/Items/Addons/DartBoard.cs index 18333c337..71f973ae6 100644 --- a/Projects/UOContent/Items/Addons/DartBoard.cs +++ b/Projects/UOContent/Items/Addons/DartBoard.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Network; namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class DartBoard : AddonComponent { [Constructible] @@ -86,7 +87,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DartBoardEastAddon : BaseAddon { public DartBoardEastAddon() @@ -97,7 +98,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new DartBoardEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DartBoardEastDeed : BaseAddonDeed { [Constructible] @@ -110,7 +111,7 @@ namespace Server.Items public override int LabelNumber => 1044326; // dartboard (east) } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DartBoardSouthAddon : BaseAddon { public DartBoardSouthAddon() @@ -121,7 +122,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new DartBoardSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DartBoardSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenBedEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenBedEastAddon.cs index 69dd2c529..507282b2c 100644 --- a/Projects/UOContent/Items/Addons/ElvenBedEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenBedEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenBedEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenBedEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenBedEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenBedSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenBedSouthAddon.cs index 96dc0d99d..5f53debec 100644 --- a/Projects/UOContent/Items/Addons/ElvenBedSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenBedSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenBedSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenBedSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenBedSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs index 1bf3bebd2..642021c39 100644 --- a/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenDresserEastAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x42; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenDresserEastDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs index c49be3673..9820186c7 100644 --- a/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenDresserSouthAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x42; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenDresserSouthDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs b/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs index 52dd5add9..a3ffc8be5 100644 --- a/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenForgeAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenForgeDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenForgeDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs index cbd01379c..04c6695dc 100644 --- a/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenLoveseatEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenLoveseatEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenLoveseatEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs index 879ca82ce..29b9056da 100644 --- a/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenLoveseatSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenLoveseatSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenLoveseatSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs index fe27635e8..e6958aed2 100644 --- a/Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs @@ -1,4 +1,5 @@ using System; +using ModernUO.Serialization; namespace Server.Items { @@ -10,7 +11,7 @@ namespace Server.Items void BeginSpin(SpinCallback callback, Mobile from, int hue); } - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.ElvenSpinningwheelEastAddon")] public partial class ElvenSpinningWheelEastAddon : BaseAddon, ISpinningWheel { @@ -99,7 +100,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.ElvenSpinningwheelEastDeed")] public partial class ElvenSpinningWheelEastDeed : BaseAddonDeed { diff --git a/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs index e737c3d0f..8d99ba515 100644 --- a/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.ElvenSpinningwheelSouthAddon")] public partial class ElvenSpinningWheelSouthAddon : BaseAddon, ISpinningWheel { @@ -90,7 +91,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.ElvenSpinningwheelSouthDeed")] public partial class ElvenSpinningWheelSouthDeed : BaseAddonDeed { diff --git a/Projects/UOContent/Items/Addons/ElvenStoveEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenStoveEastAddon.cs index 687989ffa..cdd58cf90 100644 --- a/Projects/UOContent/Items/Addons/ElvenStoveEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenStoveEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenStoveEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenStoveEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenStoveEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenStoveSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenStoveSouthAddon.cs index d63db83fe..aa71d591b 100644 --- a/Projects/UOContent/Items/Addons/ElvenStoveSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenStoveSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenStoveSouthAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ElvenStoveSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenStoveSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs index a47e6dbbb..986a19a3b 100644 --- a/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenWashBasinEastAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x0042; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenWashBasinEastDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs index a46b37fcc..1358fea17 100644 --- a/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenWashBasinSouthAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x0042; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenWashBasinSouthDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/FancyElvenTableEastAddon.cs b/Projects/UOContent/Items/Addons/FancyElvenTableEastAddon.cs index e31e67f22..6528ea581 100644 --- a/Projects/UOContent/Items/Addons/FancyElvenTableEastAddon.cs +++ b/Projects/UOContent/Items/Addons/FancyElvenTableEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class FancyElvenTableEastAddon : BaseAddon { [Constructible] @@ -14,7 +16,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new FancyElvenTableEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class FancyElvenTableEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/FancyElvenTableSouthAddon.cs b/Projects/UOContent/Items/Addons/FancyElvenTableSouthAddon.cs index 7bed9dcf7..72886fcca 100644 --- a/Projects/UOContent/Items/Addons/FancyElvenTableSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/FancyElvenTableSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class FancyElvenTableSouthAddon : BaseAddon { [Constructible] @@ -14,7 +16,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new FancyElvenTableSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class FancyElvenTableSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/FireColumnAddon.cs b/Projects/UOContent/Items/Addons/FireColumnAddon.cs index c3672ea98..e312066ca 100644 --- a/Projects/UOContent/Items/Addons/FireColumnAddon.cs +++ b/Projects/UOContent/Items/Addons/FireColumnAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FireColumnAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs index b729fd392..1fd8c3072 100644 --- a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs @@ -1,4 +1,5 @@ using System; +using ModernUO.Serialization; using Server.Network; namespace Server.Items @@ -16,7 +17,7 @@ namespace Server.Items Working } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FlourMillEastAddon : BaseAddon, IFlourMill { private static readonly int[][] m_StageTable = @@ -171,7 +172,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FlourMillEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs index f5bb4ab63..cfd7c7c72 100644 --- a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FlourMillSouthAddon : BaseAddon, IFlourMill { private static readonly int[][] m_StageTable = @@ -159,7 +160,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FlourMillSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/FlowerTapestries.cs b/Projects/UOContent/Items/Addons/FlowerTapestries.cs index 29b655295..a67b8ea15 100644 --- a/Projects/UOContent/Items/Addons/FlowerTapestries.cs +++ b/Projects/UOContent/Items/Addons/FlowerTapestries.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightFlowerTapestryEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LightFlowerTapestryEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightFlowerTapestryEastDeed : BaseAddonDeed { [Constructible] @@ -25,7 +27,7 @@ namespace Server.Items public override int LabelNumber => 1049393; // a flower tapestry deed facing east } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightFlowerTapestrySouthAddon : BaseAddon { [Constructible] @@ -38,7 +40,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LightFlowerTapestrySouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightFlowerTapestrySouthDeed : BaseAddonDeed { [Constructible] @@ -50,7 +52,7 @@ namespace Server.Items public override int LabelNumber => 1049394; // a flower tapestry deed facing south } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkFlowerTapestryEastAddon : BaseAddon { [Constructible] @@ -63,7 +65,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new DarkFlowerTapestryEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkFlowerTapestryEastDeed : BaseAddonDeed { [Constructible] @@ -75,7 +77,7 @@ namespace Server.Items public override int LabelNumber => 1049395; // a dark flower tapestry deed facing east } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkFlowerTapestrySouthAddon : BaseAddon { [Constructible] @@ -88,7 +90,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new DarkFlowerTapestrySouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkFlowerTapestrySouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/GiantWebs.cs b/Projects/UOContent/Items/Addons/GiantWebs.cs index bf0160eb8..d766c51e6 100644 --- a/Projects/UOContent/Items/Addons/GiantWebs.cs +++ b/Projects/UOContent/Items/Addons/GiantWebs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantWeb1 : BaseAddon { [Constructible] @@ -21,7 +23,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantWeb2 : BaseAddon { [Constructible] @@ -42,7 +44,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantWeb3 : BaseAddon { [Constructible] @@ -63,7 +65,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantWeb4 : BaseAddon { [Constructible] @@ -84,7 +86,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantWeb5 : BaseAddon { [Constructible] @@ -105,7 +107,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantWeb6 : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/GozaMats.cs b/Projects/UOContent/Items/Addons/GozaMats.cs index 3127f367a..6d199c110 100644 --- a/Projects/UOContent/Items/Addons/GozaMats.cs +++ b/Projects/UOContent/Items/Addons/GozaMats.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GozaMatEastAddon : BaseAddon { [Constructible] @@ -16,7 +18,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GozaMatEastDeed : BaseAddonDeed { [Constructible] @@ -28,7 +30,7 @@ namespace Server.Items public override int LabelNumber => 1030404; // goza (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GozaMatSouthAddon : BaseAddon { [Constructible] @@ -44,7 +46,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GozaMatSouthDeed : BaseAddonDeed { [Constructible] @@ -56,7 +58,7 @@ namespace Server.Items public override int LabelNumber => 1030405; // goza (south) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SquareGozaMatEastAddon : BaseAddon { [Constructible] @@ -72,7 +74,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SquareGozaMatEastDeed : BaseAddonDeed { [Constructible] @@ -84,7 +86,7 @@ namespace Server.Items public override int LabelNumber => 1030407; // square goza (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SquareGozaMatSouthAddon : BaseAddon { [Constructible] @@ -99,7 +101,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SquareGozaMatSouthDeed : BaseAddonDeed { [Constructible] @@ -111,7 +113,7 @@ namespace Server.Items public override int LabelNumber => 1030406; // square goza (south) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeGozaMatEastAddon : BaseAddon { [Constructible] @@ -127,7 +129,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeGozaMatEastDeed : BaseAddonDeed { [Constructible] @@ -139,7 +141,7 @@ namespace Server.Items public override int LabelNumber => 1030408; // brocade goza (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeGozaMatSouthAddon : BaseAddon { [Constructible] @@ -155,7 +157,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeGozaMatSouthDeed : BaseAddonDeed { [Constructible] @@ -167,7 +169,7 @@ namespace Server.Items public override int LabelNumber => 1030409; // brocade goza (south) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeSquareGozaMatEastAddon : BaseAddon { [Constructible] @@ -182,7 +184,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeSquareGozaMatEastDeed : BaseAddonDeed { [Constructible] @@ -194,7 +196,7 @@ namespace Server.Items public override int LabelNumber => 1030411; // brocade square goza (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeSquareGozaMatSouthAddon : BaseAddon { [Constructible] @@ -209,7 +211,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrocadeSquareGozaMatSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/GrayBrickFireplaceEastAddon.cs b/Projects/UOContent/Items/Addons/GrayBrickFireplaceEastAddon.cs index 2567bccc4..f8500eabf 100644 --- a/Projects/UOContent/Items/Addons/GrayBrickFireplaceEastAddon.cs +++ b/Projects/UOContent/Items/Addons/GrayBrickFireplaceEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GrayBrickFireplaceEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new GrayBrickFireplaceEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GrayBrickFireplaceEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/GrayBrickFireplaceSouthAddon.cs b/Projects/UOContent/Items/Addons/GrayBrickFireplaceSouthAddon.cs index 50fdee87e..792f50cb0 100644 --- a/Projects/UOContent/Items/Addons/GrayBrickFireplaceSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/GrayBrickFireplaceSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GrayBrickFireplaceSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new GrayBrickFireplaceSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GrayBrickFireplaceSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/JackOLantern.cs b/Projects/UOContent/Items/Addons/JackOLantern.cs index 0ff84fb93..c2f13b753 100644 --- a/Projects/UOContent/Items/Addons/JackOLantern.cs +++ b/Projects/UOContent/Items/Addons/JackOLantern.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class JackOLantern : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LargeBedEastAddon.cs b/Projects/UOContent/Items/Addons/LargeBedEastAddon.cs index e77a27d52..4fc2b7ad3 100644 --- a/Projects/UOContent/Items/Addons/LargeBedEastAddon.cs +++ b/Projects/UOContent/Items/Addons/LargeBedEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeBedEastAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LargeBedSouthAddon.cs b/Projects/UOContent/Items/Addons/LargeBedSouthAddon.cs index 328702270..b7c48baf5 100644 --- a/Projects/UOContent/Items/Addons/LargeBedSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/LargeBedSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeBedSouthAddon : BaseAddon { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LargeBedSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeBedSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LargeForgeEastAddon.cs b/Projects/UOContent/Items/Addons/LargeForgeEastAddon.cs index 197002acc..c3ebe12a6 100644 --- a/Projects/UOContent/Items/Addons/LargeForgeEastAddon.cs +++ b/Projects/UOContent/Items/Addons/LargeForgeEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeForgeEastAddon : BaseAddon { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LargeForgeEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeForgeEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LargeForgeSouthAddon.cs b/Projects/UOContent/Items/Addons/LargeForgeSouthAddon.cs index 1c9885c56..761c804dd 100644 --- a/Projects/UOContent/Items/Addons/LargeForgeSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/LargeForgeSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeForgeSouthAddon : BaseAddon { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LargeForgeSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeForgeSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LargeStoneTableEastAddon.cs b/Projects/UOContent/Items/Addons/LargeStoneTableEastAddon.cs index 20f652234..67568d247 100644 --- a/Projects/UOContent/Items/Addons/LargeStoneTableEastAddon.cs +++ b/Projects/UOContent/Items/Addons/LargeStoneTableEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeStoneTableEastAddon : BaseAddon { [Constructible] @@ -16,7 +18,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeStoneTableEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LargeStoneTableSouthAddon.cs b/Projects/UOContent/Items/Addons/LargeStoneTableSouthAddon.cs index 79ae3fc18..78e5f0e91 100644 --- a/Projects/UOContent/Items/Addons/LargeStoneTableSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/LargeStoneTableSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeStoneTableSouthAddon : BaseAddon { [Constructible] @@ -17,7 +19,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeStoneTableSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LoomEastAddon.cs b/Projects/UOContent/Items/Addons/LoomEastAddon.cs index 034cdbf1c..db3af2a52 100644 --- a/Projects/UOContent/Items/Addons/LoomEastAddon.cs +++ b/Projects/UOContent/Items/Addons/LoomEastAddon.cs @@ -1,3 +1,5 @@ +using ModernUO.Serialization; + namespace Server.Items { public interface ILoom @@ -5,7 +7,7 @@ namespace Server.Items int Phase { get; set; } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LoomEastAddon : BaseAddon, ILoom { [SerializableField(0)] @@ -21,7 +23,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LoomEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LoomEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/LoomSouthAddon.cs b/Projects/UOContent/Items/Addons/LoomSouthAddon.cs index 596ff2170..9ec8f5595 100644 --- a/Projects/UOContent/Items/Addons/LoomSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/LoomSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LoomSouthAddon : BaseAddon, ILoom { [SerializableField(0)] @@ -16,7 +18,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new LoomSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LoomSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/MediumStoneTableEastAddon.cs b/Projects/UOContent/Items/Addons/MediumStoneTableEastAddon.cs index 91f1807f9..dd20cdde4 100644 --- a/Projects/UOContent/Items/Addons/MediumStoneTableEastAddon.cs +++ b/Projects/UOContent/Items/Addons/MediumStoneTableEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStoneTableEastAddon : BaseAddon { [Constructible] @@ -16,7 +18,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStoneTableEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/MediumStoneTableSouthAddon.cs b/Projects/UOContent/Items/Addons/MediumStoneTableSouthAddon.cs index 4eb79450b..09e4f4518 100644 --- a/Projects/UOContent/Items/Addons/MediumStoneTableSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/MediumStoneTableSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStoneTableSouthAddon : BaseAddon { [Constructible] @@ -16,7 +18,7 @@ namespace Server.Items public override bool RetainDeedHue => true; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStoneTableSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs b/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs index f936d0da7..066ca2870 100644 --- a/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs +++ b/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenChestEastAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x42; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenChestEastDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs b/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs index 109433111..cc0dc3f85 100644 --- a/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenChestSouthAddon : BaseAddonContainer { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override int DefaultDropSound => 0x42; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenChestSouthDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/OrnateElvenTableEastAddon.cs b/Projects/UOContent/Items/Addons/OrnateElvenTableEastAddon.cs index 8328eb52f..0401e04bd 100644 --- a/Projects/UOContent/Items/Addons/OrnateElvenTableEastAddon.cs +++ b/Projects/UOContent/Items/Addons/OrnateElvenTableEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenTableEastAddon : BaseAddon { [Constructible] @@ -14,7 +16,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new OrnateElvenTableEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenTableEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/OrnateElvenTableSouthAddon.cs b/Projects/UOContent/Items/Addons/OrnateElvenTableSouthAddon.cs index 04f3553a4..14f65f529 100644 --- a/Projects/UOContent/Items/Addons/OrnateElvenTableSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/OrnateElvenTableSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenTableSouthAddon : BaseAddon { [Constructible] @@ -14,7 +16,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new OrnateElvenTableSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenTableSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ParrotPerchAddon.cs b/Projects/UOContent/Items/Addons/ParrotPerchAddon.cs index 94278a0a8..5629d8c36 100644 --- a/Projects/UOContent/Items/Addons/ParrotPerchAddon.cs +++ b/Projects/UOContent/Items/Addons/ParrotPerchAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ParrotPerchAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new ParrotPerchDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ParrotPerchDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/PentagramAddon.cs b/Projects/UOContent/Items/Addons/PentagramAddon.cs index 7190bb289..99777409d 100644 --- a/Projects/UOContent/Items/Addons/PentagramAddon.cs +++ b/Projects/UOContent/Items/Addons/PentagramAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PentagramAddon : BaseAddon { [Constructible] @@ -20,7 +22,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new PentagramDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PentagramDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/PickpocketDips.cs b/Projects/UOContent/Items/Addons/PickpocketDips.cs index aa20d1ef6..c990ff7d4 100644 --- a/Projects/UOContent/Items/Addons/PickpocketDips.cs +++ b/Projects/UOContent/Items/Addons/PickpocketDips.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1EC0, 0x1EC3)] public partial class PickpocketDip : AddonComponent { @@ -118,7 +119,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PickpocketDipEastAddon : BaseAddon { [Constructible] @@ -128,7 +129,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PickpocketDipEastDeed : BaseAddonDeed { [Constructible] @@ -140,7 +141,7 @@ namespace Server.Items public override int LabelNumber => 1044337; // pickpocket dip (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PickpocketDipSouthAddon : BaseAddon { [Constructible] @@ -152,7 +153,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new PickpocketDipSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PickpocketDipSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/PyramidAddon.cs b/Projects/UOContent/Items/Addons/PyramidAddon.cs index 4133f9c86..f717d576d 100644 --- a/Projects/UOContent/Items/Addons/PyramidAddon.cs +++ b/Projects/UOContent/Items/Addons/PyramidAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class PyramidAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs b/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs index e8e059cf1..fa6b00c95 100644 --- a/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs +++ b/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RejuvinationAddonComponent : AddonComponent { public RejuvinationAddonComponent(int itemID) : base(itemID) @@ -51,7 +52,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseRejuvinationAnkh : BaseAddon { private DateTime m_NextMessage; @@ -79,7 +80,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RejuvinationAnkhWest : BaseRejuvinationAnkh { [Constructible] @@ -90,7 +91,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RejuvinationAnkhNorth : BaseRejuvinationAnkh { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SHTeleporter.cs b/Projects/UOContent/Items/Addons/SHTeleporter.cs index cdca627cc..7c56698dd 100644 --- a/Projects/UOContent/Items/Addons/SHTeleporter.cs +++ b/Projects/UOContent/Items/Addons/SHTeleporter.cs @@ -1,9 +1,10 @@ using System.Linq; +using ModernUO.Serialization; using Server.Mobiles; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SHTeleComponent : AddonComponent { private bool _active; @@ -105,7 +106,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SHTeleporter : BaseAddon { private bool m_Changing; diff --git a/Projects/UOContent/Items/Addons/SandstoneFireplaceEastAddon.cs b/Projects/UOContent/Items/Addons/SandstoneFireplaceEastAddon.cs index 0c692b860..cdd44ad82 100644 --- a/Projects/UOContent/Items/Addons/SandstoneFireplaceEastAddon.cs +++ b/Projects/UOContent/Items/Addons/SandstoneFireplaceEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SandstoneFireplaceEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SandstoneFireplaceEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SandstoneFireplaceEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SandstoneFireplaceSouthAddon.cs b/Projects/UOContent/Items/Addons/SandstoneFireplaceSouthAddon.cs index 72cc30e26..888b7baa6 100644 --- a/Projects/UOContent/Items/Addons/SandstoneFireplaceSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/SandstoneFireplaceSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SandstoneFireplaceSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SandstoneFireplaceSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SandstoneFireplaceSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SandstoneFountainAddon.cs b/Projects/UOContent/Items/Addons/SandstoneFountainAddon.cs index 34b764788..38189257d 100644 --- a/Projects/UOContent/Items/Addons/SandstoneFountainAddon.cs +++ b/Projects/UOContent/Items/Addons/SandstoneFountainAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SandstoneFountainAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SerpentPillarAddon.cs b/Projects/UOContent/Items/Addons/SerpentPillarAddon.cs index 7170a56e3..da1c4f065 100644 --- a/Projects/UOContent/Items/Addons/SerpentPillarAddon.cs +++ b/Projects/UOContent/Items/Addons/SerpentPillarAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SerpentPillarAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/ShrineOfWisdomAddon.cs b/Projects/UOContent/Items/Addons/ShrineOfWisdomAddon.cs index 81703f6de..5f68ef9bd 100644 --- a/Projects/UOContent/Items/Addons/ShrineOfWisdomAddon.cs +++ b/Projects/UOContent/Items/Addons/ShrineOfWisdomAddon.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.Craft; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShrineOfWisdomAddon : BaseAddon { [Constructible] @@ -17,7 +18,7 @@ namespace Server.Items } [Forge] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShrineOfWisdomComponent : AddonComponent { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SkullPileAddon.cs b/Projects/UOContent/Items/Addons/SkullPileAddon.cs index aa07f9fb2..732a01ffa 100644 --- a/Projects/UOContent/Items/Addons/SkullPileAddon.cs +++ b/Projects/UOContent/Items/Addons/SkullPileAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SkullPileAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SmallBedEastAddon.cs b/Projects/UOContent/Items/Addons/SmallBedEastAddon.cs index c0adb5eaf..0474f0684 100644 --- a/Projects/UOContent/Items/Addons/SmallBedEastAddon.cs +++ b/Projects/UOContent/Items/Addons/SmallBedEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallBedEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SmallBedEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallBedEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SmallBedSouthAddon.cs b/Projects/UOContent/Items/Addons/SmallBedSouthAddon.cs index c133bfbb1..b53288984 100644 --- a/Projects/UOContent/Items/Addons/SmallBedSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/SmallBedSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallBedSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SmallBedSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallBedSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SmallForgeAddon.cs b/Projects/UOContent/Items/Addons/SmallForgeAddon.cs index ebf4ae75a..ad9aedd6a 100644 --- a/Projects/UOContent/Items/Addons/SmallForgeAddon.cs +++ b/Projects/UOContent/Items/Addons/SmallForgeAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallForgeAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SmallForgeDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallForgeDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SolenAntHole.cs b/Projects/UOContent/Items/Addons/SolenAntHole.cs index 00209f6bc..9cd1a469f 100644 --- a/Projects/UOContent/Items/Addons/SolenAntHole.cs +++ b/Projects/UOContent/Items/Addons/SolenAntHole.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.Mobiles; using Server.Network; using Server.Spells; namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SolenAntHoleComponent : AddonComponent { public SolenAntHoleComponent(int itemID) : base(itemID) @@ -36,7 +37,7 @@ namespace Server.Items } } - [Serializable(1)] + [SerializationGenerator(1)] public partial class SolenAntHole : BaseAddon { [SerializableField(0, getter: "private", setter: "private")] diff --git a/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs b/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs index 7f3803ae1..9cdb98041 100644 --- a/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs +++ b/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.SpinningwheelEastAddon")] public partial class SpinningWheelEastAddon : BaseAddon, ISpinningWheel { @@ -97,7 +98,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.SpinningwheelEastDeed")] public partial class SpinningWheelEastDeed : BaseAddonDeed { diff --git a/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs b/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs index 062e7eccf..6ee83298e 100644 --- a/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.SpinningwheelSouthAddon")] public partial class SpinningWheelSouthAddon : BaseAddon, ISpinningWheel { @@ -97,7 +98,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.SpinningwheelSouthDeed")] public partial class SpinningWheelSouthDeed : BaseAddonDeed { diff --git a/Projects/UOContent/Items/Addons/SquirrelStatueEastAddon.cs b/Projects/UOContent/Items/Addons/SquirrelStatueEastAddon.cs index f81c70973..19f958d99 100644 --- a/Projects/UOContent/Items/Addons/SquirrelStatueEastAddon.cs +++ b/Projects/UOContent/Items/Addons/SquirrelStatueEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SquirrelStatueEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SquirrelStatueEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class SquirrelStatueEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/SquirrelStatueSouthAddon.cs b/Projects/UOContent/Items/Addons/SquirrelStatueSouthAddon.cs index 3429b1404..cacef3870 100644 --- a/Projects/UOContent/Items/Addons/SquirrelStatueSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/SquirrelStatueSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SquirrelStatueSouthAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SquirrelStatueSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class SquirrelStatueSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/StoneFireplaceEastAddon.cs b/Projects/UOContent/Items/Addons/StoneFireplaceEastAddon.cs index e4b7284ee..363227d55 100644 --- a/Projects/UOContent/Items/Addons/StoneFireplaceEastAddon.cs +++ b/Projects/UOContent/Items/Addons/StoneFireplaceEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneFireplaceEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new StoneFireplaceEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneFireplaceEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/StoneFireplaceSouthAddon.cs b/Projects/UOContent/Items/Addons/StoneFireplaceSouthAddon.cs index 7b10c6972..1116ac86b 100644 --- a/Projects/UOContent/Items/Addons/StoneFireplaceSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/StoneFireplaceSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneFireplaceSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new StoneFireplaceSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneFireplaceSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/StoneFountainAddon.cs b/Projects/UOContent/Items/Addons/StoneFountainAddon.cs index f4307c4c7..31189f654 100644 --- a/Projects/UOContent/Items/Addons/StoneFountainAddon.cs +++ b/Projects/UOContent/Items/Addons/StoneFountainAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneFountainAddon : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/StoneOvenEastAddon.cs b/Projects/UOContent/Items/Addons/StoneOvenEastAddon.cs index b160916da..06378063a 100644 --- a/Projects/UOContent/Items/Addons/StoneOvenEastAddon.cs +++ b/Projects/UOContent/Items/Addons/StoneOvenEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneOvenEastAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new StoneOvenEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneOvenEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/StoneOvenSouthAddon.cs b/Projects/UOContent/Items/Addons/StoneOvenSouthAddon.cs index 5f54e0f1e..afe43d098 100644 --- a/Projects/UOContent/Items/Addons/StoneOvenSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/StoneOvenSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneOvenSouthAddon : BaseAddon { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new StoneOvenSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneOvenSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/StretchedHides.cs b/Projects/UOContent/Items/Addons/StretchedHides.cs index e6a8f9aff..6911e1324 100644 --- a/Projects/UOContent/Items/Addons/StretchedHides.cs +++ b/Projects/UOContent/Items/Addons/StretchedHides.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallStretchedHideEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SmallStretchedHideEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallStretchedHideEastDeed : BaseAddonDeed { [Constructible] @@ -24,7 +26,7 @@ namespace Server.Items public override int LabelNumber => 1049401; // a small stretched hide deed facing east } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallStretchedHideSouthAddon : BaseAddon { [Constructible] @@ -36,7 +38,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new SmallStretchedHideSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallStretchedHideSouthDeed : BaseAddonDeed { [Constructible] @@ -48,7 +50,7 @@ namespace Server.Items public override int LabelNumber => 1049402; // a small stretched hide deed facing south } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStretchedHideEastAddon : BaseAddon { [Constructible] @@ -60,7 +62,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new MediumStretchedHideEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStretchedHideEastDeed : BaseAddonDeed { [Constructible] @@ -72,7 +74,7 @@ namespace Server.Items public override int LabelNumber => 1049403; // a medium stretched hide deed facing east } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStretchedHideSouthAddon : BaseAddon { [Constructible] @@ -84,7 +86,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new MediumStretchedHideSouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MediumStretchedHideSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/TallElvenBedEastAddon.cs b/Projects/UOContent/Items/Addons/TallElvenBedEastAddon.cs index c4a26ad76..74cb13a78 100644 --- a/Projects/UOContent/Items/Addons/TallElvenBedEastAddon.cs +++ b/Projects/UOContent/Items/Addons/TallElvenBedEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TallElvenBedEastAddon : BaseAddon { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new TallElvenBedEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TallElvenBedEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/TallElvenBedSouthAddon.cs b/Projects/UOContent/Items/Addons/TallElvenBedSouthAddon.cs index 9b319957c..85cf51b48 100644 --- a/Projects/UOContent/Items/Addons/TallElvenBedSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/TallElvenBedSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TallElvenBedSouthAddon : BaseAddon { [Constructible] @@ -15,7 +17,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new TallElvenBedSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TallElvenBedSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/Telescope.cs b/Projects/UOContent/Items/Addons/Telescope.cs index 0fe081fca..ec5c0711b 100644 --- a/Projects/UOContent/Items/Addons/Telescope.cs +++ b/Projects/UOContent/Items/Addons/Telescope.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Telescope : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Addons/TrainingDummies.cs b/Projects/UOContent/Items/Addons/TrainingDummies.cs index 760b1a8a5..61f81dc1a 100644 --- a/Projects/UOContent/Items/Addons/TrainingDummies.cs +++ b/Projects/UOContent/Items/Addons/TrainingDummies.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1070, 0x1074)] public partial class TrainingDummy : AddonComponent { @@ -131,7 +132,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TrainingDummyEastAddon : BaseAddon { [Constructible] @@ -143,7 +144,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new TrainingDummyEastDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TrainingDummyEastDeed : BaseAddonDeed { [Constructible] @@ -155,7 +156,7 @@ namespace Server.Items public override int LabelNumber => 1044335; // training dummy (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TrainingDummySouthAddon : BaseAddon { [Constructible] @@ -167,7 +168,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new TrainingDummySouthDeed(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TrainingDummySouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/WarriorStatueEastAddon.cs b/Projects/UOContent/Items/Addons/WarriorStatueEastAddon.cs index a33b062ed..24d178904 100644 --- a/Projects/UOContent/Items/Addons/WarriorStatueEastAddon.cs +++ b/Projects/UOContent/Items/Addons/WarriorStatueEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class WarriorStatueEastAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new WarriorStatueEastDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class WarriorStatueEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/WarriorStatueSouthAddon.cs b/Projects/UOContent/Items/Addons/WarriorStatueSouthAddon.cs index 8c24908c7..d89ea386b 100644 --- a/Projects/UOContent/Items/Addons/WarriorStatueSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/WarriorStatueSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class WarriorStatueSouthAddon : BaseAddon { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override BaseAddonDeed Deed => new WarriorStatueSouthDeed(); } - [Serializable(0)] + [SerializationGenerator(0)] public partial class WarriorStatueSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/WaterTroughEastAddon.cs b/Projects/UOContent/Items/Addons/WaterTroughEastAddon.cs index 6253a849b..fe55a496f 100644 --- a/Projects/UOContent/Items/Addons/WaterTroughEastAddon.cs +++ b/Projects/UOContent/Items/Addons/WaterTroughEastAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WaterTroughEastAddon : BaseAddon, IWaterSource { [Constructible] @@ -19,7 +21,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WaterTroughEastDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/WaterTroughSouthAddon.cs b/Projects/UOContent/Items/Addons/WaterTroughSouthAddon.cs index 6e17e71b5..f51fe665c 100644 --- a/Projects/UOContent/Items/Addons/WaterTroughSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/WaterTroughSouthAddon.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WaterTroughSouthAddon : BaseAddon, IWaterSource { [Constructible] @@ -19,7 +21,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WaterTroughSouthDeed : BaseAddonDeed { [Constructible] diff --git a/Projects/UOContent/Items/Addons/WaterVat.cs b/Projects/UOContent/Items/Addons/WaterVat.cs index 0ecdc51f1..43b58dc0d 100644 --- a/Projects/UOContent/Items/Addons/WaterVat.cs +++ b/Projects/UOContent/Items/Addons/WaterVat.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class WaterVatEast : BaseAddon { [Constructible] @@ -24,7 +26,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class WaterVatSouth : BaseAddon { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Aquarium.cs b/Projects/UOContent/Items/Aquarium/Aquarium.cs index c228b2779..165811484 100644 --- a/Projects/UOContent/Items/Aquarium/Aquarium.cs +++ b/Projects/UOContent/Items/Aquarium/Aquarium.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Multis; using Server.Network; @@ -7,7 +8,7 @@ using Server.Utilities; namespace Server.Items { - [Serializable(4, false)] + [SerializationGenerator(4, false)] public partial class Aquarium : BaseAddonContainer { public static readonly TimeSpan EvaluationInterval = TimeSpan.FromDays(1); @@ -363,18 +364,18 @@ namespace Server.Items } } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); if (_vacationLeft > 0) { - list.Add(1074430, _vacationLeft.ToString()); // Vacation days left: ~1_DAYS + list.Add(1074430, $"{_vacationLeft}"); // Vacation days left: ~1_DAYS } if (_events.Count > 0) { - list.Add(1074426, _events.Count.ToString()); // ~1_NUM~ event(s) to view! + list.Add(1074426, $"{_events.Count}"); // ~1_NUM~ event(s) to view! } if (_rewardAvailable) @@ -382,60 +383,54 @@ namespace Server.Items list.Add(1074362); // A reward is available! } - list.Add(1074247, "{0}\t{1}", LiveCreatures, MaxLiveCreatures); // Live Creatures: ~1_NUM~ / ~2_MAX~ + list.Add(1074247, $"{LiveCreatures}\t{MaxLiveCreatures}"); // Live Creatures: ~1_NUM~ / ~2_MAX~ var dead = DeadCreatures; if (dead > 0) { - list.Add(1074248, dead.ToString()); // Dead Creatures: ~1_NUM~ + list.Add(1074248, dead); // Dead Creatures: ~1_NUM~ } var decorations = Items.Count - LiveCreatures - dead; if (decorations > 0) { - list.Add(1074249, decorations.ToString()); // Decorations: ~1_NUM~ + list.Add(1074249, decorations); // Decorations: ~1_NUM~ } - list.Add(1074250, "#{0}", FoodNumber()); // Food state: ~1_STATE~ - list.Add(1074251, "#{0}", WaterNumber()); // Water state: ~1_STATE~ + list.AddLocalized(1074250, FoodNumber()); // Food state: ~1_STATE~ + list.AddLocalized(1074251, WaterNumber()); // Water state: ~1_STATE~ if (_food.State == (int)FoodState.Dead) { - list.Add(1074577, "{0}\t{1}", _food.Added, _food.Improve); // Food Added: ~1_CUR~ Needed: ~2_NEED~ + list.Add(1074577, $"{_food.Added}\t{_food.Improve}"); // Food Added: ~1_CUR~ Needed: ~2_NEED~ } else if (_food.State == (int)FoodState.Overfed) { - list.Add(1074577, "{0}\t{1}", _food.Added, _food.Maintain); // Food Added: ~1_CUR~ Needed: ~2_NEED~ + list.Add(1074577, $"{_food.Added}\t{_food.Maintain}"); // Food Added: ~1_CUR~ Needed: ~2_NEED~ } else { list.Add( 1074253, // Food Added: ~1_CUR~ Feed: ~2_NEED~ Improve: ~3_GROW~ - "{0}\t{1}\t{2}", - _food.Added, - _food.Maintain, - _food.Improve + $"{_food.Added}\t{_food.Maintain}\t{_food.Improve}" ); } if (_water.State == (int)WaterState.Dead) { - list.Add(1074578, "{0}\t{1}", _water.Added, _water.Improve); // Water Added: ~1_CUR~ Needed: ~2_NEED~ + list.Add(1074578, $"{_water.Added}\t{_water.Improve}"); // Water Added: ~1_CUR~ Needed: ~2_NEED~ } else if (_water.State == (int)WaterState.Strong) { - list.Add(1074578, "{0}\t{1}", _water.Added, _water.Maintain); // Water Added: ~1_CUR~ Needed: ~2_NEED~ + list.Add(1074578, $"{_water.Added}\t{_water.Maintain}"); // Water Added: ~1_CUR~ Needed: ~2_NEED~ } else { list.Add( 1074254, // Water Added: ~1_CUR~ Maintain: ~2_NEED~ Improve: ~3_GROW~ - "{0}\t{1}\t{2}", - _water.Added, - _water.Maintain, - _water.Improve + $"{_water.Added}\t{_water.Maintain}\t{_water.Improve}" ); } } @@ -917,12 +912,8 @@ namespace Server.Items AddItem(item); - from?.SendLocalizedMessage( - 1073635, - item.LabelNumber != 0 - ? $"#{item.LabelNumber}" - : item.Name - ); // You add the following decoration to your aquarium: ~1_NAME~ + // You add the following decoration to your aquarium: ~1_NAME~ + from?.SendLocalizedMessage(1073635, item.LabelNumber != 0 ? $"#{item.LabelNumber}" : item.Name); InvalidateProperties(); return true; @@ -1130,7 +1121,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AquariumEastDeed : BaseAddonContainerDeed { [Constructible] @@ -1142,7 +1133,7 @@ namespace Server.Items public override int LabelNumber => 1074501; // Large Aquarium (east) } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AquariumNorthDeed : BaseAddonContainerDeed { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/AquariumFishingNet.cs b/Projects/UOContent/Items/Aquarium/AquariumFishingNet.cs index 474abcde4..d1da09a97 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumFishingNet.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumFishingNet.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AquariumFishNet : SpecialFishingNet { [Constructible] @@ -13,7 +15,7 @@ namespace Server.Items public override bool RequireDeepWater => false; - protected override void AddNetProperties(ObjectPropertyList list) + protected override void AddNetProperties(IPropertyList list) { } diff --git a/Projects/UOContent/Items/Aquarium/AquariumFood.cs b/Projects/UOContent/Items/Aquarium/AquariumFood.cs index db2cc1bc9..e8dd8162c 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumFood.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumFood.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AquariumFood : Item { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/AquariumState.cs b/Projects/UOContent/Items/Aquarium/AquariumState.cs index 2692d3521..ed38e98f7 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumState.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumState.cs @@ -1,4 +1,5 @@ using System; +using ModernUO.Serialization; namespace Server.Items { @@ -21,10 +22,10 @@ namespace Server.Items } [PropertyObject] - [EmbeddedSerializable(0, false)] + [SerializationGenerator(0, false)] public partial class AquariumState { - [SerializableParent] + [DirtyTrackingEntity] private Aquarium _aquarium; private int _state; @@ -41,7 +42,7 @@ namespace Server.Items if (_state != value) { _state = Math.Clamp(value, 0, 4); - _aquarium.MarkDirty(); + this.MarkDirty(); } } } diff --git a/Projects/UOContent/Items/Aquarium/BaseFish.cs b/Projects/UOContent/Items/Aquarium/BaseFish.cs index 9611a5429..358bd3b4b 100644 --- a/Projects/UOContent/Items/Aquarium/BaseFish.cs +++ b/Projects/UOContent/Items/Aquarium/BaseFish.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseFish : Item { private static readonly TimeSpan DeathDelay = TimeSpan.FromMinutes(5); @@ -59,7 +60,7 @@ namespace Server.Items return Dead ? 1073623 : 1073622; // A [dead/live] aquarium creature } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Fish/AlbinoCourtesanFish.cs b/Projects/UOContent/Items/Aquarium/Fish/AlbinoCourtesanFish.cs index b916c715d..b542864b6 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/AlbinoCourtesanFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/AlbinoCourtesanFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AlbinoCourtesanFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/AlbinoFrog.cs b/Projects/UOContent/Items/Aquarium/Fish/AlbinoFrog.cs index 58db24162..2e95417dc 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/AlbinoFrog.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/AlbinoFrog.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AlbinoFrog : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/BritainCrownFish.cs b/Projects/UOContent/Items/Aquarium/Fish/BritainCrownFish.cs index 35c649ad5..a4ee40404 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/BritainCrownFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/BritainCrownFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BritainCrownFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/FandancerFish.cs b/Projects/UOContent/Items/Aquarium/Fish/FandancerFish.cs index b27074e5b..c65e9ac6c 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/FandancerFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/FandancerFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FandancerFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/GoldenBroadtail.cs b/Projects/UOContent/Items/Aquarium/Fish/GoldenBroadtail.cs index 984178f1e..b01f38c2b 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/GoldenBroadtail.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/GoldenBroadtail.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GoldenBroadtail : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/Jellyfish.cs b/Projects/UOContent/Items/Aquarium/Fish/Jellyfish.cs index 05820fa3c..5751c35d8 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/Jellyfish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/Jellyfish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Jellyfish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/KillerFrog.cs b/Projects/UOContent/Items/Aquarium/Fish/KillerFrog.cs index 0fa0d33d1..239545245 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/KillerFrog.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/KillerFrog.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class KillerFrog : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/LongClawCrab.cs b/Projects/UOContent/Items/Aquarium/Fish/LongClawCrab.cs index 5e4e8f920..4bbcfccb7 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/LongClawCrab.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/LongClawCrab.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LongClawCrab : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/MakotoCourtesanFish.cs b/Projects/UOContent/Items/Aquarium/Fish/MakotoCourtesanFish.cs index 2e597a684..b2729ec10 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/MakotoCourtesanFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/MakotoCourtesanFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MakotoCourtesanFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/MinocBlueFish.cs b/Projects/UOContent/Items/Aquarium/Fish/MinocBlueFish.cs index cc98591dc..0b0924c2b 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/MinocBlueFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/MinocBlueFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MinocBlueFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/NujelmHoneyFish.cs b/Projects/UOContent/Items/Aquarium/Fish/NujelmHoneyFish.cs index 1264e7f3d..f80d24923 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/NujelmHoneyFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/NujelmHoneyFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NujelmHoneyFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/PurpleFrog.cs b/Projects/UOContent/Items/Aquarium/Fish/PurpleFrog.cs index 44b131e95..fb32ce954 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/PurpleFrog.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/PurpleFrog.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PurpleFrog : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/RedDartFish.cs b/Projects/UOContent/Items/Aquarium/Fish/RedDartFish.cs index 5593ef753..d75f46245 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/RedDartFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/RedDartFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RedDartFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/Shrimp.cs b/Projects/UOContent/Items/Aquarium/Fish/Shrimp.cs index a431db254..ae66c1b3a 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/Shrimp.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/Shrimp.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Shrimp : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/SmallMouthSuckerFin.cs b/Projects/UOContent/Items/Aquarium/Fish/SmallMouthSuckerFin.cs index 748e7f8cd..65e0d4f3b 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/SmallMouthSuckerFin.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/SmallMouthSuckerFin.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallMouthSuckerFin : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/SpeckledCrab.cs b/Projects/UOContent/Items/Aquarium/Fish/SpeckledCrab.cs index 044618b18..0c5e82c60 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/SpeckledCrab.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/SpeckledCrab.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SpeckledCrab : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/SpinedScratcherFish.cs b/Projects/UOContent/Items/Aquarium/Fish/SpinedScratcherFish.cs index 7490419cc..c9e91b504 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/SpinedScratcherFish.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/SpinedScratcherFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SpinedScratcherFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/SpottedBuccaneer.cs b/Projects/UOContent/Items/Aquarium/Fish/SpottedBuccaneer.cs index 54b4b284d..979afc170 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/SpottedBuccaneer.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/SpottedBuccaneer.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SpottedBuccaneer : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/VesperReefTiger.cs b/Projects/UOContent/Items/Aquarium/Fish/VesperReefTiger.cs index 7d7ac4fde..46d575d08 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/VesperReefTiger.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/VesperReefTiger.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class VesperReefTiger : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Fish/YellowFinBluebelly.cs b/Projects/UOContent/Items/Aquarium/Fish/YellowFinBluebelly.cs index 5a707f132..e2e6f152d 100644 --- a/Projects/UOContent/Items/Aquarium/Fish/YellowFinBluebelly.cs +++ b/Projects/UOContent/Items/Aquarium/Fish/YellowFinBluebelly.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class YellowFinBluebelly : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/FishBowl.cs b/Projects/UOContent/Items/Aquarium/FishBowl.cs index 71d364305..52f73788f 100644 --- a/Projects/UOContent/Items/Aquarium/FishBowl.cs +++ b/Projects/UOContent/Items/Aquarium/FishBowl.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FishBowl : BaseContainer { [Constructible] @@ -77,7 +78,7 @@ namespace Server.Items return base.CheckLift(from, item, ref reject); } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); @@ -87,7 +88,7 @@ namespace Server.Items if (fish != null) { - list.Add(1074494, "#{0}", fish.LabelNumber); // Contains: ~1_CREATURE~ + list.AddLocalized(1074494, fish.LabelNumber); // Contains: ~1_CREATURE~ } } } diff --git a/Projects/UOContent/Items/Aquarium/Reward Fish/BrineShrimp.cs b/Projects/UOContent/Items/Aquarium/Reward Fish/BrineShrimp.cs index ac95c67b6..fe53aa46b 100644 --- a/Projects/UOContent/Items/Aquarium/Reward Fish/BrineShrimp.cs +++ b/Projects/UOContent/Items/Aquarium/Reward Fish/BrineShrimp.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrineShrimp : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Reward Fish/Coral.cs b/Projects/UOContent/Items/Aquarium/Reward Fish/Coral.cs index 9d50f152e..d5308177d 100644 --- a/Projects/UOContent/Items/Aquarium/Reward Fish/Coral.cs +++ b/Projects/UOContent/Items/Aquarium/Reward Fish/Coral.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Coral : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Reward Fish/FullMoonFish.cs b/Projects/UOContent/Items/Aquarium/Reward Fish/FullMoonFish.cs index aa048fe97..0136ad42e 100644 --- a/Projects/UOContent/Items/Aquarium/Reward Fish/FullMoonFish.cs +++ b/Projects/UOContent/Items/Aquarium/Reward Fish/FullMoonFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FullMoonFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Reward Fish/SeaHorse.cs b/Projects/UOContent/Items/Aquarium/Reward Fish/SeaHorse.cs index 2bbb36e37..f7d07eb71 100644 --- a/Projects/UOContent/Items/Aquarium/Reward Fish/SeaHorse.cs +++ b/Projects/UOContent/Items/Aquarium/Reward Fish/SeaHorse.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SeaHorseFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedFlakeFish.cs b/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedFlakeFish.cs index 6da23490b..665405180 100644 --- a/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedFlakeFish.cs +++ b/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedFlakeFish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StrippedFlakeFish : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedSosarianSwill.cs b/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedSosarianSwill.cs index 024593418..2d00c8122 100644 --- a/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedSosarianSwill.cs +++ b/Projects/UOContent/Items/Aquarium/Reward Fish/StrippedSosarianSwill.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StrippedSosarianSwill : BaseFish { [Constructible] diff --git a/Projects/UOContent/Items/Aquarium/Rewards/AquariumMessage.cs b/Projects/UOContent/Items/Aquarium/Rewards/AquariumMessage.cs index d8f1ec031..219135b62 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/AquariumMessage.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/AquariumMessage.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AquariumMessage : MessageInABottle { [Constructible] @@ -10,7 +12,7 @@ namespace Server.Items public override int LabelNumber => 1073894; // Message in a Bottle - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/CaptainBlackheartsFishingPole.cs b/Projects/UOContent/Items/Aquarium/Rewards/CaptainBlackheartsFishingPole.cs index 6432239cb..f46d8d9ac 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/CaptainBlackheartsFishingPole.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/CaptainBlackheartsFishingPole.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CaptainBlackheartsFishingPole : FishingPole { [Constructible] @@ -10,7 +12,7 @@ namespace Server.Items public override int LabelNumber => 1074571; // Captain Blackheart's Fishing Pole - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/CraftysFishingHat.cs b/Projects/UOContent/Items/Aquarium/Rewards/CraftysFishingHat.cs index d7fbbdc46..fb5b91174 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/CraftysFishingHat.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/CraftysFishingHat.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CraftysFishingHat : BaseHat { [Constructible] @@ -19,7 +21,7 @@ namespace Server.Items public override int InitMinHits => 20; public override int InitMaxHits => 30; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/FishBones.cs b/Projects/UOContent/Items/Aquarium/Rewards/FishBones.cs index 2a3183355..149802bfc 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/FishBones.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/FishBones.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FishBones : Item { [Constructible] @@ -11,7 +13,7 @@ namespace Server.Items public override int LabelNumber => 1074601; // Fish bones public override double DefaultWeight => 1.0; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/IslandStatue.cs b/Projects/UOContent/Items/Aquarium/Rewards/IslandStatue.cs index e8e57e963..cd4362581 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/IslandStatue.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/IslandStatue.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class IslandStatue : Item { [Constructible] @@ -11,7 +13,7 @@ namespace Server.Items public override int LabelNumber => 1074600; // An island statue public override double DefaultWeight => 1.0; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/Shell.cs b/Projects/UOContent/Items/Aquarium/Rewards/Shell.cs index ce3bc4060..451c20fef 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/Shell.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/Shell.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Shell : Item { [Constructible] @@ -11,7 +13,7 @@ namespace Server.Items public override int LabelNumber => 1074598; // A shell public override double DefaultWeight => 1.0; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/ToyBoat.cs b/Projects/UOContent/Items/Aquarium/Rewards/ToyBoat.cs index 06e470d5a..be2346c4a 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/ToyBoat.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/ToyBoat.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x14F3, 0x14F4)] public partial class ToyBoat : Item { @@ -12,7 +14,7 @@ namespace Server.Items public override int LabelNumber => 1074363; // A toy boat public override double DefaultWeight => 1.0; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs b/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs index ea3a6efd5..ae50b2e81 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WaterloggedBoots : BaseShoes { [Constructible] @@ -22,7 +24,7 @@ namespace Server.Items public override int LabelNumber => 1074364; // Waterlogged boots - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Aquarium/VacationWafer.cs b/Projects/UOContent/Items/Aquarium/VacationWafer.cs index 1021afc37..f7ba9b7f2 100644 --- a/Projects/UOContent/Items/Aquarium/VacationWafer.cs +++ b/Projects/UOContent/Items/Aquarium/VacationWafer.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class VacationWafer : Item { public const int VacationDays = 7; @@ -12,11 +14,11 @@ namespace Server.Items public override int LabelNumber => 1074431; // An aquarium flake sphere - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); - list.Add(1074432, VacationDays.ToString()); // Vacation days: ~1_DAYS~ + list.AddLocalized(1074432, VacationDays); // Vacation days: ~1_DAYS~ } } } diff --git a/Projects/UOContent/Items/Armor/Artifacts/ArmorOfFortune.cs b/Projects/UOContent/Items/Armor/Artifacts/ArmorOfFortune.cs index 624692b54..6379b8463 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/ArmorOfFortune.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/ArmorOfFortune.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ArmorOfFortune : StuddedChest { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/Craftable/BrambleCoat.cs b/Projects/UOContent/Items/Armor/Artifacts/Craftable/BrambleCoat.cs index 7327a9b52..6f694b926 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/Craftable/BrambleCoat.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/Craftable/BrambleCoat.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class BrambleCoat : WoodlandChest { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs b/Projects/UOContent/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs index dd00c0838..10c01388e 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class IronwoodCrown : RavenHelm { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs b/Projects/UOContent/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs index 007bf2165..db318d0e2 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SongWovenMantle : LeafArms { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs b/Projects/UOContent/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs index 1ad94ff66..219625192 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SpellWovenBritches : LeafLegs { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/Craftable/StitchersMittens.cs b/Projects/UOContent/Items/Armor/Artifacts/Craftable/StitchersMittens.cs index c2b0e0a13..e60595ae6 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/Craftable/StitchersMittens.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/Craftable/StitchersMittens.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class StitchersMittens : LeafGloves { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/GauntletsOfNobility.cs b/Projects/UOContent/Items/Armor/Artifacts/GauntletsOfNobility.cs index 872851cd6..c182d7fb4 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/GauntletsOfNobility.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/GauntletsOfNobility.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GauntletsOfNobility : RingmailGloves { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/HelmOfInsight.cs b/Projects/UOContent/Items/Armor/Artifacts/HelmOfInsight.cs index c19866380..c913ccefa 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/HelmOfInsight.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/HelmOfInsight.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HelmOfInsight : PlateHelm { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/HolyKnightsBreastplate.cs b/Projects/UOContent/Items/Armor/Artifacts/HolyKnightsBreastplate.cs index 2f7b0951c..20043d69c 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/HolyKnightsBreastplate.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/HolyKnightsBreastplate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HolyKnightsBreastplate : PlateChest { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/InquisitorsResolution.cs b/Projects/UOContent/Items/Armor/Artifacts/InquisitorsResolution.cs index 3aee07ed6..17b535ec8 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/InquisitorsResolution.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/InquisitorsResolution.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class InquisitorsResolution : PlateGloves { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/JackalsCollar.cs b/Projects/UOContent/Items/Armor/Artifacts/JackalsCollar.cs index 32114470e..c24a4b668 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/JackalsCollar.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/JackalsCollar.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class JackalsCollar : PlateGorget { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/LeggingsOfBane.cs b/Projects/UOContent/Items/Armor/Artifacts/LeggingsOfBane.cs index 6a7fa1da7..1d8c7de2d 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/LeggingsOfBane.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/LeggingsOfBane.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeggingsOfBane : ChainLegs { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/MidnightBracers.cs b/Projects/UOContent/Items/Armor/Artifacts/MidnightBracers.cs index 57d6d661d..6379b05cb 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/MidnightBracers.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/MidnightBracers.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MidnightBracers : BoneArms { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs b/Projects/UOContent/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs index c94054d0d..7ae2a0ee3 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class OrnateCrownOfTheHarrower : BoneHelm { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/ShadowDancerLeggings.cs b/Projects/UOContent/Items/Armor/Artifacts/ShadowDancerLeggings.cs index 188c50b2b..1395a55b6 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/ShadowDancerLeggings.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/ShadowDancerLeggings.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShadowDancerLeggings : LeatherLegs { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/TunicOfFire.cs b/Projects/UOContent/Items/Armor/Artifacts/TunicOfFire.cs index 9b83136fd..41a3be68a 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/TunicOfFire.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/TunicOfFire.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TunicOfFire : ChainChest { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs b/Projects/UOContent/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs index e4db5af05..62e57d825 100644 --- a/Projects/UOContent/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs +++ b/Projects/UOContent/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class VoiceOfTheFallenKing : LeatherGorget { [Constructible] diff --git a/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs b/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs new file mode 100644 index 000000000..de8904f0c --- /dev/null +++ b/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs @@ -0,0 +1,167 @@ +using AMA = Server.Items.ArmorMeditationAllowance; + +namespace Server.Items; + +public partial class BaseArmor +{ + private void MigrateFrom(V8Content content) + { + _attributes = content.Attributes ?? AttributesDefaultValue(); + _armorAttributes = content.ArmorAttributes ?? ArmorAttributesDefaultValue(); + _physicalBonus = content.PhysicalBonus ?? 0; + _fireBonus = content.FireBonus ?? 0; + _coldBonus = content.ColdBonus ?? 0; + _poisonBonus = content.PoisonBonus ?? 0; + _energyBonus = content.EnergyBonus ?? 0; + _identified = content.Identified; + _maxHitPoints = content.MaxHitPoints ?? 0; + _hitPoints = content.HitPoints ?? 0; + var crafter = content.Crafter; + Timer.StartTimer(() => _crafter = crafter?.RawName); + _quality = content.Quality ?? ArmorQuality.Regular; + _durability = content.Durability ?? ArmorDurabilityLevel.Regular; + _rawResource = content.RawResource ?? DefaultResource; + _armorBase = content.BaseArmorRating ?? -1; + _strBonus = content.StrBonus ?? -1; + _dexBonus = content.DexBonus ?? -1; + _intBonus = content.IntBonus ?? -1; + _strReq = content.StrRequirement ?? -1; + _dexReq = content.DexRequirement ?? -1; + _intReq = content.IntRequirement ?? -1; + _meditate = content.MeditationAllowance ?? (AMA)(-1); + _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); + _playerConstructed = content.PlayerConstructed; + } + + // Version 7 (pre-codegen) + private void Deserialize(IGenericReader reader, int version) + { + var flags = (OldSaveFlag)reader.ReadEncodedInt(); + + Attributes = new AosAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.Attributes)) + { + Attributes.Deserialize(reader); + } + + ArmorAttributes = new AosArmorAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.ArmorAttributes)) + { + ArmorAttributes.Deserialize(reader); + } + + if (GetSaveFlag(flags, OldSaveFlag.PhysicalBonus)) + { + _physicalBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.FireBonus)) + { + _fireBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.ColdBonus)) + { + _coldBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.PoisonBonus)) + { + _poisonBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.EnergyBonus)) + { + _energyBonus = reader.ReadEncodedInt(); + } + + _identified = GetSaveFlag(flags, OldSaveFlag.Identified); + + if (GetSaveFlag(flags, OldSaveFlag.MaxHitPoints)) + { + _maxHitPoints = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.HitPoints)) + { + _hitPoints = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Crafter)) + { + var crafter = reader.ReadEntity(); + Timer.StartTimer(() => _crafter = crafter?.RawName); + } + + if (GetSaveFlag(flags, OldSaveFlag.Quality)) + { + _quality = (ArmorQuality)reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Durability)) + { + _durability = (ArmorDurabilityLevel)reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Protection)) + { + _protection = (ArmorProtectionLevel)reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Resource)) + { + _rawResource = (CraftResource)reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.BaseArmor)) + { + _armorBase = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.StrBonus)) + { + _strBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.DexBonus)) + { + _dexBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.IntBonus)) + { + _intBonus = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.StrReq)) + { + _strReq = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.DexReq)) + { + _dexReq = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.IntReq)) + { + _intReq = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.MedAllowance)) + { + _meditate = (AMA)reader.ReadEncodedInt(); + } + + SkillBonuses = new AosSkillBonuses(this); + + if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) + { + SkillBonuses.Deserialize(reader); + } + + PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); + } +} diff --git a/Projects/UOContent/Items/Armor/BaseArmor.cs b/Projects/UOContent/Items/Armor/BaseArmor.cs index dd79ac923..2cb758847 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Craft; using Server.Ethics; using Server.Factions; @@ -10,7 +11,7 @@ using AMT = Server.Items.ArmorMaterialType; namespace Server.Items { - [Serializable(8, false)] + [SerializationGenerator(9, false)] public abstract partial class BaseArmor : Item, IScissorable, IFactionItem, ICraftable, IWearableDurability { [SerializableField(0, setter: "private")] @@ -99,7 +100,7 @@ namespace Server.Items [InvalidateProperties] [SerializableField(10)] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] - private Mobile _crafter; + private string _crafter; [SerializableFieldSaveFlag(10)] private bool ShouldSerializeCrafter() => _crafter != null; @@ -572,7 +573,7 @@ namespace Server.Items if (makersMark) { - Crafter = from; + Crafter = from.RawName; } var resourceType = typeRes ?? craftItem.Resources[0].ItemType; @@ -589,8 +590,8 @@ namespace Server.Items if (Quality == ArmorQuality.Exceptional) { - if (!(Core.ML && this is BaseShield) - ) // Guessed Core.ML removed exceptional resist bonuses from crafted shields + // Guessed Core.ML removed exceptional resist bonuses from crafted shields + if (!(Core.ML && this is BaseShield)) { DistributeBonuses( tool is BaseRunicTool ? 6 : @@ -1055,164 +1056,27 @@ namespace Server.Items if (m != null && (strBonus != 0 || dexBonus != 0 || intBonus != 0)) { - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - m.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - m.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - m.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } m?.CheckStatTimers(); } - private void Deserialize(IGenericReader reader, int version) - { - var flags = (OldSaveFlag)reader.ReadEncodedInt(); - - Attributes = new AosAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.Attributes)) - { - Attributes.Deserialize(reader); - } - - ArmorAttributes = new AosArmorAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.ArmorAttributes)) - { - ArmorAttributes.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.PhysicalBonus)) - { - _physicalBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.FireBonus)) - { - _fireBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.ColdBonus)) - { - _coldBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.PoisonBonus)) - { - _poisonBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.EnergyBonus)) - { - _energyBonus = reader.ReadEncodedInt(); - } - - _identified = GetSaveFlag(flags, OldSaveFlag.Identified); - - if (GetSaveFlag(flags, OldSaveFlag.MaxHitPoints)) - { - _maxHitPoints = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.HitPoints)) - { - _hitPoints = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Crafter)) - { - _crafter = reader.ReadEntity(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Quality)) - { - _quality = (ArmorQuality)reader.ReadEncodedInt(); - } - else - { - _quality = ArmorQuality.Regular; - } - - if (GetSaveFlag(flags, OldSaveFlag.Durability)) - { - _durability = (ArmorDurabilityLevel)reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Protection)) - { - _protection = (ArmorProtectionLevel)reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Resource)) - { - _rawResource = (CraftResource)reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.BaseArmor)) - { - _armorBase = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.StrBonus)) - { - _strBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.DexBonus)) - { - _dexBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.IntBonus)) - { - _intBonus = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.StrReq)) - { - _strReq = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.DexReq)) - { - _dexReq = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.IntReq)) - { - _intReq = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.MedAllowance)) - { - _meditate = (AMA)reader.ReadEncodedInt(); - } - - SkillBonuses = new AosSkillBonuses(this); - - if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) - { - SkillBonuses.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.PlayerConstructed)) - { - PlayerConstructed = true; - } - } - public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) { if (!Ethic.CheckTrade(from, to, newOwner, this)) @@ -1316,21 +1180,21 @@ namespace Server.Items if (strBonus != 0 || dexBonus != 0 || intBonus != 0) { - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - from.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + from.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - from.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + from.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - from.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + from.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } @@ -1341,11 +1205,11 @@ namespace Server.Items { if (parent is Mobile m) { - var modName = Serial.ToString(); + var serial = Serial; - m.RemoveStatMod($"{modName}Str"); - m.RemoveStatMod($"{modName}Dex"); - m.RemoveStatMod($"{modName}Int"); + m.RemoveStatMod($"{serial}Str"); + m.RemoveStatMod($"{serial}Dex"); + m.RemoveStatMod($"{serial}Int"); if (Core.AOS) { @@ -1359,9 +1223,7 @@ namespace Server.Items base.OnRemoved(parent); } - private string GetNameString() => Name ?? $"#{LabelNumber}"; - - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var oreType = _rawResource switch { @@ -1385,31 +1247,39 @@ namespace Server.Items _ => 0 }; - if (_quality == ArmorQuality.Exceptional) + var name = Name; + + if (oreType != 0) { - if (oreType != 0) + var qualityNumber = _quality == ArmorQuality.Exceptional ? 1053100 : 1053099; + + if (name != null) { - list.Add(1053100, "#{0}\t{1}", oreType, GetNameString()); // exceptional ~1_oretype~ ~2_armortype~ + list.Add(qualityNumber, $"{oreType:#}\t{Name}"); } else { - list.Add(1050040, GetNameString()); // exceptional ~1_ITEMNAME~ + list.Add(qualityNumber, $"{oreType:#}\t{LabelNumber:#}"); } } + else if (_quality == ArmorQuality.Exceptional) + { + if (name != null) + { + list.Add(1050040, name); // exceptional ~1_ITEMNAME~ + } + else + { + list.AddLocalized(1050040, LabelNumber); // exceptional ~1_ITEMNAME~ + } + } + else if (name == null) + { + list.Add(LabelNumber); + } 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); - } + list.Add(Name); } } @@ -1425,13 +1295,13 @@ namespace Server.Items public virtual int GetLuckBonus() => CraftResources.GetInfo(_rawResource)?.AttributeInfo?.ArmorLuck ?? 0; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (_crafter != null) { - list.Add(1050043, _crafter.Name); // crafted by ~1_NAME~ + list.Add(1050043, _crafter); // crafted by ~1_NAME~ } if (m_FactionState != null) @@ -1455,72 +1325,72 @@ namespace Server.Items if ((prop = ArtifactRarity) > 0) { - list.Add(1061078, prop.ToString()); // artifact rarity ~1_val~ + list.Add(1061078, prop); // artifact rarity ~1_val~ } if ((prop = Attributes.WeaponDamage) != 0) { - list.Add(1060401, prop.ToString()); // damage increase ~1_val~% + list.Add(1060401, prop); // damage increase ~1_val~% } if ((prop = Attributes.DefendChance) != 0) { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% + list.Add(1060408, prop); // defense chance increase ~1_val~% } if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ + list.Add(1060409, prop); // dexterity bonus ~1_val~ } if ((prop = Attributes.EnhancePotions) != 0) { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% + list.Add(1060411, prop); // enhance potions ~1_val~% } if ((prop = Attributes.CastRecovery) != 0) { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ + list.Add(1060412, prop); // faster cast recovery ~1_val~ } if ((prop = Attributes.CastSpeed) != 0) { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ + list.Add(1060413, prop); // faster casting ~1_val~ } if ((prop = Attributes.AttackChance) != 0) { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% + list.Add(1060415, prop); // hit chance increase ~1_val~% } if ((prop = Attributes.BonusHits) != 0) { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ + list.Add(1060431, prop); // hit point increase ~1_val~ } if ((prop = Attributes.BonusInt) != 0) { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ + list.Add(1060432, prop); // intelligence bonus ~1_val~ } if ((prop = Attributes.LowerManaCost) != 0) { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% + list.Add(1060433, prop); // lower mana cost ~1_val~% } if ((prop = Attributes.LowerRegCost) != 0) { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% + list.Add(1060434, prop); // lower reagent cost ~1_val~% } if ((prop = GetLowerStatReq()) != 0) { - list.Add(1060435, prop.ToString()); // lower requirements ~1_val~% + list.Add(1060435, prop); // lower requirements ~1_val~% } if ((prop = GetLuckBonus() + Attributes.Luck) != 0) { - list.Add(1060436, prop.ToString()); // luck ~1_val~ + list.Add(1060436, prop); // luck ~1_val~ } if (ArmorAttributes.MageArmor != 0) @@ -1530,12 +1400,12 @@ namespace Server.Items if ((prop = Attributes.BonusMana) != 0) { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ + list.Add(1060439, prop); // mana increase ~1_val~ } if ((prop = Attributes.RegenMana) != 0) { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ + list.Add(1060440, prop); // mana regeneration ~1_val~ } if (Attributes.NightSight != 0) @@ -1545,22 +1415,22 @@ namespace Server.Items if ((prop = Attributes.ReflectPhysical) != 0) { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% + list.Add(1060442, prop); // reflect physical damage ~1_val~% } if ((prop = Attributes.RegenStam) != 0) { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ + list.Add(1060443, prop); // stamina regeneration ~1_val~ } if ((prop = Attributes.RegenHits) != 0) { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ + list.Add(1060444, prop); // hit point regeneration ~1_val~ } if ((prop = ArmorAttributes.SelfRepair) != 0) { - list.Add(1060450, prop.ToString()); // self repair ~1_val~ + list.Add(1060450, prop); // self repair ~1_val~ } if (Attributes.SpellChanneling != 0) @@ -1570,44 +1440,44 @@ namespace Server.Items if ((prop = Attributes.SpellDamage) != 0) { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% + list.Add(1060483, prop); // spell damage increase ~1_val~% } if ((prop = Attributes.BonusStam) != 0) { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ + list.Add(1060484, prop); // stamina increase ~1_val~ } if ((prop = Attributes.BonusStr) != 0) { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ + list.Add(1060485, prop); // strength bonus ~1_val~ } if ((prop = Attributes.WeaponSpeed) != 0) { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% + list.Add(1060486, prop); // swing speed increase ~1_val~% } if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) { - list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~% + list.Add(1075210, prop); // Increased Karma Loss ~1val~% } AddResistanceProperties(list); if ((prop = GetDurabilityBonus()) > 0) { - list.Add(1060410, prop.ToString()); // durability ~1_val~% + list.Add(1060410, prop); // durability ~1_val~% } if ((prop = ComputeStatReq(StatType.Str)) > 0) { - list.Add(1061170, prop.ToString()); // strength requirement ~1_val~ + list.Add(1061170, prop); // strength requirement ~1_val~ } if (_hitPoints >= 0 && _maxHitPoints > 0) { - list.Add(1060639, "{0}\t{1}", _hitPoints, _maxHitPoints); // durability ~1_val~ / ~2_val~ + list.Add(1060639, $"{_hitPoints}\t{_maxHitPoints}"); // durability ~1_val~ / ~2_val~ } } @@ -1672,7 +1542,7 @@ namespace Server.Items return; } - from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter?.RawName, false, attrs); + from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs); } [Flags] diff --git a/Projects/UOContent/Items/Armor/Bone/BoneArms.cs b/Projects/UOContent/Items/Armor/Bone/BoneArms.cs index 886bff8a1..a5a41d647 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneArms.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x144e, 0x1453)] public partial class BoneArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Bone/BoneChest.cs b/Projects/UOContent/Items/Armor/Bone/BoneChest.cs index 6431abd75..edbb82f10 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneChest.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x144f, 0x1454)] public partial class BoneChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs b/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs index 795fe85f4..5958198ad 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1450, 0x1455)] public partial class BoneGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs b/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs index ed0357a12..67c3f9653 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1452, 0x1457)] public partial class BoneLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Chain/ChainChest.cs b/Projects/UOContent/Items/Armor/Chain/ChainChest.cs index 57df216de..9d4536d80 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainChest.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13bf, 0x13c4)] public partial class ChainChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs b/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs index 4f537f4b1..b5fd1431f 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ChainHatsuburi : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs b/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs index 2e40231f7..edfbea1e5 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13be, 0x13c3)] public partial class ChainLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs index 819d4e44e..0d124f8f4 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.GargishClothArms", "Server.Items.GargishClothArmsArmor")] public partial class GargishClothArmsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs index c46c5ebf0..96027ec54 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.FemaleGargishClothArms", "Server.Items.FemaleGargishClothArmsArmor")] public partial class GargishClothArmsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs index 4785b91bb..76f8ac65c 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.GargishClothChest", "Server.Items.GargishClothChestArmor")] public partial class GargishClothChestType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs index a1ba296a2..0867ec5c7 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.FemaleGargishClothChest", "Server.Items.FemaleGargishClothChestArmor")] public partial class GargishClothChestType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs index b3b8c1093..3200848bc 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.GargishClothKilt", "Server.Items.GargishClothKiltArmor")] public partial class GargishClothKiltType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs index e18718dc1..45c836fb4 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.GargishClothKilt", "Server.Items.GargishClothKiltArmor")] public partial class GargishClothKiltType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs index 45084fbc3..53e724d47 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.GargishClothLegs", "Server.Items.GargishClothLegsArmor")] public partial class GargishClothLegsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs index bad26d399..0016b886f 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [TypeAlias("Server.Items.FemaleGargishClothLegs", "Server.Items.FemaleGargishClothLegsArmor")] public partial class GargishClothLegsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs index fd997f57f..8c22af849 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x144e, 0x1453)] public partial class DaemonArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs index 5c591a497..e2a0ec586 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x144f, 0x1454)] public partial class DaemonChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs index 070102ebe..e7d0793cd 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1450, 0x1455)] public partial class DaemonGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs index f74377813..f07aeeae6 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1452, 0x1457)] public partial class DaemonLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs b/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs index 1412755c8..970be0af2 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2657, 0x2658)] public partial class DragonArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs b/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs index 796a76329..7e012296e 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2641, 0x2642)] public partial class DragonChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs b/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs index a327b29d9..4d2ec9c8d 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2643, 0x2644)] public partial class DragonGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs b/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs index 900a9db5b..79190e074 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2645, 0x2646)] public partial class DragonHelm : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs b/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs index d708fa77c..2f02eea46 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2647, 0x2648)] public partial class DragonLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Glasses/AnthropomorphistGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/AnthropomorphistGlasses.cs index d367588bc..059d8274c 100644 --- a/Projects/UOContent/Items/Armor/Glasses/AnthropomorphistGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/AnthropomorphistGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnthropomorphistGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/ArtsGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/ArtsGlasses.cs index 5982541ad..e1d502342 100644 --- a/Projects/UOContent/Items/Armor/Glasses/ArtsGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/ArtsGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ArtsGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs index 1b4bbd3e3..05c81c3c7 100644 --- a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ElvenGlasses : BaseArmor { [Constructible] @@ -40,7 +42,7 @@ namespace Server.Items [SerializableFieldDefault(0)] private AosWeaponAttributes WeaponAttributesDefaultValue() => new(this); - public override void AppendChildNameProperties(ObjectPropertyList list) + public override void AppendChildNameProperties(IPropertyList list) { base.AppendChildNameProperties(list); @@ -48,77 +50,77 @@ namespace Server.Items if ((prop = _weaponAttributes.HitColdArea) != 0) { - list.Add(1060416, prop.ToString()); // hit cold area ~1_val~% + list.Add(1060416, prop); // hit cold area ~1_val~% } if ((prop = _weaponAttributes.HitDispel) != 0) { - list.Add(1060417, prop.ToString()); // hit dispel ~1_val~% + list.Add(1060417, prop); // hit dispel ~1_val~% } if ((prop = _weaponAttributes.HitEnergyArea) != 0) { - list.Add(1060418, prop.ToString()); // hit energy area ~1_val~% + list.Add(1060418, prop); // hit energy area ~1_val~% } if ((prop = _weaponAttributes.HitFireArea) != 0) { - list.Add(1060419, prop.ToString()); // hit fire area ~1_val~% + list.Add(1060419, prop); // hit fire area ~1_val~% } if ((prop = _weaponAttributes.HitFireball) != 0) { - list.Add(1060420, prop.ToString()); // hit fireball ~1_val~% + list.Add(1060420, prop); // hit fireball ~1_val~% } if ((prop = _weaponAttributes.HitHarm) != 0) { - list.Add(1060421, prop.ToString()); // hit harm ~1_val~% + list.Add(1060421, prop); // hit harm ~1_val~% } if ((prop = _weaponAttributes.HitLeechHits) != 0) { - list.Add(1060422, prop.ToString()); // hit life leech ~1_val~% + list.Add(1060422, prop); // hit life leech ~1_val~% } if ((prop = _weaponAttributes.HitLightning) != 0) { - list.Add(1060423, prop.ToString()); // hit lightning ~1_val~% + list.Add(1060423, prop); // hit lightning ~1_val~% } if ((prop = _weaponAttributes.HitLowerAttack) != 0) { - list.Add(1060424, prop.ToString()); // hit lower attack ~1_val~% + list.Add(1060424, prop); // hit lower attack ~1_val~% } if ((prop = _weaponAttributes.HitLowerDefend) != 0) { - list.Add(1060425, prop.ToString()); // hit lower defense ~1_val~% + list.Add(1060425, prop); // hit lower defense ~1_val~% } if ((prop = _weaponAttributes.HitMagicArrow) != 0) { - list.Add(1060426, prop.ToString()); // hit magic arrow ~1_val~% + list.Add(1060426, prop); // hit magic arrow ~1_val~% } if ((prop = _weaponAttributes.HitLeechMana) != 0) { - list.Add(1060427, prop.ToString()); // hit mana leech ~1_val~% + list.Add(1060427, prop); // hit mana leech ~1_val~% } if ((prop = _weaponAttributes.HitPhysicalArea) != 0) { - list.Add(1060428, prop.ToString()); // hit physical area ~1_val~% + list.Add(1060428, prop); // hit physical area ~1_val~% } if ((prop = _weaponAttributes.HitPoisonArea) != 0) { - list.Add(1060429, prop.ToString()); // hit poison area ~1_val~% + list.Add(1060429, prop); // hit poison area ~1_val~% } if ((prop = _weaponAttributes.HitLeechStam) != 0) { - list.Add(1060430, prop.ToString()); // hit stamina leech ~1_val~% + list.Add(1060430, prop); // hit stamina leech ~1_val~% } } } diff --git a/Projects/UOContent/Items/Armor/Glasses/FoldedSteelGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/FoldedSteelGlasses.cs index 52ad9a712..a28392ff9 100644 --- a/Projects/UOContent/Items/Armor/Glasses/FoldedSteelGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/FoldedSteelGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FoldedSteelGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/LightOfWayGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/LightOfWayGlasses.cs index bc41b7a3f..fdc40425a 100644 --- a/Projects/UOContent/Items/Armor/Glasses/LightOfWayGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/LightOfWayGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightOfWayGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/LyricalGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/LyricalGlasses.cs index 4a83f3d33..cf453cb1e 100644 --- a/Projects/UOContent/Items/Armor/Glasses/LyricalGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/LyricalGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LyricalGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/MaceShieldGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/MaceShieldGlasses.cs index 2a71e599f..dc7ca8b8b 100644 --- a/Projects/UOContent/Items/Armor/Glasses/MaceShieldGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/MaceShieldGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MaceShieldGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/MaritimeGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/MaritimeGlasses.cs index 7afdc60d6..0f892737e 100644 --- a/Projects/UOContent/Items/Armor/Glasses/MaritimeGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/MaritimeGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MaritimeGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/NecromanticGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/NecromanticGlasses.cs index 334cb7469..cbf3b455f 100644 --- a/Projects/UOContent/Items/Armor/Glasses/NecromanticGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/NecromanticGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NecromanticGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/PoisonedGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/PoisonedGlasses.cs index e96c2e41c..706d33f3a 100644 --- a/Projects/UOContent/Items/Armor/Glasses/PoisonedGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/PoisonedGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PoisonedGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/TradeGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/TradeGlasses.cs index 7bfd740a9..224a83870 100644 --- a/Projects/UOContent/Items/Armor/Glasses/TradeGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/TradeGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TradeGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/TreasureTrinketGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/TreasureTrinketGlasses.cs index a4e4df3be..f355f166c 100644 --- a/Projects/UOContent/Items/Armor/Glasses/TreasureTrinketGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/TreasureTrinketGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TreasureTrinketGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Glasses/WizardsGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/WizardsGlasses.cs index 1f9f93573..628dcfd0e 100644 --- a/Projects/UOContent/Items/Armor/Glasses/WizardsGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/WizardsGlasses.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WizardsGlasses : ElvenGlasses { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs b/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs index a4f7b9f23..3885e397a 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bascinet : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs b/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs index e6d891e3d..f863f6d7a 100644 --- a/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1451, 0x1456)] public partial class BoneHelm : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs index 6db814674..d1277dd77 100644 --- a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs +++ b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13BB, 0x13C0)] public partial class ChainCoif : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Helmets/Circlet.cs b/Projects/UOContent/Items/Armor/Helmets/Circlet.cs index 7d504bfa2..7544eb134 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Circlet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Circlet.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B6E, 0x3165)] public partial class Circlet : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs b/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs index 0de794c6b..b7a5197a4 100644 --- a/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CloseHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs b/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs index 81fb497da..80f2de0b5 100644 --- a/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1451, 0x1456)] public partial class DaemonHelm : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs b/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs index 26c0d373d..3df8b821a 100644 --- a/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2B70, 0x3167)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class GemmedCirclet : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/Helmet.cs b/Projects/UOContent/Items/Armor/Helmets/Helmet.cs index 9dd8a7a15..01b138abb 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Helmet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Helmet.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Helmet : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs b/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs index 455bc4550..dfeafd35b 100644 --- a/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs +++ b/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1db9, 0x1dba)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherCap : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs b/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs index 5278c1437..84926b157 100644 --- a/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NorseHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/OrcHelm.cs b/Projects/UOContent/Items/Armor/Helmets/OrcHelm.cs index 263922d7b..e7e34a718 100644 --- a/Projects/UOContent/Items/Armor/Helmets/OrcHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/OrcHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class OrcHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs b/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs index a9401e970..c4e2060cf 100644 --- a/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs b/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs index ce38ba388..5cd85bb25 100644 --- a/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2B71, 0x3168)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class RavenHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs b/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs index 673eb2e12..ed2b78800 100644 --- a/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2B6F, 0x3166)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class RoyalCirclet : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs b/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs index 59f169fc4..8d5e89a25 100644 --- a/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2B72, 0x3169)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class VultureHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs b/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs index f8609a28c..91c2f3b59 100644 --- a/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2B73, 0x316A)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class WingedHelm : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs index e46b414b6..83b7bb84c 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2FCB, 0x3181)] public partial class FemaleLeafChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs index 30e3edd50..0a6433ff9 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c06, 0x1c07)] public partial class FemaleLeatherChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs index 950141073..007d64209 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishLeatherArms")] public partial class GargishLeatherArmsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs index 8c973c861..5167c8e15 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishLeatherArms")] public partial class GargishLeatherArmsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs index 1af594db3..21a38d83c 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishLeatherChest")] public partial class GargishLeatherChestType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs index 5e0278116..5e91ef3a0 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishLeatherChest")] public partial class GargishLeatherChestType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs index e84097924..98d468165 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishLeatherKilt")] public partial class GargishLeatherKiltType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs index 89438bfb9..ef7e3491c 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishLeatherKilt")] public partial class GargishLeatherKiltType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs index 22bb593c9..6decd20a6 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishLeatherLegs")] public partial class GargishLeatherLegsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs index 5aa010f10..be0e8b395 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishLeatherLegs")] public partial class GargishLeatherLegsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs index 3773586de..52505040e 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x457E, 0x457F)] public partial class GargishLeatherWingArmor : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeafArms.cs b/Projects/UOContent/Items/Armor/Leather/LeafArms.cs index cafacb422..2eb21fbfd 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2FC8, 0x317E)] public partial class LeafArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeafChest.cs b/Projects/UOContent/Items/Armor/Leather/LeafChest.cs index 5a1919187..853aad33d 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2FC5, 0x317B)] public partial class LeafChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs index 611e5952f..fc8028be6 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable] - [Serializable(1)] + [SerializationGenerator(1)] public partial class LeafGloves : BaseArmor, IArcaneEquip { private int _maxArcaneCharges; @@ -87,13 +89,13 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (IsArcane) { - list.Add(1061837, "{0}\t{1}", _curArcaneCharges, _maxArcaneCharges); // arcane charges: ~1_val~ / ~2_val~ + list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); // arcane charges: ~1_val~ / ~2_val~ } } diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs b/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs index 0e024ef06..7361479ce 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class LeafGorget : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs b/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs index 1867289e3..6ba3d3593 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2FC9, 0x317F)] public partial class LeafLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs b/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs index 2aa652ddf..b627757e7 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2FCA, 0x3180)] public partial class LeafTonlet : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs index d2b399f33..f092fb371 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13cd, 0x13c5)] public partial class LeatherArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs index e480f1753..5e7bd8e9a 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c0a, 0x1c0b)] public partial class LeatherBustierArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs index 7bf28c097..e66f20ef1 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13cc, 0x13d3)] public partial class LeatherChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs b/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs index 0b389285c..a0450165e 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherDo : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs index 7a79e95fe..ef8005160 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable] - [Serializable(2, false)] + [SerializationGenerator(2, false)] public partial class LeatherGloves : BaseArmor, IArcaneEquip { private int _maxArcaneCharges; @@ -86,13 +88,13 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (IsArcane) { - list.Add(1061837, "{0}\t{1}", _curArcaneCharges, _maxArcaneCharges); // arcane charges: ~1_val~ / ~2_val~ + list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); // arcane charges: ~1_val~ / ~2_val~ } } diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs index 07b8baf08..42eb16daf 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherGorget : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs index 5a267ff4d..a4509020d 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherHaidate : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs index 107940e01..6b54d1695 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherHiroSode : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs b/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs index d26cd8793..a37ed89c0 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherJingasa : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs b/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs index 9c3d88b98..2d83b9148 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13cb, 0x13d2)] public partial class LeatherLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs b/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs index 4fda47995..5887692ab 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherMempo : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs index b0c09710b..19d7cd049 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherNinjaHood : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs index 05c5ab220..c27194003 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherNinjaJacket : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs index 3e3586bfc..0112fafa3 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherNinjaMitts : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs index c7fbd22e6..b21807d32 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherNinjaPants : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs b/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs index 08cadaf62..b18307d99 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c00, 0x1c01)] public partial class LeatherShorts : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs index 6c351757c..acaaa09fb 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c08, 0x1c09)] public partial class LeatherSkirt : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs index 6262a83dc..31df6ddeb 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeatherSuneate : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs b/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs index f24620620..e8d99a675 100644 --- a/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativePlateKabuto : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs b/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs index 5298e68f9..76bd975b4 100644 --- a/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c04, 0x1c05)] public partial class FemalePlateChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs b/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs index 2dcfce8a1..5ac5a42ab 100644 --- a/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B6D, 0x3164)] public partial class FemaleElvenPlateChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs index f98e3c58b..b35003750 100644 --- a/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HeavyPlateJingasa : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs index a8024d88f..1f35c50ae 100644 --- a/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightPlateJingasa : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateArms.cs b/Projects/UOContent/Items/Armor/Plate/PlateArms.cs index 2269150fa..1e4505195 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateArms.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1410, 0x1417)] public partial class PlateArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs b/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs index 8a6aa5289..cc882ec5b 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateBattleKabuto : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateChest.cs b/Projects/UOContent/Items/Armor/Plate/PlateChest.cs index 4a00f339d..60375189a 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1415, 0x1416)] public partial class PlateChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/PlateDo.cs b/Projects/UOContent/Items/Armor/Plate/PlateDo.cs index 44fdf73f2..de8ecd5f1 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateDo.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateDo.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateDo : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs b/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs index bf10b5751..2303f2a83 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1414, 0x1418)] public partial class PlateGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs b/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs index 10880dad2..27ea17e0f 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateGorget : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs b/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs index 04e192ce2..ae238cf3b 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateHaidate : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs b/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs index 7d0e43054..cb629b4ce 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateHatsuburi : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs b/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs index e565693f6..2f272a4a7 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateHiroSode : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs b/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs index c633ca873..cc69bfce6 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1411, 0x141a)] public partial class PlateLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs b/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs index c42e3c84b..257dcce97 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateMempo : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs b/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs index ae8298b63..dce3c24fa 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlateSuneate : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs index 592cc447c..3d3112286 100644 --- a/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SmallPlateJingasa : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs b/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs index cf23240c3..be98d3a65 100644 --- a/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StandardPlateKabuto : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs index 8517da829..582bfed08 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B6C, 0x3163)] public partial class WoodlandArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs index c90b21fce..9fb9e6567 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B67, 0x315E)] public partial class WoodlandChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs index 7ea514016..40455c089 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B6A, 0x3161)] public partial class WoodlandGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandGorget.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandGorget.cs index 492d1216e..1554576fe 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandGorget.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B69, 0x3160)] public partial class WoodlandGorget : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs index ee10d8f65..ec16588aa 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B6B, 0x3162)] public partial class WoodlandLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs b/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs index 5745de85a..6eb5864db 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13dc, 0x13d4)] public partial class RangerArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs b/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs index 2b573a11a..e2a6054f5 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13db, 0x13e2)] public partial class RangerChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs b/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs index 2a42a14d3..b51b766e6 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13d5, 0x13dd)] public partial class RangerGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs b/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs index 13c751303..e9a4a6949 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RangerGorget : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs b/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs index 39549df6e..7fae0424a 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13da, 0x13e1)] public partial class RangerLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs b/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs index b22ac9ca0..c9d1061e0 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13ee, 0x13ef)] public partial class RingmailArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs b/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs index 3c43daf7d..e01dd908e 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13ec, 0x13ed)] public partial class RingmailChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs b/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs index 15f3ab0d2..b57887f80 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13eb, 0x13f2)] public partial class RingmailGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs b/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs index 1704cbf71..189a8597d 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13f0, 0x13f1)] public partial class RingmailLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs index e9223b58f..8e28498d0 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStoneArms")] public partial class GargishStoneArmsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs index ec9a5cefc..9198bf879 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStoneArms")] public partial class GargishStoneArmsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs index 8aafab468..25b39d0eb 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStoneChest")] public partial class GargishStoneChestType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs index 78fa08650..81bb3b928 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStoneChest")] public partial class GargishStoneChestType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs index cf826661f..501a9cbf2 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStoneKilt")] public partial class GargishStoneKiltType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs index 7ca6f2363..43103cd72 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStoneKilt")] public partial class GargishStoneKiltType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs index 3974d969a..c8a683f91 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStoneLegs")] public partial class GargishStoneLegsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs index 90707fc02..f5ab12bbf 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStoneLegs")] public partial class GargishStoneLegsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs b/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs index 5a378aeed..9034f797a 100644 --- a/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c02, 0x1c03)] public partial class FemaleStuddedChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs index 6cd2fcb02..9d3279ae2 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStuddedArms")] public partial class GargishStuddedArmsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs index 35d76fbe2..c30396f45 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStuddedArms")] public partial class GargishStuddedArmsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs index 4245fbd33..6d3305544 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStuddedChest")] public partial class GargishStuddedChestType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs index 1c5a78f59..ef80c6847 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStuddedChest")] public partial class GargishStuddedChestType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs index fda72ba8d..2b61adb4d 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStuddedKilt")] public partial class GargishStuddedKiltType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs index 9ea5e74c9..a5d60040f 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStuddedKilt")] public partial class GargishStuddedKiltType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs index 81cb4bb1d..8b98f861b 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.GargishStuddedLegs")] public partial class GargishStuddedLegsType1 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs index aa2496357..3984ad6eb 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [TypeAlias("Server.Items.FemaleGargishStuddedLegs")] public partial class GargishStuddedLegsType2 : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/HideChest.cs b/Projects/UOContent/Items/Armor/Studded/HideChest.cs index d7c2ab30d..b853279ba 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B74, 0x316B)] public partial class HideChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs b/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs index cd7529b4b..acafa6b7f 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B79, 0x3170)] public partial class HideFemaleChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/HideGloves.cs b/Projects/UOContent/Items/Armor/Studded/HideGloves.cs index c3a6a915c..cc9bde92a 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B75, 0x316C)] public partial class HideGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/HideGorget.cs b/Projects/UOContent/Items/Armor/Studded/HideGorget.cs index 9473afc3d..831c816da 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2B76, 0x316D)] public partial class HideGorget : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/HidePants.cs b/Projects/UOContent/Items/Armor/Studded/HidePants.cs index 200861c35..3b952d3bc 100644 --- a/Projects/UOContent/Items/Armor/Studded/HidePants.cs +++ b/Projects/UOContent/Items/Armor/Studded/HidePants.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B78, 0x316F)] public partial class HidePants : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs b/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs index a91c1cb1a..0eb6b8b3d 100644 --- a/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs +++ b/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2B77, 0x316E)] public partial class HidePauldrons : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs index 3d16400d2..4fa9de780 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13dc, 0x13d4)] public partial class StuddedArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs index 5f55712ff..3dd466c49 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1c0c, 0x1c0d)] public partial class StuddedBustierArms : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs b/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs index 99d1ac8b8..2698f5b09 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13db, 0x13e2)] public partial class StuddedChest : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs b/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs index adf62707a..692076af7 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StuddedDo : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs index c6977efb1..aa2d585ea 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13d5, 0x13dd)] public partial class StuddedGloves : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs index d72ba4199..079d99e3a 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StuddedGorget : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs index b719c1fe9..26b41dd45 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StuddedHaidate : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs index 145e1c7cb..0b0afd5bd 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StuddedHiroSode : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs b/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs index 24a77c890..85863125b 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13da, 0x13e1)] public partial class StuddedLegs : BaseArmor { diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs b/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs index 252c836e7..31b0b9a82 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StuddedMempo : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs b/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs index 55b77c2a5..f57939ea1 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StuddedSuneate : BaseArmor { [Constructible] diff --git a/Projects/UOContent/Items/Body Parts/BonePile.cs b/Projects/UOContent/Items/Body Parts/BonePile.cs index 64e3e90d0..04fcadcda 100644 --- a/Projects/UOContent/Items/Body Parts/BonePile.cs +++ b/Projects/UOContent/Items/Body Parts/BonePile.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1B09, 0x1B10)] public partial class BonePile : Item, IScissorable { diff --git a/Projects/UOContent/Items/Body Parts/Head.cs b/Projects/UOContent/Items/Body Parts/Head.cs index 39f4078f7..4bcf2cdae 100644 --- a/Projects/UOContent/Items/Body Parts/Head.cs +++ b/Projects/UOContent/Items/Body Parts/Head.cs @@ -1,3 +1,5 @@ +using ModernUO.Serialization; + namespace Server.Items { public enum HeadType @@ -7,13 +9,15 @@ namespace Server.Items Tournament } - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class Head : Item { + [InvalidateProperties] [SerializableField(0)] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] private string _playerName; + [InvalidateProperties] [SerializableField(1)] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] private HeadType _headType; diff --git a/Projects/UOContent/Items/Body Parts/LeftArm.cs b/Projects/UOContent/Items/Body Parts/LeftArm.cs index 6e514fc52..1ff09dea0 100644 --- a/Projects/UOContent/Items/Body Parts/LeftArm.cs +++ b/Projects/UOContent/Items/Body Parts/LeftArm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeftArm : Item { [Constructible] diff --git a/Projects/UOContent/Items/Body Parts/LeftLeg.cs b/Projects/UOContent/Items/Body Parts/LeftLeg.cs index 620e169f9..2495b97d6 100644 --- a/Projects/UOContent/Items/Body Parts/LeftLeg.cs +++ b/Projects/UOContent/Items/Body Parts/LeftLeg.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LeftLeg : Item { [Constructible] diff --git a/Projects/UOContent/Items/Body Parts/RibCage.cs b/Projects/UOContent/Items/Body Parts/RibCage.cs index 0267b8165..0b02dfce0 100644 --- a/Projects/UOContent/Items/Body Parts/RibCage.cs +++ b/Projects/UOContent/Items/Body Parts/RibCage.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1B17, 0x1B18)] public partial class RibCage : Item, IScissorable { diff --git a/Projects/UOContent/Items/Body Parts/RightArm.cs b/Projects/UOContent/Items/Body Parts/RightArm.cs index 4adc9ca5a..2360cc447 100644 --- a/Projects/UOContent/Items/Body Parts/RightArm.cs +++ b/Projects/UOContent/Items/Body Parts/RightArm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RightArm : Item { [Constructible] diff --git a/Projects/UOContent/Items/Body Parts/RightLeg.cs b/Projects/UOContent/Items/Body Parts/RightLeg.cs index b9fcd60fb..c7eff68e7 100644 --- a/Projects/UOContent/Items/Body Parts/RightLeg.cs +++ b/Projects/UOContent/Items/Body Parts/RightLeg.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RightLeg : Item { [Constructible] diff --git a/Projects/UOContent/Items/Body Parts/Torso.cs b/Projects/UOContent/Items/Body Parts/Torso.cs index 68a1add55..8a26dbecf 100644 --- a/Projects/UOContent/Items/Body Parts/Torso.cs +++ b/Projects/UOContent/Items/Body Parts/Torso.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Torso : Item { [Constructible] diff --git a/Projects/UOContent/Items/Books/BaseBook.cs b/Projects/UOContent/Items/Books/BaseBook.cs index 52a703f82..ffd4ddd0b 100644 --- a/Projects/UOContent/Items/Books/BaseBook.cs +++ b/Projects/UOContent/Items/Books/BaseBook.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Buffers; using Server.ContextMenus; using Server.Gumps; @@ -7,7 +8,7 @@ using Server.Multis; namespace Server.Items { - [Serializable(5, false)] + [SerializationGenerator(5, false)] public partial class BaseBook : Item, ISecurable { [SerializableField(0)] @@ -173,7 +174,7 @@ namespace Server.Items } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (!string.IsNullOrEmpty(_title)) { diff --git a/Projects/UOContent/Items/Books/BlueBook.cs b/Projects/UOContent/Items/Books/BlueBook.cs index f8e1ef965..2a47dd817 100644 --- a/Projects/UOContent/Items/Books/BlueBook.cs +++ b/Projects/UOContent/Items/Books/BlueBook.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BlueBook : BaseBook { [Constructible] diff --git a/Projects/UOContent/Items/Books/BookPackets.cs b/Projects/UOContent/Items/Books/BookPackets.cs index 6eee9c451..8e9c6d257 100644 --- a/Projects/UOContent/Items/Books/BookPackets.cs +++ b/Projects/UOContent/Items/Books/BookPackets.cs @@ -22,14 +22,14 @@ namespace Server.Items { public static class BookPackets { - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0xD4, 0, true, HeaderChange); - IncomingPackets.Register(0x66, 0, true, ContentChange); - IncomingPackets.Register(0x93, 99, true, OldHeaderChange); + IncomingPackets.Register(0xD4, 0, true, &HeaderChange); + IncomingPackets.Register(0x66, 0, true, &ContentChange); + IncomingPackets.Register(0x93, 99, true, &OldHeaderChange); } - public static void OldHeaderChange(NetState state, CircularBufferReader reader, ref int packetLength) + public static void OldHeaderChange(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; @@ -48,7 +48,7 @@ namespace Server.Items book.Author = Utility.FixHtml(author); } - public static void HeaderChange(NetState state, CircularBufferReader reader, ref int packetLength) + public static void HeaderChange(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; @@ -84,7 +84,7 @@ namespace Server.Items book.Author = Utility.FixHtml(author); } - public static void ContentChange(NetState state, CircularBufferReader reader, ref int packetLength) + public static void ContentChange(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; @@ -135,7 +135,7 @@ namespace Server.Items public static void SendBookContent(this NetState ns, BaseBook book) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -183,7 +183,7 @@ namespace Server.Items public static void SendBookCover(this NetState ns, Mobile from, BaseBook book) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Books/BrownBook.cs b/Projects/UOContent/Items/Books/BrownBook.cs index e0083a744..f845a8875 100644 --- a/Projects/UOContent/Items/Books/BrownBook.cs +++ b/Projects/UOContent/Items/Books/BrownBook.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrownBook : BaseBook { [Constructible] diff --git a/Projects/UOContent/Items/Books/Defined/BlackthornWelcomeBook.cs b/Projects/UOContent/Items/Books/Defined/BlackthornWelcomeBook.cs index 78e0ffffd..2f103ccec 100644 --- a/Projects/UOContent/Items/Books/Defined/BlackthornWelcomeBook.cs +++ b/Projects/UOContent/Items/Books/Defined/BlackthornWelcomeBook.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class BlackthornWelcomeBook : RedBook { public static readonly BookContent Content = new( diff --git a/Projects/UOContent/Items/Books/Defined/DrakovsJournal.cs b/Projects/UOContent/Items/Books/Defined/DrakovsJournal.cs index 09b8c4bf6..eab1cc2e5 100644 --- a/Projects/UOContent/Items/Books/Defined/DrakovsJournal.cs +++ b/Projects/UOContent/Items/Books/Defined/DrakovsJournal.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class DrakovsJournal : BlueBook { public static readonly BookContent Content = new( diff --git a/Projects/UOContent/Items/Books/Defined/FropozJournal.cs b/Projects/UOContent/Items/Books/Defined/FropozJournal.cs index a2d361ea6..5fdbf1640 100644 --- a/Projects/UOContent/Items/Books/Defined/FropozJournal.cs +++ b/Projects/UOContent/Items/Books/Defined/FropozJournal.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class FropozJournal : RedBook { public static readonly BookContent Content = new( @@ -135,7 +137,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add("Fropoz's Journal"); } diff --git a/Projects/UOContent/Items/Books/Defined/KaburJournal.cs b/Projects/UOContent/Items/Books/Defined/KaburJournal.cs index 69da71ca6..7fa55e582 100644 --- a/Projects/UOContent/Items/Books/Defined/KaburJournal.cs +++ b/Projects/UOContent/Items/Books/Defined/KaburJournal.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class KaburJournal : RedBook { public static readonly BookContent Content = new( @@ -214,7 +216,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add("Khabur's Journal"); } diff --git a/Projects/UOContent/Items/Books/Defined/LibraryBooks.cs b/Projects/UOContent/Items/Books/Defined/LibraryBooks.cs index 6bdf73831..615bb756b 100644 --- a/Projects/UOContent/Items/Books/Defined/LibraryBooks.cs +++ b/Projects/UOContent/Items/Books/Defined/LibraryBooks.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class GrammarOfOrcish : BaseBook { public static readonly BookContent Content = new( @@ -246,7 +248,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class CallToAnarchy : BaseBook { public static readonly BookContent Content = new( @@ -407,7 +409,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArmsAndWeaponsPrimer : BaseBook { public static readonly BookContent Content = new( @@ -578,7 +580,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class SongOfSamlethe : BaseBook { public static readonly BookContent Content = new( @@ -647,7 +649,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TaleOfThreeTribes : BaseBook { public static readonly BookContent Content = new( @@ -759,7 +761,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class GuideToGuilds : BaseBook { public static readonly BookContent Content = new( @@ -947,7 +949,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class BirdsOfBritannia : BaseBook { public static readonly BookContent Content = new( @@ -1237,7 +1239,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class BritannianFlora : BaseBook { public static readonly BookContent Content = new( @@ -1432,7 +1434,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ChildrenTalesVol2 : BaseBook { public static readonly BookContent Content = new( @@ -1539,7 +1541,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TalesOfVesperVol1 : BaseBook { public static readonly BookContent Content = new( @@ -1760,7 +1762,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DeceitDungeonOfHorror : BaseBook { public static readonly BookContent Content = new( @@ -1892,7 +1894,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DimensionalTravel : BaseBook { public static readonly BookContent Content = new( @@ -2088,7 +2090,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class EthicalHedonism : BaseBook { public static readonly BookContent Content = new( @@ -2349,7 +2351,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class MyStory : BaseBook { public static readonly BookContent Content = new( @@ -2851,7 +2853,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DiversityOfOurLand : BaseBook { public static readonly BookContent Content = new( @@ -3014,7 +3016,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class QuestOfVirtues : BaseBook { public static readonly BookContent Content = new( @@ -3407,7 +3409,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class RegardingLlamas : BaseBook { public static readonly BookContent Content = new( @@ -3461,7 +3463,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TalkingToWisps : BaseBook { public static readonly BookContent Content = new( @@ -3561,7 +3563,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TamingDragons : BaseBook { public static readonly BookContent Content = new( @@ -3651,7 +3653,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class BoldStranger : BaseBook { public static readonly BookContent Content = new( @@ -3804,7 +3806,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class BurningOfTrinsic : BaseBook { public static readonly BookContent Content = new( @@ -4048,7 +4050,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TheFight : BaseBook { public static readonly BookContent Content = new( @@ -4190,7 +4192,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class LifeOfATravellingMinstrel : BaseBook { public static readonly BookContent Content = new( @@ -4379,7 +4381,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class MajorTradeAssociation : BaseBook { public static readonly BookContent Content = new( @@ -4569,7 +4571,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class RankingsOfTrades : BaseBook { public static readonly BookContent Content = new( @@ -4670,7 +4672,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class WildGirlOfTheForest : BaseBook { public static readonly BookContent Content = new( @@ -4844,7 +4846,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class TreatiseOnAlchemy : BaseBook { public static readonly BookContent Content = new( @@ -4973,7 +4975,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class VirtueBook : BaseBook { public static readonly BookContent Content = new( diff --git a/Projects/UOContent/Items/Books/Defined/NewAquariumBook.cs b/Projects/UOContent/Items/Books/Defined/NewAquariumBook.cs index 5604809c5..da04506f7 100644 --- a/Projects/UOContent/Items/Books/Defined/NewAquariumBook.cs +++ b/Projects/UOContent/Items/Books/Defined/NewAquariumBook.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class NewAquariumBook : BlueBook { public static readonly BookContent Content = new( diff --git a/Projects/UOContent/Items/Books/Defined/TranslatedGargoyleJournal.cs b/Projects/UOContent/Items/Books/Defined/TranslatedGargoyleJournal.cs index dd676212d..f7f46ad6f 100644 --- a/Projects/UOContent/Items/Books/Defined/TranslatedGargoyleJournal.cs +++ b/Projects/UOContent/Items/Books/Defined/TranslatedGargoyleJournal.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TranslatedGargoyleJournal : BlueBook { public static readonly BookContent Content = new( @@ -116,7 +118,7 @@ namespace Server.Items public override BookContent DefaultContent => Content; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add("Translated Gargoyle Journal"); } diff --git a/Projects/UOContent/Items/Books/RedBook.cs b/Projects/UOContent/Items/Books/RedBook.cs index 2ea2a86c5..4832ca690 100644 --- a/Projects/UOContent/Items/Books/RedBook.cs +++ b/Projects/UOContent/Items/Books/RedBook.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RedBook : BaseBook { [Constructible] diff --git a/Projects/UOContent/Items/Books/TanBook.cs b/Projects/UOContent/Items/Books/TanBook.cs index 0bb86277f..07fdd67cd 100644 --- a/Projects/UOContent/Items/Books/TanBook.cs +++ b/Projects/UOContent/Items/Books/TanBook.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TanBook : BaseBook { [Constructible] diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs index 21c57f33e..17d3670af 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using ModernUO.Serialization; using Server.Collections; using Server.Network; @@ -33,7 +34,7 @@ namespace Server.Items public static bool CheckReplyTime(DateTime time) => time + ThreadReplyTime < Core.Now; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1E5E, 0x1E5F)] public partial class BulletinBoard : BaseBulletinBoard { @@ -43,7 +44,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseBulletinBoard : Item { [SerializableField(0)] diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs index 5a5ada1c9..53086b70f 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs @@ -24,9 +24,9 @@ namespace Server.Network { public static class BulletinBoardPackets { - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0x71, 0, true, BBClientRequest); + IncomingPackets.Register(0x71, 0, true, &BBClientRequest); } public static string FormatTS(TimeSpan ts) @@ -48,7 +48,7 @@ namespace Server.Network return $"{seconds} second{(seconds == 1 ? "" : "s")}"; } - public static void BBClientRequest(NetState state, CircularBufferReader reader, ref int packetLength) + public static void BBClientRequest(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; @@ -168,7 +168,7 @@ namespace Server.Network public static void SendBBDisplayBoard(this NetState ns, BaseBulletinBoard board) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -203,7 +203,7 @@ namespace Server.Network public static void SendBBMessage(this NetState ns, BaseBulletinBoard board, BulletinMessage msg, bool content = false) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs index 3a2ff59f7..cfc3b2024 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] public partial class BulletinMessage : Item { public BulletinMessage(Mobile poster, BulletinMessage thread, string subject, string[] lines) : base(0xEB0) diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactLargeVase.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactLargeVase.cs index 8c54e75c3..b71bd9f6b 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactLargeVase.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactLargeVase.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ArtifactLargeVase : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactVase.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactVase.cs index 183fb8dd8..a78cc2208 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactVase.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/ArtifactVase.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ArtifactVase : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/DemonSkull.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/DemonSkull.cs index 2b80cff4a..177553ed7 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/DemonSkull.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/DemonSkull.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DemonSkull : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/DirtPatch.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/DirtPatch.cs index 20ba1d3d0..0940a950d 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/DirtPatch.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/DirtPatch.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DirtPatch : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/EvilIdolSkull.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/EvilIdolSkull.cs index 1f04c447a..fa9310a36 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/EvilIdolSkull.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/EvilIdolSkull.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class EvilIdolSkull : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/Futon.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/Futon.cs index 6b60903b0..78e53af72 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/Futon.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/Futon.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Futon : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/LavaTile.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/LavaTile.cs index 1c5fa4861..b74a353d5 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/LavaTile.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/LavaTile.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LavaTile : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/Pier.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/Pier.cs index ab0fc2255..45f842475 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/Pier.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/Pier.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Pier : Item { /* diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs index 4263727e9..5834239ec 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SkullPole : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/SwampTile.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/SwampTile.cs index 40121b44b..6982e4887 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/SwampTile.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/SwampTile.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SwampTile : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/TatteredAncientMummyWrapping.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/TatteredAncientMummyWrapping.cs index fbb942680..2c06008e6 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/TatteredAncientMummyWrapping.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/TatteredAncientMummyWrapping.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TatteredAncientMummyWrapping : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/WallBlood.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/WallBlood.cs index 8c0aecbc0..cc4e20d33 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/WallBlood.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/WallBlood.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WallBlood : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/WaterTile.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/WaterTile.cs index 313491582..172e427a4 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/WaterTile.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/WaterTile.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WaterTile : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/Web.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/Web.cs index 75758ccfd..b93c086f3 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/Web.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/Web.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Web : Item { private static readonly int[] ItemIds = { 0x10d7, 0x10d8, 0x10dd }; diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/WindSpirit.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/WindSpirit.cs index 4fd415434..7e1d98e34 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/WindSpirit.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/WindSpirit.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WindSpirit : Item { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/ANecromancerShroud.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/ANecromancerShroud.cs index d668d63df..7c9cade7d 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/ANecromancerShroud.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/ANecromancerShroud.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ANecromancerShroud : Robe { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/BraveKnightOfTheBritannia.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/BraveKnightOfTheBritannia.cs index bbcf01a26..f85db031c 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/BraveKnightOfTheBritannia.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/BraveKnightOfTheBritannia.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BraveKnightOfTheBritannia : Katana { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/CaptainJohnsHat.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/CaptainJohnsHat.cs index 08b0a775d..23451b324 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/CaptainJohnsHat.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/CaptainJohnsHat.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CaptainJohnsHat : TricorneHat { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/DetectiveBoots.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/DetectiveBoots.cs index 5654d2f2d..ad10d215a 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/DetectiveBoots.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/DetectiveBoots.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DetectiveBoots : Boots { private int m_Level; diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/DjinnisRing.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/DjinnisRing.cs index b43cf24eb..a14acdba1 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/DjinnisRing.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/DjinnisRing.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DjinnisRing : SilverRing { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/EmbroideredOakLeafCloak.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/EmbroideredOakLeafCloak.cs index 0b7e24153..3ae676a8a 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/EmbroideredOakLeafCloak.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/EmbroideredOakLeafCloak.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class EmbroideredOakLeafCloak : BaseOuterTorso { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/GauntletsOfAnger.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/GauntletsOfAnger.cs index 2c5bc40ec..8369e6d08 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/GauntletsOfAnger.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/GauntletsOfAnger.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GuantletsOfAnger : PlateGloves { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/LieutenantOfTheBritannianRoyalGuard.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/LieutenantOfTheBritannianRoyalGuard.cs index 5dad1988a..f0d5f9446 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/LieutenantOfTheBritannianRoyalGuard.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/LieutenantOfTheBritannianRoyalGuard.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LieutenantOfTheBritannianRoyalGuard : BodySash { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/OblivionsNeedle.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/OblivionsNeedle.cs index 38413d3a5..628aaedbd 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/OblivionsNeedle.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/OblivionsNeedle.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class OblivionsNeedle : Dagger { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/RoyalGuardSurvivalKnife.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/RoyalGuardSurvivalKnife.cs index 386d03981..63418410d 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/RoyalGuardSurvivalKnife.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/RoyalGuardSurvivalKnife.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RoyalGuardSurvivalKnife : SkinningKnife { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/SamaritanRobe.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/SamaritanRobe.cs index 05d33aff1..bb06bd893 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/SamaritanRobe.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/SamaritanRobe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SamaritanRobe : Robe { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/TheMostKnowledgePerson.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/TheMostKnowledgePerson.cs index 26e825c14..604f860f0 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/TheMostKnowledgePerson.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/TheMostKnowledgePerson.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheMostKnowledgePerson : BaseOuterTorso { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Shared/TheRobeOfBritanniaAri.cs b/Projects/UOContent/Items/Champion Artifacts/Shared/TheRobeOfBritanniaAri.cs index c9ebf2fa5..77d947fc8 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Shared/TheRobeOfBritanniaAri.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Shared/TheRobeOfBritanniaAri.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheRobeOfBritanniaAri : BaseOuterTorso { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/AcidProofRobe.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/AcidProofRobe.cs index 5da7d1c85..7f81126d5 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/AcidProofRobe.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/AcidProofRobe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AcidProofRobe : Robe { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/Calm.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/Calm.cs index 7c22325c7..8aca9981e 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/Calm.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/Calm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Calm : Halberd { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/CrownOfTalKeesh.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/CrownOfTalKeesh.cs index 483529384..58eefd2db 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/CrownOfTalKeesh.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/CrownOfTalKeesh.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CrownOfTalKeesh : Bandana { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/FangOfRactus.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/FangOfRactus.cs index 6586b7216..91c023041 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/FangOfRactus.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/FangOfRactus.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FangOfRactus : Kryss { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/GladiatorsCollar.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/GladiatorsCollar.cs index 197b7f9af..efe64c461 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/GladiatorsCollar.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/GladiatorsCollar.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GladiatorsCollar : PlateGorget { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/OrcChieftainHelm.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/OrcChieftainHelm.cs index ebd3a5c79..ce30b89af 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/OrcChieftainHelm.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/OrcChieftainHelm.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class OrcChieftainHelm : OrcHelm { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/Pacify.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/Pacify.cs index c78ac17f7..996fcf0cc 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/Pacify.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/Pacify.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Pacify : Pike { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/Quell.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/Quell.cs index 821c2b5f6..56cd24045 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/Quell.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/Quell.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Quell : Bardiche { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/ShroudOfDeceit.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/ShroudOfDeceit.cs index 83c94194c..77a304f35 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/ShroudOfDeceit.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/ShroudOfDeceit.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShroudOfDeciet : BoneChest { [Constructible] diff --git a/Projects/UOContent/Items/Champion Artifacts/Unique/Subdue.cs b/Projects/UOContent/Items/Champion Artifacts/Unique/Subdue.cs index 61b3efc9e..2fcc7e9ee 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Unique/Subdue.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Unique/Subdue.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Subdue : Scythe { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Artifacts/CrimsonCincture.cs b/Projects/UOContent/Items/Clothing/Artifacts/CrimsonCincture.cs index ea81189ec..9088a8a64 100644 --- a/Projects/UOContent/Items/Clothing/Artifacts/CrimsonCincture.cs +++ b/Projects/UOContent/Items/Clothing/Artifacts/CrimsonCincture.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CrimsonCincture : HalfApron { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Artifacts/DivineCountenance.cs b/Projects/UOContent/Items/Clothing/Artifacts/DivineCountenance.cs index 302c2acec..b67adf5b2 100644 --- a/Projects/UOContent/Items/Clothing/Artifacts/DivineCountenance.cs +++ b/Projects/UOContent/Items/Clothing/Artifacts/DivineCountenance.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DivineCountenance : HornedTribalMask { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Artifacts/HatOfTheMagi.cs b/Projects/UOContent/Items/Clothing/Artifacts/HatOfTheMagi.cs index 717d14ac1..a4ca80c28 100644 --- a/Projects/UOContent/Items/Clothing/Artifacts/HatOfTheMagi.cs +++ b/Projects/UOContent/Items/Clothing/Artifacts/HatOfTheMagi.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HatOfTheMagi : WizardsHat { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Artifacts/HuntersHeaddress.cs b/Projects/UOContent/Items/Clothing/Artifacts/HuntersHeaddress.cs index eef2c19e4..eac1867bd 100644 --- a/Projects/UOContent/Items/Clothing/Artifacts/HuntersHeaddress.cs +++ b/Projects/UOContent/Items/Clothing/Artifacts/HuntersHeaddress.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HuntersHeaddress : DeerMask { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Artifacts/SpiritOfTheTotem.cs b/Projects/UOContent/Items/Clothing/Artifacts/SpiritOfTheTotem.cs index ff90f494f..bb995fa71 100644 --- a/Projects/UOContent/Items/Clothing/Artifacts/SpiritOfTheTotem.cs +++ b/Projects/UOContent/Items/Clothing/Artifacts/SpiritOfTheTotem.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SpiritOfTheTotem : BearMask { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs b/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs new file mode 100644 index 000000000..fe0902747 --- /dev/null +++ b/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs @@ -0,0 +1,90 @@ +namespace Server.Items; + +public partial class BaseClothing +{ + private void MigrateFrom(V6Content content) + { + _rawResource = content.RawResource ?? DefaultResource; + _attributes = content.Attributes ?? AttributesDefaultValue(); + _clothingAttributes = content.ClothingAttributes ?? ClothingAttributesDefaultValue(); + _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); + _resistances = content.Resistances ?? ResistancesDefaultValue(); + _maxHitPoints = content.MaxHitPoints ?? 0; + _playerConstructed = content.PlayerConstructed; + var crafter = content.Crafter; + Timer.StartTimer(() => _crafter = crafter?.RawName); + _quality = content.Quality ?? ClothingQuality.Regular; + _strReq = content.StrRequirement ?? -1; + } + + // Version 5 (pre-codegen) + private void Deserialize(IGenericReader reader, int version) + { + var flags = (OldSaveFlag)reader.ReadEncodedInt(); + + if (GetSaveFlag(flags, OldSaveFlag.Resource)) + { + _rawResource = (CraftResource)reader.ReadEncodedInt(); + } + else + { + _rawResource = DefaultResource; + } + + Attributes = new AosAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.Attributes)) + { + Attributes.Deserialize(reader); + } + + ClothingAttributes = new AosArmorAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.ClothingAttributes)) + { + ClothingAttributes.Deserialize(reader); + } + + SkillBonuses = new AosSkillBonuses(this); + + if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) + { + SkillBonuses.Deserialize(reader); + } + + Resistances = new AosElementAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.Resistances)) + { + Resistances.Deserialize(reader); + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxHitPoints)) + { + _maxHitPoints = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.HitPoints)) + { + _hitPoints = reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Crafter)) + { + var crafter = reader.ReadEntity(); + Timer.StartTimer(() => _crafter = crafter?.RawName); + } + + if (GetSaveFlag(flags, OldSaveFlag.Quality)) + { + _quality = (ClothingQuality)reader.ReadEncodedInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.StrReq)) + { + _strReq = reader.ReadEncodedInt(); + } + + PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); + } +} diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.cs b/Projects/UOContent/Items/Clothing/BaseClothing.cs index 251d86ad0..11dd69859 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Craft; using Server.Ethics; using Server.Factions; @@ -22,7 +23,7 @@ namespace Server.Items int MaxArcaneCharges { get; set; } } - [Serializable(6, false)] + [SerializationGenerator(7, false)] public abstract partial class BaseClothing : Item, IDyable, IScissorable, IFactionItem, ICraftable, IWearableDurability { [SerializableField(0, "private", "private")] @@ -93,7 +94,7 @@ namespace Server.Items [InvalidateProperties] [SerializableField(8)] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] - private Mobile _crafter; + private string _crafter; [SerializableFieldSaveFlag(8)] private bool ShouldSerializeCrafter() => _crafter != null; @@ -193,7 +194,7 @@ namespace Server.Items if (makersMark) { - Crafter = from; + Crafter = from.RawName; } if (DefaultResource != CraftResource.None) @@ -270,25 +271,33 @@ namespace Server.Items var item = system.CraftItems.SearchFor(GetType()); - if (item?.Resources.Count == 1 && item.Resources[0].Amount >= 2) + if (item?.Resources.Count == 1) { - try + var resource = item.Resources[0]; + if (resource.Amount >= 2) { - var info = CraftResources.GetInfo(_rawResource); + try + { + var info = CraftResources.GetInfo(_rawResource); - var resourceType = info.ResourceTypes?[0] ?? item.Resources[0].ItemType; + Type resourceType = null; + if (info?.ResourceTypes.Length > 0) + { + resourceType = info.ResourceTypes[0]; + } - var res = resourceType.CreateInstance(); + var res = (resourceType ?? resource.ItemType).CreateInstance(); - ScissorHelper(from, res, PlayerConstructed ? item.Resources[0].Amount / 2 : 1); + ScissorHelper(from, res, PlayerConstructed ? resource.Amount / 2 : 1); - res.LootType = LootType.Regular; + res.LootType = LootType.Regular; - return true; - } - catch - { - // ignored + return true; + } + catch + { + // ignored + } } } @@ -509,21 +518,21 @@ namespace Server.Items return; } - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - parent.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + parent.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - parent.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + parent.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - parent.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + parent.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } @@ -610,11 +619,11 @@ namespace Server.Items SkillBonuses.Remove(); } - var modName = Serial.ToString(); + var serial = Serial; - mob.RemoveStatMod($"{modName}Str"); - mob.RemoveStatMod($"{modName}Dex"); - mob.RemoveStatMod($"{modName}Int"); + mob.RemoveStatMod($"{serial}Str"); + mob.RemoveStatMod($"{serial}Dex"); + mob.RemoveStatMod($"{serial}Int"); mob.CheckStatTimers(); } @@ -653,9 +662,7 @@ namespace Server.Items }; } - private string GetNameString() => Name ?? $"#{LabelNumber}"; - - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var oreType = _rawResource switch { @@ -679,27 +686,36 @@ namespace Server.Items _ => 0 }; + var name = Name; + if (oreType != 0) { - list.Add(1053099, "#{0}\t{1}", oreType, GetNameString()); // ~1_oretype~ ~2_armortype~ + if (name != null) + { + list.Add(1053099, $"{oreType:#}\t{name}"); // ~1_oretype~ ~2_armortype~ + } + else + { + list.Add(1053099, $"{oreType:#}\t{LabelNumber:#}"); // ~1_oretype~ ~2_armortype~ + } } - else if (Name == null) + else if (name == null) { list.Add(LabelNumber); } else { - list.Add(Name); + list.Add(name); } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (_crafter != null) { - list.Add(1050043, _crafter.Name); // crafted by ~1_NAME~ + list.Add(1050043, _crafter); // crafted by ~1_NAME~ } if (_factionState != null) @@ -728,72 +744,72 @@ namespace Server.Items if ((prop = ArtifactRarity) > 0) { - list.Add(1061078, prop.ToString()); // artifact rarity ~1_val~ + list.Add(1061078, prop); // artifact rarity ~1_val~ } if ((prop = Attributes.WeaponDamage) != 0) { - list.Add(1060401, prop.ToString()); // damage increase ~1_val~% + list.Add(1060401, prop); // damage increase ~1_val~% } if ((prop = Attributes.DefendChance) != 0) { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% + list.Add(1060408, prop); // defense chance increase ~1_val~% } if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ + list.Add(1060409, prop); // dexterity bonus ~1_val~ } if ((prop = Attributes.EnhancePotions) != 0) { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% + list.Add(1060411, prop); // enhance potions ~1_val~% } if ((prop = Attributes.CastRecovery) != 0) { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ + list.Add(1060412, prop); // faster cast recovery ~1_val~ } if ((prop = Attributes.CastSpeed) != 0) { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ + list.Add(1060413, prop); // faster casting ~1_val~ } if ((prop = Attributes.AttackChance) != 0) { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% + list.Add(1060415, prop); // hit chance increase ~1_val~% } if ((prop = Attributes.BonusHits) != 0) { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ + list.Add(1060431, prop); // hit point increase ~1_val~ } if ((prop = Attributes.BonusInt) != 0) { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ + list.Add(1060432, prop); // intelligence bonus ~1_val~ } if ((prop = Attributes.LowerManaCost) != 0) { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% + list.Add(1060433, prop); // lower mana cost ~1_val~% } if ((prop = Attributes.LowerRegCost) != 0) { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% + list.Add(1060434, prop); // lower reagent cost ~1_val~% } if ((prop = ClothingAttributes.LowerStatReq) != 0) { - list.Add(1060435, prop.ToString()); // lower requirements ~1_val~% + list.Add(1060435, prop); // lower requirements ~1_val~% } if ((prop = Attributes.Luck) != 0) { - list.Add(1060436, prop.ToString()); // luck ~1_val~ + list.Add(1060436, prop); // luck ~1_val~ } if (ClothingAttributes.MageArmor != 0) @@ -803,12 +819,12 @@ namespace Server.Items if ((prop = Attributes.BonusMana) != 0) { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ + list.Add(1060439, prop); // mana increase ~1_val~ } if ((prop = Attributes.RegenMana) != 0) { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ + list.Add(1060440, prop); // mana regeneration ~1_val~ } if (Attributes.NightSight != 0) @@ -818,22 +834,22 @@ namespace Server.Items if ((prop = Attributes.ReflectPhysical) != 0) { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% + list.Add(1060442, prop); // reflect physical damage ~1_val~% } if ((prop = Attributes.RegenStam) != 0) { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ + list.Add(1060443, prop); // stamina regeneration ~1_val~ } if ((prop = Attributes.RegenHits) != 0) { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ + list.Add(1060444, prop); // hit point regeneration ~1_val~ } if ((prop = ClothingAttributes.SelfRepair) != 0) { - list.Add(1060450, prop.ToString()); // self repair ~1_val~ + list.Add(1060450, prop); // self repair ~1_val~ } if (Attributes.SpellChanneling != 0) @@ -843,44 +859,44 @@ namespace Server.Items if ((prop = Attributes.SpellDamage) != 0) { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% + list.Add(1060483, prop); // spell damage increase ~1_val~% } if ((prop = Attributes.BonusStam) != 0) { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ + list.Add(1060484, prop); // stamina increase ~1_val~ } if ((prop = Attributes.BonusStr) != 0) { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ + list.Add(1060485, prop); // strength bonus ~1_val~ } if ((prop = Attributes.WeaponSpeed) != 0) { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% + list.Add(1060486, prop); // swing speed increase ~1_val~% } if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) { - list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~% + list.Add(1075210, prop); // Increased Karma Loss ~1val~% } AddResistanceProperties(list); if ((prop = ClothingAttributes.DurabilityBonus) > 0) { - list.Add(1060410, prop.ToString()); // durability ~1_val~% + list.Add(1060410, prop); // durability ~1_val~% } if ((prop = ComputeStatReq(StatType.Str)) > 0) { - list.Add(1061170, prop.ToString()); // strength requirement ~1_val~ + list.Add(1061170, prop); // strength requirement ~1_val~ } if (_hitPoints >= 0 && _maxHitPoints > 0) { - list.Add(1060639, "{0}\t{1}", _hitPoints, _maxHitPoints); // durability ~1_val~ / ~2_val~ + list.Add(1060639, $"{_hitPoints}\t{_maxHitPoints}"); // durability ~1_val~ / ~2_val~ } } @@ -907,7 +923,7 @@ namespace Server.Items return; } - from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter?.RawName, false, attrs); + from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs); } public virtual void AddEquipInfoAttributes(Mobile from, List attrs) @@ -964,83 +980,6 @@ namespace Server.Items private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - private void Deserialize(IGenericReader reader, int version) - { - var flags = (OldSaveFlag)reader.ReadEncodedInt(); - - if (GetSaveFlag(flags, OldSaveFlag.Resource)) - { - _rawResource = (CraftResource)reader.ReadEncodedInt(); - } - else - { - _rawResource = DefaultResource; - } - - Attributes = new AosAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.Attributes)) - { - Attributes.Deserialize(reader); - } - - ClothingAttributes = new AosArmorAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.ClothingAttributes)) - { - ClothingAttributes.Deserialize(reader); - } - - SkillBonuses = new AosSkillBonuses(this); - - if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) - { - SkillBonuses.Deserialize(reader); - } - - Resistances = new AosElementAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.Resistances)) - { - Resistances.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxHitPoints)) - { - _maxHitPoints = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.HitPoints)) - { - _hitPoints = reader.ReadEncodedInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Crafter)) - { - _crafter = reader.ReadEntity(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Quality)) - { - _quality = (ClothingQuality)reader.ReadEncodedInt(); - } - else - { - _quality = ClothingQuality.Regular; - } - - if (GetSaveFlag(flags, OldSaveFlag.StrReq)) - { - _strReq = reader.ReadEncodedInt(); - } - else - { - _strReq = -1; - } - - PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); - } - [AfterDeserialization] private void AfterDeserialization() { diff --git a/Projects/UOContent/Items/Clothing/Cloaks.cs b/Projects/UOContent/Items/Clothing/Cloaks.cs index f5b3a0a5f..d5356dde2 100644 --- a/Projects/UOContent/Items/Clothing/Cloaks.cs +++ b/Projects/UOContent/Items/Clothing/Cloaks.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseCloak : BaseClothing { public BaseCloak(int itemID, int hue = 0) : base(itemID, Layer.Cloak, hue) @@ -11,7 +12,7 @@ namespace Server.Items } [Flippable] - [Serializable(2, false)] + [SerializationGenerator(2, false)] public partial class Cloak : BaseCloak, IArcaneEquip { private int _maxArcaneCharges; @@ -79,13 +80,13 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (IsArcane) { - list.Add(1061837, "{0}\t{1}", _curArcaneCharges, _maxArcaneCharges); // arcane charges: ~1_val~ / ~2_val~ + list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); // arcane charges: ~1_val~ / ~2_val~ } } @@ -111,7 +112,7 @@ namespace Server.Items } [Flippable] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RewardCloak : BaseCloak, IRewardItem { [InvalidateProperties] @@ -162,7 +163,7 @@ namespace Server.Items return false; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -188,7 +189,7 @@ namespace Server.Items } [Flippable(0x230A, 0x2309)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FurCape : BaseCloak { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Hats.cs b/Projects/UOContent/Items/Clothing/Hats.cs index 98c31d746..d6a9167d7 100644 --- a/Projects/UOContent/Items/Clothing/Hats.cs +++ b/Projects/UOContent/Items/Clothing/Hats.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Craft; using Server.Misc; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseHat : BaseClothing, IShipwreckedItem { [SerializableField(0)] @@ -27,7 +28,7 @@ namespace Server.Items } } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); @@ -57,7 +58,7 @@ namespace Server.Items } [Flippable(0x2798, 0x27E3)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Kasa : BaseHat { [Constructible] @@ -74,7 +75,7 @@ namespace Server.Items } [Flippable(0x278F, 0x27DA)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ClothNinjaHood : BaseHat { [Constructible] @@ -91,7 +92,7 @@ namespace Server.Items } [Flippable(0x2306, 0x2305)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FlowerGarland : BaseHat { [Constructible] @@ -107,7 +108,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FloppyHat : BaseHat { [Constructible] @@ -123,7 +124,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WideBrimHat : BaseHat { [Constructible] @@ -139,7 +140,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Cap : BaseHat { [Constructible] @@ -155,7 +156,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SkullCap : BaseHat { [Constructible] @@ -171,7 +172,7 @@ namespace Server.Items public override int InitMaxHits => Core.ML ? 28 : 12; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bandana : BaseHat { [Constructible] @@ -187,7 +188,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BearMask : BaseHat { [Constructible] @@ -209,7 +210,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DeerMask : BaseHat { [Constructible] @@ -231,7 +232,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HornedTribalMask : BaseHat { [Constructible] @@ -253,7 +254,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TribalMask : BaseHat { [Constructible] @@ -275,7 +276,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TallStrawHat : BaseHat { [Constructible] @@ -291,7 +292,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StrawHat : BaseHat { [Constructible] @@ -307,7 +308,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class OrcishKinMask : BaseHat { [Constructible] @@ -357,7 +358,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SavageMask : BaseHat { [Constructible] @@ -396,7 +397,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WizardsHat : BaseHat { [Constructible] @@ -412,7 +413,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MagicWizardsHat : BaseHat { [Constructible] @@ -434,7 +435,7 @@ namespace Server.Items public override int BaseIntBonus => +5; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bonnet : BaseHat { [Constructible] @@ -450,7 +451,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FeatheredHat : BaseHat { [Constructible] @@ -466,7 +467,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TricorneHat : BaseHat { [Constructible] @@ -482,7 +483,7 @@ namespace Server.Items public override int InitMaxHits => 30; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class JesterHat : BaseHat { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/MiddleTorso.cs b/Projects/UOContent/Items/Clothing/MiddleTorso.cs index ae7957bcd..27e3bc4b6 100644 --- a/Projects/UOContent/Items/Clothing/MiddleTorso.cs +++ b/Projects/UOContent/Items/Clothing/MiddleTorso.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseMiddleTorso : BaseClothing { public BaseMiddleTorso(int itemID, int hue = 0) : base(itemID, Layer.MiddleTorso, hue) @@ -8,7 +10,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1541, 0x1542)] public partial class BodySash : BaseMiddleTorso { @@ -16,7 +18,7 @@ namespace Server.Items public BodySash(int hue = 0) : base(0x1541, hue) => Weight = 1.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x153d, 0x153e)] public partial class FullApron : BaseMiddleTorso { @@ -24,7 +26,7 @@ namespace Server.Items public FullApron(int hue = 0) : base(0x153d, hue) => Weight = 4.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1f7b, 0x1f7c)] public partial class Doublet : BaseMiddleTorso { @@ -32,7 +34,7 @@ namespace Server.Items public Doublet(int hue = 0) : base(0x1F7B, hue) => Weight = 2.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1ffd, 0x1ffe)] public partial class Surcoat : BaseMiddleTorso { @@ -40,7 +42,7 @@ namespace Server.Items public Surcoat(int hue = 0) : base(0x1FFD, hue) => Weight = 6.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1fa1, 0x1fa2)] public partial class Tunic : BaseMiddleTorso { @@ -48,7 +50,7 @@ namespace Server.Items public Tunic(int hue = 0) : base(0x1FA1, hue) => Weight = 5.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x2310, 0x230F)] public partial class FormalShirt : BaseMiddleTorso { @@ -56,7 +58,7 @@ namespace Server.Items public FormalShirt(int hue = 0) : base(0x2310, hue) => Weight = 1.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1f9f, 0x1fa0)] public partial class JesterSuit : BaseMiddleTorso { @@ -64,7 +66,7 @@ namespace Server.Items public JesterSuit(int hue = 0) : base(0x1F9F, hue) => Weight = 4.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x27A1, 0x27EC)] public partial class JinBaori : BaseMiddleTorso { diff --git a/Projects/UOContent/Items/Clothing/OuterLegs.cs b/Projects/UOContent/Items/Clothing/OuterLegs.cs index d5bef9eea..d060c306f 100644 --- a/Projects/UOContent/Items/Clothing/OuterLegs.cs +++ b/Projects/UOContent/Items/Clothing/OuterLegs.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseOuterLegs : BaseClothing { public BaseOuterLegs(int itemID, int hue = 0) : base(itemID, Layer.OuterLegs, hue) @@ -8,7 +10,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x230C, 0x230B)] public partial class FurSarong : BaseOuterLegs { @@ -16,7 +18,7 @@ namespace Server.Items public FurSarong(int hue = 0) : base(0x230C, hue) => Weight = 3.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1516, 0x1531)] public partial class Skirt : BaseOuterLegs { @@ -24,7 +26,7 @@ namespace Server.Items public Skirt(int hue = 0) : base(0x1516, hue) => Weight = 4.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1537, 0x1538)] public partial class Kilt : BaseOuterLegs { @@ -32,7 +34,7 @@ namespace Server.Items public Kilt(int hue = 0) : base(0x1537, hue) => Weight = 2.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x279A, 0x27E5)] public partial class Hakama : BaseOuterLegs { diff --git a/Projects/UOContent/Items/Clothing/OuterTorso.cs b/Projects/UOContent/Items/Clothing/OuterTorso.cs index 07cdf59ab..edd737c2f 100644 --- a/Projects/UOContent/Items/Clothing/OuterTorso.cs +++ b/Projects/UOContent/Items/Clothing/OuterTorso.cs @@ -1,10 +1,11 @@ using System; using System.Runtime.CompilerServices; +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseOuterTorso : BaseClothing { public BaseOuterTorso(int itemID, int hue = 0) : base(itemID, Layer.OuterTorso, hue) @@ -12,7 +13,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x230E, 0x230D)] public partial class GildedDress : BaseOuterTorso { @@ -20,7 +21,7 @@ namespace Server.Items public GildedDress(int hue = 0) : base(0x230E, hue) => Weight = 3.0; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1F00, 0x1EFF)] public partial class FancyDress : BaseOuterTorso { @@ -28,7 +29,7 @@ namespace Server.Items public FancyDress(int hue = 0) : base(0x1F00, hue) => Weight = 3.0; } - [Serializable(3, false)] + [SerializationGenerator(3, false)] public partial class DeathRobe : Robe { private static readonly TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(1.0); @@ -141,7 +142,7 @@ namespace Server.Items } [Flippable] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RewardRobe : BaseOuterTorso, IRewardItem { [InvalidateProperties] @@ -192,7 +193,7 @@ namespace Server.Items return false; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -223,7 +224,7 @@ namespace Server.Items } [Flippable] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RewardDress : BaseOuterTorso, IRewardItem { [InvalidateProperties] @@ -274,7 +275,7 @@ namespace Server.Items return false; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -305,7 +306,7 @@ namespace Server.Items } [Flippable] - [Serializable(2, false)] + [SerializationGenerator(2, false)] public partial class Robe : BaseOuterTorso, IArcaneEquip { private int _curArcaneCharges; @@ -373,13 +374,13 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (IsArcane) { - list.Add(1061837, "{0}\t{1}", _curArcaneCharges, _maxArcaneCharges); // arcane charges: ~1_val~ / ~2_val~ + list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); // arcane charges: ~1_val~ / ~2_val~ } } @@ -404,7 +405,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MonkRobe : BaseOuterTorso { [Constructible] @@ -425,7 +426,7 @@ namespace Server.Items } [Flippable(0x1f01, 0x1f02)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlainDress : BaseOuterTorso { [Constructible] @@ -433,7 +434,7 @@ namespace Server.Items } [Flippable(0x2799, 0x27E4)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Kamishimo : BaseOuterTorso { [Constructible] @@ -441,7 +442,7 @@ namespace Server.Items } [Flippable(0x279C, 0x27E7)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HakamaShita : BaseOuterTorso { [Constructible] @@ -449,7 +450,7 @@ namespace Server.Items } [Flippable(0x2782, 0x27CD)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MaleKimono : BaseOuterTorso { [Constructible] @@ -457,7 +458,7 @@ namespace Server.Items } [Flippable(0x2783, 0x27CE)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FemaleKimono : BaseOuterTorso { [Constructible] @@ -465,7 +466,7 @@ namespace Server.Items } [Flippable(0x2FB9, 0x3173)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class MaleElvenRobe : BaseOuterTorso { [Constructible] @@ -473,7 +474,7 @@ namespace Server.Items } [Flippable(0x2FBA, 0x3174)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class FemaleElvenRobe : BaseOuterTorso { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Pants.cs b/Projects/UOContent/Items/Clothing/Pants.cs index 00fc3df06..efee4e379 100644 --- a/Projects/UOContent/Items/Clothing/Pants.cs +++ b/Projects/UOContent/Items/Clothing/Pants.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BasePants : BaseClothing { public BasePants(int itemID, int hue = 0) : base(itemID, Layer.Pants, hue) @@ -9,7 +11,7 @@ namespace Server.Items } [Flippable(0x152e, 0x152f)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShortPants : BasePants { [Constructible] @@ -17,7 +19,7 @@ namespace Server.Items } [Flippable(0x1539, 0x153a)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LongPants : BasePants { [Constructible] @@ -25,7 +27,7 @@ namespace Server.Items } [Flippable(0x279B, 0x27E6)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TattsukeHakama : BasePants { [Constructible] @@ -33,7 +35,7 @@ namespace Server.Items } [Flippable(0x2FC3, 0x3179)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenPants : BasePants { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Shirts.cs b/Projects/UOContent/Items/Clothing/Shirts.cs index cf8f8b316..6ba43a870 100644 --- a/Projects/UOContent/Items/Clothing/Shirts.cs +++ b/Projects/UOContent/Items/Clothing/Shirts.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseShirt : BaseClothing { public BaseShirt(int itemID, int hue = 0) : base(itemID, Layer.Shirt, hue) @@ -9,7 +11,7 @@ namespace Server.Items } [Flippable(0x1efd, 0x1efe)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FancyShirt : BaseShirt { [Constructible] @@ -17,7 +19,7 @@ namespace Server.Items } [Flippable(0x1517, 0x1518)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Shirt : BaseShirt { [Constructible] @@ -25,7 +27,7 @@ namespace Server.Items } [Flippable(0x2794, 0x27DF)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ClothNinjaJacket : BaseShirt { [Constructible] @@ -36,7 +38,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenShirt : BaseShirt { [Constructible] @@ -45,7 +47,7 @@ namespace Server.Items public override int RequiredRaces => Race.AllowElvesOnly; } - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenDarkShirt : BaseShirt { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Shoes.cs b/Projects/UOContent/Items/Clothing/Shoes.cs index 0887acd81..0615335d9 100644 --- a/Projects/UOContent/Items/Clothing/Shoes.cs +++ b/Projects/UOContent/Items/Clothing/Shoes.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseShoes : BaseClothing { public BaseShoes(int itemID, int hue = 0) : base(itemID, Layer.Shoes, hue) @@ -20,7 +22,7 @@ namespace Server.Items } [Flippable(0x2307, 0x2308)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FurBoots : BaseShoes { [Constructible] @@ -28,7 +30,7 @@ namespace Server.Items } [Flippable(0x170b, 0x170c)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Boots : BaseShoes { [Constructible] @@ -38,7 +40,7 @@ namespace Server.Items } [Flippable] - [Serializable(2, false)] + [SerializationGenerator(2, false)] public partial class ThighBoots : BaseShoes, IArcaneEquip { private int _maxArcaneCharges; @@ -118,13 +120,13 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (IsArcane) { - list.Add(1061837, "{0}\t{1}", _curArcaneCharges, _maxArcaneCharges); // arcane charges: ~1_val~ / ~2_val~ + list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); // arcane charges: ~1_val~ / ~2_val~ } } @@ -140,7 +142,7 @@ namespace Server.Items } [Flippable(0x170f, 0x1710)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Shoes : BaseShoes { [Constructible] @@ -150,7 +152,7 @@ namespace Server.Items } [Flippable(0x170d, 0x170e)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Sandals : BaseShoes { [Constructible] @@ -162,7 +164,7 @@ namespace Server.Items } [Flippable(0x2797, 0x27E2)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NinjaTabi : BaseShoes { [Constructible] @@ -170,7 +172,7 @@ namespace Server.Items } [Flippable(0x2796, 0x27E1)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SamuraiTabi : BaseShoes { [Constructible] @@ -178,7 +180,7 @@ namespace Server.Items } [Flippable(0x2796, 0x27E1)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Waraji : BaseShoes { [Constructible] @@ -186,7 +188,7 @@ namespace Server.Items } [Flippable(0x2FC4, 0x317A)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenBoots : BaseShoes { [Constructible] diff --git a/Projects/UOContent/Items/Clothing/Waist.cs b/Projects/UOContent/Items/Clothing/Waist.cs index eaccfd32b..45f34d768 100644 --- a/Projects/UOContent/Items/Clothing/Waist.cs +++ b/Projects/UOContent/Items/Clothing/Waist.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseWaist : BaseClothing { public BaseWaist(int itemID, int hue = 0) : base(itemID, Layer.Waist, hue) @@ -9,7 +11,7 @@ namespace Server.Items } [Flippable(0x153b, 0x153c)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HalfApron : BaseWaist { [Constructible] @@ -17,7 +19,7 @@ namespace Server.Items } [Flippable(0x27A0, 0x27EB)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Obi : BaseWaist { [Constructible] @@ -25,7 +27,7 @@ namespace Server.Items } [Flippable(0x2B68, 0x315F)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class WoodlandBelt : BaseWaist { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Ankhs.cs b/Projects/UOContent/Items/Construction/Ankhs.cs index d75e968f5..41e69dec3 100644 --- a/Projects/UOContent/Items/Construction/Ankhs.cs +++ b/Projects/UOContent/Items/Construction/Ankhs.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Mobiles; @@ -111,7 +112,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnkhWest : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -183,7 +184,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0)] @@ -257,7 +258,7 @@ namespace Server.Items } [TypeAlias("Server.Items.AnkhEast")] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AnkhNorth : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -331,7 +332,7 @@ namespace Server.Items } [TypeAlias("Server.Items.AnkhEast+InternalItem")] - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0)] diff --git a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs index a29606366..f37deba0b 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xB2D, 0xB2C)] public partial class WoodenBench : Item { diff --git a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs index 67dfd1878..12b2b9ad8 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs @@ -1,8 +1,10 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] [Flippable(0xB4F, 0xB4E, 0xB50, 0xB51)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FancyWoodenChairCushion : Item { [Constructible] @@ -11,7 +13,7 @@ namespace Server.Items [Furniture] [Flippable(0xB53, 0xB52, 0xB54, 0xB55)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WoodenChairCushion : Item { [Constructible] @@ -20,7 +22,7 @@ namespace Server.Items [Furniture] [Flippable(0xB57, 0xB56, 0xB59, 0xB58)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WoodenChair : Item { [Constructible] @@ -29,7 +31,7 @@ namespace Server.Items [Furniture] [Flippable(0xB5B, 0xB5A, 0xB5C, 0xB5D)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BambooChair : Item { [Constructible] @@ -38,7 +40,7 @@ namespace Server.Items [DynamicFlipping] [Flippable(0x1218, 0x1219, 0x121A, 0x121B)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class StoneChair : Item { [Constructible] @@ -47,7 +49,7 @@ namespace Server.Items [DynamicFlipping] [Flippable(0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateElvenChair : Item { [Constructible] @@ -56,7 +58,7 @@ namespace Server.Items [DynamicFlipping] [Flippable(0x2DEB, 0x2DEC, 0x2DED, 0x2DEE)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class BigElvenChair : Item { [Constructible] @@ -67,7 +69,7 @@ namespace Server.Items [DynamicFlipping] [Flippable(0x2DF5, 0x2DF6)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenReadingChair : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Chairs/Stools.cs b/Projects/UOContent/Items/Construction/Chairs/Stools.cs index a9e76afc6..837b36972 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Stools.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Stools.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Stool : Item { [Constructible] @@ -9,7 +11,7 @@ namespace Server.Items } [Furniture] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FootStool : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs index 2478489ab..de189504f 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs @@ -1,8 +1,10 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] [Flippable(0xB32, 0xB33)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Throne : Item { [Constructible] @@ -11,7 +13,7 @@ namespace Server.Items [Furniture] [Flippable(0xB2E, 0xB2F, 0xB31, 0xB30)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WoodenThrone : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs b/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs index 286bd0a98..157f240c3 100644 --- a/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs +++ b/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x156C, 0x156D)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield1 : Item { [Constructible] @@ -9,7 +11,7 @@ namespace Server.Items } [Flippable(0x156E, 0x156F)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield2 : Item { [Constructible] @@ -17,7 +19,7 @@ namespace Server.Items } [Flippable(0x1570, 0x1571)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield3 : Item { [Constructible] @@ -25,7 +27,7 @@ namespace Server.Items } [Flippable(0x1572, 0x1573)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield4 : Item { [Constructible] @@ -33,7 +35,7 @@ namespace Server.Items } [Flippable(0x1574, 0x1575)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield5 : Item { [Constructible] @@ -41,7 +43,7 @@ namespace Server.Items } [Flippable(0x1576, 0x1577)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield6 : Item { [Constructible] @@ -49,7 +51,7 @@ namespace Server.Items } [Flippable(0x1578, 0x1579)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield7 : Item { [Constructible] @@ -57,7 +59,7 @@ namespace Server.Items } [Flippable(0x157A, 0x157B)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield8 : Item { [Constructible] @@ -65,7 +67,7 @@ namespace Server.Items } [Flippable(0x157C, 0x157D)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield9 : Item { [Constructible] @@ -73,7 +75,7 @@ namespace Server.Items } [Flippable(0x157E, 0x157F)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield10 : Item { [Constructible] @@ -81,7 +83,7 @@ namespace Server.Items } [Flippable(0x1580, 0x1581)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShield11 : Item { [Constructible] @@ -89,7 +91,7 @@ namespace Server.Items } [Flippable(0x1582, 0x1583, 0x1634, 0x1635)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShieldSword1North : Item { [Constructible] @@ -97,7 +99,7 @@ namespace Server.Items } [Flippable(0x1634, 0x1635, 0x1582, 0x1583)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShieldSword1West : Item { [Constructible] @@ -105,7 +107,7 @@ namespace Server.Items } [Flippable(0x1584, 0x1585, 0x1636, 0x1637)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShieldSword2North : Item { [Constructible] @@ -113,7 +115,7 @@ namespace Server.Items } [Flippable(0x1636, 0x1637, 0x1584, 0x1585)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeShieldSword2West : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs b/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs index 4b3d9f087..72157087f 100644 --- a/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs +++ b/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x155E, 0x155F, 0x155C, 0x155D)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeBowWest : Item { [Constructible] @@ -9,7 +11,7 @@ namespace Server.Items } [Flippable(0x155C, 0x155D, 0x155E, 0x155F)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeBowNorth : Item { [Constructible] @@ -17,7 +19,7 @@ namespace Server.Items } [Flippable(0x1560, 0x1561, 0x1562, 0x1563)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeAxeNorth : Item { [Constructible] @@ -25,14 +27,14 @@ namespace Server.Items } [Flippable(0x1562, 0x1563, 0x1560, 0x1561)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeAxeWest : Item { [Constructible] public DecorativeAxeWest() : base(Utility.Random(0x1562, 2)) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DecorativeSwordNorth : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -68,7 +70,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -106,7 +108,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DecorativeSwordWest : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -142,7 +144,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0)] + [SerializationGenerator(0)] private partial class InternalItem : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -179,7 +181,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DecorativeDAxeNorth : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -215,7 +217,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0)] + [SerializationGenerator(0)] private partial class InternalItem : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -253,7 +255,7 @@ namespace Server.Items } } - [Serializable(0)] + [SerializationGenerator(0)] public partial class DecorativeDAxeWest : Item { [SerializableField(0, getter: "private", setter: "private")] @@ -290,7 +292,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0)] + [SerializationGenerator(0)] private partial class InternalItem : Item { [SerializableField(0, getter: "private", setter: "private")] diff --git a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs index e522d12ed..d0a5c7a3e 100644 --- a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] - [Serializable(0)] + [SerializationGenerator(0)] public partial class GiantReplicaAcorn : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs index d73518208..c3126fff4 100644 --- a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x3158, 0x3159)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class MountedDreadHorn : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs b/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs index 6695b94f5..9ba0d9b4f 100644 --- a/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs +++ b/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs @@ -1,13 +1,15 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargePainting : Item { [Constructible] public LargePainting() : base(0x0EA0) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x0E9F, 0x0EC8)] public partial class WomanPortrait1 : Item { @@ -15,7 +17,7 @@ namespace Server.Items public WomanPortrait1() : base(0x0E9F) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x0EE7, 0x0EC9)] public partial class WomanPortrait2 : Item { @@ -23,7 +25,7 @@ namespace Server.Items public WomanPortrait2() : base(0x0EE7) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x0EA2, 0x0EA1)] public partial class ManPortrait1 : Item { @@ -31,7 +33,7 @@ namespace Server.Items public ManPortrait1() : base(0x0EA2) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x0EA3, 0x0EA4)] public partial class ManPortrait2 : Item { @@ -39,7 +41,7 @@ namespace Server.Items public ManPortrait2() : base(0x0EA3) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x0EA6, 0x0EA5)] public partial class LadyPortrait1 : Item { @@ -47,7 +49,7 @@ namespace Server.Items public LadyPortrait1() : base(0x0EA6) => Movable = false; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x0EA7, 0x0EA8)] public partial class LadyPortrait2 : Item { diff --git a/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs b/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs index 1a5868096..a057d93b2 100644 --- a/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs +++ b/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry1N : Item { [SerializableField(0, "private", "private")] @@ -35,7 +37,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0)] + [SerializationGenerator(0)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -71,7 +73,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry2N : Item { [SerializableField(0, "private", "private")] @@ -106,7 +108,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -142,7 +144,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry2W : Item { [SerializableField(0, "private", "private")] @@ -177,7 +179,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -213,7 +215,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry3N : Item { [SerializableField(0, "private", "private")] @@ -248,7 +250,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -284,7 +286,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry3W : Item { [SerializableField(0, "private", "private")] @@ -319,7 +321,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -355,7 +357,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry4N : Item { [SerializableField(0, "private", "private")] @@ -390,7 +392,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -426,7 +428,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry4W : Item { [SerializableField(0, "private", "private")] @@ -461,7 +463,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -497,7 +499,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry5N : Item { [SerializableField(0, "private", "private")] @@ -532,7 +534,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -568,7 +570,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry5W : Item { [SerializableField(0, "private", "private")] @@ -603,7 +605,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -639,7 +641,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry6N : Item { [SerializableField(0, "private", "private")] @@ -674,7 +676,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] @@ -710,7 +712,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tapestry6W : Item { [SerializableField(0, "private", "private")] @@ -745,7 +747,7 @@ namespace Server.Items _item?.Delete(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] private partial class InternalItem : Item { [SerializableField(0, "private", "private")] diff --git a/Projects/UOContent/Items/Construction/Signs/SubtextSign.cs b/Projects/UOContent/Items/Construction/Signs/SubtextSign.cs index 7541fdfed..9b7b63e1d 100644 --- a/Projects/UOContent/Items/Construction/Signs/SubtextSign.cs +++ b/Projects/UOContent/Items/Construction/Signs/SubtextSign.cs @@ -40,7 +40,7 @@ namespace Server.Items } } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs index df9620907..c17fbe287 100644 --- a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs +++ b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] - [Serializable(0)] + [SerializationGenerator(0)] [Flippable(0x2DDD, 0x2DDE)] public partial class ElvenPodium : Item { diff --git a/Projects/UOContent/Items/Construction/Tables/Tables.cs b/Projects/UOContent/Items/Construction/Tables/Tables.cs index 65f1bdbf1..b7602efaa 100644 --- a/Projects/UOContent/Items/Construction/Tables/Tables.cs +++ b/Projects/UOContent/Items/Construction/Tables/Tables.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ElegantLowTable : Item { [Constructible] @@ -9,7 +11,7 @@ namespace Server.Items } [Furniture] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PlainLowTable : Item { [Constructible] @@ -18,7 +20,7 @@ namespace Server.Items [Furniture] [Flippable(0xB90, 0xB7D)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LargeTable : Item { [Constructible] @@ -27,7 +29,7 @@ namespace Server.Items [Furniture] [Flippable(0xB35, 0xB34)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Nightstand : Item { [Constructible] @@ -36,7 +38,7 @@ namespace Server.Items [Furniture] [Flippable(0xB8F, 0xB7C)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class YewWoodTable : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs index 35ec06c37..077978a8d 100644 --- a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs +++ b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs @@ -1,8 +1,10 @@ +using ModernUO.Serialization; + namespace Server.Items { [Furniture] [Flippable(0xB4A, 0xB49, 0xB4B, 0xB4C)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WritingTable : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Walls/BaseWall.cs b/Projects/UOContent/Items/Construction/Walls/BaseWall.cs index 6b5914a07..7c5684fbe 100644 --- a/Projects/UOContent/Items/Construction/Walls/BaseWall.cs +++ b/Projects/UOContent/Items/Construction/Walls/BaseWall.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseWall : Item { public BaseWall(int itemID) : base(itemID) => Movable = false; diff --git a/Projects/UOContent/Items/Construction/Walls/DarkWoodWall.cs b/Projects/UOContent/Items/Construction/Walls/DarkWoodWall.cs index a1e6e4077..39036ea51 100644 --- a/Projects/UOContent/Items/Construction/Walls/DarkWoodWall.cs +++ b/Projects/UOContent/Items/Construction/Walls/DarkWoodWall.cs @@ -1,3 +1,5 @@ +using ModernUO.Serialization; + namespace Server.Items { public enum DarkWoodWallTypes @@ -24,7 +26,7 @@ namespace Server.Items EastWallVShort } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkWoodWall : BaseWall { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Walls/ThickGrayStoneWall.cs b/Projects/UOContent/Items/Construction/Walls/ThickGrayStoneWall.cs index 17514de42..609ce720b 100644 --- a/Projects/UOContent/Items/Construction/Walls/ThickGrayStoneWall.cs +++ b/Projects/UOContent/Items/Construction/Walls/ThickGrayStoneWall.cs @@ -6,6 +6,8 @@ * CREATED : 10-07.2002 * * **************************************/ +using ModernUO.Serialization; + namespace Server.Items { public enum ThickGrayStoneWallTypes @@ -34,7 +36,7 @@ namespace Server.Items EastWindow2 } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ThickGrayStoneWall : BaseWall { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Walls/ThinBrickWall.cs b/Projects/UOContent/Items/Construction/Walls/ThinBrickWall.cs index f69a3cda3..4616b6ad0 100644 --- a/Projects/UOContent/Items/Construction/Walls/ThinBrickWall.cs +++ b/Projects/UOContent/Items/Construction/Walls/ThinBrickWall.cs @@ -1,3 +1,5 @@ +using ModernUO.Serialization; + namespace Server.Items { public enum ThinBrickWallTypes @@ -40,7 +42,7 @@ namespace Server.Items EastWallVShort } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ThinBrickWall : BaseWall { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Walls/ThinStoneWall.cs b/Projects/UOContent/Items/Construction/Walls/ThinStoneWall.cs index 7754c7b10..0b0f14991 100644 --- a/Projects/UOContent/Items/Construction/Walls/ThinStoneWall.cs +++ b/Projects/UOContent/Items/Construction/Walls/ThinStoneWall.cs @@ -1,3 +1,5 @@ +using ModernUO.Serialization; + namespace Server.Items { public enum ThinStoneWallTypes @@ -29,7 +31,7 @@ namespace Server.Items EastWallShort2 } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ThinStoneWall : BaseWall { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Walls/WhiteStoneWall.cs b/Projects/UOContent/Items/Construction/Walls/WhiteStoneWall.cs index 07a6f0d16..3679905a5 100644 --- a/Projects/UOContent/Items/Construction/Walls/WhiteStoneWall.cs +++ b/Projects/UOContent/Items/Construction/Walls/WhiteStoneWall.cs @@ -6,6 +6,8 @@ * CREATED : 10-07.2002 * * **************************************/ +using ModernUO.Serialization; + namespace Server.Items { public enum WhiteStoneWallTypes @@ -47,7 +49,7 @@ namespace Server.Items EastWallVVShort } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WhiteStoneWall : BaseWall { [Constructible] diff --git a/Projects/UOContent/Items/Containers/BaseTreasureChest.cs b/Projects/UOContent/Items/Containers/BaseTreasureChest.cs index 8d5276940..8e0294f09 100644 --- a/Projects/UOContent/Items/Containers/BaseTreasureChest.cs +++ b/Projects/UOContent/Items/Containers/BaseTreasureChest.cs @@ -1,214 +1,141 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(1, false)] +public partial class BaseTreasureChest : LockableContainer { - public class BaseTreasureChest : LockableContainer + public enum TreasureLevel { - public enum TreasureLevel + Level1, + Level2, + Level3, + Level4, + Level5, + Level6 + } + + private TimerExecutionToken _resetTimer; + + public BaseTreasureChest(int itemID, TreasureLevel level = TreasureLevel.Level2) : base(itemID) + { + _level = level; + _minSpawnTime = TimeSpan.FromMinutes(10); + _maxSpawnTime = TimeSpan.FromMinutes(60); + + Locked = true; + Movable = false; + + SetLockLevel(); + GenerateTreasure(); + } + + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private TreasureLevel _level; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private TimeSpan _minSpawnTime; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private TimeSpan _maxSpawnTime; + + [CommandProperty(AccessLevel.GameMaster)] + public override bool Locked + { + get => base.Locked; + set { - Level1, - Level2, - Level3, - Level4, - Level5, - Level6 - } - - private TreasureResetTimer m_ResetTimer; - - public BaseTreasureChest(int itemID, TreasureLevel level = TreasureLevel.Level2) - : base(itemID) - { - Level = level; - Locked = true; - Movable = false; - - SetLockLevel(); - GenerateTreasure(); - } - - public BaseTreasureChest(Serial serial) : base(serial) - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public TreasureLevel Level { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public short MaxSpawnTime { get; set; } = 60; - - [CommandProperty(AccessLevel.GameMaster)] - public short MinSpawnTime { get; set; } = 10; - - [CommandProperty(AccessLevel.GameMaster)] - public override bool Locked - { - get => base.Locked; - set + if (base.Locked != value) { - if (base.Locked != value) + base.Locked = value; + + if (!value) { - base.Locked = value; - - if (!value) - { - StartResetTimer(); - } - } - } - } - - public override bool IsDecoContainer => false; - - public override string DefaultName - { - get - { - if (Locked) - { - return "a locked treasure chest"; + StartResetTimer(); } - return "a treasure chest"; - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - writer.Write((byte)Level); - writer.Write(MinSpawnTime); - writer.Write(MaxSpawnTime); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - Level = (TreasureLevel)reader.ReadByte(); - MinSpawnTime = reader.ReadShort(); - MaxSpawnTime = reader.ReadShort(); - - if (!Locked) - { - StartResetTimer(); - } - } - - protected virtual void SetLockLevel() - { - RequiredSkill = Level switch - { - TreasureLevel.Level1 => LockLevel = 5, - TreasureLevel.Level2 => LockLevel = 20, - TreasureLevel.Level3 => LockLevel = 50, - TreasureLevel.Level4 => LockLevel = 70, - TreasureLevel.Level5 => LockLevel = 90, - TreasureLevel.Level6 => LockLevel = 100, - _ => RequiredSkill - }; - } - - private void StartResetTimer() - { - if (m_ResetTimer == null) - { - m_ResetTimer = new TreasureResetTimer(this); - } - else - { - m_ResetTimer.Delay = TimeSpan.FromMinutes(Utility.Random(MinSpawnTime, MaxSpawnTime)); - } - - m_ResetTimer.Start(); - } - - protected virtual void GenerateTreasure() - { - var MinGold = 1; - var MaxGold = 2; - - switch (Level) - { - 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 void ClearContents() - { - for (var 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(); - } - - private class TreasureResetTimer : Timer - { - private readonly BaseTreasureChest m_Chest; - - public TreasureResetTimer(BaseTreasureChest chest) : base( - TimeSpan.FromMinutes(Utility.Random(chest.MinSpawnTime, chest.MaxSpawnTime)) - ) - { - m_Chest = chest; - } - - protected override void OnTick() - { - m_Chest.Reset(); + InvalidateProperties(); } } } + + public override bool IsDecoContainer => false; + + public override string DefaultName => Locked ? "a locked treasure chest" : "a treasure chest"; + + [AfterDeserialization] + private void AfterDeserialization() + { + if (!Locked) + { + StartResetTimer(); + } + } + + private void Deserialize(IGenericReader reader, int version) + { + _level = (TreasureLevel)reader.ReadByte(); + _minSpawnTime = TimeSpan.FromMinutes(reader.ReadShort()); + _maxSpawnTime = TimeSpan.FromMinutes(reader.ReadShort()); + } + + protected virtual void SetLockLevel() + { + RequiredSkill = _level switch + { + TreasureLevel.Level1 => LockLevel = 5, + TreasureLevel.Level2 => LockLevel = 20, + TreasureLevel.Level3 => LockLevel = 50, + TreasureLevel.Level4 => LockLevel = 70, + TreasureLevel.Level5 => LockLevel = 90, + TreasureLevel.Level6 => LockLevel = 100, + _ => LockLevel = 120 + }; + } + + private void StartResetTimer() + { + _resetTimer.Cancel(); + + var randomDuration = Utility.RandomMinMax(_minSpawnTime.Ticks, _maxSpawnTime.Ticks); + Timer.StartTimer(TimeSpan.FromTicks(randomDuration), Reset, out _resetTimer); + } + + protected virtual void GenerateTreasure() + { + var gold = _level switch + { + TreasureLevel.Level1 => Utility.RandomMinMax(100, 300), + TreasureLevel.Level2 => Utility.RandomMinMax(300, 600), + TreasureLevel.Level3 => Utility.RandomMinMax(600, 900), + TreasureLevel.Level4 => Utility.RandomMinMax(900, 1200), + TreasureLevel.Level5 => Utility.RandomMinMax(1200, 5000), + _ => Utility.RandomMinMax(5000, 9000), + }; + + DropItem(new Gold(gold)); + } + + public void ClearContents() + { + for (var i = Items.Count - 1; i >= 0; --i) + { + if (i < Items.Count) + { + Items[i].Delete(); + } + } + } + + public void Reset() + { + _resetTimer.Cancel(); + Locked = true; + ClearContents(); + GenerateTreasure(); + } } diff --git a/Projects/UOContent/Items/Containers/Container.cs b/Projects/UOContent/Items/Containers/Container.cs index 1fce1751a..c3112705b 100644 --- a/Projects/UOContent/Items/Containers/Container.cs +++ b/Projects/UOContent/Items/Containers/Container.cs @@ -1,1016 +1,509 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Mobiles; using Server.Multis; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[ManualDirtyChecking] +public abstract class BaseContainer : Container { - public abstract class BaseContainer : Container + public BaseContainer(int itemID) : base(itemID) { - public BaseContainer(int itemID) : base(itemID) - { - } - - public BaseContainer(Serial serial) : base(serial) - { - } - - public override int DefaultMaxWeight => IsSecure ? 0 : base.DefaultMaxWeight; - - public override bool IsAccessibleTo(Mobile m) => BaseHouse.CheckAccessible(m, this) && base.IsAccessibleTo(m); - - public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) - { - if (IsSecure && !BaseHouse.CheckHold(m, this, item, message, checkItems, plusItems, plusWeight)) - { - return false; - } - - return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); - } - - public override bool CheckItemUse(Mobile from, Item item) - { - if (IsDecoContainer && item is BaseBook) - { - return true; - } - - return base.CheckItemUse(from, item); - } - - 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; - } - - var house = BaseHouse.FindHouseAt(this); - - if (house?.HasLockedDownItem(this) == true) - { - if (dropped is VendorRentalContract || dropped is Container container && - container.FindItemByType() != 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; - } - } - - var list = Items; - - for (var i = 0; i < list.Count; ++i) - { - var item = list[i]; - - if (item is not 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; - } - - var house = BaseHouse.FindHouseAt(this); - - if (house?.HasLockedDownItem(this) == true) - { - if (item is VendorRentalContract || item is Container container && - container.FindItemByType() != 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 UpdateTotal(Item sender, TotalType type, int delta) - { - base.UpdateTotal(sender, type, delta); - - if (type == TotalType.Weight) - { - (RootParent as Mobile)?.InvalidateProperties(); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (from.AccessLevel > AccessLevel.Player || from.InRange(GetWorldLocation(), 2) || 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); - } - - /* Note: base class insertion; we cannot serialize anything here */ - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - } } - public class CreatureBackpack : Backpack // Used on BaseCreature + public BaseContainer(Serial serial) : base(serial) { - [Constructible] - public CreatureBackpack(string name) + } + + public override int DefaultMaxWeight => IsSecure ? 0 : base.DefaultMaxWeight; + + public override bool IsAccessibleTo(Mobile m) => BaseHouse.CheckAccessible(m, this) && base.IsAccessibleTo(m); + + public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) + { + if (IsSecure && !BaseHouse.CheckHold(m, this, item, message, checkItems, plusItems, plusWeight)) { - Name = name; - Layer = Layer.Backpack; - Hue = 5; - Weight = 3.0; - } - - public CreatureBackpack(Serial serial) : base(serial) - { - } - - public override void AddNameProperty(ObjectPropertyList list) - { - if (Name != null) - { - list.Add(1075257, Name); // Contents of ~1_PETNAME~'s pack. - } - else - { - base.AddNameProperty(list); - } - } - - public override void OnItemRemoved(Item item) - { - if (Items.Count == 0) - { - Delete(); - } - - base.OnItemRemoved(item); - } - - public override bool OnDragLift(Mobile from) - { - if (from.AccessLevel > AccessLevel.Player) - { - return true; - } - - from.SendLocalizedMessage(500169); // You cannot pick that up. return false; } - public override bool OnDragDropInto(Mobile from, Item item, Point3D p) => false; - - public override bool TryDropItem(Mobile from, Item dropped, bool sendFullMessage) => false; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0) - { - Weight = 13.0; - } - } + return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); } - public class StrongBackpack : Backpack // Used on Pack animals + public override bool CheckItemUse(Mobile from, Item item) { - [Constructible] - public StrongBackpack() + if (IsDecoContainer && item is BaseBook) { - Layer = Layer.Backpack; - Weight = 13.0; - } - - public StrongBackpack(Serial serial) : base(serial) - { - } - - public override int DefaultMaxWeight => 1600; - - public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) => - base.CheckHold(m, item, false, checkItems, plusItems, plusWeight); - - public override bool CheckContentDisplay(Mobile from) => - RootParent is BaseCreature creature && creature.Controlled && creature.ControlMaster == from || - base.CheckContentDisplay(from); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0) - { - Weight = 13.0; - } - } - } - - public class Backpack : BaseContainer, IDyable - { - [Constructible] - public Backpack() : base(0xE75) - { - Layer = Layer.Backpack; - Weight = 3.0; - } - - public Backpack(Serial serial) : base(serial) - { - } - - public override int DefaultMaxWeight - { - get - { - if (Core.ML && Parent is Mobile m && m.Player && m.Backpack == this) - { - return 550; - } - - return base.DefaultMaxWeight; - } - } - - public bool Dye(Mobile from, DyeTub sender) - { - if (Deleted) - { - return false; - } - - Hue = sender.DyedHue; - return true; } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && ItemID == 0x9B2) - { - ItemID = 0xE75; - } - } + return base.CheckItemUse(from, item); } - public class Pouch : TrappableContainer + public override void GetContextMenuEntries(Mobile from, List list) { - [Constructible] - public Pouch() : base(0xE79) => Weight = 1.0; - - public Pouch(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + base.GetContextMenuEntries(from, list); + SetSecureLevelEntry.AddTo(from, this, list); } - public abstract class BaseBagBall : BaseContainer, IDyable + public override bool TryDropItem(Mobile from, Item dropped, bool sendFullMessage) { - public BaseBagBall(int itemID) : base(itemID) => Weight = 1.0; - - public BaseBagBall(Serial serial) : base(serial) + if (!CheckHold(from, dropped, sendFullMessage, true)) { + return false; } - public bool Dye(Mobile from, DyeTub sender) + var house = BaseHouse.FindHouseAt(this); + + if (house?.HasLockedDownItem(this) == true) { - if (Deleted) + if (dropped is VendorRentalContract || dropped is Container container && + container.FindItemByType() != null) { + from.SendLocalizedMessage(1062492); // You cannot place a rental contract in a locked down container. return false; } - Hue = sender.DyedHue; - - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SmallBagBall : BaseBagBall - { - [Constructible] - public SmallBagBall() : base(0x2256) - { - } - - public SmallBagBall(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class LargeBagBall : BaseBagBall - { - [Constructible] - public LargeBagBall() : base(0x2257) - { - } - - public LargeBagBall(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Bag : BaseContainer, IDyable - { - [Constructible] - public Bag() : base(0xE76) => Weight = 2.0; - - public Bag(Serial serial) : base(serial) - { - } - - public bool Dye(Mobile from, DyeTub sender) - { - if (Deleted) + if (!house.LockDown(from, dropped, false)) { return false; } - - Hue = sender.DyedHue; - - return true; } - public override void Serialize(IGenericWriter writer) + var list = Items; + + for (var i = 0; i < list.Count; ++i) { - base.Serialize(writer); + var item = list[i]; - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Barrel : BaseContainer - { - [Constructible] - public Barrel() : base(0xE77) => Weight = 25.0; - - public Barrel(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Weight == 0.0) + if (item is not Container && item.StackWith(from, dropped, false)) { - Weight = 25.0; + return true; } } + + DropItem(dropped); + + return true; } - public class Keg : BaseContainer + public override bool OnDragDropInto(Mobile from, Item item, Point3D p) { - [Constructible] - public Keg() : base(0xE7F) => Weight = 15.0; - - public Keg(Serial serial) : base(serial) + if (!CheckHold(from, item, true, true)) { + return false; } - public override void Serialize(IGenericWriter writer) + var house = BaseHouse.FindHouseAt(this); + + if (house?.HasLockedDownItem(this) == true) { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PicnicBasket : BaseContainer - { - [Constructible] - public PicnicBasket() : base(0xE7A) => Weight = 2.0; - - public PicnicBasket(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Basket : BaseContainer - { - [Constructible] - public Basket() : base(0x990) => Weight = 1.0; - - public Basket(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0x9AA, 0xE7D)] - public class WoodenBox : LockableContainer - { - [Constructible] - public WoodenBox() : base(0x9AA) => Weight = 4.0; - - public WoodenBox(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0x9A9, 0xE7E)] - public class SmallCrate : LockableContainer - { - [Constructible] - public SmallCrate() : base(0x9A9) => Weight = 2.0; - - public SmallCrate(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Weight == 4.0) + if (item is VendorRentalContract || item is Container container && + container.FindItemByType() != null) { - Weight = 2.0; - } - } - } - - [Furniture] - [Flippable(0xE3F, 0xE3E)] - public class MediumCrate : LockableContainer - { - [Constructible] - public MediumCrate() : base(0xE3F) => Weight = 2.0; - - public MediumCrate(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Weight == 6.0) - { - Weight = 2.0; - } - } - } - - [Furniture] - [Flippable(0xE3D, 0xE3C)] - public class LargeCrate : LockableContainer - { - [Constructible] - public LargeCrate() : base(0xE3D) => Weight = 1.0; - - public LargeCrate(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Weight == 8.0) - { - Weight = 1.0; - } - } - } - - [DynamicFlipping, Flippable(0x9A8, 0xE80)] - public class MetalBox : LockableContainer - { - [Constructible] - public MetalBox() : base(0x9A8) - { - } - - public MetalBox(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 3) - { - Weight = -1; - } - } - } - - [DynamicFlipping, Flippable(0x9AB, 0xE7C)] - public class MetalChest : LockableContainer - { - [Constructible] - public MetalChest() : base(0x9AB) - { - } - - public MetalChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 25) - { - Weight = -1; - } - } - } - - [DynamicFlipping, Flippable(0xE41, 0xE40)] - public class MetalGoldenChest : LockableContainer - { - [Constructible] - public MetalGoldenChest() : base(0xE41) - { - } - - public MetalGoldenChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 25) - { - Weight = -1; - } - } - } - - [Furniture] - [Flippable(0xe43, 0xe42)] - public class WoodenChest : LockableContainer - { - [Constructible] - public WoodenChest() : base(0xe43) => Weight = 2.0; - - public WoodenChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Weight == 15.0) - { - Weight = 2.0; - } - } - } - - [Furniture] - [Flippable(0x280B, 0x280C)] - public class PlainWoodenChest : LockableContainer - { - [Constructible] - public PlainWoodenChest() : base(0x280B) - { - } - - public PlainWoodenChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 15) - { - Weight = -1; - } - } - } - - [Furniture] - [Flippable(0x280D, 0x280E)] - public class OrnateWoodenChest : LockableContainer - { - [Constructible] - public OrnateWoodenChest() : base(0x280D) - { - } - - public OrnateWoodenChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 15) - { - Weight = -1; - } - } - } - - [Furniture] - [Flippable(0x280F, 0x2810)] - public class GildedWoodenChest : LockableContainer - { - [Constructible] - public GildedWoodenChest() : base(0x280F) - { - } - - public GildedWoodenChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 15) - { - Weight = -1; - } - } - } - - [Furniture] - [Flippable(0x2811, 0x2812)] - public class WoodenFootLocker : LockableContainer - { - [Constructible] - public WoodenFootLocker() : base(0x2811) => GumpID = 0x10B; - - public WoodenFootLocker(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(2); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 15) - { - Weight = -1; + from.SendLocalizedMessage(1062492); // You cannot place a rental contract in a locked down container. + return false; } - if (version < 2) + if (!house.LockDown(from, item, false)) { - GumpID = 0x10B; + return false; } } + + item.Location = new Point3D(p.X, p.Y, 0); + AddItem(item); + + from.SendSound(GetDroppedSound(item), GetWorldLocation()); + + return true; } - [Furniture] - [Flippable(0x2813, 0x2814)] - public class FinishedWoodenChest : LockableContainer + public override void UpdateTotal(Item sender, TotalType type, int delta) { - [Constructible] - public FinishedWoodenChest() : base(0x2813) + base.UpdateTotal(sender, type, delta); + + if (type == TotalType.Weight) { - } - - public FinishedWoodenChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 15) - { - Weight = -1; - } + (RootParent as Mobile)?.InvalidateProperties(); } } - [Furniture] - [Serializable(0)] - [Flippable(0x2DF1, 0x2DF2)] - public partial class RarewoodChest : LockableContainer + public override void OnDoubleClick(Mobile from) { - [Constructible] - public RarewoodChest() : base(0x2DF1) + if (from.AccessLevel > AccessLevel.Player || from.InRange(GetWorldLocation(), 2) || RootParent is PlayerVendor) { + Open(from); + } + else + { + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } - [Furniture] - [Serializable(0)] - [Flippable(0x2DF3, 0x2DF4)] - public partial class DecorativeBox : LockableContainer + public virtual void Open(Mobile from) + { + DisplayTo(from); + } + + /* Note: base class insertion; we cannot serialize anything here */ + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + } +} + +[SerializationGenerator(0, false)] +public partial class CreatureBackpack : Backpack // Used on BaseCreature +{ + [Constructible] + public CreatureBackpack(string name) + { + Name = name; + Layer = Layer.Backpack; + Hue = 5; + Weight = 3.0; + } + + public override void AddNameProperty(IPropertyList list) + { + if (Name != null) + { + list.Add(1075257, Name); // Contents of ~1_PETNAME~'s pack. + } + else + { + base.AddNameProperty(list); + } + } + + public override void OnItemRemoved(Item item) + { + if (Items.Count == 0) + { + Delete(); + } + + base.OnItemRemoved(item); + } + + public override bool OnDragLift(Mobile from) + { + if (from.AccessLevel > AccessLevel.Player) + { + return true; + } + + from.SendLocalizedMessage(500169); // You cannot pick that up. + return false; + } + + public override bool OnDragDropInto(Mobile from, Item item, Point3D p) => false; + + public override bool TryDropItem(Mobile from, Item dropped, bool sendFullMessage) => false; +} + +[SerializationGenerator(0, false)] +public partial class StrongBackpack : Backpack // Used on Pack animals +{ + [Constructible] + public StrongBackpack() + { + Layer = Layer.Backpack; + Weight = 13.0; + } + + public override int DefaultMaxWeight => 1600; + + public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) => + base.CheckHold(m, item, false, checkItems, plusItems, plusWeight); + + public override bool CheckContentDisplay(Mobile from) => + RootParent is BaseCreature creature && creature.Controlled && creature.ControlMaster == from || + base.CheckContentDisplay(from); +} + +[SerializationGenerator(0, false)] +public partial class Backpack : BaseContainer, IDyable +{ + [Constructible] + public Backpack() : base(0xE75) + { + Layer = Layer.Backpack; + Weight = 3.0; + } + + public override int DefaultMaxWeight + { + get + { + if (Core.ML && Parent is Mobile m && m.Player && m.Backpack == this) + { + return 550; + } + + return base.DefaultMaxWeight; + } + } + + public bool Dye(Mobile from, DyeTub sender) + { + if (Deleted) + { + return false; + } + + Hue = sender.DyedHue; + + return true; + } +} + +[SerializationGenerator(0, false)] +public partial class Pouch : TrappableContainer +{ + [Constructible] + public Pouch() : base(0xE79) => Weight = 1.0; +} + +[SerializationGenerator(0, false)] +public abstract partial class BaseBagBall : BaseContainer, IDyable +{ + public BaseBagBall(int itemID) : base(itemID) => Weight = 1.0; + + public bool Dye(Mobile from, DyeTub sender) + { + if (Deleted) + { + return false; + } + + Hue = sender.DyedHue; + + return true; + } +} + +[SerializationGenerator(0, false)] +public partial class SmallBagBall : BaseBagBall +{ + [Constructible] + public SmallBagBall() : base(0x2256) + { + } +} + +[SerializationGenerator(0, false)] +public partial class LargeBagBall : BaseBagBall +{ + [Constructible] + public LargeBagBall() : base(0x2257) + { + } +} + +[SerializationGenerator(0, false)] +public partial class Bag : BaseContainer, IDyable +{ + [Constructible] + public Bag() : base(0xE76) => Weight = 2.0; + + public bool Dye(Mobile from, DyeTub sender) + { + if (Deleted) + { + return false; + } + + Hue = sender.DyedHue; + + return true; + } +} + +[SerializationGenerator(0, false)] +public partial class Barrel : BaseContainer +{ + [Constructible] + public Barrel() : base(0xE77) => Weight = 25.0; +} + +[SerializationGenerator(0, false)] +public partial class Keg : BaseContainer +{ + [Constructible] + public Keg() : base(0xE7F) => Weight = 15.0; +} + +[SerializationGenerator(0, false)] +public partial class PicnicBasket : BaseContainer +{ + [Constructible] + public PicnicBasket() : base(0xE7A) => Weight = 2.0; +} + +[SerializationGenerator(0, false)] +public partial class Basket : BaseContainer +{ + [Constructible] + public Basket() : base(0x990) => Weight = 1.0; +} + +[Furniture] +[Flippable(0x9AA, 0xE7D)] +[SerializationGenerator(0, false)] +public partial class WoodenBox : LockableContainer +{ + [Constructible] + public WoodenBox() : base(0x9AA) => Weight = 4.0; +} + +[Furniture] +[Flippable(0x9A9, 0xE7E)] +[SerializationGenerator(0, false)] +public partial class SmallCrate : LockableContainer +{ + [Constructible] + public SmallCrate() : base(0x9A9) => Weight = 2.0; +} + +[Furniture] +[Flippable(0xE3F, 0xE3E)] +[SerializationGenerator(0, false)] +public partial class MediumCrate : LockableContainer +{ + [Constructible] + public MediumCrate() : base(0xE3F) => Weight = 2.0; +} + +[Furniture] +[Flippable(0xE3D, 0xE3C)] +[SerializationGenerator(0, false)] +public partial class LargeCrate : LockableContainer +{ + [Constructible] + public LargeCrate() : base(0xE3D) => Weight = 1.0; +} + +[DynamicFlipping] +[Flippable(0x9A8, 0xE80)] +[SerializationGenerator(0, false)] +public partial class MetalBox : LockableContainer +{ + [Constructible] + public MetalBox() : base(0x9A8) + { + } +} + +[DynamicFlipping] +[Flippable(0x9AB, 0xE7C)] +[SerializationGenerator(0, false)] +public partial class MetalChest : LockableContainer +{ + [Constructible] + public MetalChest() : base(0x9AB) + { + } +} + +[DynamicFlipping, Flippable(0xE41, 0xE40)] +[SerializationGenerator(0, false)] +public partial class MetalGoldenChest : LockableContainer +{ + [Constructible] + public MetalGoldenChest() : base(0xE41) + { + } +} + +[Furniture] +[Flippable(0xe43, 0xe42)] +[SerializationGenerator(0, false)] +public partial class WoodenChest : LockableContainer +{ + [Constructible] + public WoodenChest() : base(0xe43) => Weight = 2.0; +} + +[Furniture] +[Flippable(0x280B, 0x280C)] +[SerializationGenerator(0, false)] +public partial class PlainWoodenChest : LockableContainer +{ + [Constructible] + public PlainWoodenChest() : base(0x280B) + { + } +} + +[Furniture] +[Flippable(0x280D, 0x280E)] +[SerializationGenerator(0, false)] +public partial class OrnateWoodenChest : LockableContainer +{ + [Constructible] + public OrnateWoodenChest() : base(0x280D) + { + } +} + +[Furniture] +[Flippable(0x280F, 0x2810)] +[SerializationGenerator(0, false)] +public partial class GildedWoodenChest : LockableContainer +{ + [Constructible] + public GildedWoodenChest() : base(0x280F) + { + } +} + +[Furniture] +[Flippable(0x2811, 0x2812)] +[SerializationGenerator(0, false)] +public partial class WoodenFootLocker : LockableContainer +{ + [Constructible] + public WoodenFootLocker() : base(0x2811) => GumpID = 0x10B; +} + +[Furniture] +[Flippable(0x2813, 0x2814)] +[SerializationGenerator(0, false)] +public partial class FinishedWoodenChest : LockableContainer +{ + [Constructible] + public FinishedWoodenChest() : base(0x2813) + { + } +} + +[Furniture] +[SerializationGenerator(0)] +[Flippable(0x2DF1, 0x2DF2)] +public partial class RarewoodChest : LockableContainer +{ + [Constructible] + public RarewoodChest() : base(0x2DF1) + { + } +} + +[Furniture] +[SerializationGenerator(0)] +[Flippable(0x2DF3, 0x2DF4)] +public partial class DecorativeBox : LockableContainer +{ + [Constructible] + public DecorativeBox() : base(0x2DF4) { - [Constructible] - public DecorativeBox() : base(0x2DF4) - { - } } } diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs new file mode 100644 index 000000000..9f035a477 --- /dev/null +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs @@ -0,0 +1,254 @@ +using System; +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(2, false)] +public abstract partial class FillableContainer : LockableContainer +{ + [SerializableField(0)] + protected FillableContentType _rawContentType; + + [TimerDrift] + [SerializableField(1)] + private Timer _respawnTimer; + + [DeserializeTimerField(1)] + private void DeserializeRespawnTimer(TimeSpan delay) + { + if (delay > TimeSpan.MinValue) + { + _respawnTimer = Timer.DelayCall(delay, Respawn); + } + } + + public FillableContainer(int itemID) : base(itemID) => Movable = false; + + public virtual int MinRespawnMinutes => 60; + public virtual int MaxRespawnMinutes => 90; + + public virtual bool IsLockable => true; + public virtual bool IsTrappable => IsLockable; + + public virtual int SpawnThreshold => 2; + + [CommandProperty(AccessLevel.GameMaster)] + public DateTime NextRespawnTime => _respawnTimer?.Next ?? DateTime.MinValue; + + [CommandProperty(AccessLevel.GameMaster)] + public FillableContentType ContentType + { + get => _rawContentType; + set + { + if (_rawContentType == value) + { + return; + } + + ClearContents(); + _rawContentType = value; + Respawn(); + } + } + + protected void ClearContents() + { + for (var i = Items.Count - 1; i >= 0; --i) + { + if (i < Items.Count) + { + Items[i].Delete(); + } + } + } + + public override void OnMapChange() + { + base.OnMapChange(); + AcquireContent(); + } + + public override void OnLocationChange(Point3D oldLocation) + { + base.OnLocationChange(oldLocation); + AcquireContent(); + } + + public virtual void AcquireContent() + { + if (_rawContentType != FillableContentType.None) + { + return; + } + + RawContentType = FillableContent.Acquire(GetWorldLocation(), Map); + + if (_rawContentType != FillableContentType.None) + { + Respawn(); + } + } + + public override void OnItemRemoved(Item item) + { + CheckRespawn(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + _respawnTimer?.Stop(); + _respawnTimer = null; + } + + public int GetItemsCount() + { + var count = 0; + + foreach (var item in Items) + { + count += item.Amount; + } + + return count; + } + + public void CheckRespawn() + { + var canSpawn = + _rawContentType != FillableContentType.None && + !Deleted && !Movable && Parent == null && !IsLockedDown && !IsSecure && + ( + GetItemsCount() <= SpawnThreshold || + IsLockable && !Locked || + IsTrappable && TrapType == TrapType.None + ); + + if (canSpawn) + { + if (_respawnTimer?.Running != true) + { + var mins = Utility.RandomMinMax(MinRespawnMinutes, MaxRespawnMinutes); + var delay = TimeSpan.FromMinutes(mins); + _respawnTimer = Timer.DelayCall(delay, Respawn); + } + } + else + { + _respawnTimer?.Stop(); + _respawnTimer = null; + } + } + + public void Respawn() + { + _respawnTimer?.Stop(); + _respawnTimer = null; + + if (_rawContentType == FillableContentType.None || Deleted) + { + return; + } + + GenerateContent(); + + var level = FillableContent.Lookup(_rawContentType).Level; + + if (IsLockable) + { + Locked = true; + + var difficulty = (level - 1) * 30; + + LockLevel = difficulty - 10; + MaxLockLevel = difficulty + 30; + RequiredSkill = difficulty; + } + + if (IsTrappable && (level > 1 || Utility.Random(5) < 4)) + { + TrapType = level > Utility.Random(5) ? TrapType.PoisonTrap : TrapType.ExplosionTrap; + TrapPower = level * Utility.RandomMinMax(10, 30); + TrapLevel = level; + } + else + { + TrapType = TrapType.None; + TrapPower = 0; + TrapLevel = 0; + } + + CheckRespawn(); + } + + protected virtual int GetSpawnCount() + { + var itemsCount = GetItemsCount(); + + if (itemsCount > SpawnThreshold) + { + return 0; + } + + var maxSpawnCount = (1 + SpawnThreshold - itemsCount) * 2; + + return Utility.RandomMinMax(0, maxSpawnCount); + } + + public virtual void GenerateContent() + { + if (_rawContentType == FillableContentType.None || Deleted) + { + return; + } + + var content = FillableContent.Lookup(_rawContentType); + + var toSpawn = GetSpawnCount(); + + for (var i = 0; i < toSpawn; ++i) + { + var item = content.Construct(); + + if (item == null) + { + continue; + } + + var list = Items; + + for (var j = 0; j < list.Count; ++j) + { + var subItem = list[j]; + + if (subItem is not Container && subItem.StackWith(null, item, false)) + { + break; + } + } + + if (!item.Deleted) + { + DropItem(item); + } + } + } + + private void Deserialize(IGenericReader reader, int version) + { + _rawContentType = (FillableContentType)reader.ReadInt(); + var respawnTimerNext = reader.ReadDeltaTime(); + DeserializeRespawnTimer(respawnTimerNext == DateTime.MinValue ? TimeSpan.MinValue : respawnTimerNext - Core.Now); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + if (_respawnTimer?.Running != true) + { + CheckRespawn(); + } + } +} diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs new file mode 100644 index 000000000..0c3afc853 --- /dev/null +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs @@ -0,0 +1,100 @@ +using ModernUO.Serialization; + +namespace Server.Items; + +[Flippable(0xA97, 0xA99, 0xA98, 0xA9A, 0xA9B, 0xA9C)] +[SerializationGenerator(0)] +public partial class LibraryBookcase : FillableContainer +{ + [Constructible] + public LibraryBookcase() : base(0xA97) => Weight = 1.0; + + public override bool IsLockable => false; + public override int SpawnThreshold => 5; + + protected override int GetSpawnCount() => 5 - GetItemsCount(); + + public override void AcquireContent() + { + if (_rawContentType != FillableContentType.None) + { + return; + } + + RawContentType = FillableContentType.Library; + Respawn(); + } +} + +[Flippable(0xE3D, 0xE3C)] +[SerializationGenerator(0)] +public partial class FillableLargeCrate : FillableContainer +{ + [Constructible] + public FillableLargeCrate() : base(0xE3D) => Weight = 1.0; +} + +[Flippable(0x9A9, 0xE7E)] +[SerializationGenerator(0)] +public partial class FillableSmallCrate : FillableContainer +{ + [Constructible] + public FillableSmallCrate() : base(0x9A9) => Weight = 1.0; +} + +[Flippable(0x9AA, 0xE7D)] +[SerializationGenerator(0)] +public partial class FillableWoodenBox : FillableContainer +{ + [Constructible] + public FillableWoodenBox() : base(0x9AA) => Weight = 4.0; +} + +[Flippable(0x9A8, 0xE80)] +[SerializationGenerator(0)] +public partial class FillableMetalBox : FillableContainer +{ + [Constructible] + public FillableMetalBox() : base(0x9A8) + { + } +} + +[SerializationGenerator(0)] +public partial class FillableBarrel : FillableContainer +{ + [Constructible] + public FillableBarrel() : base(0xE77) + { + } +} + +[Flippable(0x9AB, 0xE7C)] +[SerializationGenerator(0, false)] +public partial class FillableMetalChest : FillableContainer +{ + [Constructible] + public FillableMetalChest() : base(0x9AB) + { + } +} + +[Flippable(0xE41, 0xE40)] +[SerializationGenerator(0, false)] +public partial class FillableMetalGoldenChest : FillableContainer +{ + [Constructible] + public FillableMetalGoldenChest() : base(0xE41) + { + } +} + +[Flippable(0xE43, 0xE42)] +[SerializationGenerator(0, false)] +public partial class FillableWoodenChest : FillableContainer +{ + [Constructible] + public FillableWoodenChest() : base(0xE43) + { + } +} diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs new file mode 100644 index 000000000..55a7b2ab1 --- /dev/null +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs @@ -0,0 +1,810 @@ +using System; +using System.Collections.Generic; +using Server.Mobiles; + +namespace Server.Items; + +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 partial class FillableContent +{ + private static readonly FillableContent Alchemist = new( + 1, + new[] + { + typeof(Alchemist) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Armorer = new( + 2, + new[] + { + typeof(Armorer) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent ArtisanGuild = new( + 1, + Array.Empty(), + new[] + { + 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)) + } + ); + + private static readonly FillableContent Baker = new( + 1, + new[] + { + typeof(Baker) + }, + new[] + { + new FillableEntry(1, typeof(RollingPin)), + new FillableEntry(2, typeof(SackFlour)), + new FillableEntry(2, typeof(BreadLoaf)), + new FillableEntry(1, typeof(FrenchBread)) + } + ); + + private static readonly FillableContent Bard = new( + 1, + new[] + { + typeof(Bard), + typeof(BardGuildmaster) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Blacksmith = new( + 2, + new[] + { + typeof(Blacksmith), + typeof(BlacksmithGuildmaster) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Bowyer = new( + 2, + new[] + { + typeof(Bowyer) + }, + new[] + { + new FillableEntry(2, typeof(Bow)), + new FillableEntry(2, typeof(Crossbow)), + new FillableEntry(1, typeof(Arrow)) + } + ); + + private static readonly FillableContent Butcher = new( + 1, + new[] + { + typeof(Butcher) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Carpenter = new( + 1, + new[] + { + typeof(Carpenter), + typeof(Architect), + typeof(RealEstateBroker) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Clothier = new( + 1, + new[] + { + typeof(Tailor), + typeof(Weaver), + typeof(TailorGuildmaster) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Cobbler = new( + 1, + new[] + { + typeof(Cobbler) + }, + new[] + { + new FillableEntry(1, typeof(Boots)), + new FillableEntry(2, typeof(Shoes)), + new FillableEntry(2, typeof(Sandals)), + new FillableEntry(1, typeof(ThighBoots)) + } + ); + + private static readonly FillableContent Docks = new( + 1, + new[] + { + typeof(Fisherman), + typeof(FisherGuildmaster) + }, + new[] + { + new FillableEntry(1, typeof(FishingPole)), + // Two different types + // new FillableEntry( 1, typeof( SmallFish ) ), + // new FillableEntry( 1, typeof( SmallFish ) ), + new FillableEntry(4, typeof(Fish)) + } + ); + + private static readonly FillableContent Farm = new( + 1, + new[] + { + typeof(Farmer), + typeof(Rancher) + }, + new[] + { + 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) + } + ); + + private static readonly FillableContent FighterGuild = new( + 3, + new[] + { + typeof(WarriorGuildmaster) + }, + new[] + { + new FillableEntry(12, Loot.ArmorTypes), + new FillableEntry(8, Loot.WeaponTypes), + new FillableEntry(3, Loot.ShieldTypes), + new FillableEntry(1, typeof(Arrow)) + } + ); + + private static readonly FillableContent Guard = new( + 3, + Array.Empty(), + new[] + { + new FillableEntry(12, Loot.ArmorTypes), + new FillableEntry(8, Loot.WeaponTypes), + new FillableEntry(3, Loot.ShieldTypes), + new FillableEntry(1, typeof(Arrow)) + } + ); + + private static readonly FillableContent Healer = new( + 1, + new[] + { + typeof(Healer), + typeof(HealerGuildmaster) + }, + new[] + { + new FillableEntry(1, typeof(Bandage)), + new FillableEntry(1, typeof(MortarPestle)), + new FillableEntry(1, typeof(LesserHealPotion)) + } + ); + + private static readonly FillableContent Herbalist = new( + 1, + new[] + { + typeof(Herbalist) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Inn = new( + 1, + Array.Empty(), + new[] + { + new FillableEntry(1, typeof(Candle)), + new FillableEntry(1, typeof(Torch)), + new FillableEntry(1, typeof(Lantern)) + } + ); + + private static readonly FillableContent Jeweler = new( + 2, + new[] + { + typeof(Jeweler) + }, + new[] + { + 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) + } + ); + + private static readonly FillableContent Library = new( + 1, + new[] + { + typeof(Scribe) + }, + new[] + { + new FillableEntry(8, Loot.LibraryBookTypes), + new FillableEntry(1, typeof(RedBook)), + new FillableEntry(1, typeof(BlueBook)) + } + ); + + private static readonly FillableContent Mage = new( + 2, + new[] + { + typeof(Mage), + typeof(HolyMage), + typeof(MageGuildmaster) + }, + new[] + { + 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) + } + ); + + private static readonly FillableContent Merchant = new( + 1, + new[] + { + typeof(MerchantGuildmaster) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Mill = new( + 1, + Array.Empty(), + new[] + { + new FillableEntry(1, typeof(SackFlour)) + } + ); + + private static readonly FillableContent Mine = new( + 1, + new[] + { + typeof(Miner) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Observatory = new( + 1, + Array.Empty(), + new[] + { + new FillableEntry(2, typeof(Sextant)), + new FillableEntry(2, typeof(Clock)), + new FillableEntry(1, typeof(Spyglass)) + } + ); + + private static readonly FillableContent Painter = new( + 1, + Array.Empty(), + new[] + { + new FillableEntry(1, typeof(PaintsAndBrush)), + new FillableEntry(2, typeof(PenAndInk)) + } + ); + + private static readonly FillableContent Provisioner = new( + 1, + new[] + { + typeof(Provisioner) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Ranger = new( + 2, + new[] + { + typeof(Ranger), + typeof(RangerGuildmaster) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Stables = new( + 1, + new[] + { + typeof(AnimalTrainer), + typeof(GypsyAnimalTrainer) + }, + new[] + { + // new FillableEntry( 1, typeof( Wheat ) ), + new FillableEntry(1, typeof(Carrot)) + } + ); + + private static readonly FillableContent Tanner = new( + 2, + new[] + { + typeof(Tanner), + typeof(LeatherWorker), + typeof(Furtrader) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Tavern = new( + 1, + new[] + { + 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) + } + ); + + private static readonly FillableContent ThiefGuild = new( + 1, + new[] + { + typeof(Thief), + typeof(ThiefGuildmaster) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Tinker = new( + 1, + new[] + { + typeof(Tinker), + typeof(TinkerGuildmaster) + }, + new[] + { + 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)), + new FillableEntry(1, typeof(DecoArrowShafts)), + new FillableEntry(1, typeof(Lockpicks)), + new FillableEntry(1, typeof(ToolKit)) + } + ); + + private static readonly FillableContent Veterinarian = new( + 1, + new[] + { + typeof(Veterinarian) + }, + new[] + { + 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)) + } + ); + + private static readonly FillableContent Weaponsmith = new( + 2, + new[] + { + typeof(Weaponsmith) + }, + new[] + { + new FillableEntry(8, Loot.WeaponTypes), + new FillableEntry(1, typeof(Arrow)) + } + ); + + private static Dictionary _acquireTable; + + // This should match the FillableContentType enum + private static readonly FillableContent[] ContentTypes = + { + 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 + }; +} diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs new file mode 100644 index 000000000..f9752d9b7 --- /dev/null +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; + +namespace Server.Items; + +public partial class FillableContent +{ + private readonly FillableEntry[] _entries; + private readonly int _weight; + + public FillableContent(int level, Type[] vendors, FillableEntry[] entries) + { + Level = level; + Vendors = vendors; + _entries = entries; + + for (var i = 0; i < entries.Length; ++i) + { + _weight += entries[i].Weight; + } + } + + public int Level { get; } + + public Type[] Vendors { get; } + + public FillableContentType TypeID => Lookup(this); + + public virtual Item Construct() + { + var index = Utility.Random(_weight); + + for (var i = 0; i < _entries.Length; ++i) + { + var entry = _entries[i]; + + if (index < entry.Weight) + { + return entry.Construct(); + } + + index -= entry.Weight; + } + + return null; + } + + public static FillableContent Lookup(FillableContentType type) + { + var v = (int)type; + + if (v >= 0 && v < ContentTypes.Length) + { + return ContentTypes[v]; + } + + return null; + } + + public static FillableContentType Lookup(FillableContent content) + { + if (content == null) + { + return FillableContentType.None; + } + + return (FillableContentType)Array.IndexOf(ContentTypes, content); + } + + public static FillableContentType Acquire(Point3D loc, Map map) + { + FillableContentType content = FillableContentType.None; + + if (map == null || map == Map.Internal) + { + return content; + } + + if (_acquireTable == null) + { + _acquireTable = new Dictionary(); + + for (var i = 0; i < ContentTypes.Length; ++i) + { + var fill = ContentTypes[i]; + + for (var j = 0; j < fill.Vendors.Length; ++j) + { + _acquireTable[fill.Vendors[j]] = fill.TypeID; + } + } + } + + Mobile nearest = null; + + // TODO: Replace with vendor shop regions and a fallback override. + foreach (var mob in map.GetMobilesInRange(loc, 20)) + { + if (nearest != null && mob.GetDistanceToSqrt(loc) > nearest.GetDistanceToSqrt(loc) && + !(nearest is Mobiles.Cobbler && mob is Mobiles.Provisioner)) + { + continue; + } + + if (_acquireTable.TryGetValue(mob.GetType(), out var check)) + { + nearest = mob; + content = check; + } + } + + return content; + } +} diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableEntry.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableEntry.cs new file mode 100644 index 000000000..ee7ca724a --- /dev/null +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableEntry.cs @@ -0,0 +1,102 @@ +using System; + +namespace Server.Items; + +public class FillableEntry +{ + protected Type[] _types; + protected int _weight; + + public FillableEntry(Type type) : this(1, new[] { type }) + { + } + + public FillableEntry(int weight, Type type) : this(weight, new[] { type }) + { + } + + public FillableEntry(Type[] types) : this(1, types) + { + } + + public FillableEntry(int weight, Type[] types) + { + _weight = weight; + _types = types; + } + + public FillableEntry(int weight, Type[] types, int offset, int count) + { + _weight = weight; + _types = new Type[count]; + Array.Copy(types, offset, _types, 0, count); + } + + public Type[] Types => _types; + public int Weight => _weight; + + public virtual Item Construct() + { + var item = Loot.Construct(_types); + + if (item is Key key) + { + key.ItemID = Utility.RandomList( + (int)KeyType.Copper, + (int)KeyType.Gold, + (int)KeyType.Iron, + (int)KeyType.Rusty + ); + } + else if (item is Arrow or Bolt) + { + item.Amount = Utility.RandomMinMax(2, 6); + } + else if (item is Bandage or Lockpick) + { + item.Amount = Utility.RandomMinMax(1, 3); + } + + return item; + } +} + +public class FillableBvrge : FillableEntry +{ + public FillableBvrge(Type type, BeverageType content) : this(1, type, content) + { + } + + public FillableBvrge(int weight, Type type, BeverageType content) : base(weight, type) => + Content = content; + + public BeverageType Content { get; } + + public override Item Construct() + { + Item item; + + var index = Utility.Random(_types.Length); + + if (_types[index] == typeof(BeverageBottle)) + { + item = new BeverageBottle(Content); + } + else if (_types[index] == typeof(Jug)) + { + item = new Jug(Content); + } + else + { + item = base.Construct(); + + if (item is BaseBeverage bev) + { + bev.Content = Content; + bev.Quantity = bev.MaxQuantity; + } + } + + return item; + } +} diff --git a/Projects/UOContent/Items/Containers/FillableContainers.cs b/Projects/UOContent/Items/Containers/FillableContainers.cs deleted file mode 100644 index 54ccdd410..000000000 --- a/Projects/UOContent/Items/Containers/FillableContainers.cs +++ /dev/null @@ -1,1612 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Mobiles; - -namespace Server.Items -{ - public abstract class FillableContainer : LockableContainer - { - protected FillableContent m_Content; - - protected DateTime m_NextRespawnTime; - protected Timer _respawnTimer; - - public FillableContainer(int itemID) : base(itemID) => Movable = false; - - public FillableContainer(Serial serial) - : base(serial) - { - } - - public virtual int MinRespawnMinutes => 60; - public virtual int MaxRespawnMinutes => 90; - - public virtual bool IsLockable => true; - public virtual bool IsTrappable => IsLockable; - - public virtual int SpawnThreshold => 2; - - [CommandProperty(AccessLevel.GameMaster)] - public DateTime NextRespawnTime => m_NextRespawnTime; - - [CommandProperty(AccessLevel.GameMaster)] - public FillableContentType ContentType - { - get => FillableContent.Lookup(m_Content); - set => Content = FillableContent.Lookup(value); - } - - public FillableContent Content - { - get => m_Content; - set - { - if (m_Content == value) - { - return; - } - - m_Content = value; - - for (var i = Items.Count - 1; i >= 0; --i) - { - if (i < Items.Count) - { - Items[i].Delete(); - } - } - - Respawn(); - } - } - - 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(GetWorldLocation(), Map); - - if (m_Content != null) - { - Respawn(); - } - } - - public override void OnItemRemoved(Item item) - { - CheckRespawn(); - } - - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - _respawnTimer?.Stop(); - _respawnTimer = null; - } - - public int GetItemsCount() - { - var count = 0; - - foreach (var item in Items) - { - count += item.Amount; - } - - return count; - } - - public void CheckRespawn() - { - var canSpawn = m_Content != null && !Deleted && GetItemsCount() <= SpawnThreshold && !Movable && - Parent == null && !IsLockedDown && !IsSecure; - - if (canSpawn) - { - if (_respawnTimer?.Running != true) - { - var mins = Utility.RandomMinMax(MinRespawnMinutes, MaxRespawnMinutes); - var delay = TimeSpan.FromMinutes(mins); - - m_NextRespawnTime = Core.Now + delay; - _respawnTimer = Timer.DelayCall(delay, Respawn); - } - } - else - { - _respawnTimer?.Stop(); - _respawnTimer = null; - } - } - - public void Respawn() - { - _respawnTimer?.Stop(); - _respawnTimer = null; - - if (m_Content == null || Deleted) - { - return; - } - - GenerateContent(); - - if (IsLockable) - { - Locked = true; - - var difficulty = (m_Content.Level - 1) * 30; - - LockLevel = difficulty - 10; - MaxLockLevel = difficulty + 30; - RequiredSkill = difficulty; - } - - if (IsTrappable && (m_Content.Level > 1 || Utility.Random(5) < 4)) - { - 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() - { - var itemsCount = GetItemsCount(); - - if (itemsCount > SpawnThreshold) - { - return 0; - } - - var maxSpawnCount = (1 + SpawnThreshold - itemsCount) * 2; - - return Utility.RandomMinMax(0, maxSpawnCount); - } - - public virtual void GenerateContent() - { - if (m_Content == null || Deleted) - { - return; - } - - var toSpawn = GetSpawnCount(); - - for (var i = 0; i < toSpawn; ++i) - { - var item = m_Content.Construct(); - - if (item == null) - { - continue; - } - - var list = Items; - - for (var j = 0; j < list.Count; ++j) - { - var subItem = list[j]; - - if (subItem is not Container && subItem.StackWith(null, item, false)) - { - break; - } - } - - if (!item.Deleted) - { - DropItem(item); - } - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(1); // version - - writer.Write((int)ContentType); - - if (_respawnTimer?.Running == true) - { - writer.Write(true); - writer.WriteDeltaTime(m_NextRespawnTime); - } - else - { - writer.Write(false); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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(); - - var delay = m_NextRespawnTime - Core.Now; - Timer.DelayCall(delay, Respawn); - } - else - { - CheckRespawn(); - } - - break; - } - } - } - } - - [Flippable(0xA97, 0xA99, 0xA98, 0xA9A, 0xA9B, 0xA9C)] - public class LibraryBookcase : FillableContainer - { - [Constructible] - public LibraryBookcase() - : base(0xA97) => - Weight = 1.0; - - public LibraryBookcase(Serial serial) - : base(serial) - { - } - - public override bool IsLockable => false; - public override int SpawnThreshold => 5; - - protected override int GetSpawnCount() => 5 - GetItemsCount(); - - public override void AcquireContent() - { - if (m_Content != null) - { - return; - } - - m_Content = FillableContent.Library; - - if (m_Content != null) - { - Respawn(); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - if (version == 0 && m_Content == null) - { - Timer.StartTimer(AcquireContent); - } - } - } - - [Flippable(0xE3D, 0xE3C)] - public class FillableLargeCrate : FillableContainer - { - [Constructible] - public FillableLargeCrate() - : base(0xE3D) => - Weight = 1.0; - - public FillableLargeCrate(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - [Flippable(0x9A9, 0xE7E)] - public class FillableSmallCrate : FillableContainer - { - [Constructible] - public FillableSmallCrate() - : base(0x9A9) => - Weight = 1.0; - - public FillableSmallCrate(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - [Flippable(0x9AA, 0xE7D)] - public class FillableWoodenBox : FillableContainer - { - [Constructible] - public FillableWoodenBox() - : base(0x9AA) => - Weight = 4.0; - - public FillableWoodenBox(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0x9A8, 0xE80)] - public class FillableMetalBox : FillableContainer - { - [Constructible] - public FillableMetalBox() - : base(0x9A8) - { - } - - public FillableMetalBox(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - if (version == 0 && Weight == 3) - { - Weight = -1; - } - } - } - - public class FillableBarrel : FillableContainer - { - [Constructible] - public FillableBarrel() - : base(0xE77) - { - } - - public FillableBarrel(Serial serial) - : base(serial) - { - } - - public override bool IsLockable => false; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - if (version == 0 && Weight == 25) - { - Weight = -1; - } - } - } - - [Flippable(0x9AB, 0xE7C)] - public class FillableMetalChest : FillableContainer - { - [Constructible] - public FillableMetalChest() - : base(0x9AB) - { - } - - public FillableMetalChest(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 25) - { - Weight = -1; - } - } - } - - [Flippable(0xE41, 0xE40)] - public class FillableMetalGoldenChest : FillableContainer - { - [Constructible] - public FillableMetalGoldenChest() - : base(0xE41) - { - } - - public FillableMetalGoldenChest(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 25) - { - Weight = -1; - } - } - } - - [Flippable(0xE43, 0xE42)] - public class FillableWoodenChest : FillableContainer - { - [Constructible] - public FillableWoodenChest() - : base(0xE43) - { - } - - public FillableWoodenChest(Serial serial) - : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 2) - { - Weight = -1; - } - } - } - - public class FillableEntry - { - protected Type[] m_Types; - protected int m_Weight; - - public FillableEntry(Type type) - : this(1, new[] { type }) - { - } - - public FillableEntry(int weight, Type type) - : this(weight, new[] { 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 (var i = 0; i < m_Types.Length; ++i) - { - m_Types[i] = types[offset + i]; - } - } - - public Type[] Types => m_Types; - public int Weight => m_Weight; - - public virtual Item Construct() - { - var item = Loot.Construct(m_Types); - - if (item is Key key) - { - key.ItemID = Utility.RandomList( - (int)KeyType.Copper, - (int)KeyType.Gold, - (int)KeyType.Iron, - (int)KeyType.Rusty - ); - } - else if (item is Arrow or Bolt) - { - item.Amount = Utility.RandomMinMax(2, 6); - } - else if (item is Bandage or Lockpick) - { - item.Amount = Utility.RandomMinMax(1, 3); - } - - return item; - } - } - - public class FillableBvrge : FillableEntry - { - public FillableBvrge(Type type, BeverageType content) - : this(1, type, content) - { - } - - public FillableBvrge(int weight, Type type, BeverageType content) - : base(weight, type) => - Content = content; - - public BeverageType Content { get; } - - public override Item Construct() - { - Item item; - - var index = Utility.Random(m_Types.Length); - - if (m_Types[index] == typeof(BeverageBottle)) - { - item = new BeverageBottle(Content); - } - else if (m_Types[index] == typeof(Jug)) - { - item = new Jug(Content); - } - else - { - item = base.Construct(); - - if (item is BaseBeverage bev) - { - bev.Content = 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 - { - public static FillableContent Alchemist = new( - 1, - new[] - { - typeof(Alchemist) - }, - new[] - { - 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( - 2, - new[] - { - typeof(Armorer) - }, - new[] - { - 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( - 1, - Array.Empty(), - new[] - { - 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( - 1, - new[] - { - typeof(Baker) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Bard), - typeof(BardGuildmaster) - }, - new[] - { - 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( - 2, - new[] - { - typeof(Blacksmith), - typeof(BlacksmithGuildmaster) - }, - new[] - { - 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( - 2, - new[] - { - typeof(Bowyer) - }, - new[] - { - new FillableEntry(2, typeof(Bow)), - new FillableEntry(2, typeof(Crossbow)), - new FillableEntry(1, typeof(Arrow)) - } - ); - - public static FillableContent Butcher = new( - 1, - new[] - { - typeof(Butcher) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Carpenter), - typeof(Architect), - typeof(RealEstateBroker) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Tailor), - typeof(Weaver), - typeof(TailorGuildmaster) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Cobbler) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Fisherman), - typeof(FisherGuildmaster) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Farmer), - typeof(Rancher) - }, - new[] - { - 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( - 3, - new[] - { - typeof(WarriorGuildmaster) - }, - new[] - { - 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( - 3, - Array.Empty(), - new[] - { - 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( - 1, - new[] - { - typeof(Healer), - typeof(HealerGuildmaster) - }, - new[] - { - new FillableEntry(1, typeof(Bandage)), - new FillableEntry(1, typeof(MortarPestle)), - new FillableEntry(1, typeof(LesserHealPotion)) - } - ); - - public static FillableContent Herbalist = new( - 1, - new[] - { - typeof(Herbalist) - }, - new[] - { - 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( - 1, - Array.Empty(), - new[] - { - new FillableEntry(1, typeof(Candle)), - new FillableEntry(1, typeof(Torch)), - new FillableEntry(1, typeof(Lantern)) - } - ); - - public static FillableContent Jeweler = new( - 2, - new[] - { - typeof(Jeweler) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Scribe) - }, - new[] - { - new FillableEntry(8, Loot.LibraryBookTypes), - new FillableEntry(1, typeof(RedBook)), - new FillableEntry(1, typeof(BlueBook)) - } - ); - - public static FillableContent Mage = new( - 2, - new[] - { - typeof(Mage), - typeof(HolyMage), - typeof(MageGuildmaster) - }, - new[] - { - 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( - 1, - new[] - { - typeof(MerchantGuildmaster) - }, - new[] - { - 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( - 1, - Array.Empty(), - new[] - { - new FillableEntry(1, typeof(SackFlour)) - } - ); - - public static FillableContent Mine = new( - 1, - new[] - { - typeof(Miner) - }, - new[] - { - 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( - 1, - Array.Empty(), - new[] - { - new FillableEntry(2, typeof(Sextant)), - new FillableEntry(2, typeof(Clock)), - new FillableEntry(1, typeof(Spyglass)) - } - ); - - public static FillableContent Painter = new( - 1, - Array.Empty(), - new[] - { - new FillableEntry(1, typeof(PaintsAndBrush)), - new FillableEntry(2, typeof(PenAndInk)) - } - ); - - public static FillableContent Provisioner = new( - 1, - new[] - { - typeof(Provisioner) - }, - new[] - { - 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( - 2, - new[] - { - typeof(Ranger), - typeof(RangerGuildmaster) - }, - new[] - { - 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( - 1, - new[] - { - typeof(AnimalTrainer), - typeof(GypsyAnimalTrainer) - }, - new[] - { - // new FillableEntry( 1, typeof( Wheat ) ), - new FillableEntry(1, typeof(Carrot)) - } - ); - - public static FillableContent Tanner = new( - 2, - new[] - { - typeof(Tanner), - typeof(LeatherWorker), - typeof(Furtrader) - }, - new[] - { - 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( - 1, - new[] - { - 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( - 1, - new[] - { - typeof(Thief), - typeof(ThiefGuildmaster) - }, - new[] - { - 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( - 1, - new[] - { - typeof(Tinker), - typeof(TinkerGuildmaster) - }, - new[] - { - 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)), - new FillableEntry(1, typeof(DecoArrowShafts)), - new FillableEntry(1, typeof(Lockpicks)), - new FillableEntry(1, typeof(ToolKit)) - } - ); - - public static FillableContent Veterinarian = new( - 1, - new[] - { - typeof(Veterinarian) - }, - new[] - { - 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( - 2, - new[] - { - typeof(Weaponsmith) - }, - new[] - { - new FillableEntry(8, Loot.WeaponTypes), - new FillableEntry(1, typeof(Arrow)) - } - ); - - private static Dictionary m_AcquireTable; - - private static readonly FillableContent[] m_ContentTypes = - { - 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 - }; - - private readonly FillableEntry[] m_Entries; - private readonly int m_Weight; - - public FillableContent(int level, Type[] vendors, FillableEntry[] entries) - { - Level = level; - Vendors = vendors; - m_Entries = entries; - - for (var i = 0; i < entries.Length; ++i) - { - m_Weight += entries[i].Weight; - } - } - - public int Level { get; } - - public Type[] Vendors { get; } - - public FillableContentType TypeID => Lookup(this); - - public virtual Item Construct() - { - var index = Utility.Random(m_Weight); - - for (var i = 0; i < m_Entries.Length; ++i) - { - var entry = m_Entries[i]; - - if (index < entry.Weight) - { - return entry.Construct(); - } - - index -= entry.Weight; - } - - return null; - } - - public static FillableContent Lookup(FillableContentType type) - { - var 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); - } - - public static FillableContent Acquire(Point3D loc, Map map) - { - if (map == null || map == Map.Internal) - { - return null; - } - - if (m_AcquireTable == null) - { - m_AcquireTable = new Dictionary(); - - for (var i = 0; i < m_ContentTypes.Length; ++i) - { - var fill = m_ContentTypes[i]; - - for (var j = 0; j < fill.Vendors.Length; ++j) - { - m_AcquireTable[fill.Vendors[j]] = fill; - } - } - } - - Mobile nearest = null; - FillableContent content = null; - - foreach (var mob in map.GetMobilesInRange(loc, 20)) - { - if (nearest != null && mob.GetDistanceToSqrt(loc) > nearest.GetDistanceToSqrt(loc) && - !(nearest is Cobbler && mob is Provisioner)) - { - continue; - } - - if (m_AcquireTable.TryGetValue(mob.GetType(), out var check)) - { - nearest = mob; - content = check; - } - } - - return content; - } - } -} diff --git a/Projects/UOContent/Items/Containers/FurnitureContainer.cs b/Projects/UOContent/Items/Containers/FurnitureContainer.cs index 07dbe1597..e638ab2d7 100644 --- a/Projects/UOContent/Items/Containers/FurnitureContainer.cs +++ b/Projects/UOContent/Items/Containers/FurnitureContainer.cs @@ -1,419 +1,241 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Furniture] +[Flippable(0x2815, 0x2816)] +[SerializationGenerator(0, false)] +public partial class TallCabinet : BaseContainer { - [Furniture] - [Flippable(0x2815, 0x2816)] - public class TallCabinet : BaseContainer + [Constructible] + public TallCabinet() : base(0x2815) => Weight = 1.0; +} + +[Furniture] +[Flippable(0x2817, 0x2818)] +[SerializationGenerator(0, false)] +public partial class ShortCabinet : BaseContainer +{ + [Constructible] + public ShortCabinet() : base(0x2817) => Weight = 1.0; +} + +[Furniture] +[Flippable(0x2857, 0x2858)] +[SerializationGenerator(0, false)] +public partial class RedArmoire : BaseContainer +{ + [Constructible] + public RedArmoire() : base(0x2857) => Weight = 1.0; +} + +[Furniture] +[Flippable(0x285D, 0x285E)] +[SerializationGenerator(0, false)] +public partial class CherryArmoire : BaseContainer +{ + [Constructible] + public CherryArmoire() : base(0x285D) => Weight = 1.0; +} + +[Furniture] +[Flippable(0x285B, 0x285C)] +[SerializationGenerator(0, false)] +public partial class MapleArmoire : BaseContainer +{ + [Constructible] + public MapleArmoire() : base(0x285B) => Weight = 1.0; +} + +[Furniture] +[Flippable(0x2859, 0x285A)] +[SerializationGenerator(0, false)] +public partial class ElegantArmoire : BaseContainer +{ + [Constructible] + public ElegantArmoire() : base(0x2859) => Weight = 1.0; +} + +[Furniture] +[SerializationGenerator(0)] +[Flippable(0x2D07, 0x2D08)] +public partial class FancyElvenArmoire : BaseContainer +{ + [Constructible] + public FancyElvenArmoire() : base(0x2D07) => Weight = 1.0; + public override int DefaultGumpID => 0x4E; + public override int DefaultDropSound => 0x42; +} + +[Furniture] +[SerializationGenerator(0)] +[Flippable(0x2D05, 0x2D06)] +public partial class SimpleElvenArmoire : BaseContainer +{ + [Constructible] + public SimpleElvenArmoire() : base(0x2D05) => Weight = 1.0; + public override int DefaultGumpID => 0x4F; + public override int DefaultDropSound => 0x42; +} + +[Furniture] +[Flippable(0xa97, 0xa99, 0xa98, 0xa9a, 0xa9b, 0xa9c)] +[SerializationGenerator(0, false)] +public partial class FullBookcase : BaseContainer +{ + [Constructible] + public FullBookcase() : base(0xA97) => Weight = 1.0; +} + +[Furniture] +[Flippable(0xa9d, 0xa9e)] +[SerializationGenerator(0, false)] +public partial class EmptyBookcase : BaseContainer +{ + [Constructible] + public EmptyBookcase() : base(0xA9D) { - [Constructible] - public TallCabinet() : base(0x2815) => Weight = 1.0; + } +} - public TallCabinet(Serial serial) : base(serial) - { - } +[Furniture] +[Flippable(0xa2c, 0xa34)] +[SerializationGenerator(0, false)] +public partial class Drawer : BaseContainer +{ + [Constructible] + public Drawer() : base(0xA2C) => Weight = 1.0; +} - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } +[Furniture] +[Flippable(0xa30, 0xa38)] +[SerializationGenerator(0, false)] +public partial class FancyDrawer : BaseContainer +{ + [Constructible] + public FancyDrawer() : base(0xA30) => Weight = 1.0; +} - public override void Deserialize(IGenericReader reader) +[Furniture] +[Flippable(0xa4f, 0xa53)] +[SerializationGenerator(0, false)] +public partial class Armoire : BaseContainer +{ + [Constructible] + public Armoire() : base(0xA4F) => Weight = 1.0; + + public override void DisplayTo(Mobile m) + { + if (DynamicFurniture.Open(this, m)) { - base.Deserialize(reader); - var version = reader.ReadInt(); + base.DisplayTo(m); } } - [Furniture] - [Flippable(0x2817, 0x2818)] - public class ShortCabinet : BaseContainer + [AfterDeserialization] + private void AfterDeserialization() { - [Constructible] - public ShortCabinet() : base(0x2817) => Weight = 1.0; + DynamicFurniture.Close(this); + } +} - public ShortCabinet(Serial serial) : base(serial) - { - } +[Furniture] +[Flippable(0xa4d, 0xa51)] +[SerializationGenerator(0, false)] +public partial class FancyArmoire : BaseContainer +{ + [Constructible] + public FancyArmoire() : base(0xA4D) => Weight = 1.0; - public override void Serialize(IGenericWriter writer) + public override void DisplayTo(Mobile m) + { + if (DynamicFurniture.Open(this, m)) { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); + base.DisplayTo(m); } } - [Furniture] - [Flippable(0x2857, 0x2858)] - public class RedArmoire : BaseContainer + [AfterDeserialization] + private void AfterDeserialization() { - [Constructible] - public RedArmoire() : base(0x2857) => Weight = 1.0; - - public RedArmoire(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + DynamicFurniture.Close(this); } +} - [Furniture] - [Flippable(0x285D, 0x285E)] - public class CherryArmoire : BaseContainer +public static class DynamicFurniture +{ + private static readonly Dictionary _table = new(); + + public static bool Open(Container c, Mobile m) { - [Constructible] - public CherryArmoire() : base(0x285D) => Weight = 1.0; - - public CherryArmoire(Serial serial) : base(serial) + if (_table.ContainsKey(c)) { + c.SendRemovePacket(); + Close(c); + c.Delta(ItemDelta.Update); + c.ProcessDelta(); + return false; } - public override void Serialize(IGenericWriter writer) + if (c is Armoire or FancyArmoire) { - base.Serialize(writer); - writer.Write(0); // version - } + Timer t = new FurnitureTimer(c, m); + t.Start(); + _table[c] = t; - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0x285B, 0x285C)] - public class MapleArmoire : BaseContainer - { - [Constructible] - public MapleArmoire() : base(0x285B) => Weight = 1.0; - - public MapleArmoire(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0x2859, 0x285A)] - public class ElegantArmoire : BaseContainer - { - [Constructible] - public ElegantArmoire() : base(0x2859) => Weight = 1.0; - - public ElegantArmoire(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - [Furniture] - [Serializable(0)] - [Flippable(0x2D07, 0x2D08)] - public partial class FancyElvenArmoire : BaseContainer - { - [Constructible] - public FancyElvenArmoire() : base(0x2D07) => Weight = 1.0; - public override int DefaultGumpID => 0x4E; - public override int DefaultDropSound => 0x42; - } - - [Furniture] - [Serializable(0)] - [Flippable(0x2D05, 0x2D06)] - public partial class SimpleElvenArmoire : BaseContainer - { - [Constructible] - public SimpleElvenArmoire() : base(0x2D05) => Weight = 1.0; - public override int DefaultGumpID => 0x4F; - public override int DefaultDropSound => 0x42; - } - - [Furniture] - [Flippable(0xa97, 0xa99, 0xa98, 0xa9a, 0xa9b, 0xa9c)] - public class FullBookcase : BaseContainer - { - [Constructible] - public FullBookcase() : base(0xA97) => Weight = 1.0; - - public FullBookcase(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0xa9d, 0xa9e)] - public class EmptyBookcase : BaseContainer - { - [Constructible] - public EmptyBookcase() : base(0xA9D) - { - } - - public EmptyBookcase(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - if (version == 0 && Weight == 1.0) + c.ItemID = c.ItemID switch { - Weight = -1; - } + 0xA4D => 0xA4C, + 0xA4F => 0xA4E, + 0xA51 => 0xA50, + 0xA53 => 0xA52, + _ => c.ItemID + }; } + + return true; } - [Furniture] - [Flippable(0xa2c, 0xa34)] - public class Drawer : BaseContainer + public static void Close(Container c) { - [Constructible] - public Drawer() : base(0xA2C) => Weight = 1.0; - - public Drawer(Serial serial) : base(serial) + if (_table.Remove(c, out var t)) { + t.Stop(); } - public override void Serialize(IGenericWriter writer) + if (c is Armoire or FancyArmoire) { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0xa30, 0xa38)] - public class FancyDrawer : BaseContainer - { - [Constructible] - public FancyDrawer() : base(0xA30) => Weight = 1.0; - - public FancyDrawer(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - [Furniture] - [Flippable(0xa4f, 0xa53)] - public class Armoire : BaseContainer - { - [Constructible] - public Armoire() : base(0xA4F) => Weight = 1.0; - - public Armoire(Serial serial) : base(serial) - { - } - - public override void DisplayTo(Mobile m) - { - if (DynamicFurniture.Open(this, m)) + c.ItemID = c.ItemID switch { - base.DisplayTo(m); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - DynamicFurniture.Close(this); - } - } - - [Furniture] - [Flippable(0xa4d, 0xa51)] - public class FancyArmoire : BaseContainer - { - [Constructible] - public FancyArmoire() : base(0xA4D) => Weight = 1.0; - - public FancyArmoire(Serial serial) : base(serial) - { - } - - public override void DisplayTo(Mobile m) - { - if (DynamicFurniture.Open(this, m)) - { - base.DisplayTo(m); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - DynamicFurniture.Close(this); - } - } - - public static class DynamicFurniture - { - private static readonly Dictionary m_Table = new(); - - public static bool Open(Container c, Mobile m) - { - if (m_Table.ContainsKey(c)) - { - c.SendRemovePacket(); - Close(c); - c.Delta(ItemDelta.Update); - c.ProcessDelta(); - return false; - } - - if (c is Armoire or FancyArmoire) - { - Timer t = new FurnitureTimer(c, m); - t.Start(); - m_Table[c] = t; - - c.ItemID = c.ItemID switch - { - 0xA4D => 0xA4C, - 0xA4F => 0xA4E, - 0xA51 => 0xA50, - 0xA53 => 0xA52, - _ => c.ItemID - }; - } - - return true; - } - - public static void Close(Container c) - { - if (m_Table.Remove(c, out var t)) - { - t.Stop(); - } - - if (c is Armoire or FancyArmoire) - { - c.ItemID = c.ItemID switch - { - 0xA4C => 0xA4D, - 0xA4E => 0xA4F, - 0xA50 => 0xA51, - 0xA52 => 0xA53, - _ => c.ItemID - }; - } - } - } - - public class FurnitureTimer : Timer - { - private readonly Container m_Container; - private readonly Mobile m_Mobile; - - public FurnitureTimer(Container c, Mobile m) : base(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5)) - { - - 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); - } + 0xA4C => 0xA4D, + 0xA4E => 0xA4F, + 0xA50 => 0xA51, + 0xA52 => 0xA53, + _ => c.ItemID + }; + } + } +} + +public class FurnitureTimer : Timer +{ + private readonly Container _container; + private readonly Mobile _mobile; + + public FurnitureTimer(Container c, Mobile m) : base(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5)) + { + + _container = c; + _mobile = m; + } + + protected override void OnTick() + { + if (_mobile.Map != _container.Map || !_mobile.InRange(_container.GetWorldLocation(), 3)) + { + DynamicFurniture.Close(_container); } } } diff --git a/Projects/UOContent/Items/Containers/LockableContainer.cs b/Projects/UOContent/Items/Containers/LockableContainer.cs index c541abaa2..a65670256 100644 --- a/Projects/UOContent/Items/Containers/LockableContainer.cs +++ b/Projects/UOContent/Items/Containers/LockableContainer.cs @@ -1,356 +1,236 @@ using System; +using ModernUO.Serialization; using Server.Engines.Craft; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class LockableContainer : TrappableContainer, ILockable, ILockpickable, ICraftable, IShipwreckedItem { - public abstract class LockableContainer : TrappableContainer, ILockable, ILockpickable, ICraftable, IShipwreckedItem + public LockableContainer(int itemID) : base(itemID) => MaxLockLevel = 100; + + public override bool TrapOnOpen => !_trapOnLockpick; + + public override bool DisplaysContent => !_rawLocked; + + public int OnCraft( + int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, + CraftItem craftItem, int resHue + ) { - private bool m_Locked; - - public LockableContainer(int itemID) : base(itemID) => MaxLockLevel = 100; - - public LockableContainer(Serial serial) : base(serial) + if (from.CheckSkill(SkillName.Tinkering, -5.0, 15.0)) { + from.SendLocalizedMessage(500636); // Your tinker skill was sufficient to make the item lockable. + + var key = new Key(KeyType.Copper, Key.RandomValue()); + + _keyValue = key.KeyValue; + DropItem(key); + + var tinkering = from.Skills.Tinkering.Value; + var level = (int)(tinkering * 0.8); + + _requiredSkill = Math.Min(level - 4, 95); + _maxLockLevel = Math.Min(level + 35, 95); + + // Lock level of 0 means it is not pickable, so change it to -1 + _lockLevel = level == 14 ? -1 : Math.Min(level - 14, 95); + } + else + { + from.SendLocalizedMessage(500637); // Your tinker skill was insufficient to make the item lockable. } - public override bool TrapOnOpen => !TrapOnLockpick; + return 1; + } - [CommandProperty(AccessLevel.GameMaster)] - public bool TrapOnLockpick { get; set; } + [CommandProperty(AccessLevel.GameMaster)] + public Mobile Picker { get; set; } - public override bool DisplaysContent => !m_Locked; + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _isShipwreckedItem; - public int OnCraft( - int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, - CraftItem craftItem, int resHue - ) + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _trapOnLockpick; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _requiredSkill; + + [SerializableField(3)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _maxLockLevel; + + [SerializableField(4)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private uint _keyValue; + + [SerializableField(5)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _lockLevel; + + [SerializableField(6, getter: "private", setter: "private")] + private bool _rawLocked; + + [CommandProperty(AccessLevel.GameMaster)] + public virtual bool Locked + { + get => _rawLocked; + set { - if (from.CheckSkill(SkillName.Tinkering, -5.0, 15.0)) + _rawLocked = value; + + if (_rawLocked) { - from.SendLocalizedMessage(500636); // Your tinker skill was sufficient to make the item lockable. - - var key = new Key(KeyType.Copper, Key.RandomValue()); - - KeyValue = key.KeyValue; - DropItem(key); - - var tinkering = from.Skills.Tinkering.Value; - var 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. + Picker = null; } - return 1; + InvalidateProperties(); + this.MarkDirty(); + } + } + + public virtual void LockPick(Mobile from) + { + Locked = false; + Picker = from; + + if (_trapOnLockpick && ExecuteTrap(from)) + { + _trapOnLockpick = false; + } + } + + public override bool CheckContentDisplay(Mobile from) => !_rawLocked && base.CheckContentDisplay(from); + + public override bool TryDropItem(Mobile from, Item dropped, bool sendFullMessage) + { + if (from.AccessLevel < AccessLevel.GameMaster && _rawLocked) + { + from.SendLocalizedMessage(501747); // It appears to be locked. + return false; } - [CommandProperty(AccessLevel.GameMaster)] - public virtual bool Locked + return base.TryDropItem(from, dropped, sendFullMessage); + } + + public override bool OnDragDropInto(Mobile from, Item item, Point3D p) + { + if (from.AccessLevel < AccessLevel.GameMaster && _rawLocked) { - get => m_Locked; - set - { - m_Locked = value; - - if (m_Locked) - { - Picker = null; - } - - InvalidateProperties(); - } + from.SendLocalizedMessage(501747); // It appears to be locked. + return false; } - [CommandProperty(AccessLevel.GameMaster)] - public uint KeyValue { get; set; } + return base.OnDragDropInto(from, item, p); + } - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Picker { get; set; } + public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => + base.CheckLift(from, item, ref reject) && + (item == this || from.AccessLevel >= AccessLevel.GameMaster || !_rawLocked); - [CommandProperty(AccessLevel.GameMaster)] - public int MaxLockLevel { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int LockLevel { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int RequiredSkill { get; set; } - - public virtual void LockPick(Mobile from) + public override bool CheckItemUse(Mobile from, Item item) + { + if (!base.CheckItemUse(from, item)) { - Locked = false; - Picker = from; - - if (TrapOnLockpick && ExecuteTrap(from)) - { - TrapOnLockpick = false; - } + return false; } - [CommandProperty(AccessLevel.GameMaster)] - public bool IsShipwreckedItem { get; set; } - - public override void Serialize(IGenericWriter writer) + if (item != this && from.AccessLevel < AccessLevel.GameMaster && _rawLocked) { - base.Serialize(writer); - - writer.Write(6); // version - - writer.Write(IsShipwreckedItem); - - writer.Write(TrapOnLockpick); - - writer.Write(RequiredSkill); - - writer.Write(MaxLockLevel); - - writer.Write(KeyValue); - writer.Write(LockLevel); - writer.Write(m_Locked); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + return false; } - public override void Deserialize(IGenericReader reader) + return true; + } + + public virtual bool CheckLocked(Mobile from) + { + if (!_rawLocked) { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 6: - { - IsShipwreckedItem = reader.ReadBool(); - - goto case 5; - } - case 5: - { - TrapOnLockpick = reader.ReadBool(); - - goto case 4; - } - case 4: - { - RequiredSkill = reader.ReadInt(); - - goto case 3; - } - case 3: - { - MaxLockLevel = reader.ReadInt(); - - goto case 2; - } - case 2: - { - KeyValue = reader.ReadUInt(); - - goto case 1; - } - case 1: - { - LockLevel = reader.ReadInt(); - - goto case 0; - } - case 0: - { - if (version < 3) - { - MaxLockLevel = 100; - } - - if (version < 4) - { - if (MaxLockLevel - LockLevel == 40) - { - RequiredSkill = LockLevel + 6; - LockLevel = RequiredSkill - 10; - MaxLockLevel = RequiredSkill + 39; - } - else - { - RequiredSkill = LockLevel; - } - } - - m_Locked = reader.ReadBool(); - - break; - } - } + return false; } - public override bool CheckContentDisplay(Mobile from) => !m_Locked && base.CheckContentDisplay(from); + var inaccessible = from.AccessLevel < AccessLevel.GameMaster; - public override bool TryDropItem(Mobile from, Item dropped, bool sendFullMessage) + int number = inaccessible + ? 501747 // It appears to be locked. + : 502502; // That is locked, but you open it with your godly powers. + + from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number); + + return inaccessible; + } + + public override void OnTelekinesis(Mobile from) + { + if (CheckLocked(from)) { - if (from.AccessLevel < AccessLevel.GameMaster && m_Locked) - { - from.SendLocalizedMessage(501747); // It appears to be locked. - return false; - } - - return base.TryDropItem(from, dropped, sendFullMessage); + Effects.SendLocationParticles( + EffectItem.Create(Location, Map, EffectItem.DefaultDuration), + 0x376A, + 9, + 32, + 5022 + ); + Effects.PlaySound(Location, Map, 0x1F5); + return; } - 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; - } + base.OnTelekinesis(from); + } - return base.OnDragDropInto(from, item, p); + public override void OnDoubleClickSecureTrade(Mobile from) + { + if (CheckLocked(from)) + { + return; } - public override bool CheckLift(Mobile from, Item item, ref LRReason reject) + base.OnDoubleClickSecureTrade(from); + } + + public override void Open(Mobile from) + { + if (CheckLocked(from)) { - if (!base.CheckLift(from, item, ref reject)) - { - return false; - } - - if (item != this && from.AccessLevel < AccessLevel.GameMaster && m_Locked) - { - return false; - } - - return true; + return; } - public override bool CheckItemUse(Mobile from, Item item) + base.Open(from); + } + + public override void OnSnoop(Mobile from) + { + if (CheckLocked(from)) { - 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; + return; } - public virtual bool CheckLocked(Mobile from) + base.OnSnoop(from); + } + + public override void AddNameProperties(IPropertyList list) + { + base.AddNameProperties(list); + + if (_isShipwreckedItem) { - var 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.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number); - } - - return inaccessible; + list.Add(1041645); // recovered from a shipwreck } + } - public override void OnTelekinesis(Mobile from) + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick(from); + + if (_isShipwreckedItem) { - 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 override void AddNameProperties(ObjectPropertyList list) - { - base.AddNameProperties(list); - - if (IsShipwreckedItem) - { - list.Add(1041645); // recovered from a shipwreck - } - } - - public override void OnSingleClick(Mobile from) - { - base.OnSingleClick(from); - - if (IsShipwreckedItem) - { - LabelTo(from, 1041645); // recovered from a shipwreck - } + LabelTo(from, 1041645); // recovered from a shipwreck } } } diff --git a/Projects/UOContent/Items/Containers/MarkContainer.cs b/Projects/UOContent/Items/Containers/MarkContainer.cs index dbdd56516..59846b477 100644 --- a/Projects/UOContent/Items/Containers/MarkContainer.cs +++ b/Projects/UOContent/Items/Containers/MarkContainer.cs @@ -1,256 +1,232 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class MarkContainer : LockableContainer { - public class MarkContainer : LockableContainer + [SerializableField(0, getter: "private", setter: "private")] + private bool _rawAutoLock; + + [TimerDrift] + [SerializableField(1, getter: "private", setter: "private")] + private InternalTimer _relockTimer; + + [DeserializeTimerField(1)] + private void DeserializeRelockTimer(TimeSpan delay) { - private bool m_AutoLock; - private InternalTimer m_RelockTimer; - - [Constructible] - public MarkContainer(bool bone = false, bool locked = false) : base(bone ? 0xECA : 0xE79) + if (!Locked && _rawAutoLock) { - Movable = false; + _relockTimer = new InternalTimer(this, delay); + } + } - if (bone) + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Map _targetMap; + + [SerializableField(3)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Point3D _target; + + [SerializableField(4)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private string _description; + + [Constructible] + public MarkContainer(bool bone = false, bool locked = false) : base(bone ? 0xECA : 0xE79) + { + Movable = false; + + if (bone) + { + Hue = 1102; + } + + _rawAutoLock = locked; + Locked = locked; + + if (locked) + { + LockLevel = ILockpickable.MagicLock; + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public bool AutoLock + { + get => _rawAutoLock; + set + { + _rawAutoLock = value; + + if (!_rawAutoLock) { - Hue = 1102; + StopTimer(); } - - m_AutoLock = locked; - Locked = locked; - - if (locked) + else if (!Locked) { - LockLevel = -255; - } - } - - public MarkContainer(Serial serial) : base(serial) - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public bool AutoLock - { - get => 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; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Point3D Target { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool Bone - { - get => ItemID == 0xECA; - set - { - ItemID = value ? 0xECA : 0xE79; - Hue = value ? 1102 : 0; - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public string Description { get; set; } - - public override bool IsDecoContainer => false; - - [CommandProperty(AccessLevel.GameMaster)] - public override bool Locked - { - get => base.Locked; - set - { - base.Locked = value; - - if (m_AutoLock) - { - StopTimer(); - - if (!Locked) - { - m_RelockTimer = new InternalTimer(this); - } - } - } - } - - public static void Initialize() - { - CommandSystem.Register("SecretLocGen", AccessLevel.Administrator, 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) - { - var eable = map.GetItemsInRange(p, 0); - - foreach (var item in eable) - { - if (item.Z == p.Z) - { - eable.Free(); - return true; - break; - } - } - - eable.Free(); - return false; - } - - private static void CreateMalasPassage( - int x, int y, int z, int xTarget, int yTarget, int zTarget, bool bone, - bool locked - ) - { - var location = new Point3D(x, y, z); - - if (FindMarkContainer(location, Map.Malas)) - { - return; - } - - var cont = new MarkContainer(bone, locked) - { - TargetMap = Map.Malas, - Target = new Point3D(xTarget, yTarget, zTarget), - Description = "strange location" - }; - - cont.MoveToWorld(location, Map.Malas); - } - - public void StopTimer() - { - m_RelockTimer?.Stop(); - m_RelockTimer = null; - } - - public void Mark(RecallRune rune) - { - if (TargetMap != null) - { - rune.Marked = true; - rune.TargetMap = TargetMap; - rune.Target = Target; - rune.Description = Description; - rune.House = null; - } - } - - public override bool OnDragDrop(Mobile from, Item dropped) - { - if (dropped is RecallRune rune && base.OnDragDrop(from, dropped)) - { - Mark(rune); - return true; - } - - return false; - } - - public override bool OnDragDropInto(Mobile from, Item dropped, Point3D p) - { - if (dropped is RecallRune rune && base.OnDragDropInto(from, dropped, p)) - { - Mark(rune); - return true; - } - - return false; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_AutoLock); - - if (!Locked && m_AutoLock) - { - writer.WriteDeltaTime(m_RelockTimer.RelockTime); - } - - writer.Write(TargetMap); - writer.Write(Target); - writer.Write(Description); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_AutoLock = reader.ReadBool(); - - if (!Locked && m_AutoLock) - { - m_RelockTimer = new InternalTimer(this, reader.ReadDeltaTime() - Core.Now); - } - - TargetMap = reader.ReadMap(); - Target = reader.ReadPoint3D(); - Description = reader.ReadString(); - } - - private class InternalTimer : Timer - { - public InternalTimer(MarkContainer container) : this(container, TimeSpan.FromMinutes(5.0)) - { - } - - public InternalTimer(MarkContainer container, TimeSpan delay) : base(delay) - { - Container = container; - RelockTime = Core.Now + delay; - - Start(); - } - - public MarkContainer Container { get; } - - public DateTime RelockTime { get; } - - protected override void OnTick() - { - Container.Locked = true; - Container.LockLevel = -255; + _relockTimer ??= new InternalTimer(this); } } } + + [CommandProperty(AccessLevel.GameMaster)] + public bool Bone + { + get => ItemID == 0xECA; + set + { + ItemID = value ? 0xECA : 0xE79; + Hue = value ? 1102 : 0; + } + } + + public override bool IsDecoContainer => false; + + [CommandProperty(AccessLevel.GameMaster)] + public override bool Locked + { + get => base.Locked; + set + { + base.Locked = value; + + if (_rawAutoLock) + { + StopTimer(); + + if (!Locked) + { + _relockTimer = new InternalTimer(this); + } + } + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public DateTime NextRelock => _relockTimer.Next; + + public static void Initialize() + { + CommandSystem.Register("SecretLocGen", AccessLevel.Administrator, 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) + { + var eable = map.GetItemsInRange(p, 0); + + foreach (var item in eable) + { + if (item.Z == p.Z) + { + 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 + ) + { + var location = new Point3D(x, y, z); + + if (FindMarkContainer(location, Map.Malas)) + { + return; + } + + var cont = new MarkContainer(bone, locked) + { + TargetMap = Map.Malas, + Target = new Point3D(xTarget, yTarget, zTarget), + Description = "strange location" + }; + + cont.MoveToWorld(location, Map.Malas); + } + + public void StopTimer() + { + _relockTimer?.Stop(); + _relockTimer = null; + } + + public void Mark(RecallRune rune) + { + if (_targetMap != null) + { + rune.Marked = true; + rune.TargetMap = _targetMap; + rune.Target = _target; + rune.Description = _description; + rune.House = null; + } + } + + public override bool OnDragDrop(Mobile from, Item dropped) + { + if (dropped is RecallRune rune && base.OnDragDrop(from, dropped)) + { + Mark(rune); + return true; + } + + return false; + } + + public override bool OnDragDropInto(Mobile from, Item dropped, Point3D p) + { + if (dropped is RecallRune rune && base.OnDragDropInto(from, dropped, p)) + { + Mark(rune); + return true; + } + + return false; + } + + private class InternalTimer : Timer + { + public InternalTimer(MarkContainer container) : this(container, TimeSpan.FromMinutes(5.0)) + { + } + + public InternalTimer(MarkContainer container, TimeSpan delay) : base(delay) + { + Container = container; + + Start(); + } + + public MarkContainer Container { get; } + + protected override void OnTick() + { + Container.Locked = true; + Container.LockLevel = ILockpickable.MagicLock; + } + } } diff --git a/Projects/UOContent/Items/Containers/ParagonChest.cs b/Projects/UOContent/Items/Containers/ParagonChest.cs index 94debdc29..d6c730dff 100644 --- a/Projects/UOContent/Items/Containers/ParagonChest.cs +++ b/Projects/UOContent/Items/Containers/ParagonChest.cs @@ -1,216 +1,189 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class ParagonChest : LockableContainer { - [Flippable] - public class ParagonChest : LockableContainer + private static readonly int[] _itemIDs = { - private static readonly int[] m_ItemIDs = + 0x9AB, 0xE40, 0xE41, 0xE7C + }; + + private static readonly int[] _hues = + { + 0x0, 0x455, 0x47E, 0x89F, 0x8A5, 0x8AB, + 0x966, 0x96D, 0x972, 0x973, 0x979 + }; + + [InternString] + [SerializableField(0, "private", "private")] + private string _name; + + [Constructible] + public ParagonChest(string name, int level) : base(_itemIDs.RandomElement()) + { + _name = name; + Hue = _hues.RandomElement(); + Fill(level); + } + + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick(from); + LabelTo(from, 1063449, _name); + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + list.Add(1063449, _name); + } + + private static void GetRandomAOSStats(out int attributeCount, out int min, out int max) + { + var rnd = Utility.Random(15); + + if (rnd < 1) { - 0x9AB, 0xE40, 0xE41, 0xE7C + 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 void Flip() + { + ItemID = ItemID switch + { + 0x9AB => 0xE7C, + 0xE7C => 0x9AB, + 0xE40 => 0xE41, + 0xE41 => 0xE40, + _ => ItemID + }; + } + + private void Fill(int level) + { + TrapType = TrapType.ExplosionTrap; + TrapPower = level * 25; + TrapLevel = level; + Locked = true; + + RequiredSkill = level switch + { + 1 => 36, + 2 => 76, + 3 => 84, + 4 => 92, + 5 => 100, + _ => RequiredSkill }; - private static readonly int[] m_Hues = - { - 0x0, 0x455, 0x47E, 0x89F, 0x8A5, 0x8AB, - 0x966, 0x96D, 0x972, 0x973, 0x979 - }; + LockLevel = RequiredSkill - 10; + MaxLockLevel = RequiredSkill + 40; - private string m_Name; + DropItem(new Gold(level * 200)); - [Constructible] - public ParagonChest(string name, int level) : base(m_ItemIDs.RandomElement()) + for (var i = 0; i < level; ++i) { - m_Name = name; - Hue = m_Hues.RandomElement(); - Fill(level); + DropItem(Loot.RandomScroll(0, 63, SpellbookType.Regular)); } - public ParagonChest(Serial serial) : base(serial) + for (var i = 0; i < level * 2; ++i) { - } + var item = Core.AOS ? Loot.RandomArmorOrShieldOrWeaponOrJewelry() : Loot.RandomArmorOrShieldOrWeapon(); - public override void OnSingleClick(Mobile from) - { - base.OnSingleClick(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) - { - var rnd = Utility.Random(15); - - if (rnd < 1) + if (item is BaseWeapon weapon) { - 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 void Flip() - { - ItemID = ItemID switch - { - 0x9AB => 0xE7C, - 0xE7C => 0x9AB, - 0xE40 => 0xE41, - 0xE41 => 0xE40, - _ => ItemID - }; - } - - private void Fill(int level) - { - TrapType = TrapType.ExplosionTrap; - TrapPower = level * 25; - TrapLevel = level; - Locked = true; - - RequiredSkill = level switch - { - 1 => 36, - 2 => 76, - 3 => 84, - 4 => 92, - 5 => 100, - _ => RequiredSkill - }; - - LockLevel = RequiredSkill - 10; - MaxLockLevel = RequiredSkill + 40; - - DropItem(new Gold(level * 200)); - - for (var i = 0; i < level; ++i) - { - DropItem(Loot.RandomScroll(0, 63, SpellbookType.Regular)); - } - - for (var i = 0; i < level * 2; ++i) - { - Item item; - if (Core.AOS) { - item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(); + GetRandomAOSStats(out var attributeCount, out var min, out var max); + BaseRunicTool.ApplyAttributesTo(weapon, attributeCount, min, max); } else { - item = Loot.RandomArmorOrShieldOrWeapon(); + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random(6); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random(6); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random(6); } - if (item is BaseWeapon weapon) - { - if (Core.AOS) - { - GetRandomAOSStats(out var attributeCount, out var min, out var 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(weapon); - } - else if (item is BaseArmor armor) - { - if (Core.AOS) - { - GetRandomAOSStats(out var attributeCount, out var min, out var max); - BaseRunicTool.ApplyAttributesTo(armor, attributeCount, min, max); - } - else - { - armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(6); - armor.Durability = (ArmorDurabilityLevel)Utility.Random(6); - } - - DropItem(armor); - } - else if (item is BaseHat hat) - { - if (Core.AOS) - { - GetRandomAOSStats(out var attributeCount, out var min, out var max); - BaseRunicTool.ApplyAttributesTo(hat, attributeCount, min, max); - } - - DropItem(hat); - } - else if (item is BaseJewel jewel) + DropItem(weapon); + } + else if (item is BaseArmor armor) + { + if (Core.AOS) { GetRandomAOSStats(out var attributeCount, out var min, out var max); - BaseRunicTool.ApplyAttributesTo(jewel, attributeCount, min, max); - - DropItem(jewel); + BaseRunicTool.ApplyAttributesTo(armor, attributeCount, min, max); + } + else + { + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(6); + armor.Durability = (ArmorDurabilityLevel)Utility.Random(6); } - } - for (var i = 0; i < level; i++) + DropItem(armor); + } + else if (item is BaseHat hat) { - var item = Loot.RandomPossibleReagent(); - item.Amount = Utility.RandomMinMax(40, 60); - DropItem(item); - } + if (Core.AOS) + { + GetRandomAOSStats(out var attributeCount, out var min, out var max); + BaseRunicTool.ApplyAttributesTo(hat, attributeCount, min, max); + } - for (var i = 0; i < level; i++) + DropItem(hat); + } + else if (item is BaseJewel jewel) { - var item = Loot.RandomGem(); - DropItem(item); + GetRandomAOSStats(out var attributeCount, out var min, out var max); + BaseRunicTool.ApplyAttributesTo(jewel, attributeCount, min, max); + + DropItem(jewel); } - - DropItem(new TreasureMap(level + 1, Utility.RandomBool() ? Map.Felucca : Map.Trammel)); } - public override void Serialize(IGenericWriter writer) + for (var i = 0; i < level; i++) { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Name); + var item = Loot.RandomPossibleReagent(); + item.Amount = Utility.RandomMinMax(40, 60); + DropItem(item); } - public override void Deserialize(IGenericReader reader) + for (var i = 0; i < level; i++) { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Name = Utility.Intern(reader.ReadString()); + var item = Loot.RandomGem(); + DropItem(item); } + + DropItem(new TreasureMap(level + 1, Utility.RandomBool() ? Map.Felucca : Map.Trammel)); } } diff --git a/Projects/UOContent/Items/Containers/SalvageBag.cs b/Projects/UOContent/Items/Containers/SalvageBag.cs index 3d8be35dc..1a75f8449 100644 --- a/Projects/UOContent/Items/Containers/SalvageBag.cs +++ b/Projects/UOContent/Items/Containers/SalvageBag.cs @@ -1,398 +1,376 @@ using System; using System.Collections.Generic; using System.Linq; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Engines.Craft; using Server.Network; using Server.Utilities; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class SalvageBag : Bag { - public class SalvageBag : Bag + private bool m_Failure; + + [Constructible] + public SalvageBag() : this(Utility.RandomBlueHue()) { - private bool m_Failure; + } - [Constructible] - public SalvageBag() - : this(Utility.RandomBlueHue()) + [Constructible] + public SalvageBag(int hue) + { + Weight = 2.0; + Hue = hue; + m_Failure = false; + } + + public override int LabelNumber => 1079931; // Salvage Bag + + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); + + if (from.Alive) { + var inBackpack = IsChildOf(from.Backpack); + var resmeltables = inBackpack && Resmeltables(); + var scissorables = inBackpack && Scissorables(); + list.Add(new SalvageIngotsEntry(this, resmeltables)); + list.Add(new SalvageClothEntry(this, scissorables)); + list.Add(new SalvageAllEntry(this, resmeltables && scissorables)); } + } - [Constructible] - public SalvageBag(int hue) + private bool Resmelt(Mobile from, Item item, CraftResource resource) + { + try { - Weight = 2.0; - Hue = hue; - m_Failure = false; - } - - public SalvageBag(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1079931; // Salvage Bag - - public override void GetContextMenuEntries(Mobile from, List list) - { - base.GetContextMenuEntries(from, list); - - if (from.Alive) + if (CraftResources.GetType(resource) != CraftResourceType.Metal) { - list.Add(new SalvageIngotsEntry(this, IsChildOf(from.Backpack) && Resmeltables())); - list.Add(new SalvageClothEntry(this, IsChildOf(from.Backpack) && Scissorables())); - list.Add(new SalvageAllEntry(this, IsChildOf(from.Backpack) && Resmeltables() && Scissorables())); + return false; } - } - private bool Resmelt(Mobile from, Item item, CraftResource resource) - { - try + var info = CraftResources.GetInfo(resource); + + if (info == null || info.ResourceTypes.Length == 0) { - if (CraftResources.GetType(resource) != CraftResourceType.Metal) + return false; + } + + var craftItem = DefBlacksmithy.CraftSystem.CraftItems.SearchFor(item.GetType()); + + if (craftItem == null || craftItem.Resources.Count == 0) + { + return false; + } + + var craftResource = craftItem.Resources[0]; + + if (craftResource.Amount < 2) + { + return false; // Not enough metal to resmelt + } + + var difficulty = resource switch + { + CraftResource.DullCopper => 65.0, + CraftResource.ShadowIron => 70.0, + CraftResource.Copper => 75.0, + CraftResource.Bronze => 80.0, + CraftResource.Gold => 85.0, + CraftResource.Agapite => 90.0, + CraftResource.Verite => 95.0, + CraftResource.Valorite => 99.0, + _ => 0.0 + }; + + var ingot = info.ResourceTypes[0].CreateInstance(); + + if (item is DragonBardingDeed || item is BaseArmor armor && armor.PlayerConstructed || + item is BaseWeapon weapon && weapon.PlayerConstructed || + item is BaseClothing clothing && clothing.PlayerConstructed) + { + var mining = from.Skills.Mining.Value; + if (mining > 100.0) { - return false; + mining = 100.0; } - var info = CraftResources.GetInfo(resource); - - if (info == null || info.ResourceTypes.Length == 0) - { - return false; - } - - var craftItem = DefBlacksmithy.CraftSystem.CraftItems.SearchFor(item.GetType()); - - if (craftItem == null || craftItem.Resources.Count == 0) - { - return false; - } - - var craftResource = craftItem.Resources[0]; - - if (craftResource.Amount < 2) - { - return false; // Not enough metal to resmelt - } - - var difficulty = resource switch - { - CraftResource.DullCopper => 65.0, - CraftResource.ShadowIron => 70.0, - CraftResource.Copper => 75.0, - CraftResource.Bronze => 80.0, - CraftResource.Gold => 85.0, - CraftResource.Agapite => 90.0, - CraftResource.Verite => 95.0, - CraftResource.Valorite => 99.0, - _ => 0.0 - }; - - var ingot = info.ResourceTypes[0].CreateInstance(); - - if (item is DragonBardingDeed || item is BaseArmor armor && armor.PlayerConstructed || - item is BaseWeapon weapon && weapon.PlayerConstructed || - item is BaseClothing clothing && clothing.PlayerConstructed) - { - var mining = from.Skills.Mining.Value; - if (mining > 100.0) - { - mining = 100.0; - } - - var amount = ((4 + mining) * craftResource.Amount - 4) * 0.0068; - if (amount < 2) - { - ingot.Amount = 2; - } - else - { - ingot.Amount = (int)amount; - } - } - else + var amount = ((4 + mining) * craftResource.Amount - 4) * 0.0068; + if (amount < 2) { ingot.Amount = 2; } - - if (difficulty > from.Skills.Mining.Value) - { - m_Failure = true; - ingot.Delete(); - } else { - item.Delete(); + ingot.Amount = (int)amount; } - - from.AddToBackpack(ingot); - - from.PlaySound(0x2A); - from.PlaySound(0x240); - - return true; - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - - return false; - } - - private bool Resmeltables() // Where context menu checks for metal items and dragon barding deeds - { - foreach (var i in Items) - { - return i?.Deleted == false && ( - i is BaseWeapon weapon && CraftResources.GetType(weapon.Resource) == CraftResourceType.Metal || - i is BaseArmor armor && CraftResources.GetType(armor.Resource) == CraftResourceType.Metal || - i is DragonBardingDeed); - } - - return false; - } - - private bool Scissorables() // Where context menu checks for Leather items and cloth items - { - foreach (var i in Items) - { - if (i is not IScissorable || i.Deleted) - { - continue; - } - - if (i is BaseClothing or Cloth or BoltOfCloth or Hides or BonePile || i is BaseArmor armor && CraftResources.GetType(armor.Resource) == CraftResourceType.Leather) - { - return true; - } - } - - return false; - } - - private void SalvageIngots(Mobile from) - { - if (from.Backpack.FindItemsByType().All(tool => tool.CraftSystem != DefBlacksmithy.CraftSystem)) - { - from.SendLocalizedMessage(1079822); // You need a blacksmithing tool in order to salvage ingots. - return; - } - - DefBlacksmithy.CheckAnvilAndForge(from, 2, out _, out var forge); - - if (!forge) - { - from.SendLocalizedMessage(1044265); // You must be near a forge. - return; - } - - var salvaged = 0; - var notSalvaged = 0; - - Container sBag = this; - - var smeltables = sBag.FindItemsByType(); - - foreach (var item in smeltables) - { - if (item?.Deleted != false) - { - continue; - } - - if (item is BaseArmor armor && Resmelt(from, armor, armor.Resource) || - item is BaseWeapon weapon && Resmelt(from, weapon, weapon.Resource) || - item is DragonBardingDeed) - { - salvaged++; - } - else - { - notSalvaged++; - } - } - - if (m_Failure) - { - from.SendLocalizedMessage(1079975); // You failed to smelt some metal for lack of skill. - m_Failure = false; } else { - from.SendLocalizedMessage( - 1079973, - $"{salvaged}\t{salvaged + notSalvaged}" - ); // Salvaged: ~1_COUNT~/~2_NUM~ blacksmithed items + ingot.Amount = 2; + } + + if (difficulty > from.Skills.Mining.Value) + { + m_Failure = true; + ingot.Delete(); + } + else + { + item.Delete(); + } + + from.AddToBackpack(ingot); + + from.PlaySound(0x2A); + from.PlaySound(0x240); + + return true; + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + + return false; + } + + private bool Resmeltables() // Where context menu checks for metal items and dragon barding deeds + { + foreach (var i in Items) + { + return i?.Deleted == false && ( + i is BaseWeapon weapon && CraftResources.GetType(weapon.Resource) == CraftResourceType.Metal || + i is BaseArmor armor && CraftResources.GetType(armor.Resource) == CraftResourceType.Metal || + i is DragonBardingDeed + ); + } + + return false; + } + + private bool Scissorables() // Where context menu checks for Leather items and cloth items + { + foreach (var i in Items) + { + if (i is not IScissorable || i.Deleted) + { + continue; + } + + if (i is BaseClothing or Cloth or BoltOfCloth or Hides or BonePile || i is BaseArmor armor && CraftResources.GetType(armor.Resource) == CraftResourceType.Leather) + { + return true; } } - private void SalvageCloth(Mobile from) - { - var scissors = from.Backpack.FindItemByType(); + return false; + } - if (scissors == null) + private void SalvageIngots(Mobile from) + { + if (from.Backpack.FindItemsByType().All(tool => tool.CraftSystem != DefBlacksmithy.CraftSystem)) + { + from.SendLocalizedMessage(1079822); // You need a blacksmithing tool in order to salvage ingots. + return; + } + + DefBlacksmithy.CheckAnvilAndForge(from, 2, out _, out var forge); + + if (!forge) + { + from.SendLocalizedMessage(1044265); // You must be near a forge. + return; + } + + var salvaged = 0; + var notSalvaged = 0; + + Container sBag = this; + + var smeltables = sBag.FindItemsByType(); + + foreach (var item in smeltables) + { + if (item?.Deleted != false) + { + continue; + } + + if (item is BaseArmor armor && Resmelt(from, armor, armor.Resource) || + item is BaseWeapon weapon && Resmelt(from, weapon, weapon.Resource) || + item is DragonBardingDeed) + { + salvaged++; + } + else + { + notSalvaged++; + } + } + + if (m_Failure) + { + from.SendLocalizedMessage(1079975); // You failed to smelt some metal for lack of skill. + m_Failure = false; + } + else + { + from.SendLocalizedMessage( + 1079973, + $"{salvaged}\t{salvaged + notSalvaged}" + ); // Salvaged: ~1_COUNT~/~2_NUM~ blacksmithed items + } + } + + private static readonly Type[] _clothTypes = { + typeof(Leather), typeof(Cloth), typeof(SpinedLeather), typeof(HornedLeather), typeof(BarbedLeather), + typeof(Bandage), typeof(Bone) + }; + + private void SalvageCloth(Mobile from) + { + var scissors = from.Backpack.FindItemByType(); + + if (scissors == null) + { + from.SendLocalizedMessage(1079823); // You need scissors in order to salvage cloth. + return; + } + + var salvaged = 0; + var notSalvaged = 0; + + Container sBag = this; + + var scissorables = sBag.FindItemsByType(); + + for (var i = scissorables.Count - 1; i >= 0; --i) + { + var item = scissorables[i]; + + if (item is not IScissorable scissorable) + { + continue; + } + + if (Scissors.CanScissor(from, scissorable) && scissorable.Scissor(from, scissors)) + { + ++salvaged; + } + else + { + ++notSalvaged; + } + } + + // Salvaged: ~1_COUNT~/~2_NUM~ tailored items + from.SendLocalizedMessage(1079974, $"{salvaged}\t{salvaged + notSalvaged}"); + + var items = FindItemsByType(_clothTypes); + + for (var i = 0; i < items.Length; i++) + { + from.AddToBackpack(items[i]); + } + } + + private void SalvageAll(Mobile from) + { + SalvageIngots(from); + SalvageCloth(from); + } + + private class SalvageAllEntry : ContextMenuEntry + { + private readonly SalvageBag m_Bag; + + public SalvageAllEntry(SalvageBag bag, bool enabled) : base(6276) + { + m_Bag = bag; + + if (!enabled) + { + Flags |= CMEFlags.Disabled; + } + } + + public override void OnClick() + { + if (m_Bag.Deleted) { - from.SendLocalizedMessage(1079823); // You need scissors in order to salvage cloth. return; } - var salvaged = 0; - var notSalvaged = 0; + var from = Owner.From; - Container sBag = this; - - var scissorables = sBag.FindItemsByType(); - - for (var i = scissorables.Count - 1; i >= 0; --i) + if (from.CheckAlive()) { - var item = scissorables[i]; - - if (item is not IScissorable scissorable) - { - continue; - } - - if (Scissors.CanScissor(from, scissorable) && scissorable.Scissor(from, scissors)) - { - ++salvaged; - } - else - { - ++notSalvaged; - } + m_Bag.SalvageAll(from); } + } + } - from.SendLocalizedMessage( - 1079974, - $"{salvaged}\t{salvaged + notSalvaged}" - ); // Salvaged: ~1_COUNT~/~2_NUM~ tailored items + private class SalvageIngotsEntry : ContextMenuEntry + { + private readonly SalvageBag m_Bag; - var items = FindItemsByType( - new[] - { - typeof(Leather), typeof(Cloth), typeof(SpinedLeather), typeof(HornedLeather), typeof(BarbedLeather), - typeof(Bandage), typeof(Bone) - } - ); + public SalvageIngotsEntry(SalvageBag bag, bool enabled) : base(6277) + { + m_Bag = bag; - for (var i = 0; i < items.Length; i++) + if (!enabled) { - from.AddToBackpack(items[i]); + Flags |= CMEFlags.Disabled; } } - private void SalvageAll(Mobile from) + public override void OnClick() { - SalvageIngots(from); - - SalvageCloth(from); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - - private class SalvageAllEntry : ContextMenuEntry - { - private readonly SalvageBag m_Bag; - - public SalvageAllEntry(SalvageBag bag, bool enabled) - : base(6276) + if (m_Bag.Deleted) { - m_Bag = bag; - - if (!enabled) - { - Flags |= CMEFlags.Disabled; - } + return; } - public override void OnClick() + var from = Owner.From; + + if (from.CheckAlive()) { - if (m_Bag.Deleted) - { - return; - } + m_Bag.SalvageIngots(from); + } + } + } - var from = Owner.From; + private class SalvageClothEntry : ContextMenuEntry + { + private readonly SalvageBag m_Bag; - if (from.CheckAlive()) - { - m_Bag.SalvageAll(from); - } + public SalvageClothEntry(SalvageBag bag, bool enabled) : base(6278) + { + m_Bag = bag; + + if (!enabled) + { + Flags |= CMEFlags.Disabled; } } - private class SalvageIngotsEntry : ContextMenuEntry + public override void OnClick() { - private readonly SalvageBag m_Bag; - - public SalvageIngotsEntry(SalvageBag bag, bool enabled) - : base(6277) + if (m_Bag.Deleted) { - m_Bag = bag; - - if (!enabled) - { - Flags |= CMEFlags.Disabled; - } + return; } - public override void OnClick() + var from = Owner.From; + + if (from.CheckAlive()) { - if (m_Bag.Deleted) - { - return; - } - - var from = Owner.From; - - if (from.CheckAlive()) - { - m_Bag.SalvageIngots(from); - } - } - } - - private class SalvageClothEntry : ContextMenuEntry - { - private readonly SalvageBag m_Bag; - - public SalvageClothEntry(SalvageBag bag, bool enabled) - : base(6278) - { - m_Bag = bag; - - if (!enabled) - { - Flags |= CMEFlags.Disabled; - } - } - - public override void OnClick() - { - if (m_Bag.Deleted) - { - return; - } - - var from = Owner.From; - - if (from.CheckAlive()) - { - m_Bag.SalvageCloth(from); - } + m_Bag.SalvageCloth(from); } } } diff --git a/Projects/UOContent/Items/Containers/Strongbox.cs b/Projects/UOContent/Items/Containers/Strongbox.cs index a1d6b8ca8..e6e1eec3e 100644 --- a/Projects/UOContent/Items/Containers/Strongbox.cs +++ b/Projects/UOContent/Items/Containers/Strongbox.cs @@ -1,148 +1,116 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Multis; -namespace Server.Items +namespace Server.Items; + +[Flippable(0xE80, 0x9A8)] +[SerializationGenerator(0, false)] +public partial class StrongBox : BaseContainer, IChoppable { - [Flippable(0xE80, 0x9A8)] - public class StrongBox : BaseContainer, IChoppable + [InvalidateProperties] + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Mobile _owner; + + [InvalidateProperties] + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private BaseHouse _house; + + public StrongBox(Mobile owner, BaseHouse house) : base(0xE80) { - private BaseHouse m_House; - private Mobile m_Owner; + _owner = owner; + _house = house; - public StrongBox(Mobile owner, BaseHouse house) : base(0xE80) + MaxItems = 25; + } + + public override double DefaultWeight => 100; + public override int LabelNumber => 1023712; + + public override int DefaultMaxWeight => 0; + + public override bool Decays => _house == null || _owner?.Deleted != false || !_house.IsCoOwner(_owner); + + public override TimeSpan DecayTime => TimeSpan.FromMinutes(30.0); + + public void OnChop(Mobile from) + { + if (_house?.Deleted != false || _owner?.Deleted != false || from == _owner || _house.IsOwner(from)) { - m_Owner = owner; - m_House = house; - - MaxItems = 25; - } - - public StrongBox(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 100; - public override int LabelNumber => 1023712; - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Owner - { - get => m_Owner; - set - { - m_Owner = value; - InvalidateProperties(); - } - } - - public override int DefaultMaxWeight => 0; - - public override bool Decays => m_House == null || m_Owner?.Deleted != false || !m_House.IsCoOwner(m_Owner); - - public override TimeSpan DecayTime => TimeSpan.FromMinutes(30.0); - - public void OnChop(Mobile from) - { - if (m_House?.Deleted != false || m_Owner?.Deleted != false || from == m_Owner || m_House.IsOwner(from)) - { - Chop(from); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Owner); - writer.Write(m_House); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - m_Owner = reader.ReadEntity(); - m_House = reader.ReadEntity(); - - break; - } - } - - Timer.StartTimer(TimeSpan.FromSeconds(1.0), Validate); - } - - private void Validate() - { - if (m_Owner != null && m_House?.IsCoOwner(m_Owner) == false) - { - Console.WriteLine("Warning: Destroying strongbox of {0}", m_Owner.Name); - Destroy(); - } - } - - 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) => - m_Owner?.Deleted != false || m_House?.Deleted != false || - m.AccessLevel >= AccessLevel.GameMaster || - 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 Container ConvertToStandardContainer() - { - Container metalBox = new MetalBox(); - var subItems = new List(Items); - - foreach (var subItem in subItems) - { - metalBox.AddItem(subItem); - } - - Delete(); - - return metalBox; + Chop(from); } } + + [AfterDeserialization] + private void AfterDeserialization() + { + Timer.StartTimer(TimeSpan.FromSeconds(1.0), Validate); + } + + private void Validate() + { + if (_owner != null && _house?.IsCoOwner(_owner) == false) + { + Console.WriteLine("Warning: Destroying strongbox of {0}", _owner.Name); + Destroy(); + } + } + + public override void AddNameProperty(IPropertyList list) + { + if (_owner != null) + { + list.Add(1042887, _owner.Name); // a strong box owned by ~1_OWNER_NAME~ + } + else + { + base.AddNameProperty(list); + } + } + + public override void OnSingleClick(Mobile from) + { + if (_owner == null) + { + base.OnSingleClick(from); + return; + } + + LabelTo(from, 1042887, _owner.Name); // a strong box owned by ~1_OWNER_NAME~ + + if (CheckContentDisplay(from)) + { + LabelTo(from, "({0} items, {1} stones)", TotalItems, TotalWeight); + } + } + + public override bool IsAccessibleTo(Mobile m) => + _owner?.Deleted != false || _house?.Deleted != false || + m.AccessLevel >= AccessLevel.GameMaster || + m == _owner && _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 Container ConvertToStandardContainer() + { + Container metalBox = new MetalBox(); + var subItems = new List(Items); + + foreach (var subItem in subItems) + { + metalBox.AddItem(subItem); + } + + Delete(); + + return metalBox; + } } diff --git a/Projects/UOContent/Items/Containers/TrappableContainer.ExecuteTrap.cs b/Projects/UOContent/Items/Containers/TrappableContainer.ExecuteTrap.cs new file mode 100644 index 000000000..b4fef21d9 --- /dev/null +++ b/Projects/UOContent/Items/Containers/TrappableContainer.ExecuteTrap.cs @@ -0,0 +1,146 @@ +using System; +using System.Runtime.CompilerServices; +using Server.Network; + +namespace Server.Items; + +public partial class TrappableContainer +{ + public virtual bool ExecuteTrap(Mobile from) + { + if (_trapType == TrapType.None) + { + return false; + } + + if (from.AccessLevel >= AccessLevel.GameMaster) + { + SendMessageTo(from, "That is trapped, but you open it with your godly powers.", 0x3B2); + return false; + } + + SendMessageTo(from, 502999, 0x3B2); // You set off a trap! + + var loc = GetWorldLocation(); + + switch (_trapType) + { + case TrapType.ExplosionTrap: + { + ExecuteExplosionTrap(from, loc); + break; + } + case TrapType.MagicTrap: + { + ExecuteMagicTrap(from, loc); + break; + } + case TrapType.DartTrap: + { + ExecuteDartTrap(from, loc); + break; + } + case TrapType.PoisonTrap: + { + ExecutePoisonTrap(from, loc); + break; + } + } + + TrapType = TrapType.None; + TrapPower = 0; + TrapLevel = 0; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ExecuteExplosionTrap(Mobile from, Point3D loc) + { + var facet = Map; + if (from.InRange(loc, 3)) + { + int damage; + + if (_trapLevel > 0) + { + damage = Utility.RandomMinMax(10, 30) * _trapLevel; + } + else + { + damage = _trapPower; + } + + AOS.Damage(from, damage, 0, 100, 0, 0, 0); + + // Your skin blisters from the heat! + from.LocalOverheadMessage(MessageType.Regular, 0x2A, 503000); + } + + Effects.SendLocationEffect(loc, facet, 0x36BD, 15); + Effects.PlaySound(loc, facet, 0x307); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ExecuteMagicTrap(Mobile from, Point3D loc) + { + var facet = Map; + if (from.InRange(loc, 1)) + { + from.Damage(_trapPower); + } + + Effects.PlaySound(loc, facet, 0x307); + + Effects.SendLocationEffect(new Point3D(loc.X - 1, loc.Y, loc.Z), facet, 0x36BD, 15); + Effects.SendLocationEffect(new Point3D(loc.X + 1, loc.Y, loc.Z), facet, 0x36BD, 15); + + Effects.SendLocationEffect(new Point3D(loc.X, loc.Y - 1, loc.Z), facet, 0x36BD, 15); + Effects.SendLocationEffect(new Point3D(loc.X, loc.Y + 1, loc.Z), facet, 0x36BD, 15); + + Effects.SendLocationEffect(new Point3D(loc.X + 1, loc.Y + 1, loc.Z + 11), facet, 0x36BD, 15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ExecuteDartTrap(Mobile from, Point3D loc) + { + if (from.InRange(loc, 3)) + { + var damage = _trapLevel > 0 ? Utility.RandomMinMax(5, 15) * _trapLevel : _trapPower; + + AOS.Damage(from, damage, 100, 0, 0, 0, 0); + + // A dart embeds itself in your flesh! + from.LocalOverheadMessage(MessageType.Regular, 0x62, 502998); + } + + Effects.PlaySound(loc, Map, 0x223); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ExecutePoisonTrap(Mobile from, Point3D loc) + { + var facet = Map; + if (from.InRange(loc, 3)) + { + Poison poison; + + if (_trapLevel > 0) + { + poison = Poison.GetPoison(Math.Max(0, Math.Min(4, _trapLevel - 1))); + } + else + { + AOS.Damage(from, _trapPower, 0, 0, 0, 100, 0); + poison = Poison.Greater; + } + + from.ApplyPoison(from, poison); + + // You are enveloped in a noxious green cloud! + from.LocalOverheadMessage(MessageType.Regular, 0x44, 503004); + } + + Effects.SendLocationEffect(loc, facet, 0x113A, 10, 20); + Effects.PlaySound(loc, facet, 0x231); + } +} diff --git a/Projects/UOContent/Items/Containers/TrappableContainer.cs b/Projects/UOContent/Items/Containers/TrappableContainer.cs index ac9398ae9..ebe9cfc8b 100644 --- a/Projects/UOContent/Items/Containers/TrappableContainer.cs +++ b/Projects/UOContent/Items/Containers/TrappableContainer.cs @@ -1,243 +1,81 @@ -using System; +using ModernUO.Serialization; using Server.Network; -namespace Server.Items +namespace Server.Items; + +public enum TrapType { - public enum TrapType + None, + MagicTrap, + ExplosionTrap, + DartTrap, + PoisonTrap +} + +[SerializationGenerator(3, false)] +public abstract partial class TrappableContainer : BaseContainer, ITelekinesisable +{ + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _trapLevel; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _trapPower; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private TrapType _trapType; + + public TrappableContainer(int itemID) : base(itemID) { - None, - MagicTrap, - ExplosionTrap, - DartTrap, - PoisonTrap } - public abstract class TrappableContainer : BaseContainer, ITelekinesisable + public virtual bool TrapOnOpen => true; + + public virtual void OnTelekinesis(Mobile from) { - public TrappableContainer(int itemID) : base(itemID) + Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x376A, 9, 32, 5022); + Effects.PlaySound(Location, Map, 0x1F5); + + if (TrapOnOpen) { + ExecuteTrap(from); + } + } + + private void SendMessageTo(Mobile to, int number, int hue) + { + if (Deleted || !to.CanSee(this)) + { + return; } - public TrappableContainer(Serial serial) : base(serial) + to.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, hue, 3, number); + } + + private void SendMessageTo(Mobile to, string text, int hue) + { + if (Deleted || !to.CanSee(this)) { + return; } - [CommandProperty(AccessLevel.GameMaster)] - public TrapType TrapType { get; set; } + to.NetState.SendMessage(Serial, ItemID, MessageType.Regular, hue, 3, false, "ENU", "", text); + } - [CommandProperty(AccessLevel.GameMaster)] - public int TrapPower { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int TrapLevel { get; set; } - - public virtual bool TrapOnOpen => true; - - public virtual void OnTelekinesis(Mobile from) + public override void Open(Mobile from) + { + if (!TrapOnOpen || !ExecuteTrap(from)) { - Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x376A, 9, 32, 5022); - Effects.PlaySound(Location, Map, 0x1F5); - - if (TrapOnOpen) - { - ExecuteTrap(from); - } + base.Open(from); } + } - private void SendMessageTo(Mobile to, int number, int hue) - { - if (Deleted || !to.CanSee(this)) - { - return; - } - - to.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, hue, 3, number); - } - - private void SendMessageTo(Mobile to, string text, int hue) - { - if (Deleted || !to.CanSee(this)) - { - return; - } - - to.NetState.SendMessage(Serial, ItemID, MessageType.Regular, hue, 3, false, "ENU", "", text); - } - - public virtual bool ExecuteTrap(Mobile from) - { - if (TrapType != TrapType.None) - { - var loc = GetWorldLocation(); - var facet = Map; - - if (from.AccessLevel >= AccessLevel.GameMaster) - { - SendMessageTo(from, "That is trapped, but you open it with your godly powers.", 0x3B2); - return false; - } - - switch (TrapType) - { - case TrapType.ExplosionTrap: - { - SendMessageTo(from, 502999, 0x3B2); // You set off a trap! - - if (from.InRange(loc, 3)) - { - int damage; - - if (TrapLevel > 0) - { - damage = Utility.RandomMinMax(10, 30) * TrapLevel; - } - else - { - damage = TrapPower; - } - - AOS.Damage(from, damage, 0, 100, 0, 0, 0); - - // Your skin blisters from the heat! - from.LocalOverheadMessage(MessageType.Regular, 0x2A, 503000); - } - - Effects.SendLocationEffect(loc, facet, 0x36BD, 15); - Effects.PlaySound(loc, facet, 0x307); - - break; - } - case TrapType.MagicTrap: - { - if (from.InRange(loc, 1)) - { - from.Damage(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 (TrapLevel > 0) - { - damage = Utility.RandomMinMax(5, 15) * TrapLevel; - } - else - { - damage = TrapPower; - } - - AOS.Damage(from, damage, 100, 0, 0, 0, 0); - - // A dart imbeds itself in your flesh! - from.LocalOverheadMessage(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 (TrapLevel > 0) - { - poison = Poison.GetPoison(Math.Max(0, Math.Min(4, TrapLevel - 1))); - } - else - { - AOS.Damage(from, TrapPower, 0, 0, 0, 100, 0); - poison = Poison.Greater; - } - - from.ApplyPoison(from, poison); - - // You are enveloped in a noxious green cloud! - from.LocalOverheadMessage(MessageType.Regular, 0x44, 503004); - } - - Effects.SendLocationEffect(loc, facet, 0x113A, 10, 20); - Effects.PlaySound(loc, facet, 0x231); - - break; - } - } - - TrapType = TrapType.None; - TrapPower = 0; - TrapLevel = 0; - return true; - } - - return false; - } - - public override void Open(Mobile from) - { - if (!TrapOnOpen || !ExecuteTrap(from)) - { - base.Open(from); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(2); // version - - writer.Write(TrapLevel); - - writer.Write(TrapPower); - writer.Write((int)TrapType); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 2: - { - TrapLevel = reader.ReadInt(); - goto case 1; - } - case 1: - { - TrapPower = reader.ReadInt(); - goto case 0; - } - case 0: - { - TrapType = (TrapType)reader.ReadInt(); - break; - } - } - } + private void Deserialize(IGenericReader reader, int version) + { + _trapLevel = reader.ReadInt(); + _trapPower = reader.ReadInt(); + _trapType = (TrapType)reader.ReadInt(); } } diff --git a/Projects/UOContent/Items/Containers/TreasureChest.cs b/Projects/UOContent/Items/Containers/TreasureChest.cs index b578438c4..b1e01bb14 100644 --- a/Projects/UOContent/Items/Containers/TreasureChest.cs +++ b/Projects/UOContent/Items/Containers/TreasureChest.cs @@ -1,83 +1,33 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[Flippable(0xe43, 0xe42)] +[SerializationGenerator(0, false)] +public partial class WoodenTreasureChest : BaseTreasureChest { - [Flippable(0xe43, 0xe42)] - public class WoodenTreasureChest : BaseTreasureChest + [Constructible] + public WoodenTreasureChest() : base(0xE43) + { + } +} + +[Flippable(0xe41, 0xe40)] +[SerializationGenerator(0, false)] +public partial class MetalGoldenTreasureChest : BaseTreasureChest +{ + [Constructible] + public MetalGoldenTreasureChest() : base(0xE41) + { + } +} + +[Flippable(0x9ab, 0xe7c)] +[SerializationGenerator(0, false)] +public partial class MetalTreasureChest : BaseTreasureChest +{ + [Constructible] + public MetalTreasureChest() : base(0x9AB) { - [Constructible] - public WoodenTreasureChest() : base(0xE43) - { - } - - public WoodenTreasureChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xe41, 0xe40)] - public class MetalGoldenTreasureChest : BaseTreasureChest - { - [Constructible] - public MetalGoldenTreasureChest() : base(0xE41) - { - } - - public MetalGoldenTreasureChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0x9ab, 0xe7c)] - public class MetalTreasureChest : BaseTreasureChest - { - [Constructible] - public MetalTreasureChest() : base(0x9AB) - { - } - - public MetalTreasureChest(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } } } diff --git a/Projects/UOContent/Items/Containers/TreasureMapChest.cs b/Projects/UOContent/Items/Containers/TreasureMapChest.cs index 1bf908694..5b3153892 100644 --- a/Projects/UOContent/Items/Containers/TreasureMapChest.cs +++ b/Projects/UOContent/Items/Containers/TreasureMapChest.cs @@ -1,599 +1,512 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Engines.PartySystem; using Server.Gumps; using Server.Network; using Server.Utilities; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(2, false)] +public partial class TreasureMapChest : LockableContainer { - public class TreasureMapChest : LockableContainer + [SerializableField(0, setter: "private")] + private List _guardians; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _temporary; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Mobile _owner; + + [SerializableField(3)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _level; + + [TimerDrift] + [SerializableField(4)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Timer _expireTimer; + + [DeserializeTimerField(4)] + private void DeserializeExpireTimer(TimeSpan delay) { - private List m_Lifted = new(); - - private Timer m_Timer; - - [Constructible] - public TreasureMapChest(int level) : this(null, level) + if (!_temporary) { + _expireTimer = Timer.DelayCall(delay, Delete); } + } - public TreasureMapChest(Mobile owner, int level, bool temporary = false) : base(0xE40) + [Tidy] + [SerializableField(5, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private HashSet _lifted; + + [Constructible] + public TreasureMapChest(int level) : this(null, level) + { + } + + public TreasureMapChest(Mobile owner, int level, bool temporary = false) : base(0xE40) + { + _owner = owner; + _level = level; + + _temporary = temporary; + _guardians = new List(); + + _expireTimer = Timer.DelayCall(TimeSpan.FromHours(3.0), Delete); + Fill(this, level); + } + + public override int LabelNumber => 3000541; + + public static Type[] Artifacts { get; } = + { + 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) + }; + + [CommandProperty(AccessLevel.GameMaster)] + public DateTime DeleteTime => _expireTimer.Next; + + public override bool IsDecoContainer => false; + + private static void GetRandomAOSStats(out int attributeCount, out int min, out int max) + { + var rnd = Utility.Random(15); + + if (Core.SE) { - Owner = owner; - Level = level; - DeleteTime = Core.Now + TimeSpan.FromHours(3.0); - - Temporary = temporary; - Guardians = new List(); - - m_Timer = new DeleteTimer(this, DeleteTime); - m_Timer.Start(); - - Fill(this, level); - } - - public TreasureMapChest(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 3000541; - - public static Type[] Artifacts { get; } = - { - 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) - }; - - [CommandProperty(AccessLevel.GameMaster)] - public int Level { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Owner { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public DateTime DeleteTime { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool Temporary { get; set; } - - public List Guardians { get; private set; } - - public override bool IsDecoContainer => false; - - private static void GetRandomAOSStats(out int attributeCount, out int min, out int max) - { - var rnd = Utility.Random(15); - - if (Core.SE) + if (rnd < 1) { - if (rnd < 1) - { - attributeCount = Utility.RandomMinMax(3, 5); - min = 50; - max = 100; - } - else if (rnd < 3) - { - attributeCount = Utility.RandomMinMax(2, 5); - min = 40; - max = 80; - } - else if (rnd < 6) - { - attributeCount = Utility.RandomMinMax(2, 4); - min = 30; - max = 60; - } - else if (rnd < 10) - { - attributeCount = Utility.RandomMinMax(1, 3); - min = 20; - max = 40; - } - else - { - attributeCount = 1; - min = 10; - max = 20; - } + attributeCount = Utility.RandomMinMax(3, 5); + min = 50; + max = 100; + } + else if (rnd < 3) + { + attributeCount = Utility.RandomMinMax(2, 5); + min = 40; + max = 80; + } + else if (rnd < 6) + { + attributeCount = Utility.RandomMinMax(2, 4); + min = 30; + max = 60; + } + else if (rnd < 10) + { + attributeCount = Utility.RandomMinMax(1, 3); + min = 20; + max = 40; } else { - if (rnd < 1) - { - attributeCount = Utility.RandomMinMax(2, 5); - 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; - } + attributeCount = 1; + min = 10; + max = 20; } + + return; } - public static void Fill(LockableContainer cont, int level) + if (rnd < 1) { - cont.Movable = false; - cont.Locked = true; - int numberItems; + attributeCount = Utility.RandomMinMax(2, 5); + 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; + } + } - if (level == 0) + public static void Fill(LockableContainer cont, int level) + { + cont.Movable = false; + cont.Locked = true; + + if (level == 0) + { + cont.LockLevel = ILockpickable.CannotPick; + + cont.DropItem(new Gold(Utility.RandomMinMax(50, 100))); + + if (Utility.RandomDouble() < 0.75) { - 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)); - } + cont.DropItem(new TreasureMap(0, Map.Trammel)); } - else + } + else + { + cont.TrapType = TrapType.ExplosionTrap; + cont.TrapPower = level * 25; + cont.TrapLevel = level; + + cont.RequiredSkill = level switch { - cont.TrapType = TrapType.ExplosionTrap; - cont.TrapPower = level * 25; - cont.TrapLevel = level; + 1 => 36, + 2 => 76, + 3 => 84, + 4 => 92, + 5 => 100, + _ => 100 + }; - cont.RequiredSkill = level switch + cont.LockLevel = cont.RequiredSkill - 10; + cont.MaxLockLevel = cont.RequiredSkill + 40; + + // Publish 67 gold change + // if (Core.SA) + // cont.DropItem( new Gold( level * 5000 ) ); + // else + cont.DropItem(new Gold(level * 1000)); + + for (var i = 0; i < level * 5; ++i) + { + cont.DropItem(Loot.RandomScroll(0, 63, SpellbookType.Regular)); + } + + var numberItems = Core.SE ? level switch + { + 1 => 5, + 2 => 10, + 3 => 15, + 4 => 38, + 5 => 50, + 6 => 60, + _ => 0 + } : level * 6; + + for (var i = 0; i < numberItems; ++i) + { + var item = Core.AOS + ? Loot.RandomArmorOrShieldOrWeaponOrJewelry() + : Loot.RandomArmorOrShieldOrWeapon(); + + if (item is BaseWeapon weapon) { - 1 => 36, - 2 => 76, - 3 => 84, - 4 => 92, - 5 => 100, - 6 => 100, - _ => cont.RequiredSkill - }; - - cont.LockLevel = cont.RequiredSkill - 10; - cont.MaxLockLevel = cont.RequiredSkill + 40; - - // Publish 67 gold change - // if (Core.SA) - // cont.DropItem( new Gold( level * 5000 ) ); - // else - cont.DropItem(new Gold(level * 1000)); - - for (var i = 0; i < level * 5; ++i) - { - cont.DropItem(Loot.RandomScroll(0, 63, SpellbookType.Regular)); - } - - if (Core.SE) - { - numberItems = level switch - { - 1 => 5, - 2 => 10, - 3 => 15, - 4 => 38, - 5 => 50, - 6 => 60, - _ => 0 - }; - } - else - { - numberItems = level * 6; - } - - for (var i = 0; i < numberItems; ++i) - { - Item item; - if (Core.AOS) { - item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(); + GetRandomAOSStats(out var attributeCount, out var min, out var max); + BaseRunicTool.ApplyAttributesTo(weapon, attributeCount, min, max); } else { - item = Loot.RandomArmorOrShieldOrWeapon(); + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random(6); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random(6); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random(6); } - if (item is BaseWeapon weapon) - { - if (Core.AOS) - { - GetRandomAOSStats(out var attributeCount, out var min, out var 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(weapon); - } - else if (item is BaseArmor armor) - { - if (Core.AOS) - { - GetRandomAOSStats(out var attributeCount, out var min, out var max); - BaseRunicTool.ApplyAttributesTo(armor, attributeCount, min, max); - } - else - { - armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(6); - armor.Durability = (ArmorDurabilityLevel)Utility.Random(6); - } - - cont.DropItem(armor); - } - else if (item is BaseHat hat) - { - if (Core.AOS) - { - GetRandomAOSStats(out var attributeCount, out var min, out var max); - BaseRunicTool.ApplyAttributesTo(hat, attributeCount, min, max); - } - - cont.DropItem(hat); - } - else if (item is BaseJewel jewel) + cont.DropItem(weapon); + } + else if (item is BaseArmor armor) + { + if (Core.AOS) { GetRandomAOSStats(out var attributeCount, out var min, out var max); - BaseRunicTool.ApplyAttributesTo(jewel, attributeCount, min, max); - - cont.DropItem(jewel); + BaseRunicTool.ApplyAttributesTo(armor, attributeCount, min, max); } - } - } - - int reagents; - if (level == 0) - { - reagents = 12; - } - else - { - reagents = level * 3; - } - - for (var i = 0; i < reagents; i++) - { - var item = Loot.RandomPossibleReagent(); - item.Amount = Utility.RandomMinMax(40, 60); - cont.DropItem(item); - } - - int gems; - if (level == 0) - { - gems = 2; - } - else - { - gems = level * 3; - } - - for (var i = 0; i < gems; i++) - { - var item = Loot.RandomGem(); - cont.DropItem(item); - } - - if (level == 6 && Core.AOS) - { - cont.DropItem(Artifacts.RandomElement().CreateInstance()); - } - } - - public override bool CheckLocked(Mobile from) - { - if (!Locked) - { - return false; - } - - if (Level == 0 && from.AccessLevel < AccessLevel.GameMaster) - { - foreach (var m in Guardians) - { - if (m.Alive) + else { - from.SendLocalizedMessage( - 1046448 - ); // You must first kill the guardians before you may open this chest. - return true; + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(6); + armor.Durability = (ArmorDurabilityLevel)Utility.Random(6); } + + cont.DropItem(armor); } + else if (item is BaseHat hat) + { + if (Core.AOS) + { + GetRandomAOSStats(out var attributeCount, out var min, out var max); + BaseRunicTool.ApplyAttributesTo(hat, attributeCount, min, max); + } - LockPick(from); - return false; + cont.DropItem(hat); + } + else if (item is BaseJewel jewel) + { + GetRandomAOSStats(out var attributeCount, out var min, out var max); + BaseRunicTool.ApplyAttributesTo(jewel, attributeCount, min, max); + + cont.DropItem(jewel); + } } - - return base.CheckLocked(from); } - private bool CheckLoot(Mobile m, bool criminalAction) + var reagents = level == 0 ? 12 : level * 3; + + for (var i = 0; i < reagents; i++) { - if (Temporary) - { - return false; - } + var item = Loot.RandomPossibleReagent(); + item.Amount = Utility.RandomMinMax(40, 60); + cont.DropItem(item); + } - if (m.AccessLevel >= AccessLevel.GameMaster || Owner == null || m == Owner) - { - return true; - } + var gems = level == 0 ? 2 : level * 3; - if (Party.Get(Owner)?.Contains(m) == true) - { - return true; - } + for (var i = 0; i < gems; i++) + { + var item = Loot.RandomGem(); + cont.DropItem(item); + } - var map = Map; + if (level == 6 && Core.AOS) + { + cont.DropItem(Artifacts.RandomElement().CreateInstance()); + } + } - if ((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! + public override bool CheckLocked(Mobile from) + { + if (!Locked) + { return false; } - public override bool CheckItemUse(Mobile from, Item item) => - CheckLoot(from, item != this) && base.CheckItemUse(from, item); - - public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => - CheckLoot(from, true) && base.CheckLift(from, item, ref reject); - - public override void OnItemLifted(Mobile from, Item item) + if (_level == 0 && from.AccessLevel < AccessLevel.GameMaster) { - var notYetLifted = !m_Lifted.Contains(item); - - from.RevealingAction(); - - if (notYetLifted) + foreach (var m in _guardians) { - m_Lifted.Add(item); - - if (Utility.RandomDouble() <= 0.1) // 10% chance to spawn a new monster + if (m.Alive) { - TreasureMap.Spawn(Level, GetWorldLocation(), Map, from, false); + // You must first kill the guardians before you may open this chest. + from.SendLocalizedMessage(1046448); + return true; } } - base.OnItemLifted(from, item); + LockPick(from); + 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(1048122, "", 0x8A5); // The chest refuses to be filled with treasure again. - return false; - } + return base.CheckLocked(from); + } - return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); + private bool CheckLoot(Mobile m, bool criminalAction) + { + if (_temporary) + { + return false; } - public override void Serialize(IGenericWriter writer) + if (m.AccessLevel >= AccessLevel.GameMaster || _owner == null || m == _owner) { - base.Serialize(writer); - - writer.Write(2); // version - - Guardians.Tidy(); - writer.Write(Guardians); - writer.Write(Temporary); - - writer.Write(Owner); - - writer.Write(Level); - writer.WriteDeltaTime(DeleteTime); - m_Lifted.Tidy(); - writer.Write(m_Lifted); + return true; } - public override void Deserialize(IGenericReader reader) + if (Party.Get(_owner)?.Contains(m) == true) { - base.Deserialize(reader); + return true; + } - var version = reader.ReadInt(); + var map = Map; - switch (version) + if ((map?.Rules & MapRules.HarmfulRestrictions) == 0) + { + if (criminalAction) { - case 2: - { - Guardians = reader.ReadEntityList(); - Temporary = reader.ReadBool(); - - goto case 1; - } - case 1: - { - Owner = reader.ReadEntity(); - - goto case 0; - } - case 0: - { - Level = reader.ReadInt(); - DeleteTime = reader.ReadDeltaTime(); - m_Lifted = reader.ReadEntityList(); - - if (version < 2) - { - Guardians = new List(); - } - - break; - } - } - - if (!Temporary) - { - m_Timer = new DeleteTimer(this, DeleteTime); - m_Timer.Start(); + m.CriminalAction(true); } else { - Delete(); + m.SendLocalizedMessage(1010630); // Taking someone else's treasure is a criminal offense! } + + return true; } - public override void OnAfterDelete() + m.SendLocalizedMessage(1010631); // You did not discover this chest! + return false; + } + + public override bool CheckItemUse(Mobile from, Item item) => + CheckLoot(from, item != this) && base.CheckItemUse(from, item); + + public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => + CheckLoot(from, true) && base.CheckLift(from, item, ref reject); + + public override void OnItemLifted(Mobile from, Item item) + { + var notYetLifted = _lifted?.Contains(item) != true; + from.RevealingAction(); + + if (notYetLifted) { - m_Timer?.Stop(); + _lifted ??= new HashSet(); + _lifted.Add(item); - m_Timer = null; - - base.OnAfterDelete(); - } - - public override void GetContextMenuEntries(Mobile from, List list) - { - base.GetContextMenuEntries(from, list); - - if (from.Alive) + if (Utility.RandomDouble() <= 0.1) // 10% chance to spawn a new monster { - list.Add(new RemoveEntry(from, this)); + TreasureMap.Spawn(_level, GetWorldLocation(), Map, from, false); } } - public void BeginRemove(Mobile from) - { - if (!from.Alive) - { - return; - } + base.OnItemLifted(from, item); + } - from.CloseGump(); - from.SendGump(new RemoveGump(from, this)); + 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; } - public void EndRemove(Mobile from) - { - if (Deleted || from != Owner || !from.InRange(GetWorldLocation(), 3)) - { - return; - } + return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); + } - from.SendLocalizedMessage(1048124, "", 0x8A5); // The old, rusted chest crumbles when you hit it. + private void Deserialize(IGenericReader reader, int version) + { + _guardians = new List(); + + _owner = reader.ReadEntity(); + _level = reader.ReadInt(); + var expireTimerNext = reader.ReadDeltaTime(); + DeserializeExpireTimer(expireTimerNext == DateTime.MinValue ? TimeSpan.MinValue : expireTimerNext - Core.Now); + _lifted = reader.ReadEntitySet(); + } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + if (_expireTimer == null) + { Delete(); } + } - private class RemoveGump : Gump + public override void OnAfterDelete() + { + _expireTimer?.Stop(); + _expireTimer = null; + base.OnAfterDelete(); + } + + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); + + if (from.Alive) { - private readonly TreasureMapChest m_Chest; - private readonly Mobile m_From; + list.Add(new RemoveEntry(from, this)); + } + } - 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 - ); // When this treasure chest is removed, any items still inside of it will be lost. - AddHtmlLocalized(45, 95, 200, 60, 1048126, 0xFFFFFF); // Are you certain you're ready to remove this chest? - - AddButton(40, 153, 4005, 4007, 1); - AddHtmlLocalized(75, 155, 180, 40, 1048127, 0xFFFFFF); // Remove the Treasure Chest - - AddButton(40, 195, 4005, 4007, 2); - AddHtmlLocalized(75, 197, 180, 35, 1006045, 0xFFFFFF); // Cancel - } - - public override void OnResponse(NetState sender, RelayInfo info) - { - if (info.ButtonID == 1) - { - m_Chest.EndRemove(m_From); - } - } + public void BeginRemove(Mobile from) + { + if (!from.Alive) + { + return; } - private class RemoveEntry : ContextMenuEntry + from.CloseGump(); + from.SendGump(new RemoveGump(from, this)); + } + + public void EndRemove(Mobile from) + { + if (Deleted || from != _owner || !from.InRange(GetWorldLocation(), 3)) { - private readonly TreasureMapChest m_Chest; - private readonly Mobile m_From; - - 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); - } + return; } - private class DeleteTimer : Timer + from.SendLocalizedMessage(1048124, "", 0x8A5); // The old, rusted chest crumbles when you hit it. + Delete(); + } + + private class RemoveGump : Gump + { + private readonly TreasureMapChest _chest; + private readonly Mobile _from; + + public RemoveGump(Mobile from, TreasureMapChest chest) : base(15, 15) { - private readonly Item m_Item; + _from = from; + _chest = chest; - public DeleteTimer(Item item, DateTime time) : base(time - Core.Now) - { - m_Item = item; - } + Closable = false; + Disposable = false; - protected override void OnTick() + AddPage(0); + + AddBackground(30, 0, 240, 240, 2620); + + // When this treasure chest is removed, any items still inside of it will be lost. + AddHtmlLocalized(45, 15, 200, 80, 1048125, 0xFFFFFF); + // Are you certain you're ready to remove this chest? + AddHtmlLocalized(45, 95, 200, 60, 1048126, 0xFFFFFF); + + AddButton(40, 153, 4005, 4007, 1); + AddHtmlLocalized(75, 155, 180, 40, 1048127, 0xFFFFFF); // Remove the Treasure Chest + + AddButton(40, 195, 4005, 4007, 2); + AddHtmlLocalized(75, 197, 180, 35, 1006045, 0xFFFFFF); // Cancel + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + if (info.ButtonID == 1) { - m_Item.Delete(); + _chest.EndRemove(_from); } } } + + private class RemoveEntry : ContextMenuEntry + { + private readonly TreasureMapChest _chest; + private readonly Mobile _from; + + public RemoveEntry(Mobile from, TreasureMapChest chest) : base(6149, 3) + { + _from = from; + _chest = chest; + + Enabled = from == chest._owner; + } + + public override void OnClick() + { + if (_chest.Deleted || _from != _chest._owner || !_from.CheckAlive()) + { + return; + } + + _chest.BeginRemove(_from); + } + } } diff --git a/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs b/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs index 186e919e9..11698d565 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class AcademicBooksArtifact : BaseDecorationArtifact { public override int ArtifactRarity => 8; diff --git a/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs b/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs index 4cdc905c8..366a256e7 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs @@ -1,70 +1,37 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public abstract partial class BaseDecorationArtifact : Item { - public abstract class BaseDecorationArtifact : Item + public BaseDecorationArtifact(int itemID) : base(itemID) => Weight = 10.0; + + public abstract int ArtifactRarity { get; } + + public override bool ForceShowProperties => true; + + public override void GetProperties(IPropertyList list) { - public BaseDecorationArtifact(int itemID) : base(itemID) => Weight = 10.0; + base.GetProperties(list); - public BaseDecorationArtifact(Serial serial) : base(serial) - { - } - - public abstract int ArtifactRarity { get; } - - public override bool ForceShowProperties => true; - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - - list.Add(1061078, ArtifactRarity.ToString()); // artifact rarity ~1_val~ - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public abstract class BaseDecorationContainerArtifact : BaseContainer - { - public BaseDecorationContainerArtifact(int itemID) : base(itemID) => Weight = 10.0; - - public BaseDecorationContainerArtifact(Serial serial) : base(serial) - { - } - - public abstract int ArtifactRarity { get; } - - public override bool ForceShowProperties => true; - - public override void AddNameProperties(ObjectPropertyList list) - { - base.AddNameProperties(list); - - list.Add(1061078, ArtifactRarity.ToString()); // artifact rarity ~1_val~ - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } + list.Add(1061078, ArtifactRarity); // artifact rarity ~1_val~ + } +} + +[SerializationGenerator(0)] +public abstract partial class BaseDecorationContainerArtifact : BaseContainer +{ + public BaseDecorationContainerArtifact(int itemID) : base(itemID) => Weight = 10.0; + + public abstract int ArtifactRarity { get; } + + public override bool ForceShowProperties => true; + + public override void AddNameProperties(IPropertyList list) + { + base.AddNameProperties(list); + + list.Add(1061078, ArtifactRarity); // artifact rarity ~1_val~ } } diff --git a/Projects/UOContent/Items/Decoration Artifacts/DoomDecorationArtifacts.cs b/Projects/UOContent/Items/Decoration Artifacts/DoomDecorationArtifacts.cs index b920a1c35..c758b8000 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/DoomDecorationArtifacts.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/DoomDecorationArtifacts.cs @@ -1,640 +1,250 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class BackpackArtifact : BaseDecorationContainerArtifact { - public class BackpackArtifact : BaseDecorationContainerArtifact + [Constructible] + public BackpackArtifact() : base(0x9B2) { - [Constructible] - public BackpackArtifact() : base(0x9B2) - { - } - - public BackpackArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class BloodyWaterArtifact : BaseDecorationArtifact - { - [Constructible] - public BloodyWaterArtifact() : base(0xE23) - { - } - - public BloodyWaterArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class BooksWestArtifact : BaseDecorationArtifact - { - [Constructible] - public BooksWestArtifact() : base(0x1E25) - { - } - - public BooksWestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class BooksNorthArtifact : BaseDecorationArtifact - { - [Constructible] - public BooksNorthArtifact() : base(0x1E24) - { - } - - public BooksNorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class BooksFaceDownArtifact : BaseDecorationArtifact - { - [Constructible] - public BooksFaceDownArtifact() : base(0x1E21) - { - } - - public BooksFaceDownArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class BottleArtifact : BaseDecorationArtifact - { - [Constructible] - public BottleArtifact() : base(0xE28) - { - } - - public BottleArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class BrazierArtifact : BaseDecorationArtifact - { - [Constructible] - public BrazierArtifact() : base(0xE31) => Light = LightType.Circle150; - - public BrazierArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class CocoonArtifact : BaseDecorationArtifact - { - [Constructible] - public CocoonArtifact() : base(0x10DA) - { - } - - public CocoonArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 7; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class DamagedBooksArtifact : BaseDecorationArtifact - { - [Constructible] - public DamagedBooksArtifact() : base(0xC16) - { - } - - public DamagedBooksArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class EggCaseArtifact : BaseDecorationArtifact - { - [Constructible] - public EggCaseArtifact() : base(0x10D9) - { - } - - public EggCaseArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class GruesomeStandardArtifact : BaseDecorationArtifact - { - [Constructible] - public GruesomeStandardArtifact() : base(0x428) - { - } - - public GruesomeStandardArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class LampPostArtifact : BaseDecorationArtifact - { - [Constructible] - public LampPostArtifact() : base(0xB24) => Light = LightType.Circle300; - - public LampPostArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class LeatherTunicArtifact : BaseDecorationArtifact - { - [Constructible] - public LeatherTunicArtifact() : base(0x13CA) - { - } - - public LeatherTunicArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class RockArtifact : BaseDecorationArtifact - { - [Constructible] - public RockArtifact() : base(0x1363) - { - } - - public RockArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class RuinedPaintingArtifact : BaseDecorationArtifact - { - [Constructible] - public RuinedPaintingArtifact() : base(0xC2C) - { - } - - public RuinedPaintingArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 12; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class SaddleArtifact : BaseDecorationArtifact - { - [Constructible] - public SaddleArtifact() : base(0xF38) - { - } - - public SaddleArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class SkinnedDeerArtifact : BaseDecorationArtifact - { - [Constructible] - public SkinnedDeerArtifact() : base(0x1E91) - { - } - - public SkinnedDeerArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class SkinnedGoatArtifact : BaseDecorationArtifact - { - [Constructible] - public SkinnedGoatArtifact() : base(0x1E88) - { - } - - public SkinnedGoatArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class SkullCandleArtifact : BaseDecorationArtifact - { - [Constructible] - public SkullCandleArtifact() : base(0x1858) => Light = LightType.Circle150; - - public SkullCandleArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class StretchedHideArtifact : BaseDecorationArtifact - { - [Constructible] - public StretchedHideArtifact() : base(0x106B) - { - } - - public StretchedHideArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class StuddedLeggingsArtifact : BaseDecorationArtifact - { - [Constructible] - public StuddedLeggingsArtifact() : base(0x13D8) - { - } - - public StuddedLeggingsArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class StuddedTunicArtifact : BaseDecorationArtifact - { - [Constructible] - public StuddedTunicArtifact() : base(0x13D9) - { - } - - public StuddedTunicArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 7; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class TarotCardsArtifact : BaseDecorationArtifact - { - [Constructible] - public TarotCardsArtifact() : base(0x12A5) - { - } - - public TarotCardsArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class BloodyWaterArtifact : BaseDecorationArtifact +{ + [Constructible] + public BloodyWaterArtifact() : base(0xE23) + { + } + + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class BooksWestArtifact : BaseDecorationArtifact +{ + [Constructible] + public BooksWestArtifact() : base(0x1E25) + { + } + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class BooksNorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public BooksNorthArtifact() : base(0x1E24) + { + } + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class BooksFaceDownArtifact : BaseDecorationArtifact +{ + [Constructible] + public BooksFaceDownArtifact() : base(0x1E21) + { + } + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class BottleArtifact : BaseDecorationArtifact +{ + [Constructible] + public BottleArtifact() : base(0xE28) + { + } + + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class BrazierArtifact : BaseDecorationArtifact +{ + [Constructible] + public BrazierArtifact() : base(0xE31) => Light = LightType.Circle150; + + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class CocoonArtifact : BaseDecorationArtifact +{ + [Constructible] + public CocoonArtifact() : base(0x10DA) + { + } + + public override int ArtifactRarity => 7; +} + +[SerializationGenerator(0)] +public partial class DamagedBooksArtifact : BaseDecorationArtifact +{ + [Constructible] + public DamagedBooksArtifact() : base(0xC16) + { + } + + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class EggCaseArtifact : BaseDecorationArtifact +{ + [Constructible] + public EggCaseArtifact() : base(0x10D9) + { + } + + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class GruesomeStandardArtifact : BaseDecorationArtifact +{ + [Constructible] + public GruesomeStandardArtifact() : base(0x428) + { + } + + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class LampPostArtifact : BaseDecorationArtifact +{ + [Constructible] + public LampPostArtifact() : base(0xB24) => Light = LightType.Circle300; + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class LeatherTunicArtifact : BaseDecorationArtifact +{ + [Constructible] + public LeatherTunicArtifact() : base(0x13CA) + { + } + + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class RockArtifact : BaseDecorationArtifact +{ + [Constructible] + public RockArtifact() : base(0x1363) + { + } + + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class RuinedPaintingArtifact : BaseDecorationArtifact +{ + [Constructible] + public RuinedPaintingArtifact() : base(0xC2C) + { + } + + public override int ArtifactRarity => 12; +} + +[SerializationGenerator(0)] +public partial class SaddleArtifact : BaseDecorationArtifact +{ + [Constructible] + public SaddleArtifact() : base(0xF38) + { + } + + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class SkinnedDeerArtifact : BaseDecorationArtifact +{ + [Constructible] + public SkinnedDeerArtifact() : base(0x1E91) + { + } + + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class SkinnedGoatArtifact : BaseDecorationArtifact +{ + [Constructible] + public SkinnedGoatArtifact() : base(0x1E88) + { + } + + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class SkullCandleArtifact : BaseDecorationArtifact +{ + [Constructible] + public SkullCandleArtifact() : base(0x1858) => Light = LightType.Circle150; + + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class StretchedHideArtifact : BaseDecorationArtifact +{ + [Constructible] + public StretchedHideArtifact() : base(0x106B) + { + } + + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class StuddedLeggingsArtifact : BaseDecorationArtifact +{ + [Constructible] + public StuddedLeggingsArtifact() : base(0x13D8) + { + } + + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class StuddedTunicArtifact : BaseDecorationArtifact +{ + [Constructible] + public StuddedTunicArtifact() : base(0x13D9) + { + } + + public override int ArtifactRarity => 7; +} + +[SerializationGenerator(0)] +public partial class TarotCardsArtifact : BaseDecorationArtifact +{ + [Constructible] + public TarotCardsArtifact() : base(0x12A5) + { + } + + public override int ArtifactRarity => 5; } diff --git a/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs b/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs index b9a43810b..a637e282f 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs @@ -1,1519 +1,613 @@ +using ModernUO.Serialization; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class Basket1Artifact : BaseDecorationContainerArtifact { - public class Basket1Artifact : BaseDecorationContainerArtifact + [Constructible] + public Basket1Artifact() : base(0x24DD) { - [Constructible] - public Basket1Artifact() : base(0x24DD) - { - } - - public Basket1Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket2Artifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class Basket2Artifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket2Artifact() : base(0x24D7) { - [Constructible] - public Basket2Artifact() : base(0x24D7) - { - } - - public Basket2Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket3WestArtifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class Basket3WestArtifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket3WestArtifact() : base(0x24D9) { - [Constructible] - public Basket3WestArtifact() : base(0x24D9) - { - } - - public Basket3WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket3NorthArtifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class Basket3NorthArtifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket3NorthArtifact() : base(0x24DA) { - [Constructible] - public Basket3NorthArtifact() : base(0x24DA) - { - } - - public Basket3NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket4Artifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 1; +} + +[SerializationGenerator(0)] +public partial class Basket4Artifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket4Artifact() : base(0x24D8) { - [Constructible] - public Basket4Artifact() : base(0x24D8) - { - } - - public Basket4Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket5WestArtifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class Basket5WestArtifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket5WestArtifact() : base(0x24DC) { - [Constructible] - public Basket5WestArtifact() : base(0x24DC) - { - } - - public Basket5WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket5NorthArtifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class Basket5NorthArtifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket5NorthArtifact() : base(0x24DB) { - [Constructible] - public Basket5NorthArtifact() : base(0x24DB) - { - } - - public Basket5NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Basket6Artifact : BaseDecorationContainerArtifact + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class Basket6Artifact : BaseDecorationContainerArtifact +{ + [Constructible] + public Basket6Artifact() : base(0x24D5) { - [Constructible] - public Basket6Artifact() : base(0x24D5) - { - } - - public Basket6Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class BowlArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class BowlArtifact : BaseDecorationArtifact +{ + [Constructible] + public BowlArtifact() : base(0x24DE) { - [Constructible] - public BowlArtifact() : base(0x24DE) - { - } - - public BowlArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class BowlsVerticalArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class BowlsVerticalArtifact : BaseDecorationArtifact +{ + [Constructible] + public BowlsVerticalArtifact() : base(0x24DF) { - [Constructible] - public BowlsVerticalArtifact() : base(0x24DF) - { - } - - public BowlsVerticalArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class BowlsHorizontalArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class BowlsHorizontalArtifact : BaseDecorationArtifact +{ + [Constructible] + public BowlsHorizontalArtifact() : base(0x24E0) { - [Constructible] - public BowlsHorizontalArtifact() : base(0x24E0) - { - } - - public BowlsHorizontalArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class CupsArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class CupsArtifact : BaseDecorationArtifact +{ + [Constructible] + public CupsArtifact() : base(0x24E1) { - [Constructible] - public CupsArtifact() : base(0x24E1) - { - } - - public CupsArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class FanWestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class FanWestArtifact : BaseDecorationArtifact +{ + [Constructible] + public FanWestArtifact() : base(0x240A) { - [Constructible] - public FanWestArtifact() : base(0x240A) - { - } - - public FanWestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class FanNorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class FanNorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public FanNorthArtifact() : base(0x2409) { - [Constructible] - public FanNorthArtifact() : base(0x2409) - { - } - - public FanNorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class TripleFanWestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class TripleFanWestArtifact : BaseDecorationArtifact +{ + [Constructible] + public TripleFanWestArtifact() : base(0x240C) { - [Constructible] - public TripleFanWestArtifact() : base(0x240C) - { - } - - public TripleFanWestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class TripleFanNorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class TripleFanNorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public TripleFanNorthArtifact() : base(0x240B) { - [Constructible] - public TripleFanNorthArtifact() : base(0x240B) - { - } - - public TripleFanNorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class FlowersArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class FlowersArtifact : BaseDecorationArtifact +{ + [Constructible] + public FlowersArtifact() : base(0x284A) { - [Constructible] - public FlowersArtifact() : base(0x284A) - { - } - - public FlowersArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 7; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting1WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 7; +} + +[SerializationGenerator(0)] +public partial class Painting1WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting1WestArtifact() : base(0x240E) { - [Constructible] - public Painting1WestArtifact() : base(0x240E) - { - } - - public Painting1WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting1NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class Painting1NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting1NorthArtifact() : base(0x240D) { - [Constructible] - public Painting1NorthArtifact() : base(0x240D) - { - } - - public Painting1NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting2WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class Painting2WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting2WestArtifact() : base(0x2410) { - [Constructible] - public Painting2WestArtifact() : base(0x2410) - { - } - - public Painting2WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting2NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class Painting2NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting2NorthArtifact() : base(0x240F) { - [Constructible] - public Painting2NorthArtifact() : base(0x240F) - { - } - - public Painting2NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting3Artifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class Painting3Artifact : BaseDecorationArtifact +{ + [Constructible] + public Painting3Artifact() : base(0x2411) { - [Constructible] - public Painting3Artifact() : base(0x2411) - { - } - - public Painting3Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting4WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class Painting4WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting4WestArtifact() : base(0x2412) { - [Constructible] - public Painting4WestArtifact() : base(0x2412) - { - } - - public Painting4WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 6; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting4NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 6; +} + +[SerializationGenerator(0)] +public partial class Painting4NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting4NorthArtifact() : base(0x2411) { - [Constructible] - public Painting4NorthArtifact() : base(0x2411) - { - } - - public Painting4NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 6; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting5WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 6; +} + +[SerializationGenerator(0)] +public partial class Painting5WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting5WestArtifact() : base(0x2416) { - [Constructible] - public Painting5WestArtifact() : base(0x2416) - { - } - - public Painting5WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting5NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class Painting5NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting5NorthArtifact() : base(0x2415) { - [Constructible] - public Painting5NorthArtifact() : base(0x2415) - { - } - - public Painting5NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting6WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class Painting6WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting6WestArtifact() : base(0x2418) { - [Constructible] - public Painting6WestArtifact() : base(0x2418) - { - } - - public Painting6WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Painting6NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class Painting6NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public Painting6NorthArtifact() : base(0x2417) { - [Constructible] - public Painting6NorthArtifact() : base(0x2417) - { - } - - public Painting6NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SakeArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class SakeArtifact : BaseDecorationArtifact +{ + [Constructible] + public SakeArtifact() : base(0x24E2) { - [Constructible] - public SakeArtifact() : base(0x24E2) - { - } - - public SakeArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 4; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Sculpture1Artifact : BaseDecorationArtifact + public override int ArtifactRarity => 4; +} + +[SerializationGenerator(0)] +public partial class Sculpture1Artifact : BaseDecorationArtifact +{ + [Constructible] + public Sculpture1Artifact() : base(0x2419) { - [Constructible] - public Sculpture1Artifact() : base(0x2419) - { - } - - public Sculpture1Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class Sculpture2Artifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class Sculpture2Artifact : BaseDecorationArtifact +{ + [Constructible] + public Sculpture2Artifact() : base(0x241B) { - [Constructible] - public Sculpture2Artifact() : base(0x241B) - { - } - - public Sculpture2Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class DolphinLeftArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class DolphinLeftArtifact : BaseDecorationArtifact +{ + [Constructible] + public DolphinLeftArtifact() : base(0x2846) { - [Constructible] - public DolphinLeftArtifact() : base(0x2846) - { - } - - public DolphinLeftArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class DolphinRightArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class DolphinRightArtifact : BaseDecorationArtifact +{ + [Constructible] + public DolphinRightArtifact() : base(0x2847) { - [Constructible] - public DolphinRightArtifact() : base(0x2847) - { - } - - public DolphinRightArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class ManStatuetteSouthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class ManStatuetteSouthArtifact : BaseDecorationArtifact +{ + [Constructible] + public ManStatuetteSouthArtifact() : base(0x2848) { - [Constructible] - public ManStatuetteSouthArtifact() : base(0x2848) - { - } - - public ManStatuetteSouthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class ManStatuetteEastArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class ManStatuetteEastArtifact : BaseDecorationArtifact +{ + [Constructible] + public ManStatuetteEastArtifact() : base(0x2849) { - [Constructible] - public ManStatuetteEastArtifact() : base(0x2849) - { - } - - public ManStatuetteEastArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay1WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay1WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay1WestArtifact() : base(0x2842) { - [Constructible] - public SwordDisplay1WestArtifact() : base(0x2842) - { - } - - public SwordDisplay1WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay1NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay1NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay1NorthArtifact() : base(0x2843) { - [Constructible] - public SwordDisplay1NorthArtifact() : base(0x2843) - { - } - - public SwordDisplay1NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 5; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay2WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 5; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay2WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay2WestArtifact() : base(0x2844) { - [Constructible] - public SwordDisplay2WestArtifact() : base(0x2844) - { - } - - public SwordDisplay2WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 6; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay2NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 6; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay2NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay2NorthArtifact() : base(0x2845) { - [Constructible] - public SwordDisplay2NorthArtifact() : base(0x2845) - { - } - - public SwordDisplay2NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 6; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay3SouthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 6; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay3SouthArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay3SouthArtifact() : base(0x2855) { - [Constructible] - public SwordDisplay3SouthArtifact() : base(0x2855) - { - } - - public SwordDisplay3SouthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay3EastArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay3EastArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay3EastArtifact() : base(0x2856) { - [Constructible] - public SwordDisplay3EastArtifact() : base(0x2856) - { - } - - public SwordDisplay3EastArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay4WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay4WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay4WestArtifact() : base(0x2853) { - [Constructible] - public SwordDisplay4WestArtifact() : base(0x2853) - { - } - - public SwordDisplay4WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 8; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay4NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 8; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay4NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay4NorthArtifact() : base(0x2854) { - [Constructible] - public SwordDisplay4NorthArtifact() : base(0x2854) - { - } - - public SwordDisplay4NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay5WestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay5WestArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay5WestArtifact() : base(0x2851) { - [Constructible] - public SwordDisplay5WestArtifact() : base(0x2851) - { - } - - public SwordDisplay5WestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class SwordDisplay5NorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class SwordDisplay5NorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public SwordDisplay5NorthArtifact() : base(0x2852) { - [Constructible] - public SwordDisplay5NorthArtifact() : base(0x2852) - { - } - - public SwordDisplay5NorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 9; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class TeapotWestArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 9; +} + +[SerializationGenerator(0)] +public partial class TeapotWestArtifact : BaseDecorationArtifact +{ + [Constructible] + public TeapotWestArtifact() : base(0x24E7) { - [Constructible] - public TeapotWestArtifact() : base(0x24E7) - { - } - - public TeapotWestArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class TeapotNorthArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class TeapotNorthArtifact : BaseDecorationArtifact +{ + [Constructible] + public TeapotNorthArtifact() : base(0x24E6) { - [Constructible] - public TeapotNorthArtifact() : base(0x24E6) - { - } - - public TeapotNorthArtifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class TowerLanternArtifact : BaseDecorationArtifact + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class TowerLanternArtifact : BaseDecorationArtifact +{ + [Constructible] + public TowerLanternArtifact() : base(0x24C0) => Light = LightType.Circle225; + + public override int ArtifactRarity => 3; + + [CommandProperty(AccessLevel.GameMaster)] + public bool IsOn { - [Constructible] - public TowerLanternArtifact() : base(0x24C0) => Light = LightType.Circle225; + get => ItemID == 0x24BF; + set => ItemID = value ? 0x24BF : 0x24C0; + } - public TowerLanternArtifact(Serial serial) : base(serial) + public override void OnDoubleClick(Mobile from) + { + if (from.InRange(GetWorldLocation(), 2)) { - } - - public override int ArtifactRarity => 3; - - [CommandProperty(AccessLevel.GameMaster)] - public bool IsOn - { - get => ItemID == 0x24BF; - set => ItemID = value ? 0x24BF : 0x24C0; - } - - public override void OnDoubleClick(Mobile from) - { - if (from.InRange(GetWorldLocation(), 2)) + if (IsOn) { - if (IsOn) - { - IsOn = false; - from.PlaySound(0x3BE); - } - else - { - IsOn = true; - from.PlaySound(0x47); - } + IsOn = false; + from.PlaySound(0x3BE); } else { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + IsOn = true; + from.PlaySound(0x47); } } - - public override void Serialize(IGenericWriter writer) + else { - base.Serialize(writer); - - writer.WriteEncodedInt(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - if (version == 0) - { - Light = LightType.Circle225; - } - } - } - - public class Urn1Artifact : BaseDecorationArtifact - { - [Constructible] - public Urn1Artifact() : base(0x241D) - { - } - - public Urn1Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class Urn2Artifact : BaseDecorationArtifact - { - [Constructible] - public Urn2Artifact() : base(0x241E) - { - } - - public Urn2Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class ZenRock1Artifact : BaseDecorationArtifact - { - [Constructible] - public ZenRock1Artifact() : base(0x24E4) - { - } - - public ZenRock1Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 2; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class ZenRock2Artifact : BaseDecorationArtifact - { - [Constructible] - public ZenRock2Artifact() : base(0x24E3) - { - } - - public ZenRock2Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } - } - - public class ZenRock3Artifact : BaseDecorationArtifact - { - [Constructible] - public ZenRock3Artifact() : base(0x24E5) - { - } - - public ZenRock3Artifact(Serial serial) : base(serial) - { - } - - public override int ArtifactRarity => 3; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } + +[SerializationGenerator(0)] +public partial class Urn1Artifact : BaseDecorationArtifact +{ + [Constructible] + public Urn1Artifact() : base(0x241D) + { + } + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class Urn2Artifact : BaseDecorationArtifact +{ + [Constructible] + public Urn2Artifact() : base(0x241E) + { + } + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class ZenRock1Artifact : BaseDecorationArtifact +{ + [Constructible] + public ZenRock1Artifact() : base(0x24E4) + { + } + + public override int ArtifactRarity => 2; +} + +[SerializationGenerator(0)] +public partial class ZenRock2Artifact : BaseDecorationArtifact +{ + [Constructible] + public ZenRock2Artifact() : base(0x24E3) + { + } + + public override int ArtifactRarity => 3; +} + +[SerializationGenerator(0)] +public partial class ZenRock3Artifact : BaseDecorationArtifact +{ + [Constructible] + public ZenRock3Artifact() : base(0x24E5) + { + } + + public override int ArtifactRarity => 3; +} diff --git a/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs b/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs deleted file mode 100644 index d7e34a96e..000000000 --- a/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Logging; -using Server.Utilities; - -namespace Server.Items -{ - public class StealableArtifactsSpawner : Item - { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(StealableArtifactsSpawner)); - - private static Type[] m_TypesOfEntries; - private StealableInstance[] m_Artifacts; - - private Timer _respawnTimer; - private Dictionary m_Table; - - private StealableArtifactsSpawner() : base(1) - { - Movable = false; - - m_Artifacts = new StealableInstance[Entries.Length]; - m_Table = new Dictionary(Entries.Length); - - for (var i = 0; i < Entries.Length; i++) - { - m_Artifacts[i] = new StealableInstance(Entries[i]); - } - - _respawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); - } - - public StealableArtifactsSpawner(Serial serial) : base(serial) => Instance = this; - - public static StealableEntry[] Entries { get; } = - { - // Doom - Artifact rarity 1 - new(Map.Malas, new Point3D(317, 56, -1), 72, 108, typeof(RockArtifact)), - new(Map.Malas, new Point3D(360, 31, 8), 72, 108, typeof(SkullCandleArtifact)), - new(Map.Malas, new Point3D(369, 372, -1), 72, 108, typeof(BottleArtifact)), - new(Map.Malas, new Point3D(378, 372, 0), 72, 108, typeof(DamagedBooksArtifact)), - // Doom - Artifact rarity 2 - new(Map.Malas, new Point3D(432, 16, -1), 144, 216, typeof(StretchedHideArtifact)), - new(Map.Malas, new Point3D(489, 9, 0), 144, 216, typeof(BrazierArtifact)), - // Doom - Artifact rarity 3 - new(Map.Malas, new Point3D(471, 96, -1), 288, 432, typeof(LampPostArtifact), GetLampPostHue()), - new(Map.Malas, new Point3D(421, 198, 2), 288, 432, typeof(BooksNorthArtifact)), - new(Map.Malas, new Point3D(431, 189, -1), 288, 432, typeof(BooksWestArtifact)), - new(Map.Malas, new Point3D(435, 196, -1), 288, 432, typeof(BooksFaceDownArtifact)), - // Doom - Artifact rarity 5 - new(Map.Malas, new Point3D(447, 9, 8), 1152, 1728, typeof(StuddedLeggingsArtifact)), - new(Map.Malas, new Point3D(423, 28, 0), 1152, 1728, typeof(EggCaseArtifact)), - new(Map.Malas, new Point3D(347, 44, 4), 1152, 1728, typeof(SkinnedGoatArtifact)), - new(Map.Malas, new Point3D(497, 57, -1), 1152, 1728, typeof(GruesomeStandardArtifact)), - new(Map.Malas, new Point3D(381, 375, 11), 1152, 1728, typeof(BloodyWaterArtifact)), - new(Map.Malas, new Point3D(489, 369, 2), 1152, 1728, typeof(TarotCardsArtifact)), - new(Map.Malas, new Point3D(497, 369, 5), 1152, 1728, typeof(BackpackArtifact)), - // Doom - Artifact rarity 7 - new(Map.Malas, new Point3D(475, 23, 4), 4608, 6912, typeof(StuddedTunicArtifact)), - new(Map.Malas, new Point3D(423, 28, 0), 4608, 6912, typeof(CocoonArtifact)), - // Doom - Artifact rarity 8 - new(Map.Malas, new Point3D(354, 36, -1), 9216, 13824, typeof(SkinnedDeerArtifact)), - // Doom - Artifact rarity 9 - new(Map.Malas, new Point3D(433, 11, -1), 18432, 27648, typeof(SaddleArtifact)), - new(Map.Malas, new Point3D(403, 31, 4), 18432, 27648, typeof(LeatherTunicArtifact)), - // Doom - Artifact rarity 10 - new(Map.Malas, new Point3D(257, 70, -2), 36864, 55296, typeof(ZyronicClaw)), - new(Map.Malas, new Point3D(354, 176, 7), 36864, 55296, typeof(TitansHammer)), - new(Map.Malas, new Point3D(369, 389, -1), 36864, 55296, typeof(BladeOfTheRighteous)), - new(Map.Malas, new Point3D(467, 92, 4), 36864, 55296, typeof(InquisitorsResolution)), - // Doom - Artifact rarity 12 - new(Map.Malas, new Point3D(487, 364, -1), 147456, 221184, typeof(RuinedPaintingArtifact)), - - // Yomotsu Mines - Artifact rarity 1 - new(Map.Malas, new Point3D(18, 110, -1), 72, 108, typeof(Basket1Artifact)), - new(Map.Malas, new Point3D(66, 114, -1), 72, 108, typeof(Basket2Artifact)), - // Yomotsu Mines - Artifact rarity 2 - new(Map.Malas, new Point3D(63, 12, 11), 144, 216, typeof(Basket4Artifact)), - new(Map.Malas, new Point3D(5, 29, -1), 144, 216, typeof(Basket5NorthArtifact)), - new(Map.Malas, new Point3D(30, 81, 3), 144, 216, typeof(Basket5WestArtifact)), - // Yomotsu Mines - Artifact rarity 3 - new(Map.Malas, new Point3D(115, 7, -1), 288, 432, typeof(Urn1Artifact)), - new(Map.Malas, new Point3D(85, 13, -1), 288, 432, typeof(Urn2Artifact)), - new(Map.Malas, new Point3D(110, 53, -1), 288, 432, typeof(Sculpture1Artifact)), - new(Map.Malas, new Point3D(108, 37, -1), 288, 432, typeof(Sculpture2Artifact)), - new(Map.Malas, new Point3D(121, 14, -1), 288, 432, typeof(TeapotNorthArtifact)), - new(Map.Malas, new Point3D(121, 115, -1), 288, 432, typeof(TeapotWestArtifact)), - new(Map.Malas, new Point3D(84, 40, -1), 288, 432, typeof(TowerLanternArtifact)), - // Yomotsu Mines - Artifact rarity 9 - new(Map.Malas, new Point3D(94, 7, -1), 18432, 27648, typeof(ManStatuetteSouthArtifact)), - - // Fan Dancer's Dojo - Artifact rarity 1 - new(Map.Malas, new Point3D(113, 640, -2), 72, 108, typeof(Basket3NorthArtifact)), - new(Map.Malas, new Point3D(102, 355, -1), 72, 108, typeof(Basket3WestArtifact)), - // Fan Dancer's Dojo - Artifact rarity 2 - new(Map.Malas, new Point3D(99, 370, -1), 144, 216, typeof(Basket6Artifact)), - new(Map.Malas, new Point3D(100, 357, -1), 144, 216, typeof(ZenRock1Artifact)), - // Fan Dancer's Dojo - Artifact rarity 3 - new(Map.Malas, new Point3D(73, 473, -1), 288, 432, typeof(FanNorthArtifact)), - new(Map.Malas, new Point3D(99, 372, -1), 288, 432, typeof(FanWestArtifact)), - new(Map.Malas, new Point3D(92, 326, -1), 288, 432, typeof(BowlsVerticalArtifact)), - new(Map.Malas, new Point3D(97, 470, -1), 288, 432, typeof(ZenRock2Artifact)), - new(Map.Malas, new Point3D(103, 691, -1), 288, 432, typeof(ZenRock3Artifact)), - // Fan Dancer's Dojo - Artifact rarity 4 - new(Map.Malas, new Point3D(103, 336, 4), 576, 864, typeof(Painting1NorthArtifact)), - new(Map.Malas, new Point3D(59, 381, 4), 576, 864, typeof(Painting1WestArtifact)), - new(Map.Malas, new Point3D(84, 401, 2), 576, 864, typeof(Painting2NorthArtifact)), - new(Map.Malas, new Point3D(59, 392, 2), 576, 864, typeof(Painting2WestArtifact)), - new(Map.Malas, new Point3D(107, 483, -1), 576, 864, typeof(TripleFanNorthArtifact)), - new(Map.Malas, new Point3D(50, 475, -1), 576, 864, typeof(TripleFanWestArtifact)), - new(Map.Malas, new Point3D(107, 460, -1), 576, 864, typeof(BowlArtifact)), - new(Map.Malas, new Point3D(90, 502, -1), 576, 864, typeof(CupsArtifact)), - new(Map.Malas, new Point3D(107, 688, -1), 576, 864, typeof(BowlsHorizontalArtifact)), - new(Map.Malas, new Point3D(112, 676, -1), 576, 864, typeof(SakeArtifact)), - // Fan Dancer's Dojo - Artifact rarity 5 - new(Map.Malas, new Point3D(135, 614, -1), 1152, 1728, typeof(SwordDisplay1NorthArtifact)), - new(Map.Malas, new Point3D(50, 482, -1), 1152, 1728, typeof(SwordDisplay1WestArtifact)), - new(Map.Malas, new Point3D(119, 672, -1), 1152, 1728, typeof(Painting3Artifact)), - // Fan Dancer's Dojo - Artifact rarity 6 - new(Map.Malas, new Point3D(90, 326, -1), 2304, 3456, typeof(Painting4NorthArtifact)), - new(Map.Malas, new Point3D(99, 354, -1), 2304, 3456, typeof(Painting4WestArtifact)), - new(Map.Malas, new Point3D(179, 652, -1), 2304, 3456, typeof(SwordDisplay2NorthArtifact)), - new(Map.Malas, new Point3D(118, 627, -1), 2304, 3456, typeof(SwordDisplay2WestArtifact)), - // Fan Dancer's Dojo - Artifact rarity 7 - new(Map.Malas, new Point3D(90, 483, -1), 4608, 6912, typeof(FlowersArtifact)), - // Fan Dancer's Dojo - Artifact rarity 8 - new(Map.Malas, new Point3D(71, 562, -1), 9216, 13824, typeof(DolphinLeftArtifact)), - new(Map.Malas, new Point3D(102, 677, -1), 9216, 13824, typeof(DolphinRightArtifact)), - new(Map.Malas, new Point3D(61, 499, 0), 9216, 13824, typeof(SwordDisplay3SouthArtifact)), - new(Map.Malas, new Point3D(182, 669, -1), 9216, 13824, typeof(SwordDisplay3EastArtifact)), - new(Map.Malas, new Point3D(162, 647, -1), 9216, 13824, typeof(SwordDisplay4WestArtifact)), - new(Map.Malas, new Point3D(124, 624, 0), 9216, 13824, typeof(Painting5NorthArtifact)), - new(Map.Malas, new Point3D(146, 649, 2), 9216, 13824, typeof(Painting5WestArtifact)), - // Fan Dancer's Dojo - Artifact rarity 9 - new(Map.Malas, new Point3D(100, 488, -1), 18432, 27648, typeof(SwordDisplay4NorthArtifact)), - new(Map.Malas, new Point3D(175, 606, 0), 18432, 27648, typeof(SwordDisplay5NorthArtifact)), - new(Map.Malas, new Point3D(157, 608, -1), 18432, 27648, typeof(SwordDisplay5WestArtifact)), - new(Map.Malas, new Point3D(187, 643, 1), 18432, 27648, typeof(Painting6NorthArtifact)), - new(Map.Malas, new Point3D(146, 623, 1), 18432, 27648, typeof(Painting6WestArtifact)), - new(Map.Malas, new Point3D(178, 629, -1), 18432, 27648, typeof(ManStatuetteEastArtifact)) - }; - - public static Type[] TypesOfEntires - { - get - { - if (m_TypesOfEntries == null) - { - m_TypesOfEntries = new Type[Entries.Length]; - - for (var i = 0; i < Entries.Length; i++) - { - m_TypesOfEntries[i] = Entries[i].Type; - } - } - - return m_TypesOfEntries; - } - } - - public static StealableArtifactsSpawner Instance { get; private set; } - - public override string DefaultName => "Stealable Artifacts Spawner - Internal"; - - private static int GetLampPostHue() - { - if (Utility.RandomDouble() < 0.9) - { - return 0; - } - - return Utility.RandomList(0x455, 0x47E, 0x482, 0x486, 0x48F, 0x4F2, 0x58C, 0x66C); - } - - public static void Initialize() - { - CommandSystem.Register("GenStealArties", AccessLevel.Administrator, GenStealArties_OnCommand); - CommandSystem.Register("RemoveStealArties", AccessLevel.Administrator, RemoveStealArties_OnCommand); - } - - [Usage("GenStealArties"), Description("Generates the stealable artifacts spawner.")] - private static void GenStealArties_OnCommand(CommandEventArgs args) - { - var 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) - { - var from = args.Mobile; - - if (Remove()) - { - from.SendMessage("Stealable artifacts spawner removed."); - } - else - { - from.SendMessage("Stealable artifacts spawner not present."); - } - } - - public static bool Create() - { - if (Instance?.Deleted == false) - { - return false; - } - - Instance = new StealableArtifactsSpawner(); - return true; - } - - public static bool Remove() - { - if (Instance == null) - { - return false; - } - - Instance.Delete(); - Instance = null; - return true; - } - - public static StealableInstance GetStealableInstance(Item item) - { - if (Instance == null) - { - return null; - } - - Instance.m_Table.TryGetValue(item, out var value); - return value; - } - - public override void OnDelete() - { - base.OnDelete(); - - _respawnTimer.Stop(); - _respawnTimer = null; - - foreach (var si in m_Artifacts) - { - si.Item?.Delete(); - } - - Instance = null; - } - - public void CheckRespawn() - { - foreach (var si in m_Artifacts) - { - si.CheckRespawn(); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - - writer.WriteEncodedInt(m_Artifacts.Length); - - for (var i = 0; i < m_Artifacts.Length; i++) - { - var si = m_Artifacts[i]; - - writer.Write(si.Item); - writer.WriteDeltaTime(si.NextRespawn); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - m_Artifacts = new StealableInstance[Entries.Length]; - m_Table = new Dictionary(Entries.Length); - - var length = reader.ReadEncodedInt(); - - for (var i = 0; i < length; i++) - { - var item = reader.ReadEntity(); - var nextRespawn = reader.ReadDeltaTime(); - - if (i < m_Artifacts.Length) - { - var si = new StealableInstance(Entries[i], item, nextRespawn); - m_Artifacts[i] = si; - - if (si.Item != null) - { - m_Table[si.Item] = si; - } - } - } - - for (var i = length; i < Entries.Length; i++) - { - m_Artifacts[i] = new StealableInstance(Entries[i]); - } - - _respawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); - } - - public class StealableEntry - { - public StealableEntry(Map map, Point3D location, int minDelay, int maxDelay, Type type, int hue = 0) - { - Map = map; - Location = location; - MinDelay = minDelay; - MaxDelay = maxDelay; - Type = type; - Hue = hue; - } - - public Map Map { get; } - - public Point3D Location { get; } - - public int MinDelay { get; } - - public int MaxDelay { get; } - - public Type Type { get; } - - public int Hue { get; } - - public Item CreateInstance() - { - try - { - var item = Type.CreateInstance(); - - if (Hue > 0) - { - item.Hue = Hue; - } - - item.Movable = false; - item.MoveToWorld(Location, Map); - - return item; - } - catch (Exception e) - { - logger.Warning(e, $"Failed to construct stealable artifact: {Type.FullName}"); - return null; - } - } - } - - public class StealableInstance - { - private Item m_Item; - - public StealableInstance(StealableEntry entry) : this(entry, null, Core.Now) - { - } - - public StealableInstance(StealableEntry entry, Item item, DateTime nextRespawn) - { - m_Item = item; - NextRespawn = nextRespawn; - Entry = entry; - } - - public StealableEntry Entry { get; } - - public Item Item - { - get => m_Item; - set - { - if (m_Item != null && value == null) - { - var delay = Utility.RandomMinMax(Entry.MinDelay, Entry.MaxDelay); - NextRespawn = Core.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; set; } - - public void CheckRespawn() - { - if (Item != null && (Item.Deleted || Item.Movable || Item.Parent != null)) - { - Item = null; - } - - if (Item == null && Core.Now >= NextRespawn) - { - Item = Entry.CreateInstance(); - } - } - } - } -} diff --git a/Projects/UOContent/Items/Deeds/BarkeepContract.cs b/Projects/UOContent/Items/Deeds/BarkeepContract.cs index 086088722..c3f94ee22 100644 --- a/Projects/UOContent/Items/Deeds/BarkeepContract.cs +++ b/Projects/UOContent/Items/Deeds/BarkeepContract.cs @@ -1,97 +1,80 @@ +using ModernUO.Serialization; using Server.Mobiles; using Server.Multis; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class BarkeepContract : Item { - public class BarkeepContract : Item + [Constructible] + public BarkeepContract() : base(0x14F0) { - [Constructible] - public BarkeepContract() : base(0x14F0) + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override string DefaultName => "a barkeep contract"; + + public override void OnDoubleClick(Mobile from) + { + if (!IsChildOf(from.Backpack)) { - Weight = 1.0; - LootType = LootType.Blessed; + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - - public BarkeepContract(Serial serial) : base(serial) + 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); + + Delete(); } - - public override string DefaultName => "a barkeep contract"; - - public override void Serialize(IGenericWriter writer) + else { - base.Serialize(writer); + var house = BaseHouse.FindHouseAt(from); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) + if (house?.IsOwner(from) != true) { - from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + from.LocalOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "You are not the full owner of this house." + ); } - else if (from.AccessLevel >= AccessLevel.GameMaster) + else if (!house.CanPlaceNewBarkeep()) { - 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); - - Delete(); + from.SendLocalizedMessage( + 1062490 + ); // That action would exceed the maximum number of barkeeps for this house. } else { - var house = BaseHouse.FindHouseAt(from); + BaseHouse.IsThereVendor(from.Location, from.Map, out var vendor, out var contract); - if (house?.IsOwner(from) != true) + if (vendor) { - from.LocalOverheadMessage( - MessageType.Regular, - 0x3B2, - false, - "You are not the full owner of this house." - ); + from.SendLocalizedMessage(1062677); // You cannot place a vendor or barkeep at this location. } - else if (!house.CanPlaceNewBarkeep()) + else if (contract) { from.SendLocalizedMessage( - 1062490 - ); // That action would exceed the maximum number of barkeeps for this house. + 1062678 + ); // You cannot place a vendor or barkeep on top of a rental contract! } else { - BaseHouse.IsThereVendor(from.Location, from.Map, out var vendor, out var contract); + Mobile v = new PlayerBarkeeper(from, house); - 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); - v.Direction = from.Direction & Direction.Mask; - v.MoveToWorld(from.Location, from.Map); - - Delete(); - } + Delete(); } } } diff --git a/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs b/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs index 598c17bab..816a0cc0c 100644 --- a/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs +++ b/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs @@ -1,99 +1,81 @@ +using ModernUO.Serialization; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +public class ClothingBlessTarget : Target // Create our targeting class (which we derive from the base target class) { - public class ClothingBlessTarget : Target // Create our targeting class (which we derive from the base target class) + private readonly 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 { - private readonly 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) { - if (m_Deed.Deleted || m_Deed.RootParent != from) + return; + } + + if (target is BaseClothing item) + { + if ((item as IArcaneEquip)?.IsArcane == true) { + from.SendLocalizedMessage(1005019); // This bless deed is for Clothes only. return; } - if (target is BaseClothing item) + // Check if its already newbied (blessed) + if (item.LootType == LootType.Blessed || item.BlessedFor == from || Mobile.InsuranceEnabled && item.Insured) { - if ((item as IArcaneEquip)?.IsArcane == true) - { - from.SendLocalizedMessage(1005019); // This bless deed is for Clothes only. - return; - } - - 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 - } + from.SendLocalizedMessage(1045113); // That item is already blessed } - else + 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 } - } - } - - public class ClothingBlessDeed : Item // Create the item class which is derived from the base item class - { - [Constructible] - public ClothingBlessDeed() : base(0x14F0) - { - Weight = 1.0; - LootType = LootType.Blessed; - } - - public ClothingBlessDeed(Serial serial) : base(serial) - { - } - - public override string DefaultName => "a clothing bless deed"; - - public override bool DisplayLootType => false; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - LootType = LootType.Blessed; - - var version = reader.ReadInt(); - } - - 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 + 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 + } + } +} + +[SerializationGenerator(0, false)] +public partial class ClothingBlessDeed : Item // Create the item class which is derived from the base item class +{ + [Constructible] + public ClothingBlessDeed() : base(0x14F0) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override string DefaultName => "a clothing bless deed"; + + public override bool DisplayLootType => 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 + } } } diff --git a/Projects/UOContent/Items/Deeds/CommodityDeed.cs b/Projects/UOContent/Items/Deeds/CommodityDeed.cs index 5133f2b3c..459a45b93 100644 --- a/Projects/UOContent/Items/Deeds/CommodityDeed.cs +++ b/Projects/UOContent/Items/Deeds/CommodityDeed.cs @@ -1,183 +1,219 @@ +using ModernUO.Serialization; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +public interface ICommodity /* added IsDeedable prop so expansion-based deedables can determine true/false */ { - public interface ICommodity /* added IsDeedable prop so expansion-based deedables can determine true/false */ + int DescriptionNumber { get; } + bool IsDeedable { get; } +} + +[SerializationGenerator(1, false)] +public partial class CommodityDeed : Item +{ + [SerializableField(0, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + public Item _commodity; + + [Constructible] + public CommodityDeed(Item commodity = null) : base(0x14F0) { - int DescriptionNumber { get; } - bool IsDeedable { get; } + Weight = 1.0; + Hue = 0x47; + + Commodity = commodity; + + LootType = LootType.Blessed; } - public class CommodityDeed : Item + public override int LabelNumber => Commodity == null ? 1047016 : 1047017; + + public bool SetCommodity(Item item) { - [Constructible] - public CommodityDeed(Item commodity = null) : base(0x14F0) - { - Weight = 1.0; - Hue = 0x47; - - Commodity = commodity; - - LootType = LootType.Blessed; - } - - public CommodityDeed(Serial serial) : base(serial) - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public Item Commodity { get; private set; } - - public override int LabelNumber => Commodity == null ? 1047016 : 1047017; - - public bool SetCommodity(Item item) + InvalidateProperties(); + + if (Commodity == null && (item as ICommodity)?.IsDeedable == true) { + Commodity = item; + Commodity.Internalize(); InvalidateProperties(); - if (Commodity == null && (item as ICommodity)?.IsDeedable == true) + return true; + } + + return false; + } + + private void Deserialize(IGenericReader reader, int version) + { + Commodity = reader.ReadEntity(); + + if (Commodity != null) + { + Hue = 0x592; + } + } + + public override void OnDelete() + { + Commodity?.Delete(); + + base.OnDelete(); + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (Commodity is ICommodity ic) + { + list.Add(1060658, $"{ic.DescriptionNumber:#}\t{Commodity.Amount}"); // ~1_val~: ~2_val~ + } + else if (Commodity != null) + { + if (Commodity.Name == null) { - Commodity = item; - Commodity.Internalize(); - InvalidateProperties(); - - return true; - } - - return false; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - - writer.Write(Commodity); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - Commodity = reader.ReadEntity(); - - switch (version) - { - case 0: - { - if (Commodity != null) - { - Hue = 0x592; - } - - break; - } - } - } - - public override void OnDelete() - { - Commodity?.Delete(); - - base.OnDelete(); - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - - if (Commodity != null) - { - var args = Commodity.Name == null - ? $"#{(Commodity is ICommodity commodity ? commodity.DescriptionNumber : Commodity.LabelNumber)}\t{Commodity.Amount}" - : $"{Commodity.Name}\t{Commodity.Amount}"; - - list.Add(1060658, args); // ~1_val~: ~2_val~ + list.Add(1060658, $"{Commodity.LabelNumber:#}\t{Commodity.Amount}"); // ~1_val~: ~2_val~ } else { - list.Add(1060748); // unfilled + list.Add(1060658, $"{Commodity.Name}\t{Commodity.Amount}"); // ~1_val~: ~2_val~ } } - - public override void OnSingleClick(Mobile from) + else { - base.OnSingleClick(from); - - if (Commodity != null) - { - string args; - - if (Commodity.Name == null) - { - args = - $"#{(Commodity is ICommodity commodity ? commodity.DescriptionNumber : Commodity.LabelNumber)}\t{Commodity.Amount}"; - } - else - { - args = $"{Commodity.Name}\t{Commodity.Amount}"; - } - - LabelTo(from, 1060658, args); // ~1_val~: ~2_val~ - } + list.Add(1060748); // unfilled } + } - public override void OnDoubleClick(Mobile from) + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick(from); + + if (Commodity != null) { - int number; + LabelTo( + from, + 1060658, // ~1_val~: ~2_val~ + Commodity.Name == null + ? $"#{(Commodity as ICommodity)?.DescriptionNumber ?? Commodity.LabelNumber}\t{Commodity.Amount}" + : $"{Commodity.Name}\t{Commodity.Amount}" + ); + } + } - var box = from.FindBankNoCreate(); - var cox = CommodityDeedBox.Find(this); + public override void OnDoubleClick(Mobile from) + { + int number; - // Veteran Rewards mods - if (Commodity != null) + var box = from.FindBankNoCreate(); + var cox = CommodityDeedBox.Find(this); + + // Veteran Rewards mods + if (Commodity != null) + { + if (box != null && IsChildOf(box)) { - if (box != null && IsChildOf(box)) + number = 1047031; // The commodity has been redeemed. + + box.DropItem(Commodity); + + Commodity = null; + Delete(); + } + else if (cox != null) + { + if (cox.IsSecure) { number = 1047031; // The commodity has been redeemed. - box.DropItem(Commodity); + cox.DropItem(Commodity); Commodity = null; Delete(); } - else if (cox != null) + else { - if (cox.IsSecure) - { - number = 1047031; // The commodity has been redeemed. - - cox.DropItem(Commodity); - - Commodity = null; - Delete(); - } - else - { - number = 1080525; // The commodity deed box must be secured before you can use it. - } + number = 1080525; // The commodity deed box must be secured before you can use it. + } + } + else + { + if (Core.ML) + { + number = 1080526; // That must be in your bank box or commodity deed box to use it. } else { - if (Core.ML) + number = 1047024; // To claim the resources .... + } + } + } + else if (cox?.IsSecure == false) + { + number = 1080525; // The commodity deed box must be secured before you can use it. + } + else if ((box == null || !IsChildOf(box)) && cox == null) + { + if (Core.ML) + { + number = 1080526; // That must be in your bank box or commodity deed box to use it. + } + else + { + 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 readonly 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 item) + { + var box = from.FindBankNoCreate(); + var cox = CommodityDeedBox.Find(m_Deed); + + // Veteran Rewards mods + if (box != null && m_Deed.IsChildOf(box) && item.IsChildOf(box) || + cox?.IsSecure != true && item.IsChildOf(cox)) + { + if (m_Deed.SetCommodity(item)) { - number = 1080526; // That must be in your bank box or commodity deed box to use it. + m_Deed.Hue = 0x592; + number = 1047030; // The commodity deed has been filled. } else { - number = 1047024; // To claim the resources .... + number = 1047027; // That is not a commodity the bankers will fill a commodity deed with. } } - } - else if (cox?.IsSecure == false) - { - number = 1080525; // The commodity deed box must be secured before you can use it. - } - else if ((box == null || !IsChildOf(box)) && cox == null) - { - if (Core.ML) + else if (Core.ML) { number = 1080526; // That must be in your bank box or commodity deed box to use it. } @@ -188,68 +224,10 @@ namespace Server.Items } else { - number = 1047029; // Target the commodity to fill this deed with. - - from.Target = new InternalTarget(this); + number = 1047027; // That is not a commodity the bankers will fill a commodity deed with. } from.SendLocalizedMessage(number); } - - private class InternalTarget : Target - { - private readonly 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 item) - { - var box = from.FindBankNoCreate(); - var cox = CommodityDeedBox.Find(m_Deed); - - // Veteran Rewards mods - if (box != null && m_Deed.IsChildOf(box) && item.IsChildOf(box) || - cox?.IsSecure != true && item.IsChildOf(cox)) - { - if (m_Deed.SetCommodity(item)) - { - m_Deed.Hue = 0x592; - 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 if (Core.ML) - { - number = 1080526; // That must be in your bank box or commodity deed box to use it. - } - 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); - } - } } } diff --git a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs index cf3e814a4..78a13404e 100644 --- a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs +++ b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs @@ -1,178 +1,139 @@ using System; +using ModernUO.Serialization; using Server.Engines.Craft; using Server.Mobiles; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[TypeAlias("Server.Items.DragonBarding")] +[SerializationGenerator(2, false)] +public partial class DragonBardingDeed : Item, ICraftable { - [TypeAlias("Server.Items.DragonBarding")] - public class DragonBardingDeed : Item, ICraftable + [InvalidateProperties] + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private string _craftedBy; + + [InvalidateProperties] + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _exceptional; + + [SerializableField(2, "private", "private")] + private CraftResource _rawResource; + + public DragonBardingDeed() : base(0x14F0) => Weight = 1.0; + + public override int LabelNumber => _exceptional ? 1053181 : 1053012; // dragon barding deed + + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - private Mobile m_Crafter; - private bool m_Exceptional; - private CraftResource m_Resource; - - public DragonBardingDeed() : base(0x14F0) => Weight = 1.0; - - public DragonBardingDeed(Serial serial) : base(serial) + get => _rawResource; + set { - } - - public override int LabelNumber => m_Exceptional ? 1053181 : 1053012; // dragon barding deed - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Crafter - { - get => m_Crafter; - set - { - m_Crafter = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public bool Exceptional - { - get => m_Exceptional; - set - { - m_Exceptional = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => m_Resource; - set - { - m_Resource = value; - Hue = CraftResources.GetHue(value); - InvalidateProperties(); - } - } - - 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; - } - - var resourceType = typeRes ?? craftItem.Resources[0].ItemType; - - Resource = CraftResources.GetFromType(resourceType); - - var context = craftSystem.GetContext(from); - - if (context?.DoNotColor == true) - { - Hue = 0; - } - - return quality; - } - - 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, 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; - } - - if (obj is not SwampDragon pet || 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 = Exceptional; - pet.BardingCrafter = Crafter; - pet.BardingHP = pet.BardingMaxHP; - pet.BardingResource = Resource; - pet.HasBarding = true; - pet.Hue = Hue; - - Delete(); - - from.SendLocalizedMessage( - 1053027 - ); // You place the barding on your swamp dragon. Use a bladed item on your dragon to remove the armor. - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - - writer.Write(m_Exceptional); - writer.Write(m_Crafter); - writer.Write((int)m_Resource); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 1: - case 0: - { - m_Exceptional = reader.ReadBool(); - m_Crafter = reader.ReadEntity(); - - if (version < 1) - { - reader.ReadInt(); - } - - m_Resource = (CraftResource)reader.ReadInt(); - break; - } - } + _rawResource = value; + Hue = CraftResources.GetHue(value); + InvalidateProperties(); } } + + public int OnCraft( + int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, + CraftItem craftItem, int resHue + ) + { + Exceptional = quality >= 2; + + if (makersMark) + { + CraftedBy = from?.RawName; + } + + var resourceType = typeRes ?? craftItem.Resources[0].ItemType; + + Resource = CraftResources.GetFromType(resourceType); + + var context = craftSystem.GetContext(from); + + if (context?.DoNotColor == true) + { + Hue = 0; + } + + return quality; + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (_exceptional && _craftedBy != null) + { + list.Add(1050043, _craftedBy); // crafted by ~1_NAME~ + } + } + + public override void OnDoubleClick(Mobile from) + { + if (IsChildOf(from.Backpack)) + { + from.BeginTarget(6, false, TargetFlags.None, 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; + } + + if (obj is not SwampDragon pet || 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 = Exceptional; + pet.BardingCraftedBy = _craftedBy; + pet.BardingHP = pet.BardingMaxHP; + pet.BardingResource = Resource; + pet.HasBarding = true; + pet.Hue = Hue; + + Delete(); + + // You place the barding on your swamp dragon. Use a bladed item on your dragon to remove the armor. + from.SendLocalizedMessage(1053027); + } + } + + private void Deserialize(IGenericReader reader, int version) + { + _exceptional = reader.ReadBool(); + var crafter = reader.ReadEntity(); + Timer.StartTimer(() => _craftedBy = crafter?.RawName); + + if (version < 1) + { + reader.ReadInt(); + } + + _rawResource = (CraftResource)reader.ReadInt(); + } } diff --git a/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs b/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs index 847536294..64dc56845 100644 --- a/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs +++ b/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs @@ -1,164 +1,148 @@ +using ModernUO.Serialization; using Server.Gumps; using Server.Mobiles; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class HairRestylingDeed : Item { - public class HairRestylingDeed : Item + [Constructible] + public HairRestylingDeed() : base(0x14F0) { - [Constructible] - public HairRestylingDeed() : base(0x14F0) + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override int LabelNumber => 1041061; // a coupon for a free hair restyling + + public override void OnDoubleClick(Mobile from) + { + if (!IsChildOf(from.Backpack)) { - Weight = 1.0; - LootType = LootType.Blessed; + from.SendLocalizedMessage(1042001); // That must be in your pack... } - - public HairRestylingDeed(Serial serial) : base(serial) + else { + from.SendGump(new InternalGump(from, this)); } + } - public override int LabelNumber => 1041061; // a coupon for a free hair restyling - - public override void Serialize(IGenericWriter writer) + private class InternalGump : Gump + { + private static readonly int[][] ElvenArray = { - base.Serialize(writer); + new[] { 0 }, + new[] { 1011064, 1011064, 0, 0, 0, 0 }, // bald + new[] { 1074386, 1074386, 0x2fc0, 0x2fc0, 0xedf5, 0xc6e5 }, // long feather + new[] { 1074387, 1074387, 0x2fc1, 0x2fc1, 0xedf6, 0xc6e6 }, // short + new[] { 1074388, 1074388, 0x2fc2, 0x2fc2, 0xedf7, 0xc6e7 }, // mullet + new[] { 1074391, 1074391, 0x2fce, 0x2fce, 0xeddc, 0xc6cc }, // knob + new[] { 1074392, 1074392, 0x2fcf, 0x2fcf, 0xeddd, 0xc6cd }, // braided + new[] { 1074394, 1074394, 0x2fd1, 0x2fd1, 0xeddf, 0xc6cf }, // spiked + new[] { 1074389, 1074385, 0x2fcc, 0x2fbf, 0xedda, 0xc6e4 }, // flower, mid-long + new[] { 1074393, 1074390, 0x2fd0, 0x2fcd, 0xedde, 0xc6cb } // buns, long + }; - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) + /* + racial arrays are: cliloc_F, cliloc_M, ItemID_F, ItemID_M, gump_img_F, gump_img_M + */ + private static readonly int[][] HumanArray = /* why on earth cant these utilities be consistent with hex/dec */ { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + new[] { 0 }, + new[] { 1011064, 1011064, 0, 0, 0, 0 }, // bald + new[] { 1011052, 1011052, 0x203B, 0x203B, 0xed1c, 0xC60C }, // Short + new[] { 1011053, 1011053, 0x203C, 0x203C, 0xed1d, 0xc60d }, // Long + new[] { 1011054, 1011054, 0x203D, 0x203D, 0xed1e, 0xc60e }, // Ponytail + new[] { 1011055, 1011055, 0x2044, 0x2044, 0xed27, 0xC60F }, // Mohawk + new[] { 1011047, 1011047, 0x2045, 0x2045, 0xED26, 0xED26 }, // Pageboy + new[] { 1074393, 1011048, 0x2046, 0x2048, 0xed28, 0xEDE5 }, // Buns, Receding + new[] { 1011049, 1011049, 0x2049, 0x2049, 0xede6, 0xede6 }, // 2-tails + new[] { 1011050, 1011050, 0x204A, 0x204A, 0xED29, 0xED29 }, // Topknot + new[] { 1011396, 1011396, 0x2047, 0x2047, 0xed25, 0xc618 } // Curly + }; - public override void OnDoubleClick(Mobile from) + /* + gump data: bgX, bgY, htmlX, htmlY, imgX, imgY, butX, butY + */ + private static readonly int[][] LayoutArray = { - if (!IsChildOf(from.Backpack)) + new[] { 0 }, /* padding: its more efficient than code to ++ the index/buttonid */ + new[] { 425, 280, 342, 295, 000, 000, 310, 292 }, + new[] { 235, 060, 150, 075, 168, 020, 118, 073 }, + new[] { 235, 115, 150, 130, 168, 070, 118, 128 }, + new[] { 235, 170, 150, 185, 168, 130, 118, 183 }, + new[] { 235, 225, 150, 240, 168, 185, 118, 238 }, + new[] { 425, 060, 342, 075, 358, 018, 310, 073 }, + new[] { 425, 115, 342, 130, 358, 075, 310, 128 }, + new[] { 425, 170, 342, 185, 358, 125, 310, 183 }, + new[] { 425, 225, 342, 240, 358, 185, 310, 238 }, + new[] { 235, 280, 150, 295, 168, 245, 118, 292 } // slot 10, Curly - N/A for elfs. + }; + + private readonly HairRestylingDeed m_Deed; + private readonly Mobile m_From; + + public InternalGump(Mobile from, HairRestylingDeed deed) : base(50, 50) + { + m_From = from; + m_Deed = deed; + + from.CloseGump(); + + AddBackground(100, 10, 400, 385, 0xA28); + + AddHtmlLocalized(100, 25, 400, 35, 1013008); + AddButton(175, 340, 0xFA5, 0xFA7, 0x0); // CANCEL + + AddHtmlLocalized(210, 342, 90, 35, 1011012); //
HAIRSTYLE SELECTION MENU
+ + var RacialData = from.Race == Race.Human ? HumanArray : ElvenArray; + + for (var i = 1; i < RacialData.Length; i++) { - from.SendLocalizedMessage(1042001); // That must be in your pack... - } - else - { - from.SendGump(new InternalGump(from, this)); + AddHtmlLocalized( + LayoutArray[i][2], + LayoutArray[i][3], + i == 1 ? 125 : 80, + i == 1 ? 70 : 35, + m_From.Female ? RacialData[i][0] : RacialData[i][1] + ); + if (LayoutArray[i][4] != 0) + { + AddBackground(LayoutArray[i][0], LayoutArray[i][1], 50, 50, 0xA3C); + AddImage(LayoutArray[i][4], LayoutArray[i][5], m_From.Female ? RacialData[i][4] : RacialData[i][5]); + } + + AddButton(LayoutArray[i][6], LayoutArray[i][7], 0xFA5, 0xFA7, i); } } - private class InternalGump : Gump + public override void OnResponse(NetState sender, RelayInfo info) { - private static readonly int[][] ElvenArray = + if (m_From?.Alive != true) { - new[] { 0 }, - new[] { 1011064, 1011064, 0, 0, 0, 0 }, // bald - new[] { 1074386, 1074386, 0x2fc0, 0x2fc0, 0xedf5, 0xc6e5 }, // long feather - new[] { 1074387, 1074387, 0x2fc1, 0x2fc1, 0xedf6, 0xc6e6 }, // short - new[] { 1074388, 1074388, 0x2fc2, 0x2fc2, 0xedf7, 0xc6e7 }, // mullet - new[] { 1074391, 1074391, 0x2fce, 0x2fce, 0xeddc, 0xc6cc }, // knob - new[] { 1074392, 1074392, 0x2fcf, 0x2fcf, 0xeddd, 0xc6cd }, // braided - new[] { 1074394, 1074394, 0x2fd1, 0x2fd1, 0xeddf, 0xc6cf }, // spiked - new[] { 1074389, 1074385, 0x2fcc, 0x2fbf, 0xedda, 0xc6e4 }, // flower, mid-long - new[] { 1074393, 1074390, 0x2fd0, 0x2fcd, 0xedde, 0xc6cb } // buns, long - }; - - /* - racial arrays are: cliloc_F, cliloc_M, ItemID_F, ItemID_M, gump_img_F, gump_img_M - */ - private static readonly int[][] HumanArray = /* why on earth cant these utilities be consistent with hex/dec */ - { - new[] { 0 }, - new[] { 1011064, 1011064, 0, 0, 0, 0 }, // bald - new[] { 1011052, 1011052, 0x203B, 0x203B, 0xed1c, 0xC60C }, // Short - new[] { 1011053, 1011053, 0x203C, 0x203C, 0xed1d, 0xc60d }, // Long - new[] { 1011054, 1011054, 0x203D, 0x203D, 0xed1e, 0xc60e }, // Ponytail - new[] { 1011055, 1011055, 0x2044, 0x2044, 0xed27, 0xC60F }, // Mohawk - new[] { 1011047, 1011047, 0x2045, 0x2045, 0xED26, 0xED26 }, // Pageboy - new[] { 1074393, 1011048, 0x2046, 0x2048, 0xed28, 0xEDE5 }, // Buns, Receding - new[] { 1011049, 1011049, 0x2049, 0x2049, 0xede6, 0xede6 }, // 2-tails - new[] { 1011050, 1011050, 0x204A, 0x204A, 0xED29, 0xED29 }, // Topknot - new[] { 1011396, 1011396, 0x2047, 0x2047, 0xed25, 0xc618 } // Curly - }; - - /* - gump data: bgX, bgY, htmlX, htmlY, imgX, imgY, butX, butY - */ - private static readonly int[][] LayoutArray = - { - new[] { 0 }, /* padding: its more efficient than code to ++ the index/buttonid */ - new[] { 425, 280, 342, 295, 000, 000, 310, 292 }, - new[] { 235, 060, 150, 075, 168, 020, 118, 073 }, - new[] { 235, 115, 150, 130, 168, 070, 118, 128 }, - new[] { 235, 170, 150, 185, 168, 130, 118, 183 }, - new[] { 235, 225, 150, 240, 168, 185, 118, 238 }, - new[] { 425, 060, 342, 075, 358, 018, 310, 073 }, - new[] { 425, 115, 342, 130, 358, 075, 310, 128 }, - new[] { 425, 170, 342, 185, 358, 125, 310, 183 }, - new[] { 425, 225, 342, 240, 358, 185, 310, 238 }, - new[] { 235, 280, 150, 295, 168, 245, 118, 292 } // slot 10, Curly - N/A for elfs. - }; - - private readonly HairRestylingDeed m_Deed; - private readonly Mobile m_From; - - public InternalGump(Mobile from, HairRestylingDeed deed) : base(50, 50) - { - m_From = from; - m_Deed = deed; - - from.CloseGump(); - - AddBackground(100, 10, 400, 385, 0xA28); - - AddHtmlLocalized(100, 25, 400, 35, 1013008); - AddButton(175, 340, 0xFA5, 0xFA7, 0x0); // CANCEL - - AddHtmlLocalized(210, 342, 90, 35, 1011012); //
HAIRSTYLE SELECTION MENU
- - var RacialData = from.Race == Race.Human ? HumanArray : ElvenArray; - - for (var i = 1; i < RacialData.Length; i++) - { - AddHtmlLocalized( - LayoutArray[i][2], - LayoutArray[i][3], - i == 1 ? 125 : 80, - i == 1 ? 70 : 35, - m_From.Female ? RacialData[i][0] : RacialData[i][1] - ); - if (LayoutArray[i][4] != 0) - { - AddBackground(LayoutArray[i][0], LayoutArray[i][1], 50, 50, 0xA3C); - AddImage(LayoutArray[i][4], LayoutArray[i][5], m_From.Female ? RacialData[i][4] : RacialData[i][5]); - } - - AddButton(LayoutArray[i][6], LayoutArray[i][7], 0xFA5, 0xFA7, i); - } + return; } - public override void OnResponse(NetState sender, RelayInfo info) + if (m_Deed.Deleted) { - if (m_From?.Alive != true) - { - return; - } + return; + } - if (m_Deed.Deleted) - { - return; - } + if (info.ButtonID is < 1 or > 10) + { + return; + } - if (info.ButtonID is < 1 or > 10) - { - return; - } + var RacialData = m_From.Race == Race.Human ? HumanArray : ElvenArray; - var RacialData = m_From.Race == Race.Human ? HumanArray : ElvenArray; - - if (m_From is PlayerMobile pm) - { - pm.SetHairMods(-1, -1); // clear any hairmods (disguise kit, incognito) - pm.HairItemID = pm.Female ? RacialData[info.ButtonID][2] : RacialData[info.ButtonID][3]; - m_Deed.Delete(); - } + if (m_From is PlayerMobile pm) + { + pm.SetHairMods(-1, -1); // clear any hairmods (disguise kit, incognito) + pm.HairItemID = pm.Female ? RacialData[info.ButtonID][2] : RacialData[info.ButtonID][3]; + m_Deed.Delete(); } } } diff --git a/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs b/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs index 9f3eb7962..47230c1b1 100644 --- a/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs +++ b/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs @@ -1,173 +1,154 @@ +using ModernUO.Serialization; using Server.Gumps; using Server.Multis; using Server.Network; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class HolidayTreeDeed : Item { - public class HolidayTreeDeed : Item + [Constructible] + public HolidayTreeDeed() : base(0x14F0) { - [Constructible] - public HolidayTreeDeed() : base(0x14F0) + Hue = 0x488; + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override int LabelNumber => 1041116; // a deed for a holiday tree + + public bool ValidatePlacement(Mobile from, Point3D loc) + { + if (from.AccessLevel >= AccessLevel.GameMaster) { - Hue = 0x488; - Weight = 1.0; - LootType = LootType.Blessed; - } - - public HolidayTreeDeed(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041116; // a deed for a holiday tree - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - LootType = LootType.Blessed; - } - - public bool ValidatePlacement(Mobile from, Point3D loc) - { - if (from.AccessLevel >= AccessLevel.GameMaster) - { - return true; - } - - if (!from.InRange(GetWorldLocation(), 1)) - { - from.SendLocalizedMessage(500446); // That is too far away. - return false; - } - - if (Core.Now.Month != 12) - { - from.SendLocalizedMessage( - 1005700 - ); // You will have to wait till next December to put your tree back up for display. - return false; - } - - var map = from.Map; - - if (map == null) - { - return false; - } - - var house = BaseHouse.FindHouseAt(loc, map, 20); - - if (house?.IsFriend(from) != true) - { - 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) + if (!from.InRange(GetWorldLocation(), 1)) { - from.BeginTarget(-1, true, TargetFlags.None, Placement_OnTarget, type); + from.SendLocalizedMessage(500446); // That is too far away. + return false; } - public void Placement_OnTarget(Mobile from, object targeted, HolidayTreeType type) + if (Core.Now.Month != 12) { - if (targeted is not IPoint3D p) - { - return; - } - - var loc = new Point3D(p); - - if (p is StaticTarget target) - /* 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. - */ - { - loc.Z -= TileData.ItemTable[target.ItemID] - .CalcHeight; - } - - if (ValidatePlacement(from, loc)) - { - EndPlace(from, type, loc); - } + from.SendLocalizedMessage( + 1005700 + ); // You will have to wait till next December to put your tree back up for display. + return false; } - public void EndPlace(Mobile from, HolidayTreeType type, Point3D loc) + var map = from.Map; + + if (map == null) { - Delete(); - var tree = new HolidayTree(from, type, loc); - BaseHouse.FindHouseAt(tree)?.Addons.Add(tree); + return false; } - public override void OnDoubleClick(Mobile from) + var house = BaseHouse.FindHouseAt(loc, map, 20); + + if (house?.IsFriend(from) != true) { - from.CloseGump(); - from.SendGump(new HolidayTreeChoiceGump(from, this)); + 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, Placement_OnTarget, type); + } + + public void Placement_OnTarget(Mobile from, object targeted, HolidayTreeType type) + { + if (targeted is not IPoint3D p) + { + return; + } + + var loc = new Point3D(p); + + if (p is StaticTarget target) + /* 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. + */ + { + loc.Z -= TileData.ItemTable[target.ItemID] + .CalcHeight; + } + + if (ValidatePlacement(from, loc)) + { + EndPlace(from, type, loc); } } - public class HolidayTreeChoiceGump : Gump + public void EndPlace(Mobile from, HolidayTreeType type, Point3D loc) { - private readonly HolidayTreeDeed m_Deed; - private readonly Mobile m_From; + Delete(); + var tree = new HolidayTree(from, type, loc); + BaseHouse.FindHouseAt(tree)?.Addons.Add(tree); + } - public HolidayTreeChoiceGump(Mobile from, HolidayTreeDeed deed) : base(200, 200) + public override void OnDoubleClick(Mobile from) + { + from.CloseGump(); + from.SendGump(new HolidayTreeChoiceGump(from, this)); + } +} + +public class HolidayTreeChoiceGump : Gump +{ + private readonly HolidayTreeDeed m_Deed; + private readonly Mobile m_From; + + 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); + AddHtmlLocalized(55, 35, 145, 25, 1018322); // Classic + + AddButton(20, 65, 4005, 4007, 2); + AddHtmlLocalized(55, 65, 145, 25, 1018321); // Modern + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + if (m_Deed.Deleted) { - 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); - AddHtmlLocalized(55, 35, 145, 25, 1018322); // Classic - - AddButton(20, 65, 4005, 4007, 2); - AddHtmlLocalized(55, 65, 145, 25, 1018321); // Modern + return; } - public override void OnResponse(NetState sender, RelayInfo info) + switch (info.ButtonID) { - 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; - } - } + case 1: + { + m_Deed.BeginPlace(m_From, HolidayTreeType.Classic); + break; + } + case 2: + { + m_Deed.BeginPlace(m_From, HolidayTreeType.Modern); + break; + } } } } diff --git a/Projects/UOContent/Items/Deeds/NameChangeDeed.cs b/Projects/UOContent/Items/Deeds/NameChangeDeed.cs index 05a7d3628..70f9b7bcd 100644 --- a/Projects/UOContent/Items/Deeds/NameChangeDeed.cs +++ b/Projects/UOContent/Items/Deeds/NameChangeDeed.cs @@ -1,116 +1,99 @@ +using ModernUO.Serialization; using Server.Gumps; using Server.Misc; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class NameChangeDeed : Item { - public class NameChangeDeed : Item + [Constructible] + public NameChangeDeed() : base(0x14F0) => LootType = LootType.Blessed; + + public override string DefaultName => "a name change deed"; + + public override void OnDoubleClick(Mobile from) { - [Constructible] - public NameChangeDeed() : base(0x14F0) => LootType = LootType.Blessed; - - public NameChangeDeed(Serial serial) : base(serial) + if (RootParent == from) { + from.CloseGump(); + from.SendGump(new NameChangeDeedGump(this)); } - - public override string DefaultName => "a name change deed"; - - public override void Serialize(IGenericWriter writer) + else { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override void OnDoubleClick(Mobile from) - { - if (RootParent == from) - { - from.CloseGump(); - from.SendGump(new NameChangeDeedGump(this)); - } - else - { - from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - } - } - } - - public class NameChangeDeedGump : Gump - { - private readonly Item m_Sender; - - public NameChangeDeedGump(Item sender) : base(50, 50) - { - m_Sender = sender; - - Closable = true; - Draggable = true; - Resizable = false; - - AddPage(0); - - AddBlackAlpha(10, 120, 250, 85); - AddHtml(10, 125, 250, 20, Color(Center("Name Change Deed"), 0xFFFFFF)); - - AddLabel(73, 15, 1152, ""); - AddLabel(20, 150, 0x480, "New Name:"); - AddTextField(100, 150, 150, 20, 0); - - AddButtonLabeled(75, 180, 1, "Submit"); - } - - public void AddBlackAlpha(int x, int y, int width, int height) - { - AddImageTiled(x, y, width, height, 2624); - AddAlphaRegion(x, y, width, height); - } - - 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 string Center(string text) => $"
{text}
"; - - public string Color(string text, int color) => $"{text}"; - - public void AddButtonLabeled(int x, int y, int buttonID, string text) - { - AddButton(x, y - 1, 4005, 4007, buttonID); - AddHtml(x + 35, y, 240, 20, Color(text, 0xFFFFFF)); - } - - public override void OnResponse(NetState sender, RelayInfo info) - { - if (m_Sender?.Deleted != false || info.ButtonID != 1 || m_Sender.RootParent != sender.Mobile) - { - return; - } - - var m = sender.Mobile; - var nameEntry = info.GetTextEntry(0); - - var newName = nameEntry?.Text.Trim(); - - if (!NameVerification.Validate(newName, 2, 16, true, false, true, 1, NameVerification.SpaceDashPeriodQuote)) - { - m.SendMessage("That name is unacceptable."); - return; - } - - m.RawName = newName; - m.SendMessage("Your name has been changed!"); - m.SendMessage($"You are now known as {newName}"); - m_Sender.Delete(); + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } } } + +public class NameChangeDeedGump : Gump +{ + private readonly Item m_Sender; + + public NameChangeDeedGump(Item sender) : base(50, 50) + { + m_Sender = sender; + + Closable = true; + Draggable = true; + Resizable = false; + + AddPage(0); + + AddBlackAlpha(10, 120, 250, 85); + AddHtml(10, 125, 250, 20, Color(Center("Name Change Deed"), 0xFFFFFF)); + + AddLabel(73, 15, 1152, ""); + AddLabel(20, 150, 0x480, "New Name:"); + AddTextField(100, 150, 150, 20, 0); + + AddButtonLabeled(75, 180, 1, "Submit"); + } + + public void AddBlackAlpha(int x, int y, int width, int height) + { + AddImageTiled(x, y, width, height, 2624); + AddAlphaRegion(x, y, width, height); + } + + 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 string Center(string text) => $"
{text}
"; + + public string Color(string text, int color) => $"{text}"; + + public void AddButtonLabeled(int x, int y, int buttonID, string text) + { + AddButton(x, y - 1, 4005, 4007, buttonID); + AddHtml(x + 35, y, 240, 20, Color(text, 0xFFFFFF)); + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + if (m_Sender?.Deleted != false || info.ButtonID != 1 || m_Sender.RootParent != sender.Mobile) + { + return; + } + + var m = sender.Mobile; + var nameEntry = info.GetTextEntry(0); + + var newName = nameEntry?.Text.Trim(); + + if (!NameVerification.Validate(newName, 2, 16, true, false, true, 1, NameVerification.SpaceDashPeriodQuote)) + { + m.SendMessage("That name is unacceptable."); + return; + } + + m.RawName = newName; + m.SendMessage("Your name has been changed!"); + m.SendMessage($"You are now known as {newName}"); + m_Sender.Delete(); + } +} diff --git a/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs b/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs index 5db09f6ef..36c2eea7a 100644 --- a/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs +++ b/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs @@ -1,218 +1,190 @@ +using ModernUO.Serialization; using Server.Gumps; using Server.Network; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class NewPlayerTicket : Item { - public class NewPlayerTicket : Item + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.Owner)]")] + private Mobile _owner; + + [Constructible] + public NewPlayerTicket() : base(0x14EF) { - [Constructible] - public NewPlayerTicket() : base(0x14EF) + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override int LabelNumber => 1062094; // a young player ticket + + public override bool DisplayLootType => false; + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + // 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! + list.Add(1041492); + } + + public override void OnDoubleClick(Mobile from) + { + if (from != Owner) { - Weight = 1.0; - LootType = LootType.Blessed; + from.SendLocalizedMessage(501926); // This isn't your ticket! Shame on you! You have to use YOUR ticket. } - - public NewPlayerTicket(Serial serial) : base(serial) + else if (!IsChildOf(from.Backpack)) { + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Owner { get; set; } - - public override int LabelNumber => 1062094; // a young player ticket - - public override bool DisplayLootType => false; - - public override void GetProperties(ObjectPropertyList list) + else { - 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! + from.SendLocalizedMessage(501927); // Target any other ticket marked NEW PLAYER to win a prize. + from.Target = new InternalTarget(this); } + } - public override void Serialize(IGenericWriter writer) + private class InternalTarget : Target + { + private readonly NewPlayerTicket m_Ticket; + + public InternalTarget(NewPlayerTicket ticket) : base(2, false, TargetFlags.None) => m_Ticket = ticket; + + protected override void OnTarget(Mobile from, object targeted) { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(Owner); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) + if (targeted == m_Ticket) { - case 0: - { - Owner = reader.ReadEntity(); - break; - } + from.SendLocalizedMessage(501928); // You can't target the same ticket! } - - if (Name == "a young player ticket") + else if (targeted is NewPlayerTicket theirTicket) { - Name = null; - } - } + var them = theirTicket.Owner; - public override void OnDoubleClick(Mobile from) - { - if (from != 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 readonly 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) + if (them?.Deleted != false) { - from.SendLocalizedMessage(501928); // You can't target the same ticket! - } - else if (targeted is NewPlayerTicket theirTicket) - { - var them = theirTicket.Owner; - - if (them?.Deleted != false) - { - 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 as Item)?.ItemID == 0x14F0) - { - from.SendLocalizedMessage(501931); // You need to find another ticket marked NEW PLAYER. + from.SendLocalizedMessage(501930); // That is not a valid ticket. } else { - from.SendLocalizedMessage(501929); // You will need to select a ticket. + from.SendGump(new InternalGump(from, m_Ticket)); + them.SendGump(new InternalGump(them, theirTicket)); } } + else if ((targeted as Item)?.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 class InternalGump : Gump + { + private readonly Mobile m_From; + private readonly NewPlayerTicket m_Ticket; + + public InternalGump(Mobile from, NewPlayerTicket ticket) : base(50, 50) { - private readonly Mobile m_From; - private readonly NewPlayerTicket m_Ticket; + m_From = from; + m_Ticket = ticket; - public InternalGump(Mobile from, NewPlayerTicket ticket) : base(50, 50) + AddBackground(0, 0, 400, 385, 0xA28); + + // Choose the gift you prefer. WARNING: if you cancel, and your partner does not, you will need to find another matching ticket! + AddHtmlLocalized(30, 45, 340, 70, 1013011, true, true); + + AddButton(46, 128, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(80, 130, 320, 35, 1013012); // A sextant + + AddButton(46, 163, 0xFA5, 0xFA7, 2); + AddHtmlLocalized(80, 165, 320, 35, 1013013); // A coupon for a single hair restyling + + AddButton(46, 198, 0xFA5, 0xFA7, 3); + AddHtmlLocalized(80, 200, 320, 35, 1013014); // A spellbook with all 1st - 4th spells. + + AddButton(46, 233, 0xFA5, 0xFA7, 4); + AddHtmlLocalized(80, 235, 320, 35, 1013015); // A wand of fireworks + + AddButton(46, 268, 0xFA5, 0xFA7, 5); + AddHtmlLocalized(80, 270, 320, 35, 1013016); // A spyglass + + AddButton(46, 303, 0xFA5, 0xFA7, 6); + AddHtmlLocalized(80, 305, 320, 35, 1013017); // Dyes and a dye tub + + AddButton(120, 340, 0xFA5, 0xFA7, 0); + AddHtmlLocalized(154, 342, 100, 35, 1011012); // CANCEL + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + if (m_Ticket.Deleted) { - 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); - AddHtmlLocalized(80, 130, 320, 35, 1013012); // A sextant - - AddButton(46, 163, 0xFA5, 0xFA7, 2); - AddHtmlLocalized(80, 165, 320, 35, 1013013); // A coupon for a single hair restyling - - AddButton(46, 198, 0xFA5, 0xFA7, 3); - AddHtmlLocalized(80, 200, 320, 35, 1013014); // A spellbook with all 1st - 4th spells. - - AddButton(46, 233, 0xFA5, 0xFA7, 4); - AddHtmlLocalized(80, 235, 320, 35, 1013015); // A wand of fireworks - - AddButton(46, 268, 0xFA5, 0xFA7, 5); - AddHtmlLocalized(80, 270, 320, 35, 1013016); // A spyglass - - AddButton(46, 303, 0xFA5, 0xFA7, 6); - AddHtmlLocalized(80, 305, 320, 35, 1013017); // Dyes and a dye tub - - AddButton(120, 340, 0xFA5, 0xFA7, 0); - AddHtmlLocalized(154, 342, 100, 35, 1011012); // CANCEL + return; } - public override void OnResponse(NetState sender, RelayInfo info) + var number = 0; + + Item item = null; + Item item2 = null; + + switch (info.ButtonID) { - if (m_Ticket.Deleted) - { - return; - } - - var number = 0; - - Item item = null; - Item item2 = null; - - switch (info.ButtonID) - { - case 1: + case 1: + { item = new Sextant(); number = 1010494; break; // A sextant has been placed in your backpack. - case 2: + } + case 2: + { item = new HairRestylingDeed(); number = 501933; break; // A coupon for a free hair restyling has been placed in your backpack. - case 3: + } + 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: + } + case 4: + { item = new FireworksWand(); number = 501935; break; // A wand of fireworks has been placed in your backpack. - case 5: + } + case 5: + { item = new Spyglass(); number = 501936; break; // A spyglass has been placed in your backpack. - case 6: + } + 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); } + } + + if (item != null) + { + m_Ticket.Delete(); + + m_From.SendLocalizedMessage(number); + m_From.AddToBackpack(item); + + if (item2 != null) + { + m_From.AddToBackpack(item2); } } } diff --git a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs index 6c37f9f55..8a1134014 100644 --- a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs +++ b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs @@ -1,369 +1,347 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Mobiles; using Server.Multis; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class VendorRentalContract : Item { - public class VendorRentalContract : Item + [SerializableField(0, getter: "private", setter: "private")] + private int _rentalDurationId; // TODO: Replace this with something more robust + + private VendorRentalDuration _duration; + + private Mobile _offeree; + private Timer _offerExpireTimer; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _price; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _landlordRenew; + + [Constructible] + public VendorRentalContract() : base(0x14F0) { - private VendorRentalDuration m_Duration; + Weight = 1.0; + Hue = 0x672; - private Mobile m_Offeree; - private Timer m_OfferExpireTimer; + _duration = VendorRentalDuration.Instances[0]; + Price = 1500; + } - [Constructible] - public VendorRentalContract() : base(0x14F0) + public override int LabelNumber => 1062332; // a vendor rental contract + + public VendorRentalDuration Duration + { + get => _duration; + set { - Weight = 1.0; - Hue = 0x672; - - m_Duration = VendorRentalDuration.Instances[0]; - Price = 1500; - } - - public VendorRentalContract(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1062332; // a vendor rental contract - - public VendorRentalDuration Duration - { - get => m_Duration; - set + if (value != null) { - if (value != null) - { - m_Duration = value; - } + _duration = value; + _rentalDurationId = _duration.ID; + } + } + } + + public Mobile Offeree + { + get => _offeree; + set + { + if (_offerExpireTimer != null) + { + _offerExpireTimer.Stop(); + _offerExpireTimer = null; + } + + _offeree = value; + + if (value != null) + { + _offerExpireTimer = new OfferExpireTimer(this); + _offerExpireTimer.Start(); + } + + InvalidateProperties(); + } + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (Offeree != null) + { + list.Add(1062368, Offeree.Name); // Being Offered To ~1_NAME~ + } + } + + public bool IsLandlord(Mobile m) + { + if (IsLockedDown) + { + var house = BaseHouse.FindHouseAt(this); + + if (house != null && house.DecayType != DecayType.Condemned) + { + return house.IsOwner(m); } } - [CommandProperty(AccessLevel.GameMaster)] - public int Price { get; set; } + return false; + } - [CommandProperty(AccessLevel.GameMaster)] - public bool LandlordRenew { get; set; } - - public Mobile Offeree + public bool IsUsableBy(Mobile from, bool byLandlord, bool byBackpack, bool noOfferee, bool sendMessage) + { + if (Deleted || !from.CheckAlive(sendMessage)) { - get => 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(); - } - } - - 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) - { - var 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 (noOfferee && Offeree != null) { - if (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 != 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) - { - var house = BaseHouse.FindHouseAt(this); - - house?.VendorRentalContracts.Remove(this); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (Offeree != null) + if (sendMessage) { 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; - } - var house = BaseHouse.FindHouseAt(from); + return false; + } - if (house?.IsOwner(from) != true) - { - 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 enough 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 (byBackpack && IsChildOf(from.Backpack)) + { + return true; + } + + if (byLandlord && IsLandlord(from)) + { + if (from.Map != Map || !from.InRange(this, 5)) { - if (from.InRange(this, 5)) - { - from.CloseGump(); - from.SendGump(new VendorRentalContractGump(this, from)); - } - else + if (sendMessage) { from.SendLocalizedMessage(501853); // Target is too far away. } + + return false; } + + return true; } - public override void GetContextMenuEntries(Mobile from, List list) - { - base.GetContextMenuEntries(from, list); + return false; + } - if (IsUsableBy(from, true, true, true, false)) + public override void OnDelete() + { + if (IsLockedDown) + { + var house = BaseHouse.FindHouseAt(this); + + 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)) { - list.Add(new ContractOptionEntry(this)); + from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. + return; } - } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + var house = BaseHouse.FindHouseAt(from); - writer.WriteEncodedInt(0); // version - - writer.WriteEncodedInt(m_Duration.ID); - - writer.Write(Price); - writer.Write(LandlordRenew); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - var durationID = reader.ReadEncodedInt(); - if (durationID < VendorRentalDuration.Instances.Length) + if (house?.IsOwner(from) != true) { - m_Duration = VendorRentalDuration.Instances[durationID]; + 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 enough storage available to place this contract. } else { - m_Duration = VendorRentalDuration.Instances[0]; - } - - Price = reader.ReadInt(); - LandlordRenew = reader.ReadBool(); - } - - private class ContractOptionEntry : ContextMenuEntry - { - private readonly VendorRentalContract m_Contract; - - public ContractOptionEntry(VendorRentalContract contract) : base(6209) => m_Contract = contract; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Contract.IsUsableBy(from, true, true, true, true)) - { - from.CloseGump(); - from.SendGump(new VendorRentalContractGump(m_Contract, from)); - } + from.SendLocalizedMessage(1062337); // Target the exact location you wish to rent out. + from.Target = new RentTarget(this); } } - - private class RentTarget : Target + else if (IsLandlord(from)) { - private readonly VendorRentalContract m_Contract; - - public RentTarget(VendorRentalContract contract) : base(-1, false, TargetFlags.None) => m_Contract = contract; - - protected override void OnTarget(Mobile from, object targeted) + if (from.InRange(this, 5)) { - if (!m_Contract.IsUsableBy(from, false, true, true, true)) - { - return; - } + from.CloseGump(); + from.SendGump(new VendorRentalContractGump(this, from)); + } + else + { + from.SendLocalizedMessage(501853); // Target is too far away. + } + } + } - if (targeted is not IPoint3D location) - { - return; - } + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); - var pLocation = new Point3D(location); - var map = from.Map; + if (IsUsableBy(from, true, true, true, false)) + { + list.Add(new ContractOptionEntry(this)); + } + } - var house = BaseHouse.FindHouseAt(pLocation, map, 0); + [AfterDeserialization] + private void AfterDeserialization() + { + var index = Math.Clamp(_rentalDurationId, 0, VendorRentalDuration.Instances.Length); + _duration = VendorRentalDuration.Instances[index]; + } - if (house?.IsOwner(from) != true) + private class ContractOptionEntry : ContextMenuEntry + { + private readonly VendorRentalContract m_Contract; + + public ContractOptionEntry(VendorRentalContract contract) : base(6209) => m_Contract = contract; + + public override void OnClick() + { + var from = Owner.From; + + if (m_Contract.IsUsableBy(from, true, true, true, true)) + { + from.CloseGump(); + from.SendGump(new VendorRentalContractGump(m_Contract, from)); + } + } + } + + private class RentTarget : Target + { + private readonly 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; + } + + if (targeted is not IPoint3D location) + { + return; + } + + var pLocation = new Point3D(location); + var map = from.Map; + + var house = BaseHouse.FindHouseAt(pLocation, map, 0); + + if (house?.IsOwner(from) != true) + { + from.SendLocalizedMessage(1062338); // The location being rented out must be inside of your house. + } + else if (BaseHouse.FindHouseAt(from) != house) + { + // You must be located inside of the house in which you are trying to place the contract. + from.SendLocalizedMessage(1062339); + } + 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 + { + BaseHouse.IsThereVendor(pLocation, map, out var vendor, out var contract); + + if (vendor) { - from.SendLocalizedMessage(1062338); // The location being rented out must be inside of your house. + // You may not place a rental contract at this location while other beings occupy it. + from.SendLocalizedMessage(1062342); } - else if (BaseHouse.FindHouseAt(from) != house) + else if (contract) { - // You must be located inside of the house in which you are trying to place the contract. - from.SendLocalizedMessage(1062339); - } - 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. + // That location is cluttered. Please clear out any objects there and try again. + from.SendLocalizedMessage(1062341); } else { - BaseHouse.IsThereVendor(pLocation, map, out var vendor, out var contract); + m_Contract.MoveToWorld(pLocation, map); - if (vendor) + if (!house.LockDown(from, m_Contract)) { - // You may not place a rental contract at this location while other beings occupy it. - from.SendLocalizedMessage(1062342); - } - else if (contract) - { - // That location is cluttered. Please clear out any objects there and try again. - from.SendLocalizedMessage(1062341); - } - else - { - m_Contract.MoveToWorld(pLocation, map); - - if (!house.LockDown(from, m_Contract)) - { - from.AddToBackpack(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 + protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) { - private readonly VendorRentalContract m_Contract; + from.SendLocalizedMessage(1062336); // You decide not to place the contract at this time. + } + } - public OfferExpireTimer(VendorRentalContract contract) : base(TimeSpan.FromSeconds(30.0)) + private class OfferExpireTimer : Timer + { + private readonly VendorRentalContract m_Contract; + + public OfferExpireTimer(VendorRentalContract contract) : base(TimeSpan.FromSeconds(30.0)) + { + m_Contract = contract; + } + + protected override void OnTick() + { + var offeree = m_Contract.Offeree; + + if (offeree != null) { - m_Contract = contract; - } + offeree.CloseGump(); - protected override void OnTick() - { - var offeree = m_Contract.Offeree; - - if (offeree != null) - { - offeree.CloseGump(); - - m_Contract.Offeree = null; - } + m_Contract.Offeree = null; } } } diff --git a/Projects/UOContent/Items/Farming/FarmableCabbage.cs b/Projects/UOContent/Items/Farming/FarmableCabbage.cs index f9fb4563b..3b4e32d59 100644 --- a/Projects/UOContent/Items/Farming/FarmableCabbage.cs +++ b/Projects/UOContent/Items/Farming/FarmableCabbage.cs @@ -1,41 +1,22 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableCabbage : FarmableCrop { - public class FarmableCabbage : FarmableCrop + [Constructible] + public FarmableCabbage() : base(GetCropID()) { - [Constructible] - public FarmableCabbage() : base(GetCropID()) - { - } - - public FarmableCabbage(Serial serial) : base(serial) - { - } - - public static int GetCropID() => 3254; - - public override Item GetCropObject() - { - var cabbage = new Cabbage(); - - cabbage.ItemID = Utility.Random(3195, 2); - - return cabbage; - } - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => 3254; + + public override Item GetCropObject() => + new Cabbage + { + ItemID = Utility.Random(3195, 2) + }; + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmableCarrot.cs b/Projects/UOContent/Items/Farming/FarmableCarrot.cs index 5c4e79f5d..41c3e1c7a 100644 --- a/Projects/UOContent/Items/Farming/FarmableCarrot.cs +++ b/Projects/UOContent/Items/Farming/FarmableCarrot.cs @@ -1,41 +1,22 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableCarrot : FarmableCrop { - public class FarmableCarrot : FarmableCrop + [Constructible] + public FarmableCarrot() : base(GetCropID()) { - [Constructible] - public FarmableCarrot() : base(GetCropID()) - { - } - - public FarmableCarrot(Serial serial) : base(serial) - { - } - - public static int GetCropID() => 3190; - - public override Item GetCropObject() - { - var carrot = new Carrot(); - - carrot.ItemID = Utility.Random(3191, 2); - - return carrot; - } - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => 3190; + + public override Item GetCropObject() => + new Carrot + { + ItemID = Utility.Random(3191, 2) + }; + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmableCotton.cs b/Projects/UOContent/Items/Farming/FarmableCotton.cs index f4ff49e08..1ec1e5b16 100644 --- a/Projects/UOContent/Items/Farming/FarmableCotton.cs +++ b/Projects/UOContent/Items/Farming/FarmableCotton.cs @@ -1,34 +1,18 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableCotton : FarmableCrop { - public class FarmableCotton : FarmableCrop + [Constructible] + public FarmableCotton() : base(GetCropID()) { - [Constructible] - public FarmableCotton() : base(GetCropID()) - { - } - - public FarmableCotton(Serial serial) : base(serial) - { - } - - public static int GetCropID() => Utility.Random(3153, 4); - - public override Item GetCropObject() => new Cotton(); - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => Utility.Random(3153, 4); + + public override Item GetCropObject() => new Cotton(); + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmableCrop.cs b/Projects/UOContent/Items/Farming/FarmableCrop.cs index 5ec0340f6..5f0bfa56e 100644 --- a/Projects/UOContent/Items/Farming/FarmableCrop.cs +++ b/Projects/UOContent/Items/Farming/FarmableCrop.cs @@ -1,91 +1,71 @@ using System; +using ModernUO.Serialization; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0)] +public abstract partial class FarmableCrop : Item { - public abstract class FarmableCrop : Item + [SerializableField(0)] + private bool _picked; + + public FarmableCrop(int itemID) : base(itemID) => Movable = false; + + public abstract Item GetCropObject(); + public abstract int GetPickedID(); + + public override void OnDoubleClick(Mobile from) { - private bool m_Picked; + var map = Map; + var loc = Location; - public FarmableCrop(int itemID) : base(itemID) => Movable = false; - - public FarmableCrop(Serial serial) : base(serial) + if (Parent != null || Movable || IsLockedDown || IsSecure || map == null || map == Map.Internal) { + return; } - public abstract Item GetCropObject(); - public abstract int GetPickedID(); - - public override void OnDoubleClick(Mobile from) + if (!from.InRange(loc, 2) || !from.InLOS(this)) { - var map = Map; - var loc = 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); - } + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } - - public virtual void OnPicked(Mobile from, Point3D loc, Map map) + else if (!_picked) { - ItemID = GetPickedID(); + OnPicked(from, loc, map); + } + } - var spawn = GetCropObject(); + public virtual void OnPicked(Mobile from, Point3D loc, Map map) + { + ItemID = GetPickedID(); - spawn?.MoveToWorld(loc, map); + var spawn = GetCropObject(); - m_Picked = true; + spawn?.MoveToWorld(loc, map); + _picked = true; + + Unlink(); + + Timer.StartTimer(TimeSpan.FromMinutes(5.0), Delete); + } + + public void Unlink() + { + if (Spawner != null) + { + Spawner.Remove(this); + Spawner = null; + } + } + + [AfterDeserialization] + private void AfterDeserialization() + { + if (_picked) + { Unlink(); - - Timer.StartTimer(TimeSpan.FromMinutes(5.0), Delete); - } - - public void Unlink() - { - if (Spawner != null) - { - Spawner.Remove(this); - Spawner = null; - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - - writer.Write(m_Picked); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - m_Picked = version switch - { - 0 => reader.ReadBool(), - _ => m_Picked - }; - - if (m_Picked) - { - Unlink(); - Delete(); - } + Delete(); } } } diff --git a/Projects/UOContent/Items/Farming/FarmableFlax.cs b/Projects/UOContent/Items/Farming/FarmableFlax.cs index e698b6560..5dd7fe755 100644 --- a/Projects/UOContent/Items/Farming/FarmableFlax.cs +++ b/Projects/UOContent/Items/Farming/FarmableFlax.cs @@ -1,41 +1,22 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableFlax : FarmableCrop { - public class FarmableFlax : FarmableCrop + [Constructible] + public FarmableFlax() : base(GetCropID()) { - [Constructible] - public FarmableFlax() : base(GetCropID()) - { - } - - public FarmableFlax(Serial serial) : base(serial) - { - } - - public static int GetCropID() => Utility.Random(6809, 3); - - public override Item GetCropObject() - { - var flax = new Flax(); - - flax.ItemID = Utility.Random(6812, 2); - - return flax; - } - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => Utility.Random(6809, 3); + + public override Item GetCropObject() => + new Flax + { + ItemID = Utility.Random(6812, 2) + }; + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmableLettuce.cs b/Projects/UOContent/Items/Farming/FarmableLettuce.cs index bc930b824..d443acf9f 100644 --- a/Projects/UOContent/Items/Farming/FarmableLettuce.cs +++ b/Projects/UOContent/Items/Farming/FarmableLettuce.cs @@ -1,41 +1,22 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableLettuce : FarmableCrop { - public class FarmableLettuce : FarmableCrop + [Constructible] + public FarmableLettuce() : base(GetCropID()) { - [Constructible] - public FarmableLettuce() : base(GetCropID()) - { - } - - public FarmableLettuce(Serial serial) : base(serial) - { - } - - public static int GetCropID() => 3254; - - public override Item GetCropObject() - { - var lettuce = new Lettuce(); - - lettuce.ItemID = Utility.Random(3184, 2); - - return lettuce; - } - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => 3254; + + public override Item GetCropObject() => + new Lettuce + { + ItemID = Utility.Random(3184, 2) + }; + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmableOnion.cs b/Projects/UOContent/Items/Farming/FarmableOnion.cs index 22d86df48..66921dda7 100644 --- a/Projects/UOContent/Items/Farming/FarmableOnion.cs +++ b/Projects/UOContent/Items/Farming/FarmableOnion.cs @@ -1,41 +1,22 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableOnion : FarmableCrop { - public class FarmableOnion : FarmableCrop + [Constructible] + public FarmableOnion() : base(GetCropID()) { - [Constructible] - public FarmableOnion() : base(GetCropID()) - { - } - - public FarmableOnion(Serial serial) : base(serial) - { - } - - public static int GetCropID() => 3183; - - public override Item GetCropObject() - { - var onion = new Onion(); - - onion.ItemID = Utility.Random(3181, 2); - - return onion; - } - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => 3183; + + public override Item GetCropObject() => + new Onion + { + ItemID = Utility.Random(3181, 2) + }; + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmablePumpkin.cs b/Projects/UOContent/Items/Farming/FarmablePumpkin.cs index dc6c0f4be..986345e84 100644 --- a/Projects/UOContent/Items/Farming/FarmablePumpkin.cs +++ b/Projects/UOContent/Items/Farming/FarmablePumpkin.cs @@ -1,43 +1,23 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmablePumpkin : FarmableCrop { - public class FarmablePumpkin : FarmableCrop + [Constructible] + public FarmablePumpkin() + : base(GetCropID()) { - [Constructible] - public FarmablePumpkin() - : base(GetCropID()) - { - } - - public FarmablePumpkin(Serial serial) - : base(serial) - { - } - - public static int GetCropID() => Utility.Random(3166, 3); - - public override Item GetCropObject() - { - var pumpkin = new Pumpkin(); - - pumpkin.ItemID = Utility.Random(3178, 3); - - return pumpkin; - } - - public override int GetPickedID() => Utility.Random(3166, 3); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => Utility.Random(3166, 3); + + public override Item GetCropObject() => + new Pumpkin + { + ItemID = Utility.Random(3178, 3) + }; + + public override int GetPickedID() => Utility.Random(3166, 3); } diff --git a/Projects/UOContent/Items/Farming/FarmableTurnip.cs b/Projects/UOContent/Items/Farming/FarmableTurnip.cs index eec9facbd..94fc10733 100644 --- a/Projects/UOContent/Items/Farming/FarmableTurnip.cs +++ b/Projects/UOContent/Items/Farming/FarmableTurnip.cs @@ -1,41 +1,22 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableTurnip : FarmableCrop { - public class FarmableTurnip : FarmableCrop + [Constructible] + public FarmableTurnip() : base(GetCropID()) { - [Constructible] - public FarmableTurnip() : base(GetCropID()) - { - } - - public FarmableTurnip(Serial serial) : base(serial) - { - } - - public static int GetCropID() => Utility.Random(3169, 3); - - public override Item GetCropObject() - { - var turnip = new Turnip(); - - turnip.ItemID = Utility.Random(3385, 2); - - return turnip; - } - - public override int GetPickedID() => 3254; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => Utility.Random(3169, 3); + + public override Item GetCropObject() => + new Turnip + { + ItemID = Utility.Random(3385, 2) + }; + + public override int GetPickedID() => 3254; } diff --git a/Projects/UOContent/Items/Farming/FarmableWheat.cs b/Projects/UOContent/Items/Farming/FarmableWheat.cs index d3fcbf109..f15d9b21e 100644 --- a/Projects/UOContent/Items/Farming/FarmableWheat.cs +++ b/Projects/UOContent/Items/Farming/FarmableWheat.cs @@ -1,34 +1,18 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class FarmableWheat : FarmableCrop { - public class FarmableWheat : FarmableCrop + [Constructible] + public FarmableWheat() : base(GetCropID()) { - [Constructible] - public FarmableWheat() : base(GetCropID()) - { - } - - public FarmableWheat(Serial serial) : base(serial) - { - } - - public static int GetCropID() => Utility.Random(3157, 4); - - public override Item GetCropObject() => new WheatSheaf(); - - public override int GetPickedID() => Utility.Random(3502, 2); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } + + public static int GetCropID() => Utility.Random(3157, 4); + + public override Item GetCropObject() => new WheatSheaf(); + + public override int GetPickedID() => Utility.Random(3502, 2); } diff --git a/Projects/UOContent/Items/Food/Asian.cs b/Projects/UOContent/Items/Food/Asian.cs index 98b35e0c3..b80e5f2d9 100644 --- a/Projects/UOContent/Items/Food/Asian.cs +++ b/Projects/UOContent/Items/Food/Asian.cs @@ -1,346 +1,144 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Wasabi : Food { - public class Wasabi : Item + [Constructible] + public Wasabi() : base(0x24E8) => Weight = 1.0; +} + +[SerializationGenerator(0, false)] +public partial class WasabiClumps : Food +{ + [Constructible] + public WasabiClumps() : base(0x24EB) { - [Constructible] - public Wasabi() : base(0x24E8) => Weight = 1.0; - - public Wasabi(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WasabiClumps : Food - { - [Constructible] - public WasabiClumps() : base(0x24EB, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public WasabiClumps(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class EmptyBentoBox : Item - { - [Constructible] - public EmptyBentoBox() : base(0x2834) => Weight = 5.0; - - public EmptyBentoBox(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class BentoBox : Food - { - [Constructible] - public BentoBox() : base(0x2836, 1) - { - Stackable = false; - Weight = 5.0; - FillFactor = 2; - } - - public BentoBox(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyBentoBox()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SushiRolls : Food - { - [Constructible] - public SushiRolls() : base(0x283E, 1) - { - Stackable = false; - Weight = 3.0; - FillFactor = 2; - } - - public SushiRolls(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SushiPlatter : Food - { - [Constructible] - public SushiPlatter() : base(0x2840, 1) - { - Stackable = Core.ML; - Weight = 3.0; - FillFactor = 2; - } - - public SushiPlatter(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GreenTeaBasket : Item - { - [Constructible] - public GreenTeaBasket() : base(0x284B) => Weight = 10.0; - - public GreenTeaBasket(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GreenTea : Food - { - [Constructible] - public GreenTea() : base(0x284C, 1) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public GreenTea(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class MisoSoup : Food - { - [Constructible] - public MisoSoup() : base(0x284D, 1) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public MisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WhiteMisoSoup : Food - { - [Constructible] - public WhiteMisoSoup() : base(0x284E, 1) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public WhiteMisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class RedMisoSoup : Food - { - [Constructible] - public RedMisoSoup() : base(0x284F, 1) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public RedMisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class AwaseMisoSoup : Food - { - [Constructible] - public AwaseMisoSoup() : base(0x2850, 1) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public AwaseMisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class EmptyBentoBox : Item +{ + [Constructible] + public EmptyBentoBox() : base(0x2834) => Weight = 5.0; + +} + +[SerializationGenerator(0, false)] +public partial class BentoBox : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class SushiRolls : Food +{ + [Constructible] + public SushiRolls() : base(0x283E) + { + Stackable = false; + Weight = 3.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class SushiPlatter : Food +{ + [Constructible] + public SushiPlatter() : base(0x2840) + { + Stackable = Core.ML; + Weight = 3.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class GreenTeaBasket : Item +{ + [Constructible] + public GreenTeaBasket() : base(0x284B) => Weight = 10.0; +} + +[SerializationGenerator(0, false)] +public partial class GreenTea : Food +{ + [Constructible] + public GreenTea() : base(0x284C) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class MisoSoup : Food +{ + [Constructible] + public MisoSoup() : base(0x284D) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class WhiteMisoSoup : Food +{ + [Constructible] + public WhiteMisoSoup() : base(0x284E) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class RedMisoSoup : Food +{ + [Constructible] + public RedMisoSoup() : base(0x284F) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class AwaseMisoSoup : Food +{ + [Constructible] + public AwaseMisoSoup() : base(0x2850) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; } } diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index 91edd8426..305440fcb 100644 --- a/Projects/UOContent/Items/Food/Beverage.cs +++ b/Projects/UOContent/Items/Food/Beverage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Plants; using Server.Engines.Quests; using Server.Engines.Quests.Hag; @@ -9,1233 +10,803 @@ using Server.Multis; using Server.Network; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +public enum BeverageType { - public enum BeverageType + Ale, + Cider, + Liquor, + Milk, + Wine, + Water +} + +public interface IHasQuantity +{ + int Quantity { get; set; } +} + +public interface IWaterSource : IHasQuantity +{ +} + +// TODO: Flippable attributes +[SerializationGenerator(0, false)] +[TypeAlias("Server.Items.BottleAle", "Server.Items.BottleLiquor", "Server.Items.BottleWine")] +public partial class BeverageBottle : BaseBeverage +{ + [Constructible] + public BeverageBottle(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042959; // a bottle of Ale + public override int MaxQuantity => 5; + public override bool Fillable => false; + + public override int ComputeItemID() { - Ale, - Cider, - Liquor, - Milk, - Wine, - Water - } - - public interface IHasQuantity - { - int Quantity { get; set; } - } - - public interface IWaterSource : IHasQuantity - { - } - - // TODO: Flippable attributes - - [TypeAlias("Server.Items.BottleAle", "Server.Items.BottleLiquor", "Server.Items.BottleWine")] - public class BeverageBottle : BaseBeverage - { - [Constructible] - public BeverageBottle(BeverageType type) - : base(type) => - Weight = 1.0; - - public BeverageBottle(Serial serial) - : base(serial) + if (IsEmpty) { - } - - public override int BaseLabelNumber => 1042959; // a bottle of Ale - public override int MaxQuantity => 5; - public override bool Fillable => 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; } - public override void Serialize(IGenericWriter writer) + return Content switch { - base.Serialize(writer); + BeverageType.Ale => 0x99F, + BeverageType.Cider => 0x99F, + BeverageType.Liquor => 0x99B, + BeverageType.Milk => 0x99B, + BeverageType.Wine => 0x9C7, + BeverageType.Water => 0x99B, + _ => 0 + }; + } +} - writer.Write(1); // version +[SerializationGenerator(0, false)] +public partial class Jug : BaseBeverage +{ + [Constructible] + public Jug(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042965; // a jug of Ale + public override int MaxQuantity => 10; + public override bool Fillable => false; + + public override int ComputeItemID() => !IsEmpty ? 0x9C8 : 0; +} + +[SerializationGenerator(0, false)] +public partial class CeramicMug : BaseBeverage +{ + [Constructible] + public CeramicMug() => Weight = 1.0; + + [Constructible] + public CeramicMug(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042982; // a ceramic mug of Ale + public override int MaxQuantity => 1; + + public override int ComputeItemID() => ItemID is (< 0x995 or > 0x999) and not 0x9CA ? 0x995 : ItemID; +} + +[SerializationGenerator(0, false)] +public partial class PewterMug : BaseBeverage +{ + [Constructible] + public PewterMug() => Weight = 1.0; + + [Constructible] + public PewterMug(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042994; // a pewter mug with Ale + public override int MaxQuantity => 1; + + public override int ComputeItemID() => ItemID is >= 0xFFF and <= 0x1002 ? ItemID : 0xFFF; +} + +[SerializationGenerator(0, false)] +public partial class Goblet : BaseBeverage +{ + [Constructible] + public Goblet() => Weight = 1.0; + + [Constructible] + public Goblet(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1043000; // a goblet of Ale + public override int MaxQuantity => 1; + + public override int ComputeItemID() => ItemID is 0x99A or 0x9B3 or 0x9BF or 0x9CB ? ItemID : 0x99A; +} + +[TypeAlias( + "Server.Items.MugAle", + "Server.Items.GlassCider", + "Server.Items.GlassLiquor", + "Server.Items.GlassMilk", + "Server.Items.GlassWine", + "Server.Items.GlassWater" +)] +[SerializationGenerator(0, false)] +public partial class GlassMug : BaseBeverage +{ + [Constructible] + public GlassMug() => Weight = 1.0; + + [Constructible] + public GlassMug(BeverageType type) : base(type) => Weight = 1.0; + + public override int EmptyLabelNumber => 1022456; // mug + public override int BaseLabelNumber => 1042976; // a mug of Ale + public override int MaxQuantity => 5; + + public override int ComputeItemID() + { + if (IsEmpty) + { + return ItemID is >= 0x1F81 and <= 0x1F84 ? ItemID : 0x1F81; } - public override void Deserialize(IGenericReader reader) + return Content switch { - base.Deserialize(reader); + BeverageType.Ale => ItemID == 0x9EF ? 0x9EF : 0x9EE, + BeverageType.Cider => Math.Clamp(ItemID, 0x1F7D, 0x1F80), + BeverageType.Liquor => Math.Clamp(ItemID, 0x1F85, 0x1F88), + BeverageType.Milk => Math.Clamp(ItemID, 0x1F89, 0x1F8C), + BeverageType.Wine => Math.Clamp(ItemID, 0x1F8D, 0x1F90), + BeverageType.Water => Math.Clamp(ItemID, 0x1F91, 0x1F94), + _ => 0 + }; + } +} - var version = reader.ReadInt(); +[TypeAlias( + "Server.Items.PitcherAle", + "Server.Items.PitcherCider", + "Server.Items.PitcherLiquor", + "Server.Items.PitcherMilk", + "Server.Items.PitcherWine", + "Server.Items.PitcherWater", + "Server.Items.GlassPitcher" +)] +[SerializationGenerator(0, false)] +public partial class Pitcher : BaseBeverage +{ + [Constructible] + public Pitcher() => Weight = 2.0; - 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("Invalid beverage type"); - } + [Constructible] + public Pitcher(BeverageType type) : base(type) => Weight = 2.0; - break; - } - } + public override int BaseLabelNumber => 1048128; // a Pitcher of Ale + public override int MaxQuantity => 5; + + public override int ComputeItemID() + { + if (IsEmpty) + { + return ItemID is 0x9A7 or 0xFF7 ? ItemID : 0xFF6; } + + return Content switch + { + BeverageType.Ale => ItemID == 0x1F96 ? ItemID : 0x1F95, + BeverageType.Cider => ItemID == 0x1F98 ? ItemID : 0x1F97, + BeverageType.Liquor => ItemID == 0x1F9A ? ItemID : 0x1F99, + BeverageType.Milk => ItemID == 0x9AD ? ItemID : 0x9F0, + BeverageType.Wine => ItemID == 0x1F9C ? ItemID : 0x1F9B, + BeverageType.Water => ItemID is 0xFF8 or 0xFF9 or 0x1F9E ? ItemID : 0x1F9D, + _ => 0 + }; + } +} + +[SerializationGenerator(2, false)] +public abstract partial class BaseBeverage : Item, IHasQuantity +{ + private readonly int[] _swampTiles = + { + 0x9C4, 0x9EB, + 0x3D65, 0x3D65, + 0x3DC0, 0x3DD9, + 0x3DDB, 0x3DDC, + 0x3DDE, 0x3EF0, + 0x3FF6, 0x3FF6, + 0x3FFC, 0x3FFE + }; + + private static readonly Dictionary m_Table = new(); + + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Poison _poison; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Mobile _poisoner; + + [SerializableField(2, getter: "private", setter: "private")] + private BeverageType _rawContent; + + [SerializableField(3, getter: "private", setter: "private")] + private int _rawQuantity; + + public BaseBeverage() => ItemID = ComputeItemID(); + + public BaseBeverage(BeverageType type) + { + _rawContent = type; + _rawQuantity = MaxQuantity; + ItemID = ComputeItemID(); } - public class Jug : BaseBeverage + public override int LabelNumber => + IsEmpty || BaseLabelNumber == 0 ? EmptyLabelNumber : BaseLabelNumber + (int)_rawContent; + + public virtual bool ShowQuantity => MaxQuantity > 1; + public virtual bool Fillable => true; + public virtual bool Pourable => true; + + public virtual int EmptyLabelNumber => base.LabelNumber; + public virtual int BaseLabelNumber => 0; + + public abstract int MaxQuantity { get; } + + [CommandProperty(AccessLevel.GameMaster)] + public bool IsEmpty => _rawQuantity <= 0; + + [CommandProperty(AccessLevel.GameMaster)] + public bool ContainsAlcohol => !IsEmpty && _rawContent is not BeverageType.Milk and not BeverageType.Water; + + [CommandProperty(AccessLevel.GameMaster)] + public bool IsFull => _rawQuantity >= MaxQuantity; + + [CommandProperty(AccessLevel.GameMaster)] + public BeverageType Content { - [Constructible] - public Jug(BeverageType type) - : base(type) => - Weight = 1.0; - - public Jug(Serial serial) - : base(serial) + get => _rawContent; + set { - } + RawContent = value; - public override int BaseLabelNumber => 1042965; // a jug of Ale - public override int MaxQuantity => 10; - public override bool Fillable => false; + InvalidateProperties(); - public override int ComputeItemID() - { - if (!IsEmpty) + var itemID = ComputeItemID(); + + if (itemID > 0) { - return 0x9C8; - } - - return 0; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CeramicMug : BaseBeverage - { - [Constructible] - public CeramicMug() => Weight = 1.0; - - [Constructible] - public CeramicMug(BeverageType type) - : base(type) => - Weight = 1.0; - - public CeramicMug(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1042982; // a ceramic mug of Ale - public override int MaxQuantity => 1; - - public override int ComputeItemID() - { - if (ItemID >= 0x995 && ItemID <= 0x999) - { - return ItemID; - } - - if (ItemID == 0x9CA) - { - return ItemID; - } - - return 0x995; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterMug : BaseBeverage - { - [Constructible] - public PewterMug() => Weight = 1.0; - - [Constructible] - public PewterMug(BeverageType type) - : base(type) => - Weight = 1.0; - - public PewterMug(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1042994; // a pewter mug with Ale - public override int MaxQuantity => 1; - - public override int ComputeItemID() - { - if (ItemID >= 0xFFF && ItemID <= 0x1002) - { - return ItemID; - } - - return 0xFFF; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Goblet : BaseBeverage - { - [Constructible] - public Goblet() => Weight = 1.0; - - [Constructible] - public Goblet(BeverageType type) - : base(type) => - Weight = 1.0; - - public Goblet(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1043000; // a goblet of Ale - public override int MaxQuantity => 1; - - public override int ComputeItemID() - { - if (ItemID is 0x99A or 0x9B3 or 0x9BF or 0x9CB) - { - return ItemID; - } - - return 0x99A; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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 - { - [Constructible] - public GlassMug() => Weight = 1.0; - - [Constructible] - public GlassMug(BeverageType type) - : base(type) => - Weight = 1.0; - - public GlassMug(Serial serial) - : base(serial) - { - } - - public override int EmptyLabelNumber => 1022456; // mug - public override int BaseLabelNumber => 1042976; // a mug of Ale - public override int MaxQuantity => 5; - - public override int ComputeItemID() - { - if (IsEmpty) - { - return ItemID >= 0x1F81 && ItemID <= 0x1F84 ? ItemID : 0x1F81; - } - - return Content switch - { - BeverageType.Ale => ItemID == 0x9EF ? 0x9EF : 0x9EE, - BeverageType.Cider => ItemID >= 0x1F7D && ItemID <= 0x1F80 ? ItemID : 0x1F7D, - BeverageType.Liquor => ItemID >= 0x1F85 && ItemID <= 0x1F88 ? ItemID : 0x1F85, - BeverageType.Milk => ItemID >= 0x1F89 && ItemID <= 0x1F8C ? ItemID : 0x1F89, - BeverageType.Wine => ItemID >= 0x1F8D && ItemID <= 0x1F90 ? ItemID : 0x1F8D, - BeverageType.Water => ItemID >= 0x1F91 && ItemID <= 0x1F94 ? ItemID : 0x1F91, - _ => 0 - }; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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("Invalid beverage type"); - } - - 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 - { - [Constructible] - public Pitcher() => Weight = 2.0; - - [Constructible] - public Pitcher(BeverageType type) - : base(type) => - Weight = 2.0; - - public Pitcher(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1048128; // a Pitcher of Ale - public override int MaxQuantity => 5; - - public override int ComputeItemID() - { - if (IsEmpty) - { - if (ItemID is 0x9A7 or 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 is 0xFF8 or 0xFF9 or 0x1F9E) - { - return ItemID; - } - - return 0x1F9D; - } - } - - return 0; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - if (CheckType("PitcherWater") || CheckType("GlassPitcher")) - { - InternalDeserialize(reader, false); + ItemID = itemID; } else { - InternalDeserialize(reader, true); - } - - var 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("Invalid beverage type"); - } - - break; - } + Delete(); } } } - public abstract class BaseBeverage : Item, IHasQuantity + [CommandProperty(AccessLevel.GameMaster)] + public int Quantity { - private static readonly int[] m_SwampTiles = + get => _rawQuantity; + set { - 0x9C4, 0x9EB, - 0x3D65, 0x3D65, - 0x3DC0, 0x3DD9, - 0x3DDB, 0x3DDC, - 0x3DDE, 0x3EF0, - 0x3FF6, 0x3FF6, - 0x3FFC, 0x3FFE + RawQuantity = Math.Clamp(value, 0, MaxQuantity); + + InvalidateProperties(); + + var itemID = ComputeItemID(); + + if (itemID > 0) + { + ItemID = itemID; + } + else + { + Delete(); + } + } + } + + public abstract int ComputeItemID(); + + public virtual int GetQuantityDescription() + { + return (_rawQuantity * 100 / MaxQuantity) switch + { + <= 0 => 1042975, + <= 33 => 1042974, + <= 66 => 1042973, + _ => 1042972 }; + } - private static readonly Dictionary m_Table = new(); + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); - private BeverageType m_Content; - private int m_Quantity; - - public BaseBeverage() => ItemID = ComputeItemID(); - - public BaseBeverage(BeverageType type) + if (ShowQuantity) { - m_Content = type; - m_Quantity = MaxQuantity; - ItemID = ComputeItemID(); + 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; } - public BaseBeverage(Serial serial) - : base(serial) + if (!Movable && !Fillable) { - } + var house = BaseHouse.FindHouseAt(this); - public override int LabelNumber - { - get - { - var num = BaseLabelNumber; - - if (IsEmpty || num == 0) - { - return EmptyLabelNumber; - } - - return BaseLabelNumber + (int)m_Content; - } - } - - public virtual bool ShowQuantity => MaxQuantity > 1; - public virtual bool Fillable => true; - public virtual bool Pourable => true; - - public virtual int EmptyLabelNumber => base.LabelNumber; - public virtual int BaseLabelNumber => 0; - - public abstract int MaxQuantity { get; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool IsEmpty => m_Quantity <= 0; - - [CommandProperty(AccessLevel.GameMaster)] - public bool ContainsAlchohol => !IsEmpty && m_Content != BeverageType.Milk && m_Content != BeverageType.Water; - - [CommandProperty(AccessLevel.GameMaster)] - public bool IsFull => m_Quantity >= MaxQuantity; - - [CommandProperty(AccessLevel.GameMaster)] - public Poison Poison { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Poisoner { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public BeverageType Content - { - get => m_Content; - set - { - m_Content = value; - - InvalidateProperties(); - - var itemID = ComputeItemID(); - - if (itemID > 0) - { - ItemID = itemID; - } - else - { - Delete(); - } - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int Quantity - { - get => m_Quantity; - set - { - m_Quantity = Math.Clamp(value, 0, MaxQuantity); - - InvalidateProperties(); - - var itemID = ComputeItemID(); - - if (itemID > 0) - { - ItemID = itemID; - } - else - { - Delete(); - } - } - } - - public abstract int ComputeItemID(); - - public virtual int GetQuantityDescription() - { - var perc = m_Quantity * 100 / MaxQuantity; - - if (perc <= 0) - { - return 1042975; // It's empty. - } - - if (perc <= 33) - { - return 1042974; // It's nearly empty. - } - - if (perc <= 66) - { - return 1042973; // It's half full. - } - - 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) - { - var house = BaseHouse.FindHouseAt(this); - - if (house?.HasLockedDownItem(this) != true) - { - 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 (house?.HasLockedDownItem(this) != true) { if (message) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + from.SendLocalizedMessage(502946, "", 0x59); // That belongs to someone else. } return false; } - - return true; } - public virtual void Fill_OnTarget(Mobile from, object targ) + if (from.Map != Map || !from.InRange(GetWorldLocation(), 2) || !from.InLOS(this)) { - if (!IsEmpty || !Fillable || !ValidateUse(from, false)) + if (message) { - return; - } - - if (targ is BaseBeverage bev) - { - if (bev.IsEmpty || !bev.ValidateUse(from, true)) - { - return; - } - - Content = bev.Content; - Poison = bev.Poison; - Poisoner = bev.Poisoner; - - if (bev.Quantity > MaxQuantity) - { - Quantity = MaxQuantity; - bev.Quantity -= MaxQuantity; - } - else - { - Quantity += bev.Quantity; - bev.Quantity = 0; - } - } - else if (targ is BaseWaterContainer bwc) - { - if (Quantity == 0 || Content == BeverageType.Water && !IsFull) - { - var iNeed = Math.Min(MaxQuantity - Quantity, bwc.Quantity); - - if (iNeed > 0 && !bwc.IsEmpty && !IsFull) - { - bwc.Quantity -= iNeed; - Quantity += iNeed; - Content = BeverageType.Water; - - from.PlaySound(0x4E); - } - } - } - else if (targ is Item item) - { - var src = item as IWaterSource; - - if (src == null && item is AddonComponent component) - { - src = component.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; - } - - Content = BeverageType.Water; - Poison = null; - Poisoner = null; - - if (src.Quantity > MaxQuantity) - { - Quantity = MaxQuantity; - src.Quantity -= MaxQuantity; - } - else - { - Quantity += src.Quantity; - src.Quantity = 0; - } - - from.SendLocalizedMessage(1010089); // You fill the container with water. - } - else if (targ is Cow cow) - { - if (cow.TryMilk(from)) - { - Content = BeverageType.Milk; - Quantity = MaxQuantity; - from.SendLocalizedMessage(1080197); // You fill the container with milk. - } - } - else if (targ is LandTarget target) - { - var tileID = target.TileID; - - if (from is PlayerMobile player) - { - var qs = player.Quest; - - if (qs is not WitchApprenticeQuest) - { - return; - } - - var obj = qs.FindObjective(); - - if (obj?.Completed == true && obj.Ingredient == Ingredient.SwampWater) - { - var contains = false; - - for (var 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(); - } - } - } - } - } - - public virtual void Pour_OnTarget(Mobile from, object targ) - { - if (IsEmpty || !Pourable || !ValidateUse(from, false)) - { - return; - } - - if (targ is BaseBeverage bev) - { - if (!bev.ValidateUse(from, true)) - { - return; - } - - if (bev.IsFull && bev.Content == 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 = Content; - bev.Poison = Poison; - bev.Poisoner = Poisoner; - - if (Quantity > bev.MaxQuantity) - { - bev.Quantity = bev.MaxQuantity; - Quantity -= bev.MaxQuantity; - } - else - { - bev.Quantity += Quantity; - Quantity = 0; - } - - from.PlaySound(0x4E); - } - } - else if (from == targ) - { - if (from.Thirst < 20) - { - from.Thirst += 1; - } - - if (ContainsAlchohol) - { - var bac = Content switch - { - BeverageType.Ale => 1, - BeverageType.Wine => 2, - BeverageType.Cider => 3, - BeverageType.Liquor => 4, - _ => 0 - }; - - from.BAC = Math.Min(from.BAC + bac, 60); - - CheckHeaveTimer(from); - } - - from.PlaySound(Utility.RandomList(0x30, 0x2D6)); - - if (Poison != null) - { - from.ApplyPoison(Poisoner, Poison); - } - - --Quantity; - } - else if (targ is BaseWaterContainer bwc) - { - if (Content != BeverageType.Water) - { - from.SendLocalizedMessage(500842); // Can't pour that in there. - } - else if (bwc.Items.Count != 0) - { - from.SendLocalizedMessage(500841); // That has something in it. - } - else - { - var itNeeds = Math.Min(bwc.MaxQuantity - bwc.Quantity, Quantity); - - if (itNeeds > 0) - { - bwc.Quantity += itNeeds; - Quantity -= itNeeds; - - from.PlaySound(0x4E); - } - } - } - else if (targ is PlantItem item) - { - item.Pour(from, this); - } - else if (targ is AddonComponent component && - component.Addon is WaterVatEast or WaterVatSouth && - Content == BeverageType.Water) - { - if (from is PlayerMobile player) - { - if (player.Quest is SolenMatriarchQuest qs) - { - QuestObjective obj = qs.FindObjective(); - - if (obj?.Completed == false) - { - var vat = component.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) - { - var 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, Fill_OnTarget); - SendLocalizedMessageTo(from, 500837); // Fill from what? - } - else if (Pourable && ValidateUse(from, true)) - { - from.BeginTarget(-1, true, TargetFlags.None, Pour_OnTarget); - from.SendLocalizedMessage(1010086); // What do you want to use this on? - } - } - - public static bool ConsumeTotal(Container pack, BeverageType content, int quantity) => - ConsumeTotal(pack, typeof(BaseBeverage), content, quantity); - - public static bool ConsumeTotal(Container pack, Type itemType, BeverageType content, int quantity) - { - var items = pack.FindItemsByType(itemType); - - // First pass, compute total - var total = 0; - - for (var i = 0; i < items.Length; ++i) - { - if (items[i] is BaseBeverage bev && bev.Content == content && !bev.IsEmpty) - { - total += bev.Quantity; - } - } - - if (total >= quantity) - { - // We've enough, so consume it - - var need = quantity; - - for (var i = 0; i < items.Length; ++i) - { - if (items[i] is not BaseBeverage bev || bev.Content != content || bev.IsEmpty) - { - continue; - } - - var theirQuantity = bev.Quantity; - - if (theirQuantity < need) - { - bev.Quantity = 0; - need -= theirQuantity; - } - else - { - bev.Quantity -= need; - return true; - } - } + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } return false; } - public override void Serialize(IGenericWriter writer) + return true; + } + + public virtual void Fill_OnTarget(Mobile from, object targ) + { + if (!IsEmpty || !Fillable || !ValidateUse(from, false)) { - base.Serialize(writer); - - writer.Write(1); // version - - writer.Write(Poisoner); - - Poison.Serialize(Poison, writer); - writer.Write((int)m_Content); - writer.Write(m_Quantity); + return; } - protected bool CheckType(string name) => GetType().FullName == $"Server.Items.{name}"; - - public override void Deserialize(IGenericReader reader) + if (targ is BaseBeverage bev) { - InternalDeserialize(reader, true); - } - - protected void InternalDeserialize(IGenericReader reader, bool read) - { - base.Deserialize(reader); - - if (!read) + if (bev.IsEmpty || !bev.ValidateUse(from, true)) { return; } - var version = reader.ReadInt(); + Content = bev.Content; + Poison = bev.Poison; + Poisoner = bev.Poisoner; - switch (version) + if (bev.Quantity > MaxQuantity) { - case 1: - { - Poisoner = reader.ReadEntity(); - goto case 0; - } - case 0: - { - Poison = Poison.Deserialize(reader); - m_Content = (BeverageType)reader.ReadInt(); - m_Quantity = reader.ReadInt(); - break; - } + Quantity = MaxQuantity; + bev.Quantity -= MaxQuantity; + } + else + { + Quantity += bev.Quantity; + bev.Quantity = 0; } } - - public static void Initialize() + else if (targ is BaseWaterContainer bwc) { - EventSink.Login += EventSink_Login; - } - - private static void EventSink_Login(Mobile m) - { - CheckHeaveTimer(m); - } - - public static void CheckHeaveTimer(Mobile from) - { - if (from.BAC > 0 && from.Map != Map.Internal && !from.Deleted) + if (Quantity == 0 || Content == BeverageType.Water && !IsFull) { - if (m_Table.ContainsKey(from)) + var iNeed = Math.Min(MaxQuantity - Quantity, bwc.Quantity); + + if (iNeed > 0 && !bwc.IsEmpty && !IsFull) + { + bwc.Quantity -= iNeed; + Quantity += iNeed; + Content = BeverageType.Water; + + from.PlaySound(0x4E); + } + } + } + else if (targ is Item item) + { + var src = item as IWaterSource; + + if (src == null && item is AddonComponent component) + { + src = component.Addon as IWaterSource; + } + + if (src is not { 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; + } + + Content = BeverageType.Water; + Poison = null; + Poisoner = null; + + if (src.Quantity > MaxQuantity) + { + Quantity = MaxQuantity; + src.Quantity -= MaxQuantity; + } + else + { + Quantity += src.Quantity; + src.Quantity = 0; + } + + from.SendLocalizedMessage(1010089); // You fill the container with water. + } + else if (targ is Cow cow) + { + if (cow.TryMilk(from)) + { + Content = BeverageType.Milk; + Quantity = MaxQuantity; + from.SendLocalizedMessage(1080197); // You fill the container with milk. + } + } + else if (targ is LandTarget target) + { + var tileID = target.TileID; + + if (from is PlayerMobile player) + { + var qs = player.Quest; + + if (qs is not WitchApprenticeQuest) { return; } - if (from.BAC > 60) + var obj = qs.FindObjective(); + + if (obj?.Completed == true && obj.Ingredient == Ingredient.SwampWater) { - from.BAC = 60; - } + var contains = false; - Timer t = new HeaveTimer(from); - t.Start(); - - m_Table[from] = t; - } - else if (m_Table.Remove(from, out var t)) - { - t.Stop(); - - from.SendLocalizedMessage(500850); // You feel sober. - } - } - - private class HeaveTimer : Timer - { - private readonly Mobile m_Drunk; - - public HeaveTimer(Mobile drunk) - : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) - { - m_Drunk = drunk; - } - - 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) + for (var i = 0; !contains && i < _swampTiles.Length; i += 2) { - m_Drunk.BAC = 60; + contains = tileID >= _swampTiles[i] && tileID <= _swampTiles[i + 1]; } - // chance to get sober - if (Utility.Random(100) < 10) + if (contains) { - --m_Drunk.BAC; - } + Delete(); - // 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(MessageType.Regular, 0x3B2, 500849); - } - - if (m_Drunk.BAC <= 0) - { - Stop(); - m_Table.Remove(m_Drunk); - - m_Drunk.SendLocalizedMessage(500850); // You feel sober. + // You dip the container into the disgusting swamp water, collecting enough for the Hag's vile stew. + player.SendLocalizedMessage(1055035); + obj.Complete(); } } } } } + + public virtual void Pour_OnTarget(Mobile from, object targ) + { + if (IsEmpty || !Pourable || !ValidateUse(from, false)) + { + return; + } + + if (targ is BaseBeverage bev) + { + if (!bev.ValidateUse(from, true)) + { + return; + } + + if (bev.IsFull && bev.Content == 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 = Content; + bev.Poison = Poison; + bev.Poisoner = Poisoner; + + if (Quantity > bev.MaxQuantity) + { + bev.Quantity = bev.MaxQuantity; + Quantity -= bev.MaxQuantity; + } + else + { + bev.Quantity += Quantity; + Quantity = 0; + } + + from.PlaySound(0x4E); + } + } + else if (from == targ) + { + if (from.Thirst < 20) + { + from.Thirst += 1; + } + + if (ContainsAlcohol) + { + var bac = Content switch + { + BeverageType.Ale => 1, + BeverageType.Wine => 2, + BeverageType.Cider => 3, + BeverageType.Liquor => 4, + _ => 0 + }; + + from.BAC = Math.Min(from.BAC + bac, 60); + + CheckHeaveTimer(from); + } + + from.PlaySound(Utility.RandomList(0x30, 0x2D6)); + + if (Poison != null) + { + from.ApplyPoison(Poisoner, Poison); + } + + --Quantity; + } + else if (targ is BaseWaterContainer bwc) + { + if (Content != BeverageType.Water) + { + from.SendLocalizedMessage(500842); // Can't pour that in there. + } + else if (bwc.Items.Count != 0) + { + from.SendLocalizedMessage(500841); // That has something in it. + } + else + { + var itNeeds = Math.Min(bwc.MaxQuantity - bwc.Quantity, Quantity); + + if (itNeeds > 0) + { + bwc.Quantity += itNeeds; + Quantity -= itNeeds; + + from.PlaySound(0x4E); + } + } + } + else if (targ is PlantItem item) + { + item.Pour(from, this); + } + else if (targ is AddonComponent component && + component.Addon is WaterVatEast or WaterVatSouth && + Content == BeverageType.Water) + { + if (from is PlayerMobile { Quest: SolenMatriarchQuest qs }) + { + QuestObjective obj = qs.FindObjective(); + + if (obj?.Completed == false) + { + var vat = component.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) + { + var 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, Fill_OnTarget); + SendLocalizedMessageTo(from, 500837); // Fill from what? + } + else if (Pourable && ValidateUse(from, true)) + { + from.BeginTarget(-1, true, TargetFlags.None, Pour_OnTarget); + from.SendLocalizedMessage(1010086); // What do you want to use this on? + } + } + + public static bool ConsumeTotal(Container pack, BeverageType content, int quantity) => + ConsumeTotal(pack, typeof(BaseBeverage), content, quantity); + + public static bool ConsumeTotal(Container pack, Type itemType, BeverageType content, int quantity) + { + var items = pack.FindItemsByType(itemType); + + // First pass, compute total + var total = 0; + + for (var i = 0; i < items.Length; ++i) + { + if (items[i] is BaseBeverage bev && bev.Content == content && !bev.IsEmpty) + { + total += bev.Quantity; + } + } + + if (total >= quantity) + { + // We've enough, so consume it + + var need = quantity; + + for (var i = 0; i < items.Length; ++i) + { + if (items[i] is not BaseBeverage bev || bev.Content != content || bev.IsEmpty) + { + continue; + } + + var theirQuantity = bev.Quantity; + + if (theirQuantity < need) + { + bev.Quantity = 0; + need -= theirQuantity; + } + else + { + bev.Quantity -= need; + return true; + } + } + } + + return false; + } + + private void Deserialize(IGenericReader reader, int version) + { + _poison = reader.ReadPoison(); + _poisoner = reader.ReadEntity(); + _rawContent = (BeverageType)reader.ReadInt(); + _rawQuantity = reader.ReadInt(); + } + + public static void Initialize() + { + EventSink.Login += EventSink_Login; + } + + private static void EventSink_Login(Mobile m) + { + CheckHeaveTimer(m); + } + + public static void CheckHeaveTimer(Mobile from) + { + if (from.BAC > 0 && from.Map != Map.Internal && !from.Deleted) + { + if (m_Table.ContainsKey(from)) + { + return; + } + + if (from.BAC > 60) + { + from.BAC = 60; + } + + m_Table[from] = new HeaveTimer(from).Start(); + } + else if (m_Table.Remove(from, out var t)) + { + t.Stop(); + + from.SendLocalizedMessage(500850); // You feel sober. + } + } + + private class HeaveTimer : Timer + { + private readonly Mobile m_Drunk; + + public HeaveTimer(Mobile drunk) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) => + m_Drunk = drunk; + + 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 (Utility.Random(100) < 10) + { + --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(MessageType.Regular, 0x3B2, 500849); + } + + if (m_Drunk.BAC <= 0) + { + Stop(); + m_Table.Remove(m_Drunk); + + m_Drunk.SendLocalizedMessage(500850); // You feel sober. + } + } + } + } } diff --git a/Projects/UOContent/Items/Food/BeverageEmpty.cs b/Projects/UOContent/Items/Food/BeverageEmpty.cs index fb75dad41..317635af0 100644 --- a/Projects/UOContent/Items/Food/BeverageEmpty.cs +++ b/Projects/UOContent/Items/Food/BeverageEmpty.cs @@ -1,51 +1,18 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[Flippable(0x1f81, 0x1f82, 0x1f83, 0x1f84)] +[SerializationGenerator(0, false)] +public partial class Glass : Item { - [Flippable(0x1f81, 0x1f82, 0x1f83, 0x1f84)] - public class Glass : Item - { - [Constructible] - public Glass() : base(0x1f81) => Weight = 0.1; - - public Glass(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GlassBottle : Item - { - [Constructible] - public GlassBottle() : base(0xe2b) => Weight = 0.3; - - public GlassBottle(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } + [Constructible] + public Glass() : base(0x1f81) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class GlassBottle : Item +{ + [Constructible] + public GlassBottle() : base(0xe2b) => Weight = 0.3; } diff --git a/Projects/UOContent/Items/Food/Bowls.cs b/Projects/UOContent/Items/Food/Bowls.cs index d4b485d51..f080b6d62 100644 --- a/Projects/UOContent/Items/Food/Bowls.cs +++ b/Projects/UOContent/Items/Food/Bowls.cs @@ -1,540 +1,286 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class EmptyWoodenBowl : Item { - public class EmptyWoodenBowl : Item + [Constructible] + public EmptyWoodenBowl() : base(0x15F8) => Weight = 1.0; +} + +[SerializationGenerator(0, false)] +public partial class EmptyPewterBowl : Item +{ + [Constructible] + public EmptyPewterBowl() : base(0x15FD) => Weight = 1.0; +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowlOfCarrots : Food +{ + [Constructible] + public WoodenBowlOfCarrots() : base(0x15F9) { - [Constructible] - public EmptyWoodenBowl() : base(0x15F8) => Weight = 1.0; - - public EmptyWoodenBowl(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = false; + Weight = 1.0; + FillFactor = 2; } - public class EmptyPewterBowl : Item + public override bool Eat(Mobile from) { - [Constructible] - public EmptyPewterBowl() : base(0x15FD) => Weight = 1.0; - - public EmptyPewterBowl(Serial serial) : base(serial) + if (!base.Eat(from)) { + return false; } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WoodenBowlOfCarrots : Food - { - [Constructible] - public WoodenBowlOfCarrots() : base(0x15F9, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public WoodenBowlOfCarrots(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyWoodenBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WoodenBowlOfCorn : Food - { - [Constructible] - public WoodenBowlOfCorn() : base(0x15FA, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public WoodenBowlOfCorn(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyWoodenBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WoodenBowlOfLettuce : Food - { - [Constructible] - public WoodenBowlOfLettuce() : base(0x15FB, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public WoodenBowlOfLettuce(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyWoodenBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WoodenBowlOfPeas : Food - { - [Constructible] - public WoodenBowlOfPeas() : base(0x15FC, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public WoodenBowlOfPeas(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyWoodenBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterBowlOfCarrots : Food - { - [Constructible] - public PewterBowlOfCarrots() : base(0x15FE, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public PewterBowlOfCarrots(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyPewterBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterBowlOfCorn : Food - { - [Constructible] - public PewterBowlOfCorn() : base(0x15FF, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public PewterBowlOfCorn(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyPewterBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterBowlOfLettuce : Food - { - [Constructible] - public PewterBowlOfLettuce() : base(0x1600, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public PewterBowlOfLettuce(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyPewterBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterBowlOfPeas : Food - { - [Constructible] - public PewterBowlOfPeas() : base(0x1601, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public PewterBowlOfPeas(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyPewterBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterBowlOfPotatos : Food - { - [Constructible] - public PewterBowlOfPotatos() : base(0x1602, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public PewterBowlOfPotatos(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyPewterBowl()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [TypeAlias("Server.Items.EmptyLargeWoodenBowl")] - public class EmptyWoodenTub : Item - { - [Constructible] - public EmptyWoodenTub() : base(0x1605) => Weight = 2.0; - - public EmptyWoodenTub(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [TypeAlias("Server.Items.EmptyLargePewterBowl")] - public class EmptyPewterTub : Item - { - [Constructible] - public EmptyPewterTub() : base(0x1603) => Weight = 2.0; - - public EmptyPewterTub(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WoodenBowlOfStew : Food - { - [Constructible] - public WoodenBowlOfStew() : base(0x1604, 1) - { - Stackable = false; - Weight = 2.0; - FillFactor = 2; - } - - public WoodenBowlOfStew(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyWoodenTub()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WoodenBowlOfTomatoSoup : Food - { - [Constructible] - public WoodenBowlOfTomatoSoup() : base(0x1606, 1) - { - Stackable = false; - Weight = 2.0; - FillFactor = 2; - } - - public WoodenBowlOfTomatoSoup(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyWoodenTub()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + from.AddToBackpack(new EmptyWoodenBowl()); + return true; + } +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowlOfCorn : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowlOfLettuce : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowlOfPeas : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class PewterBowlOfCarrots : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class PewterBowlOfCorn : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class PewterBowlOfLettuce : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class PewterBowlOfPeas : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class PewterBowlOfPotatos : Food +{ + [Constructible] + 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; + } +} + +[TypeAlias("Server.Items.EmptyLargeWoodenBowl")] +[SerializationGenerator(0, false)] +public partial class EmptyWoodenTub : Item +{ + [Constructible] + public EmptyWoodenTub() : base(0x1605) => Weight = 2.0; +} + +[TypeAlias("Server.Items.EmptyLargePewterBowl")] +[SerializationGenerator(0, false)] +public partial class EmptyPewterTub : Item +{ + [Constructible] + public EmptyPewterTub() : base(0x1603) => Weight = 2.0; +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowlOfStew : Food +{ + [Constructible] + 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; + } +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowlOfTomatoSoup : Food +{ + [Constructible] + 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; } } diff --git a/Projects/UOContent/Items/Food/Chocolatiering.cs b/Projects/UOContent/Items/Food/Chocolatiering.cs index 90dd2b640..af588e5a7 100644 --- a/Projects/UOContent/Items/Food/Chocolatiering.cs +++ b/Projects/UOContent/Items/Food/Chocolatiering.cs @@ -1,263 +1,110 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CocoaLiquor : Item { - public class CocoaLiquor : Item - { - [Constructible] - public CocoaLiquor() - : base(0x103F) => - Hue = 0x46A; + [Constructible] + public CocoaLiquor() : base(0x103F) => Hue = 0x46A; - public CocoaLiquor(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1080007; // Cocoa liquor - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SackOfSugar : Item - { - [Constructible] - public SackOfSugar(int amount = 1) - : base(0x1039) - { - Hue = 0x461; - Stackable = true; - Amount = amount; - } - - public SackOfSugar(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1080003; // Sack of sugar - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CocoaButter : Item - { - [Constructible] - public CocoaButter() - : base(0x1044) => - Hue = 0x457; - - public CocoaButter(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1080005; // Cocoa butter - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Vanilla : Item - { - [Constructible] - public Vanilla(int amount = 1) - : base(0xE2A) - { - Hue = 0x462; - Stackable = true; - Amount = amount; - } - - public Vanilla(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1080009; // Vanilla - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CocoaPulp : Item - { - [Constructible] - public CocoaPulp(int amount = 1) - : base(0xF7C) - { - Hue = 0x219; - Stackable = true; - Amount = amount; - } - - public CocoaPulp(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1080530; // cocoa pulp - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DarkChocolate : CandyCane - { - [Constructible] - public DarkChocolate() - : base(0xF10) - { - Hue = 0x465; - LootType = LootType.Regular; - } - - public DarkChocolate(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1079994; // Dark chocolate - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class MilkChocolate : CandyCane - { - [Constructible] - public MilkChocolate() - : base(0xF18) - { - Hue = 0x461; - LootType = LootType.Regular; - } - - public MilkChocolate(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1079995; // Milk chocolate - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WhiteChocolate : CandyCane - { - [Constructible] - public WhiteChocolate() - : base(0xF11) - { - Hue = 0x47E; - LootType = LootType.Regular; - } - - public WhiteChocolate(Serial serial) - : base(serial) - { - } - - public override int LabelNumber => 1079996; // White chocolate - public override double DefaultWeight => 1.0; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } + public override int LabelNumber => 1080007; // Cocoa liquor + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class SackOfSugar : Item +{ + [Constructible] + public SackOfSugar(int amount = 1) : base(0x1039) + { + Hue = 0x461; + Stackable = true; + Amount = amount; + } + + public override int LabelNumber => 1080003; // Sack of sugar + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class CocoaButter : Item +{ + [Constructible] + public CocoaButter() : base(0x1044) => Hue = 0x457; + + public override int LabelNumber => 1080005; // Cocoa butter + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class Vanilla : Item +{ + [Constructible] + public Vanilla(int amount = 1) : base(0xE2A) + { + Hue = 0x462; + Stackable = true; + Amount = amount; + } + + public override int LabelNumber => 1080009; // Vanilla + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class CocoaPulp : Item +{ + [Constructible] + public CocoaPulp(int amount = 1) : base(0xF7C) + { + Hue = 0x219; + Stackable = true; + Amount = amount; + } + + public override int LabelNumber => 1080530; // cocoa pulp + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class DarkChocolate : CandyCane +{ + [Constructible] + public DarkChocolate() : base(0xF10) + { + Hue = 0x465; + LootType = LootType.Regular; + } + + public override int LabelNumber => 1079994; // Dark chocolate + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class MilkChocolate : CandyCane +{ + [Constructible] + public MilkChocolate() : base(0xF18) + { + Hue = 0x461; + LootType = LootType.Regular; + } + + public override int LabelNumber => 1079995; // Milk chocolate + public override double DefaultWeight => 1.0; +} + +[SerializationGenerator(0, false)] +public partial class WhiteChocolate : CandyCane +{ + [Constructible] + public WhiteChocolate() : base(0xF11) + { + Hue = 0x47E; + LootType = LootType.Regular; + } + + public override int LabelNumber => 1079996; // White chocolate + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Food/CookableFood.cs b/Projects/UOContent/Items/Food/CookableFood.cs index 0582491b9..d7711abfb 100644 --- a/Projects/UOContent/Items/Food/CookableFood.cs +++ b/Projects/UOContent/Items/Food/CookableFood.cs @@ -1,745 +1,293 @@ -using System; +using ModernUO.Serialization; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class CookableFood : Item { - public abstract class CookableFood : Item + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _cookingLevel; + + public CookableFood(int itemID, int cookingLevel) : base(itemID) => _cookingLevel = cookingLevel; + + public abstract Food Cook(); + + public static bool IsHeatSource(object targeted) { - public CookableFood(int itemID, int cookingLevel) : base(itemID) => CookingLevel = cookingLevel; + int itemID; - public CookableFood(Serial serial) : base(serial) + if (targeted is Item item) { + itemID = item.ItemID; } - - [CommandProperty(AccessLevel.GameMaster)] - public int CookingLevel { get; set; } - - public abstract Food Cook(); - - public override void Serialize(IGenericWriter writer) + else if (targeted is StaticTarget target) { - base.Serialize(writer); - - writer.Write(1); // version - // Version 1 - writer.Write(CookingLevel); + itemID = target.ItemID; } - - public override void Deserialize(IGenericReader reader) + else { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 1: - { - CookingLevel = reader.ReadInt(); - - break; - } - } - } - - public static bool IsHeatSource(object targeted) - { - int itemID; - - if (targeted is Item item) - { - itemID = item.ItemID; - } - else if (targeted is StaticTarget target) - { - itemID = target.ItemID; - } - else - { - return false; - } - - if (itemID >= 0xDE3 && itemID <= 0xDE9) - { - return true; // Campfire - } - - if (itemID >= 0x461 && itemID <= 0x48E) - { - return true; // Sandstone oven/fireplace - } - - if (itemID >= 0x92B && itemID <= 0x96C) - { - return true; // Stone oven/fireplace - } - - if (itemID == 0xFAC) - { - return true; // Firepit - } - - if (itemID >= 0x184A && itemID <= 0x184C) - { - return true; // Heating stand (left) - } - - if (itemID >= 0x184E && itemID <= 0x1850) - { - return true; // Heating stand (right) - } - - if (itemID >= 0x398C && itemID <= 0x399F) - { - return true; // Fire field - } - return false; } - private class InternalTarget : Target + if (itemID >= 0xDE3 && itemID <= 0xDE9) { - private readonly 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 (IsHeatSource(targeted)) - { - if (from.BeginAction()) - { - from.PlaySound(0x225); - - m_Item.Consume(); - - var 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 readonly CookableFood m_CookableFood; - private readonly Mobile m_From; - private readonly Map m_Map; - private readonly IPoint3D m_Point; - - 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(); - - 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)) - { - var 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 - { - [Constructible] - public RawRibs(int amount = 1) : base(0x9F1, 10) - { - Weight = 1.0; - Stackable = true; - Amount = amount; + return true; // Campfire } - public RawRibs(Serial serial) : base(serial) + if (itemID >= 0x461 && itemID <= 0x48E) { + return true; // Sandstone oven/fireplace } - public override void Serialize(IGenericWriter writer) + if (itemID >= 0x92B && itemID <= 0x96C) { - base.Serialize(writer); - - writer.Write(0); // version + return true; // Stone oven/fireplace } - public override void Deserialize(IGenericReader reader) + if (itemID == 0xFAC) { - base.Deserialize(reader); - - var version = reader.ReadInt(); + return true; // Firepit } - public override Food Cook() => new Ribs(); - } - - // ********** RawLambLeg ********** - public class RawLambLeg : CookableFood - { - [Constructible] - public RawLambLeg(int amount = 1) : base(0x1609, 10) + if (itemID >= 0x184A && itemID <= 0x184C) { - Stackable = true; - Amount = amount; + return true; // Heating stand (left) } - public RawLambLeg(Serial serial) : base(serial) + if (itemID >= 0x184E && itemID <= 0x1850) { + return true; // Heating stand (right) } - public override void Serialize(IGenericWriter writer) + if (itemID >= 0x398C && itemID <= 0x399F) { - base.Serialize(writer); - - writer.Write(1); // version + return true; // Fire field } - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 1) - { - Weight = -1; - } - } - - public override Food Cook() => new LambLeg(); - } - - // ********** RawChickenLeg ********** - public class RawChickenLeg : CookableFood - { - [Constructible] - public RawChickenLeg() : base(0x1607, 10) - { - Weight = 1.0; - Stackable = true; - } - - public RawChickenLeg(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new ChickenLeg(); - } - - // ********** RawBird ********** - public class RawBird : CookableFood - { - [Constructible] - public RawBird(int amount = 1) : base(0x9B9, 10) - { - Weight = 1.0; - Stackable = true; - Amount = amount; - } - - public RawBird(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new CookedBird(); - } - - // ********** UnbakedPeachCobbler ********** - public class UnbakedPeachCobbler : CookableFood - { - [Constructible] - public UnbakedPeachCobbler() : base(0x1042, 25) => Weight = 1.0; - - public UnbakedPeachCobbler(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041335; // unbaked peach cobbler - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new PeachCobbler(); - } - - // ********** UnbakedFruitPie ********** - public class UnbakedFruitPie : CookableFood - { - [Constructible] - public UnbakedFruitPie() : base(0x1042, 25) => Weight = 1.0; - - public UnbakedFruitPie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041334; // unbaked fruit pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new FruitPie(); - } - - // ********** UnbakedMeatPie ********** - public class UnbakedMeatPie : CookableFood - { - [Constructible] - public UnbakedMeatPie() : base(0x1042, 25) => Weight = 1.0; - - public UnbakedMeatPie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041338; // unbaked meat pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new MeatPie(); - } - - // ********** UnbakedPumpkinPie ********** - public class UnbakedPumpkinPie : CookableFood - { - [Constructible] - public UnbakedPumpkinPie() : base(0x1042, 25) => Weight = 1.0; - - public UnbakedPumpkinPie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041342; // unbaked pumpkin pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new PumpkinPie(); - } - - // ********** UnbakedApplePie ********** - public class UnbakedApplePie : CookableFood - { - [Constructible] - public UnbakedApplePie() : base(0x1042, 25) => Weight = 1.0; - - public UnbakedApplePie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041336; // unbaked apple pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new ApplePie(); - } - - // ********** UncookedCheesePizza ********** - [TypeAlias("Server.Items.UncookedPizza")] - public class UncookedCheesePizza : CookableFood - { - [Constructible] - public UncookedCheesePizza() : base(0x1083, 20) => Weight = 1.0; - - public UncookedCheesePizza(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041341; // uncooked cheese pizza - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (ItemID == 0x1040) - { - ItemID = 0x1083; - } - - if (Hue == 51) - { - Hue = 0; - } - } - - public override Food Cook() => new CheesePizza(); - } - - // ********** UncookedSausagePizza ********** - public class UncookedSausagePizza : CookableFood - { - [Constructible] - public UncookedSausagePizza() : base(0x1083, 20) => Weight = 1.0; - - public UncookedSausagePizza(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041337; // uncooked sausage pizza - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new SausagePizza(); - } - - // ********** UnbakedQuiche ********** - public class UnbakedQuiche : CookableFood - { - [Constructible] - public UnbakedQuiche() : base(0x1042, 25) => Weight = 1.0; - - public UnbakedQuiche(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041339; // unbaked quiche - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new Quiche(); - } - - // ********** Eggs ********** - public class Eggs : CookableFood - { - [Constructible] - public Eggs(int amount = 1) : base(0x9B5, 15) - { - Weight = 1.0; - Stackable = true; - Amount = amount; - } - - public Eggs(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version < 1) - { - Stackable = true; - - if (Weight == 0.5) - { - Weight = 1.0; - } - } - } - - public override Food Cook() => new FriedEggs(); - } - - // ********** BrightlyColoredEggs ********** - public class BrightlyColoredEggs : CookableFood - { - [Constructible] - public BrightlyColoredEggs() : base(0x9B5, 15) - { - Weight = 0.5; - Hue = 3 + Utility.Random(20) * 5; - } - - public BrightlyColoredEggs(Serial serial) : base(serial) - { - } - - public override string DefaultName => "brightly colored eggs"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new FriedEggs(); - } - - // ********** EasterEggs ********** - public class EasterEggs : CookableFood - { - [Constructible] - public EasterEggs() : base(0x9B5, 15) - { - Weight = 0.5; - Hue = 3 + Utility.Random(20) * 5; - } - - public EasterEggs(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1016105; // Easter Eggs - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new FriedEggs(); - } - - // ********** CookieMix ********** - public class CookieMix : CookableFood - { - [Constructible] - public CookieMix() : base(0x103F, 20) => Weight = 1.0; - - public CookieMix(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new Cookies(); - } - - // ********** CakeMix ********** - public class CakeMix : CookableFood - { - [Constructible] - public CakeMix() : base(0x103F, 40) => Weight = 1.0; - - public CakeMix(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041002; // cake mix - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - public override Food Cook() => new Cake(); - } - - public class RawFishSteak : CookableFood - { - [Constructible] - public RawFishSteak(int amount = 1) : base(0x097A, 10) - { - Stackable = true; - Amount = amount; - } - - public RawFishSteak(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override Food Cook() => new FishSteak(); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + return false; } } + +[SerializationGenerator(0, false)] +public partial class RawRibs : CookableFood +{ + [Constructible] + public RawRibs(int amount = 1) : base(0x9F1, 10) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public override Food Cook() => new Ribs(); +} + +[SerializationGenerator(0, false)] +public partial class RawLambLeg : CookableFood +{ + [Constructible] + public RawLambLeg(int amount = 1) : base(0x1609, 10) + { + Stackable = true; + Amount = amount; + } + + public override Food Cook() => new LambLeg(); +} + +[SerializationGenerator(0, false)] +public partial class RawChickenLeg : CookableFood +{ + [Constructible] + public RawChickenLeg() : base(0x1607, 10) + { + Weight = 1.0; + Stackable = true; + } + + public override Food Cook() => new ChickenLeg(); +} + +[SerializationGenerator(0, false)] +public partial class RawBird : CookableFood +{ + [Constructible] + public RawBird(int amount = 1) : base(0x9B9, 10) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public override Food Cook() => new CookedBird(); +} + +[SerializationGenerator(0, false)] +public partial class UnbakedPeachCobbler : CookableFood +{ + [Constructible] + public UnbakedPeachCobbler() : base(0x1042, 25) => Weight = 1.0; + + public override int LabelNumber => 1041335; // unbaked peach cobbler + + public override Food Cook() => new PeachCobbler(); +} + +[SerializationGenerator(0, false)] +public partial class UnbakedFruitPie : CookableFood +{ + [Constructible] + public UnbakedFruitPie() : base(0x1042, 25) => Weight = 1.0; + + public override int LabelNumber => 1041334; // unbaked fruit pie + + public override Food Cook() => new FruitPie(); +} + +[SerializationGenerator(0, false)] +public partial class UnbakedMeatPie : CookableFood +{ + [Constructible] + public UnbakedMeatPie() : base(0x1042, 25) => Weight = 1.0; + + public override int LabelNumber => 1041338; // unbaked meat pie + + public override Food Cook() => new MeatPie(); +} + +[SerializationGenerator(0, false)] +public partial class UnbakedPumpkinPie : CookableFood +{ + [Constructible] + public UnbakedPumpkinPie() : base(0x1042, 25) => Weight = 1.0; + + public override int LabelNumber => 1041342; // unbaked pumpkin pie + + public override Food Cook() => new PumpkinPie(); +} + +[SerializationGenerator(0, false)] +public partial class UnbakedApplePie : CookableFood +{ + [Constructible] + public UnbakedApplePie() : base(0x1042, 25) => Weight = 1.0; + + public override int LabelNumber => 1041336; // unbaked apple pie + + public override Food Cook() => new ApplePie(); +} + +[TypeAlias("Server.Items.UncookedPizza")] +[SerializationGenerator(0, false)] +public partial class UncookedCheesePizza : CookableFood +{ + [Constructible] + public UncookedCheesePizza() : base(0x1083, 20) => Weight = 1.0; + + public override int LabelNumber => 1041341; // uncooked cheese pizza + + public override Food Cook() => new CheesePizza(); +} + +[SerializationGenerator(0, false)] +public partial class UncookedSausagePizza : CookableFood +{ + [Constructible] + public UncookedSausagePizza() : base(0x1083, 20) => Weight = 1.0; + + public override int LabelNumber => 1041337; // uncooked sausage pizza + + public override Food Cook() => new SausagePizza(); +} + +[SerializationGenerator(0, false)] +public partial class UnbakedQuiche : CookableFood +{ + [Constructible] + public UnbakedQuiche() : base(0x1042, 25) => Weight = 1.0; + + public override int LabelNumber => 1041339; // unbaked quiche + + public override Food Cook() => new Quiche(); +} + +[SerializationGenerator(0, false)] +public partial class Eggs : CookableFood +{ + [Constructible] + public Eggs(int amount = 1) : base(0x9B5, 15) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public override Food Cook() => new FriedEggs(); +} + +[SerializationGenerator(0, false)] +public partial class BrightlyColoredEggs : CookableFood +{ + [Constructible] + public BrightlyColoredEggs() : base(0x9B5, 15) + { + Weight = 0.5; + Hue = 3 + Utility.Random(20) * 5; + } + + public override string DefaultName => "brightly colored eggs"; + + public override Food Cook() => new FriedEggs(); +} + +[SerializationGenerator(0, false)] +public partial class EasterEggs : CookableFood +{ + [Constructible] + public EasterEggs() : base(0x9B5, 15) + { + Weight = 0.5; + Hue = 3 + Utility.Random(20) * 5; + } + + public override int LabelNumber => 1016105; // Easter Eggs + + public override Food Cook() => new FriedEggs(); +} + +[SerializationGenerator(0, false)] +public partial class CookieMix : CookableFood +{ + [Constructible] + public CookieMix() : base(0x103F, 20) => Weight = 1.0; + + public override Food Cook() => new Cookies(); +} + +[SerializationGenerator(0, false)] +public partial class CakeMix : CookableFood +{ + [Constructible] + public CakeMix() : base(0x103F, 40) => Weight = 1.0; + + public override int LabelNumber => 1041002; // cake mix + + public override Food Cook() => new Cake(); +} + +[SerializationGenerator(0, false)] +public partial class RawFishSteak : CookableFood +{ + [Constructible] + public RawFishSteak(int amount = 1) : base(0x097A, 10) + { + Stackable = true; + Amount = amount; + } + + public override double DefaultWeight => 0.1; + + public override Food Cook() => new FishSteak(); +} diff --git a/Projects/UOContent/Items/Food/Cooking.cs b/Projects/UOContent/Items/Food/Cooking.cs index 1d74d028c..e665c490f 100644 --- a/Projects/UOContent/Items/Food/Cooking.cs +++ b/Projects/UOContent/Items/Food/Cooking.cs @@ -1,534 +1,147 @@ using System; +using ModernUO.Serialization; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Dough : Item { - // ********** Dough ********** - public class Dough : Item + [Constructible] + public Dough() : base(0x103d) { - [Constructible] - public Dough() : base(0x103d) - { - Stackable = Core.ML; - Weight = 1.0; - } - - public Dough(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - private class InternalTarget : Target - { - private readonly 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 not Item targetItem || targetItem.Deleted) - { - return; - } - - m_Item.Consume(); - - if (targeted is Eggs) - { - from.AddToBackpack(new UnbakedQuiche()); - from.AddToBackpack(new Eggshells()); - } - else if (targeted is CheeseWheel) - { - from.AddToBackpack(new CheesePizza()); - } - else if (targeted is Sausage) - { - from.AddToBackpack(new SausagePizza()); - } - else if (targeted is Apple) - { - from.AddToBackpack(new UnbakedApplePie()); - } - else if (targeted is Peach) - { - from.AddToBackpack(new UnbakedPeachCobbler()); - } - else - { - return; - } - - targetItem.Consume(); - } - } - } - - // ********** SweetDough ********** - public class SweetDough : Item - { - [Constructible] - public SweetDough() : base(0x103d) - { - Stackable = Core.ML; - Weight = 1.0; - Hue = 150; - } - - public SweetDough(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041340; // sweet dough - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Hue == 51) - { - Hue = 150; - } - } - - private class InternalTarget : Target - { - private readonly 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; - } - - m_Item.Consume(); - - if (targeted is BowlFlour flour) - { - flour.Delete(); - - from.AddToBackpack(new CakeMix()); - } - else if (targeted is Campfire campfire) - { - from.PlaySound(0x225); - var t = new InternalTimer(from, campfire); - t.Start(); - } - } - - private class InternalTimer : Timer - { - private readonly Campfire m_Campfire; - private readonly Mobile m_From; - - 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 - { - [Constructible] - public JarHoney() : base(0x9ec) - { - Weight = 1.0; - Stackable = true; - } - - public JarHoney(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - Stackable = true; - } - - /*public override void OnDoubleClick( Mobile from ) - { - if (!Movable) - return; - - from.Target = new InternalTarget( this ); - }*/ - - private class InternalTarget : Target - { - private readonly 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; - } - - m_Item.Consume(); - - if (targeted is Dough dough) - { - dough.Consume(); - - from.AddToBackpack(new SweetDough()); - } - - if (targeted is BowlFlour flour) - { - flour.Delete(); - - from.AddToBackpack(new CookieMix()); - } - } - } - } - - // ********** BowlFlour ********** - public class BowlFlour : Item - { - [Constructible] - public BowlFlour() : base(0xa1e) => Weight = 1.0; - - public BowlFlour(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - // ********** WoodenBowl ********** - public class WoodenBowl : Item - { - [Constructible] - public WoodenBowl() : base(0x15f8) => Weight = 1.0; - - public WoodenBowl(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - // ********** PitcherWater ********** - /*public class PitcherWater : Item - { - [Constructible] - public PitcherWater() : base(Utility.Random( 0x1f9d, 2 )) - { + Stackable = Core.ML; Weight = 1.0; - } + } +} - public PitcherWater( Serial serial ) : base( serial ) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize( writer ); - - writer.Write( (int) 0 ); // version - } - - public override void Deserialize(IGenericReader 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 +[SerializationGenerator(0, false)] +public partial class SweetDough : Item +{ + [Constructible] + public SweetDough() : base(0x103d) { - private int m_Quantity; + Stackable = Core.ML; + Weight = 1.0; + Hue = 150; + } - [Constructible] - public SackFlour() : base(0x1039) - { - Weight = 5.0; - m_Quantity = 20; - } + public override int LabelNumber => 1041340; // sweet dough +} - public SackFlour(Serial serial) : base(serial) - { - } +[SerializationGenerator(0, false)] +public partial class JarHoney : Item +{ + [Constructible] + public JarHoney() : base(0x9ec) + { + Weight = 1.0; + Stackable = true; + } +} - [CommandProperty(AccessLevel.GameMaster)] - public int Quantity +[SerializationGenerator(0, false)] +public partial class BowlFlour : Item +{ + [Constructible] + public BowlFlour() : base(0xa1e) => Weight = 1.0; +} + +[SerializationGenerator(0, false)] +public partial class WoodenBowl : Item +{ + [Constructible] + public WoodenBowl() : base(0x15f8) => Weight = 1.0; +} + +[TypeAlias("Server.Items.SackFlourOpen")] +[SerializationGenerator(0, false)] +public partial class SackFlour : Item, IHasQuantity +{ + private int _quantity; + + [Constructible] + public SackFlour() : base(0x1039) + { + Weight = 5.0; + _quantity = 20; + } + + [CommandProperty(AccessLevel.GameMaster)] + [SerializableField(0)] + public int Quantity + { + get => _quantity; + set { - get => m_Quantity; - set + _quantity = Math.Min(20, Math.Max(0, value)); + + if (_quantity == 0) { - m_Quantity = Math.Min(20, Math.Max(0, value)); - - if (m_Quantity == 0) - { - Delete(); - } - else if (m_Quantity < 20 && ItemID is 0x1039 or 0x1045) - { - ++ItemID; - } + Delete(); } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(2); // version - - writer.Write(m_Quantity); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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 is 0x1039 or 0x1045) + else if (_quantity < 20 && ItemID is 0x1039 or 0x1045) { ++ItemID; } + + this.MarkDirty(); } } - // ********** Eggshells ********** - public class Eggshells : Item + public override void OnDoubleClick(Mobile from) { - [Constructible] - public Eggshells() : base(0x9b4) => Weight = 0.5; - - public Eggshells(Serial serial) : base(serial) + if (Movable && ItemID is 0x1039 or 0x1045) { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WheatSheaf : Item - { - [Constructible] - public WheatSheaf(int amount = 1) : base(7869) - { - Weight = 1.0; - Stackable = true; - Amount = amount; - } - - public WheatSheaf(Serial serial) : base(serial) - { - } - - public override void OnDoubleClick(Mobile from) - { - if (!Movable) - { - return; - } - - from.BeginTarget(4, false, TargetFlags.None, OnTarget); - } - - public virtual void OnTarget(Mobile from, object obj) - { - if (obj is AddonComponent addon) - { - obj = addon.Addon; - } - - if (obj is IFlourMill mill) - { - var needs = mill.MaxFlour - mill.CurFlour; - - if (needs > Amount) - { - needs = Amount; - } - - mill.CurFlour += needs; - Consume(needs); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); + ++ItemID; + } + } +} + +[SerializationGenerator(0, false)] +public partial class Eggshells : Item +{ + [Constructible] + public Eggshells() : base(0x9b4) => Weight = 0.5; +} + +[SerializationGenerator(0, false)] +public partial class WheatSheaf : Item +{ + [Constructible] + public WheatSheaf(int amount = 1) : base(7869) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public override void OnDoubleClick(Mobile from) + { + if (Movable) + { + from.BeginTarget(4, false, TargetFlags.None, OnTarget); + } + } + + public virtual void OnTarget(Mobile from, object obj) + { + if (obj is AddonComponent addon) + { + obj = addon.Addon; + } + + if (obj is IFlourMill mill) + { + var needs = mill.MaxFlour - mill.CurFlour; + + if (needs > Amount) + { + needs = Amount; + } + + mill.CurFlour += needs; + Consume(needs); } } } diff --git a/Projects/UOContent/Items/Food/Food.cs b/Projects/UOContent/Items/Food/Food.cs index cc86ebe68..42afe10e4 100644 --- a/Projects/UOContent/Items/Food/Food.cs +++ b/Projects/UOContent/Items/Food/Food.cs @@ -1,1121 +1,519 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class Food : Item { - public abstract class Food : Item + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Mobile _poisoner; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Poison _poison; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _fillFactor; + + public Food(int itemID, int amount = 1) : base(itemID) { - public Food(int itemID, int amount = 1) : base(itemID) + Stackable = true; + Amount = amount; + FillFactor = 1; + } + + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); + + if (from.Alive) { - Stackable = true; - Amount = amount; - FillFactor = 1; + list.Add(new EatEntry(from, this)); + } + } + + public override void OnDoubleClick(Mobile from) + { + if (!Movable) + { + return; } - public Food(Serial serial) : base(serial) + if (from.InRange(GetWorldLocation(), 1)) { + Eat(from); } + } - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Poisoner { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Poison Poison { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int FillFactor { get; set; } - - public override void GetContextMenuEntries(Mobile from, List list) + public override bool CanStackWith(Item dropped) + { + if (dropped is Food food) { - base.GetContextMenuEntries(from, list); - - if (from.Alive) + if (Poison != food.Poison || Poisoner != food.Poisoner) { - list.Add(new EatEntry(from, this)); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (!Movable) - { - return; - } - - if (from.InRange(GetWorldLocation(), 1)) - { - Eat(from); - } - } - - public virtual bool Eat(Mobile from) - { - // Fill the Mobile with FillFactor - if (CheckHunger(from)) - { - // 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 (Poison != null) - { - from.ApplyPoison(Poisoner, Poison); - } - - Consume(); - - return true; - } - - return false; - } - - public virtual bool CheckHunger(Mobile from) => FillHunger(from, FillFactor); - - public static bool FillHunger(Mobile from, int fillFactor) - { - if (from.Hunger >= 20) - { - from.SendLocalizedMessage(500867); // You are simply too full to eat any more! return false; } + } + return base.CanStackWith(dropped); + } - var iHunger = from.Hunger + fillFactor; - if (from.Stam < from.StamMax) + public virtual bool Eat(Mobile from) + { + // Fill the Mobile with FillFactor + if (CheckHunger(from)) + { + // Play a random "eat" sound + from.PlaySound(Utility.Random(0x3A, 3)); + + if (from.Body.IsHuman && !from.Mounted) { - from.Stam += Utility.Random(6, 3) + fillFactor / 5; + from.Animate(34, 5, 1, true, false, 0); } - if (iHunger >= 20) + if (Poison != null) { - 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. - } + from.ApplyPoison(Poisoner, Poison); } + Consume(); return true; } - public override void Serialize(IGenericWriter writer) + return false; + } + + public virtual bool CheckHunger(Mobile from) => FillHunger(from, FillFactor); + + public static bool FillHunger(Mobile from, int fillFactor) + { + if (from.Hunger >= 20) { - base.Serialize(writer); - - writer.Write(4); // version - - writer.Write(Poisoner); - - Poison.Serialize(Poison, writer); - writer.Write(FillFactor); + from.SendLocalizedMessage(500867); // You are simply too full to eat any more! + return false; } - public override void Deserialize(IGenericReader reader) + var iHunger = from.Hunger + fillFactor; + + if (from.Stam < from.StamMax) { - base.Deserialize(reader); + from.Stam += Utility.Random(6, 3) + fillFactor / 5; + } - var version = reader.ReadInt(); + if (iHunger >= 20) + { + from.Hunger = 20; + from.SendLocalizedMessage(500872); // You manage to eat the food, but you are stuffed! + } + else + { + from.Hunger = iHunger; - switch (version) + if (iHunger < 5) { - case 1: - { - Poison = reader.ReadInt() switch - { - 0 => null, - 1 => Poison.Lesser, - 2 => Poison.Regular, - 3 => Poison.Greater, - 4 => Poison.Deadly, - _ => Poison - }; - - break; - } - case 2: - { - Poison = Poison.Deserialize(reader); - break; - } - case 3: - { - Poison = Poison.Deserialize(reader); - FillFactor = reader.ReadInt(); - break; - } - case 4: - { - Poisoner = reader.ReadEntity(); - goto case 3; - } + 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. } } - } - public class BreadLoaf : Food - { - [Constructible] - public BreadLoaf(int amount = 1) : base(0x103B, amount) - { - Weight = 1.0; - FillFactor = 3; - } - - public BreadLoaf(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Bacon : Food - { - [Constructible] - public Bacon(int amount = 1) : base(0x979, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Bacon(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SlabOfBacon : Food - { - [Constructible] - public SlabOfBacon(int amount = 1) : base(0x976, amount) - { - Weight = 1.0; - FillFactor = 3; - } - - public SlabOfBacon(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class FishSteak : Food - { - [Constructible] - public FishSteak(int amount = 1) : base(0x97B, amount) => FillFactor = 3; - - public FishSteak(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CheeseWheel : Food - { - [Constructible] - public CheeseWheel(int amount = 1) : base(0x97E, amount) => FillFactor = 3; - - public CheeseWheel(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CheeseWedge : Food - { - [Constructible] - public CheeseWedge(int amount = 1) : base(0x97D, amount) => FillFactor = 3; - - public CheeseWedge(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CheeseSlice : Food - { - [Constructible] - public CheeseSlice(int amount = 1) : base(0x97C, amount) => FillFactor = 1; - - public CheeseSlice(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class FrenchBread : Food - { - [Constructible] - public FrenchBread(int amount = 1) : base(0x98C, amount) - { - Weight = 2.0; - FillFactor = 3; - } - - public FrenchBread(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class FriedEggs : Food - { - [Constructible] - public FriedEggs(int amount = 1) : base(0x9B6, amount) - { - Weight = 1.0; - FillFactor = 4; - } - - public FriedEggs(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CookedBird : Food - { - [Constructible] - public CookedBird(int amount = 1) : base(0x9B7, amount) - { - Weight = 1.0; - FillFactor = 5; - } - - public CookedBird(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class RoastPig : Food - { - [Constructible] - public RoastPig(int amount = 1) : base(0x9BB, amount) - { - Weight = 45.0; - FillFactor = 20; - } - - public RoastPig(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Sausage : Food - { - [Constructible] - public Sausage(int amount = 1) : base(0x9C0, amount) - { - Weight = 1.0; - FillFactor = 4; - } - - public Sausage(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Ham : Food - { - [Constructible] - public Ham(int amount = 1) : base(0x9C9, amount) - { - Weight = 1.0; - FillFactor = 5; - } - - public Ham(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Cake : Food - { - [Constructible] - public Cake() : base(0x9E9, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 10; - } - - public Cake(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Ribs : Food - { - [Constructible] - public Ribs(int amount = 1) : base(0x9F2, amount) - { - Weight = 1.0; - FillFactor = 5; - } - - public Ribs(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Cookies : Food - { - [Constructible] - public Cookies() : base(0x160b, 1) - { - Stackable = Core.ML; - Weight = 1.0; - FillFactor = 4; - } - - public Cookies(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Muffins : Food - { - [Constructible] - public Muffins() : base(0x9eb, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 4; - } - - public Muffins(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [TypeAlias("Server.Items.Pizza")] - public class CheesePizza : Food - { - [Constructible] - public CheesePizza() : base(0x1040, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 6; - } - - public CheesePizza(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1044516; // cheese pizza - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SausagePizza : Food - { - [Constructible] - public SausagePizza() : base(0x1040, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 6; - } - - public SausagePizza(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1044517; // sausage pizza - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class FruitPie : Food - { - [Constructible] - public FruitPie() : base(0x1041, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 5; - } - - public FruitPie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041346; // baked fruit pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class MeatPie : Food - { - [Constructible] - public MeatPie() : base(0x1041, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 5; - } - - public MeatPie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041347; // baked meat pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PumpkinPie : Food - { - [Constructible] - public PumpkinPie() : base(0x1041, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 5; - } - - public PumpkinPie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041348; // baked pumpkin pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class ApplePie : Food - { - [Constructible] - public ApplePie() : base(0x1041, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 5; - } - - public ApplePie(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041343; // baked apple pie - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PeachCobbler : Food - { - [Constructible] - public PeachCobbler() : base(0x1041, 1) - { - Stackable = false; - Weight = 1.0; - FillFactor = 5; - } - - public PeachCobbler(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041344; // baked peach cobbler - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Quiche : Food - { - [Constructible] - public Quiche() : base(0x1041, 1) - { - Stackable = Core.ML; - Weight = 1.0; - FillFactor = 5; - } - - public Quiche(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1041345; // baked quiche - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class LambLeg : Food - { - [Constructible] - public LambLeg(int amount = 1) : base(0x160a, amount) - { - Weight = 2.0; - FillFactor = 5; - } - - public LambLeg(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class ChickenLeg : Food - { - [Constructible] - public ChickenLeg(int amount = 1) : base(0x1608, amount) - { - Weight = 1.0; - FillFactor = 4; - } - - public ChickenLeg(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xC74, 0xC75)] - public class HoneydewMelon : Food - { - [Constructible] - public HoneydewMelon(int amount = 1) : base(0xC74, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public HoneydewMelon(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xC64, 0xC65)] - public class YellowGourd : Food - { - [Constructible] - public YellowGourd(int amount = 1) : base(0xC64, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public YellowGourd(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xC66, 0xC67)] - public class GreenGourd : Food - { - [Constructible] - public GreenGourd(int amount = 1) : base(0xC66, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public GreenGourd(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xC7F, 0xC81)] - public class EarOfCorn : Food - { - [Constructible] - public EarOfCorn(int amount = 1) : base(0xC81, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public EarOfCorn(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Turnip : Food - { - [Constructible] - public Turnip(int amount = 1) : base(0xD3A, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Turnip(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SheafOfHay : Item - { - [Constructible] - public SheafOfHay() : base(0xF36) => Weight = 10.0; - - public SheafOfHay(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + return true; } } + +[SerializationGenerator(0, false)] +public partial class BreadLoaf : Food +{ + [Constructible] + public BreadLoaf(int amount = 1) : base(0x103B, amount) + { + Weight = 1.0; + FillFactor = 3; + } +} + +[SerializationGenerator(0, false)] +public partial class Bacon : Food +{ + [Constructible] + public Bacon(int amount = 1) : base(0x979, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class SlabOfBacon : Food +{ + [Constructible] + public SlabOfBacon(int amount = 1) : base(0x976, amount) + { + Weight = 1.0; + FillFactor = 3; + } +} + +[SerializationGenerator(0, false)] +public partial class FishSteak : Food +{ + [Constructible] + public FishSteak(int amount = 1) : base(0x97B, amount) => FillFactor = 3; + + public override double DefaultWeight => 0.1; +} + +[SerializationGenerator(0, false)] +public partial class CheeseWheel : Food +{ + [Constructible] + public CheeseWheel(int amount = 1) : base(0x97E, amount) => FillFactor = 3; + + public override double DefaultWeight => 0.1; +} + +[SerializationGenerator(0, false)] +public partial class CheeseWedge : Food +{ + [Constructible] + public CheeseWedge(int amount = 1) : base(0x97D, amount) => FillFactor = 3; + + public override double DefaultWeight => 0.1; +} + +[SerializationGenerator(0, false)] +public partial class CheeseSlice : Food +{ + [Constructible] + public CheeseSlice(int amount = 1) : base(0x97C, amount) => FillFactor = 1; + + public override double DefaultWeight => 0.1; +} + +[SerializationGenerator(0, false)] +public partial class FrenchBread : Food +{ + [Constructible] + public FrenchBread(int amount = 1) : base(0x98C, amount) + { + Weight = 2.0; + FillFactor = 3; + } +} + +[SerializationGenerator(0, false)] +public partial class FriedEggs : Food +{ + [Constructible] + public FriedEggs(int amount = 1) : base(0x9B6, amount) + { + Weight = 1.0; + FillFactor = 4; + } +} + +[SerializationGenerator(0, false)] +public partial class CookedBird : Food +{ + [Constructible] + public CookedBird(int amount = 1) : base(0x9B7, amount) + { + Weight = 1.0; + FillFactor = 5; + } +} + +[SerializationGenerator(0, false)] +public partial class RoastPig : Food +{ + [Constructible] + public RoastPig(int amount = 1) : base(0x9BB, amount) + { + Weight = 45.0; + FillFactor = 20; + } +} + +[SerializationGenerator(0, false)] +public partial class Sausage : Food +{ + [Constructible] + public Sausage(int amount = 1) : base(0x9C0, amount) + { + Weight = 1.0; + FillFactor = 4; + } +} + +[SerializationGenerator(0, false)] +public partial class Ham : Food +{ + [Constructible] + public Ham(int amount = 1) : base(0x9C9, amount) + { + Weight = 1.0; + FillFactor = 5; + } +} + +[SerializationGenerator(0, false)] +public partial class Cake : Food +{ + [Constructible] + public Cake() : base(0x9E9) + { + Stackable = false; + Weight = 1.0; + FillFactor = 10; + } +} + +[SerializationGenerator(0, false)] +public partial class Ribs : Food +{ + [Constructible] + public Ribs(int amount = 1) : base(0x9F2, amount) + { + Weight = 1.0; + FillFactor = 5; + } +} + +[SerializationGenerator(0, false)] +public partial class Cookies : Food +{ + [Constructible] + public Cookies() : base(0x160b) + { + Stackable = Core.ML; + Weight = 1.0; + FillFactor = 4; + } +} + +[SerializationGenerator(0, false)] +public partial class Muffins : Food +{ + [Constructible] + public Muffins() : base(0x9eb) + { + Stackable = false; + Weight = 1.0; + FillFactor = 4; + } +} + +[TypeAlias("Server.Items.Pizza")] +[SerializationGenerator(0, false)] +public partial class CheesePizza : Food +{ + [Constructible] + public CheesePizza() : base(0x1040) + { + Stackable = false; + Weight = 1.0; + FillFactor = 6; + } + + public override int LabelNumber => 1044516; // cheese pizza +} + +[SerializationGenerator(0, false)] +public partial class SausagePizza : Food +{ + [Constructible] + public SausagePizza() : base(0x1040) + { + Stackable = false; + Weight = 1.0; + FillFactor = 6; + } + + public override int LabelNumber => 1044517; // sausage pizza +} + +[SerializationGenerator(0, false)] +public partial class FruitPie : Food +{ + [Constructible] + public FruitPie() : base(0x1041) + { + Stackable = false; + Weight = 1.0; + FillFactor = 5; + } + + public override int LabelNumber => 1041346; // baked fruit pie +} + +[SerializationGenerator(0, false)] +public partial class MeatPie : Food +{ + [Constructible] + public MeatPie() : base(0x1041) + { + Stackable = false; + Weight = 1.0; + FillFactor = 5; + } + + public override int LabelNumber => 1041347; // baked meat pie +} + +[SerializationGenerator(0, false)] +public partial class PumpkinPie : Food +{ + [Constructible] + public PumpkinPie() : base(0x1041) + { + Stackable = false; + Weight = 1.0; + FillFactor = 5; + } + + public override int LabelNumber => 1041348; // baked pumpkin pie +} + +[SerializationGenerator(0, false)] +public partial class ApplePie : Food +{ + [Constructible] + public ApplePie() : base(0x1041) + { + Stackable = false; + Weight = 1.0; + FillFactor = 5; + } + + public override int LabelNumber => 1041343; // baked apple pie +} + +[SerializationGenerator(0, false)] +public partial class PeachCobbler : Food +{ + [Constructible] + public PeachCobbler() : base(0x1041) + { + Stackable = false; + Weight = 1.0; + FillFactor = 5; + } + + public override int LabelNumber => 1041344; // baked peach cobbler +} + +[SerializationGenerator(0, false)] +public partial class Quiche : Food +{ + [Constructible] + public Quiche() : base(0x1041) + { + Stackable = Core.ML; + Weight = 1.0; + FillFactor = 5; + } + + public override int LabelNumber => 1041345; // baked quiche +} + +[SerializationGenerator(0, false)] +public partial class LambLeg : Food +{ + [Constructible] + public LambLeg(int amount = 1) : base(0x160a, amount) + { + Weight = 2.0; + FillFactor = 5; + } +} + +[SerializationGenerator(0, false)] +public partial class ChickenLeg : Food +{ + [Constructible] + public ChickenLeg(int amount = 1) : base(0x1608, amount) + { + Weight = 1.0; + FillFactor = 4; + } +} + +[Flippable(0xC74, 0xC75)] +[SerializationGenerator(0, false)] +public partial class HoneydewMelon : Food +{ + [Constructible] + public HoneydewMelon(int amount = 1) : base(0xC74, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xC64, 0xC65)] +[SerializationGenerator(0, false)] +public partial class YellowGourd : Food +{ + [Constructible] + public YellowGourd(int amount = 1) : base(0xC64, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xC66, 0xC67)] +[SerializationGenerator(0, false)] +public partial class GreenGourd : Food +{ + [Constructible] + public GreenGourd(int amount = 1) : base(0xC66, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xC7F, 0xC81)] +[SerializationGenerator(0, false)] +public partial class EarOfCorn : Food +{ + [Constructible] + public EarOfCorn(int amount = 1) : base(0xC81, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Turnip : Food +{ + [Constructible] + public Turnip(int amount = 1) : base(0xD3A, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class SheafOfHay : Item +{ + [Constructible] + public SheafOfHay() : base(0xF36) => Weight = 10.0; +} diff --git a/Projects/UOContent/Items/Food/Fruits.cs b/Projects/UOContent/Items/Food/Fruits.cs index 7f6c377fb..9971f1d62 100644 --- a/Projects/UOContent/Items/Food/Fruits.cs +++ b/Projects/UOContent/Items/Food/Fruits.cs @@ -1,563 +1,228 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class FruitBasket : Food { - public class FruitBasket : Food + [Constructible] + public FruitBasket() : base(0x993) { - [Constructible] - public FruitBasket() : base(0x993, 1) - { - 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(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Weight = 2.0; + FillFactor = 5; + Stackable = false; } - [Flippable(0x171f, 0x1720)] - public class Banana : Food + public override bool Eat(Mobile from) { - [Constructible] - public Banana(int amount = 1) : base(0x171f, amount) + if (!base.Eat(from)) { - Weight = 1.0; - FillFactor = 1; + return false; } - public Banana(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0x1721, 0x1722)] - public class Bananas : Food - { - [Constructible] - public Bananas(int amount = 1) : base(0x1721, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Bananas(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SplitCoconut : Food - { - [Constructible] - public SplitCoconut(int amount = 1) : base(0x1725, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public SplitCoconut(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Lemon : Food - { - [Constructible] - public Lemon(int amount = 1) : base(0x1728, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Lemon(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Lemons : Food - { - [Constructible] - public Lemons(int amount = 1) : base(0x1729, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Lemons(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Lime : Food - { - [Constructible] - public Lime(int amount = 1) : base(0x172a, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Lime(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Limes : Food - { - [Constructible] - public Limes(int amount = 1) : base(0x172B, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Limes(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Coconut : Food - { - [Constructible] - public Coconut(int amount = 1) : base(0x1726, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Coconut(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class OpenCoconut : Food - { - [Constructible] - public OpenCoconut(int amount = 1) : base(0x1723, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public OpenCoconut(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Dates : Food - { - [Constructible] - public Dates(int amount = 1) : base(0x1727, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Dates(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Grapes : Food - { - [Constructible] - public Grapes(int amount = 1) : base(0x9D1, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Grapes(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Peach : Food - { - [Constructible] - public Peach(int amount = 1) : base(0x9D2, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Peach(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Pear : Food - { - [Constructible] - public Pear(int amount = 1) : base(0x994, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Pear(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Apple : Food - { - [Constructible] - public Apple(int amount = 1) : base(0x9D0, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Apple(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Watermelon : Food - { - [Constructible] - public Watermelon(int amount = 1) : base(0xC5C, amount) - { - Weight = 5.0; - FillFactor = 5; - } - - public Watermelon(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version < 1) - { - if (FillFactor == 2) - { - FillFactor = 5; - } - - if (Weight == 2.0) - { - Weight = 5.0; - } - } - } - } - - public class SmallWatermelon : Food - { - [Constructible] - public SmallWatermelon(int amount = 1) : base(0xC5D, amount) - { - Weight = 5.0; - FillFactor = 5; - } - - public SmallWatermelon(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xc72, 0xc73)] - public class Squash : Food - { - [Constructible] - public Squash(int amount = 1) : base(0xc72, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Squash(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xc79, 0xc7a)] - public class Cantaloupe : Food - { - [Constructible] - public Cantaloupe(int amount = 1) : base(0xc79, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Cantaloupe(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + from.AddToBackpack(new Basket()); + return true; + } +} + +[Flippable(0x171f, 0x1720)] +[SerializationGenerator(0, false)] +public partial class Banana : Food +{ + [Constructible] + public Banana(int amount = 1) : base(0x171f, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0x1721, 0x1722)] +[SerializationGenerator(0, false)] +public partial class Bananas : Food +{ + [Constructible] + public Bananas(int amount = 1) : base(0x1721, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class SplitCoconut : Food +{ + [Constructible] + public SplitCoconut(int amount = 1) : base(0x1725, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Lemon : Food +{ + [Constructible] + public Lemon(int amount = 1) : base(0x1728, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Lemons : Food +{ + [Constructible] + public Lemons(int amount = 1) : base(0x1729, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Lime : Food +{ + [Constructible] + public Lime(int amount = 1) : base(0x172a, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Limes : Food +{ + [Constructible] + public Limes(int amount = 1) : base(0x172B, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Coconut : Food +{ + [Constructible] + public Coconut(int amount = 1) : base(0x1726, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class OpenCoconut : Food +{ + [Constructible] + public OpenCoconut(int amount = 1) : base(0x1723, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Dates : Food +{ + [Constructible] + public Dates(int amount = 1) : base(0x1727, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Grapes : Food +{ + [Constructible] + public Grapes(int amount = 1) : base(0x9D1, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Peach : Food +{ + [Constructible] + public Peach(int amount = 1) : base(0x9D2, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Pear : Food +{ + [Constructible] + public Pear(int amount = 1) : base(0x994, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Apple : Food +{ + [Constructible] + public Apple(int amount = 1) : base(0x9D0, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[SerializationGenerator(0, false)] +public partial class Watermelon : Food +{ + [Constructible] + public Watermelon(int amount = 1) : base(0xC5C, amount) + { + Weight = 5.0; + FillFactor = 5; + } +} + +[SerializationGenerator(0, false)] +public partial class SmallWatermelon : Food +{ + [Constructible] + public SmallWatermelon(int amount = 1) : base(0xC5D, amount) + { + Weight = 5.0; + FillFactor = 5; + } +} + +[Flippable(0xc72, 0xc73)] +[SerializationGenerator(0, false)] +public partial class Squash : Food +{ + [Constructible] + public Squash(int amount = 1) : base(0xc72, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xc79, 0xc7a)] +[SerializationGenerator(0, false)] +public partial class Cantaloupe : Food +{ + [Constructible] + public Cantaloupe(int amount = 1) : base(0xc79, amount) + { + Weight = 1.0; + FillFactor = 1; } } diff --git a/Projects/UOContent/Items/Food/Vegetables.cs b/Projects/UOContent/Items/Food/Vegetables.cs index 3c45febff..9824d23b3 100644 --- a/Projects/UOContent/Items/Food/Vegetables.cs +++ b/Projects/UOContent/Items/Food/Vegetables.cs @@ -1,188 +1,74 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[Flippable(0xc77, 0xc78)] +[SerializationGenerator(0, false)] +public partial class Carrot : Food { - [Flippable(0xc77, 0xc78)] - public class Carrot : Food + [Constructible] + public Carrot(int amount = 1) : base(0xc78, amount) { - [Constructible] - public Carrot(int amount = 1) : base(0xc78, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Carrot(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xc7b, 0xc7c)] - public class Cabbage : Food - { - [Constructible] - public Cabbage(int amount = 1) : base(0xc7b, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Cabbage(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xc6d, 0xc6e)] - public class Onion : Food - { - [Constructible] - public Onion(int amount = 1) : base(0xc6d, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Onion(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xc70, 0xc71)] - public class Lettuce : Food - { - [Constructible] - public Lettuce(int amount = 1) : base(0xc70, amount) - { - Weight = 1.0; - FillFactor = 1; - } - - public Lettuce(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [Flippable(0xC6A, 0xC6B)] - public class Pumpkin : Food - { - [Constructible] - public Pumpkin(int amount = 1) : base(0xC6A, amount) - { - Weight = 1.0; - FillFactor = 8; - } - - public Pumpkin(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version < 1) - { - if (FillFactor == 4) - { - FillFactor = 8; - } - - if (Weight == 5.0) - { - Weight = 1.0; - } - } - } - } - - public class SmallPumpkin : Food - { - [Constructible] - public SmallPumpkin(int amount = 1) : base(0xC6C, amount) - { - Weight = 1.0; - FillFactor = 8; - } - - public SmallPumpkin(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xc7b, 0xc7c)] +[SerializationGenerator(0, false)] +public partial class Cabbage : Food +{ + [Constructible] + public Cabbage(int amount = 1) : base(0xc7b, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xc6d, 0xc6e)] +[SerializationGenerator(0, false)] +public partial class Onion : Food +{ + [Constructible] + public Onion(int amount = 1) : base(0xc6d, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xc70, 0xc71)] +[SerializationGenerator(0, false)] +public partial class Lettuce : Food +{ + [Constructible] + public Lettuce(int amount = 1) : base(0xc70, amount) + { + Weight = 1.0; + FillFactor = 1; + } +} + +[Flippable(0xC6A, 0xC6B)] +[SerializationGenerator(0, false)] +public partial class Pumpkin : Food +{ + [Constructible] + public Pumpkin(int amount = 1) : base(0xC6A, amount) + { + Weight = 1.0; + FillFactor = 8; + } +} + +[SerializationGenerator(0, false)] +public partial class SmallPumpkin : Food +{ + [Constructible] + public SmallPumpkin(int amount = 1) : base(0xC6C, amount) + { + Weight = 1.0; + FillFactor = 8; } } diff --git a/Projects/UOContent/Items/Games/Backgammon.cs b/Projects/UOContent/Items/Games/Backgammon.cs index 836a62786..5b6a71b77 100644 --- a/Projects/UOContent/Items/Games/Backgammon.cs +++ b/Projects/UOContent/Items/Games/Backgammon.cs @@ -1,51 +1,37 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[Flippable(0xE1C, 0xFAD)] +[SerializationGenerator(0, false)] +public partial class Backgammon : BaseBoard { - [Flippable(0xE1C, 0xFAD)] - public class Backgammon : BaseBoard + [Constructible] + public Backgammon() : base(0xE1C) { - [Constructible] - public Backgammon() : base(0xE1C) + } + + public override void CreatePieces() + { + for (var 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); } - public Backgammon(Serial serial) : base(serial) + for (var i = 0; i < 3; i++) { + CreatePiece(new PieceBlackChecker(this), 108, 17 * i + 6); + CreatePiece(new PieceWhiteChecker(this), 108, 17 * i + 153); } - public override void CreatePieces() + for (var i = 0; i < 2; i++) { - for (var 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 (var i = 0; i < 3; i++) - { - CreatePiece(new PieceBlackChecker(this), 108, 17 * i + 6); - CreatePiece(new PieceWhiteChecker(this), 108, 17 * i + 153); - } - - for (var i = 0; i < 2; i++) - { - CreatePiece(new PieceWhiteChecker(this), 223, 17 * i + 6); - CreatePiece(new PieceBlackChecker(this), 223, 17 * i + 170); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); + CreatePiece(new PieceWhiteChecker(this), 223, 17 * i + 6); + CreatePiece(new PieceBlackChecker(this), 223, 17 * i + 170); } } } diff --git a/Projects/UOContent/Items/Games/BaseBoard.cs b/Projects/UOContent/Items/Games/BaseBoard.cs index 8426bcb84..be9194b7a 100644 --- a/Projects/UOContent/Items/Games/BaseBoard.cs +++ b/Projects/UOContent/Items/Games/BaseBoard.cs @@ -1,145 +1,127 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Multis; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(2, false)] +public abstract partial class BaseBoard : Container, ISecurable { - public abstract class BaseBoard : Container, ISecurable + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private SecureLevel _level; + + public BaseBoard(int itemID) : base(itemID) { - public BaseBoard(int itemID) : base(itemID) + CreatePieces(); + + Weight = 5.0; + } + + public override bool DisplaysContent => false; // Do not display (x items, y stones) + + public override bool IsDecoContainer => false; + + public override TimeSpan DecayTime => TimeSpan.FromDays(1.0); + + public abstract void CreatePieces(); + + public void Reset() + { + for (var i = Items.Count - 1; i >= 0; --i) { - CreatePieces(); - - Weight = 5.0; - } - - public BaseBoard(Serial serial) : base(serial) - { - } - - public override bool DisplaysContent => false; // Do not display (x items, y stones) - - public override bool IsDecoContainer => false; - - public override TimeSpan DecayTime => TimeSpan.FromDays(1.0); - - [CommandProperty(AccessLevel.GameMaster)] - public SecureLevel Level { get; set; } - - public abstract void CreatePieces(); - - public void Reset() - { - for (var i = Items.Count - 1; i >= 0; --i) + if (i < Items.Count) { - 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); + } + + private void Deserialize(IGenericReader reader, int version) + { + if (version == 1) + { + Level = (SecureLevel)reader.ReadInt(); + } + } + + public override bool OnDragDrop(Mobile from, Item dropped) => + dropped is BasePiece piece && piece.Board == this && base.OnDragDrop(from, dropped); + + public override bool OnDragDropInto(Mobile from, Item dropped, Point3D point) + { + if (dropped is BasePiece piece && piece.Board == this && base.OnDragDropInto(from, dropped, point)) + { + if (RootParent == from) + { + from.SendSound(0x127, GetWorldLocation()); + } + else + { + Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); + + foreach (var state in GetClientsInRange(2)) { - Items[i].Delete(); + OutgoingEffectPackets.CreateSoundEffect(buffer, 0x127, GetWorldLocation()); + state.Send(buffer); } } - CreatePieces(); + return true; } - public void CreatePiece(BasePiece piece, int x, int y) + return false; + } + + public override void GetContextMenuEntries(Mobile from, List list) + { + base.GetContextMenuEntries(from, list); + + if (ValidateDefault(from, this)) { - AddItem(piece); - piece.Location = new Point3D(x, y, 0); + list.Add(new DefaultEntry(from, this)); } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(1); // version + SetSecureLevelEntry.AddTo(from, this, list); + } - writer.Write((int)Level); + public static bool ValidateDefault(Mobile from, BaseBoard board) => + !board.Deleted && (from.AccessLevel >= AccessLevel.GameMaster || from.Alive && + (board.IsChildOf(from.Backpack) || board.RootParent is not Mobile && + board.Map == from.Map && from.InRange(board.GetWorldLocation(), 1) && + BaseHouse.FindHouseAt(board)?.IsOwner(from) == true)); + + public class DefaultEntry : ContextMenuEntry + { + private readonly BaseBoard m_Board; + private readonly Mobile m_From; + + public DefaultEntry(Mobile from, BaseBoard board) : base( + 6162, + from.AccessLevel >= AccessLevel.GameMaster ? -1 : 1 + ) + { + m_From = from; + m_Board = board; } - public override void Deserialize(IGenericReader reader) + public override void OnClick() { - base.Deserialize(reader); - var version = reader.ReadInt(); - - if (version == 1) + if (ValidateDefault(m_From, m_Board)) { - Level = (SecureLevel)reader.ReadInt(); - } - - if (Weight == 1.0) - { - Weight = 5.0; - } - } - - public override bool OnDragDrop(Mobile from, Item dropped) => - dropped is BasePiece piece && piece.Board == this && base.OnDragDrop(from, dropped); - - public override bool OnDragDropInto(Mobile from, Item dropped, Point3D point) - { - if (dropped is BasePiece piece && piece.Board == this && base.OnDragDropInto(from, dropped, point)) - { - if (RootParent == from) - { - from.SendSound(0x127, GetWorldLocation()); - } - else - { - Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); - - foreach (var state in GetClientsInRange(2)) - { - OutgoingEffectPackets.CreateSoundEffect(buffer, 0x127, GetWorldLocation()); - state.Send(buffer); - } - } - - return true; - } - - 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) => - !board.Deleted && (from.AccessLevel >= AccessLevel.GameMaster || from.Alive && - (board.IsChildOf(from.Backpack) || board.RootParent is not Mobile && - board.Map == from.Map && from.InRange(board.GetWorldLocation(), 1) && - BaseHouse.FindHouseAt(board)?.IsOwner(from) == true)); - - public class DefaultEntry : ContextMenuEntry - { - private readonly BaseBoard m_Board; - private readonly Mobile m_From; - - 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 (ValidateDefault(m_From, m_Board)) - { - m_Board.Reset(); - } + m_Board.Reset(); } } } diff --git a/Projects/UOContent/Items/Games/BasePiece.cs b/Projects/UOContent/Items/Games/BasePiece.cs index a1ef5347a..f80234898 100644 --- a/Projects/UOContent/Items/Games/BasePiece.cs +++ b/Projects/UOContent/Items/Games/BasePiece.cs @@ -1,89 +1,67 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class BasePiece : Item { - public class BasePiece : Item + [SerializableField(0)] + private BaseBoard _board; + + public BasePiece(int itemID, BaseBoard board) : base(itemID) => _board = board; + + public override bool IsVirtualItem => true; + + public override bool CanTarget => false; + + [AfterDeserialization(false)] + private void AfterDeserialization() { - public BasePiece(int itemID, BaseBoard board) : base(itemID) => Board = board; - - public BasePiece(Serial serial) : base(serial) + if (Board == null || Parent == null) { + Delete(); } - - public BaseBoard Board { get; set; } - - public override bool IsVirtualItem => true; - - public override bool CanTarget => false; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - writer.Write(Board); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - Board = (BaseBoard)reader.ReadEntity(); - - if (Board == null || Parent == null) - { - Delete(); - } - - break; - } - } - } - - public override void OnSingleClick(Mobile from) - { - if (Board?.Deleted != false) - { - Delete(); - } - else if (!IsChildOf(Board)) - { - Board.DropItem(this); - } - else - { - base.OnSingleClick(from); - } - } - - public override bool OnDragLift(Mobile from) - { - if (Board?.Deleted != false) - { - Delete(); - return false; - } - - if (!IsChildOf(Board)) - { - Board.DropItem(this); - return false; - } - - return true; - } - - public override bool DropToMobile(Mobile from, Mobile target, Point3D p) => false; - - public override bool DropToItem(Mobile from, Item target, Point3D p) => - target == Board && p.X != -1 && p.Y != -1 && base.DropToItem(from, target, p); - - public override bool DropToWorld(Mobile from, Point3D p) => false; - - public override int GetLiftSound(Mobile from) => -1; } + + public override void OnSingleClick(Mobile from) + { + if (Board?.Deleted != false) + { + Delete(); + } + else if (!IsChildOf(Board)) + { + Board.DropItem(this); + } + else + { + base.OnSingleClick(from); + } + } + + public override bool OnDragLift(Mobile from) + { + if (Board?.Deleted != false) + { + Delete(); + return false; + } + + if (!IsChildOf(Board)) + { + Board.DropItem(this); + return false; + } + + return true; + } + + public override bool DropToMobile(Mobile from, Mobile target, Point3D p) => false; + + public override bool DropToItem(Mobile from, Item target, Point3D p) => + target == Board && p.X != -1 && p.Y != -1 && base.DropToItem(from, target, p); + + public override bool DropToWorld(Mobile from, Point3D p) => false; + + public override int GetLiftSound(Mobile from) => -1; } diff --git a/Projects/UOContent/Items/Games/CheckerBoard.cs b/Projects/UOContent/Items/Games/CheckerBoard.cs index 815666784..84b4a5daa 100644 --- a/Projects/UOContent/Items/Games/CheckerBoard.cs +++ b/Projects/UOContent/Items/Games/CheckerBoard.cs @@ -1,41 +1,27 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CheckerBoard : BaseBoard { - public class CheckerBoard : BaseBoard + [Constructible] + public CheckerBoard() : base(0xFA6) { - [Constructible] - public CheckerBoard() : base(0xFA6) - { - } + } - public CheckerBoard(Serial serial) : base(serial) - { - } + public override int LabelNumber => 1016449; // a checker board - public override int LabelNumber => 1016449; // a checker board - - public override void CreatePieces() + public override void CreatePieces() + { + for (var i = 0; i < 4; i++) { - for (var 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 override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); + 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); } } } diff --git a/Projects/UOContent/Items/Games/CheckersPieces.cs b/Projects/UOContent/Items/Games/CheckersPieces.cs index 1030b60c8..288d65517 100644 --- a/Projects/UOContent/Items/Games/CheckersPieces.cs +++ b/Projects/UOContent/Items/Games/CheckersPieces.cs @@ -1,52 +1,23 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class PieceWhiteChecker : BasePiece { - public class PieceWhiteChecker : BasePiece + public PieceWhiteChecker(BaseBoard board) : base(0x3584, board) { - public PieceWhiteChecker(BaseBoard board) : base(0x3584, board) - { - } - - public PieceWhiteChecker(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white checker"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } } - public class PieceBlackChecker : BasePiece - { - public PieceBlackChecker(BaseBoard board) : base(0x358B, board) - { - } - - public PieceBlackChecker(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black checker"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } + public override string DefaultName => "white checker"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackChecker : BasePiece +{ + public PieceBlackChecker(BaseBoard board) : base(0x358B, board) + { + } + + public override string DefaultName => "black checker"; } diff --git a/Projects/UOContent/Items/Games/ChessPieces.cs b/Projects/UOContent/Items/Games/ChessPieces.cs index 01b76c21e..6da1afde2 100644 --- a/Projects/UOContent/Items/Games/ChessPieces.cs +++ b/Projects/UOContent/Items/Games/ChessPieces.cs @@ -1,302 +1,123 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class PieceWhiteKing : BasePiece { - public class PieceWhiteKing : BasePiece + public PieceWhiteKing(BaseBoard board) : base(0x3587, board) { - public PieceWhiteKing(BaseBoard board) : base(0x3587, board) - { - } - - public PieceWhiteKing(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white king"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } } - public class PieceBlackKing : BasePiece - { - public PieceBlackKing(BaseBoard board) : base(0x358E, board) - { - } - - public PieceBlackKing(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black king"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceWhiteQueen : BasePiece - { - public PieceWhiteQueen(BaseBoard board) : base(0x358A, board) - { - } - - public PieceWhiteQueen(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white queen"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceBlackQueen : BasePiece - { - public PieceBlackQueen(BaseBoard board) : base(0x3591, board) - { - } - - public PieceBlackQueen(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black queen"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceWhiteRook : BasePiece - { - public PieceWhiteRook(BaseBoard board) : base(0x3586, board) - { - } - - public PieceWhiteRook(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white rook"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceBlackRook : BasePiece - { - public PieceBlackRook(BaseBoard board) : base(0x358D, board) - { - } - - public PieceBlackRook(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black rook"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceWhiteBishop : BasePiece - { - public PieceWhiteBishop(BaseBoard board) : base(0x3585, board) - { - } - - public PieceWhiteBishop(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white bishop"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceBlackBishop : BasePiece - { - public PieceBlackBishop(BaseBoard board) : base(0x358C, board) - { - } - - public PieceBlackBishop(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black bishop"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceWhiteKnight : BasePiece - { - public PieceWhiteKnight(BaseBoard board) : base(0x3588, board) - { - } - - public PieceWhiteKnight(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white knight"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceBlackKnight : BasePiece - { - public PieceBlackKnight(BaseBoard board) : base(0x358F, board) - { - } - - public PieceBlackKnight(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black knight"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceWhitePawn : BasePiece - { - public PieceWhitePawn(BaseBoard board) : base(0x3589, board) - { - } - - public PieceWhitePawn(Serial serial) : base(serial) - { - } - - public override string DefaultName => "white pawn"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } - - public class PieceBlackPawn : BasePiece - { - public PieceBlackPawn(BaseBoard board) : base(0x3590, board) - { - } - - public PieceBlackPawn(Serial serial) : base(serial) - { - } - - public override string DefaultName => "black pawn"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } + public override string DefaultName => "white king"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackKing : BasePiece +{ + public PieceBlackKing(BaseBoard board) : base(0x358E, board) + { + } + + public override string DefaultName => "black king"; +} + +[SerializationGenerator(0, false)] +public partial class PieceWhiteQueen : BasePiece +{ + public PieceWhiteQueen(BaseBoard board) : base(0x358A, board) + { + } + + public override string DefaultName => "white queen"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackQueen : BasePiece +{ + public PieceBlackQueen(BaseBoard board) : base(0x3591, board) + { + } + + public override string DefaultName => "black queen"; +} + +[SerializationGenerator(0, false)] +public partial class PieceWhiteRook : BasePiece +{ + public PieceWhiteRook(BaseBoard board) : base(0x3586, board) + { + } + + public override string DefaultName => "white rook"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackRook : BasePiece +{ + public PieceBlackRook(BaseBoard board) : base(0x358D, board) + { + } + + public override string DefaultName => "black rook"; +} + +[SerializationGenerator(0, false)] +public partial class PieceWhiteBishop : BasePiece +{ + public PieceWhiteBishop(BaseBoard board) : base(0x3585, board) + { + } + + public override string DefaultName => "white bishop"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackBishop : BasePiece +{ + public PieceBlackBishop(BaseBoard board) : base(0x358C, board) + { + } + + public override string DefaultName => "black bishop"; +} + +[SerializationGenerator(0, false)] +public partial class PieceWhiteKnight : BasePiece +{ + public PieceWhiteKnight(BaseBoard board) : base(0x3588, board) + { + } + + public override string DefaultName => "white knight"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackKnight : BasePiece +{ + public PieceBlackKnight(BaseBoard board) : base(0x358F, board) + { + } + + public override string DefaultName => "black knight"; +} + +[SerializationGenerator(0, false)] +public partial class PieceWhitePawn : BasePiece +{ + public PieceWhitePawn(BaseBoard board) : base(0x3589, board) + { + } + + public override string DefaultName => "white pawn"; +} + +[SerializationGenerator(0, false)] +public partial class PieceBlackPawn : BasePiece +{ + public PieceBlackPawn(BaseBoard board) : base(0x3590, board) + { + } + + public override string DefaultName => "black pawn"; } diff --git a/Projects/UOContent/Items/Games/Chessboard.cs b/Projects/UOContent/Items/Games/Chessboard.cs index 5ac9e4141..b09f89ee6 100644 --- a/Projects/UOContent/Items/Games/Chessboard.cs +++ b/Projects/UOContent/Items/Games/Chessboard.cs @@ -1,66 +1,52 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Chessboard : BaseBoard { - public class Chessboard : BaseBoard + [Constructible] + public Chessboard() : base(0xFA6) { - [Constructible] - public Chessboard() : base(0xFA6) + } + + public override int LabelNumber => 1016450; // a chessboard + + public override void CreatePieces() + { + for (var i = 0; i < 8; i++) { + CreatePiece(new PieceBlackPawn(this), 67, 25 * i + 17); + CreatePiece(new PieceWhitePawn(this), 192, 25 * i + 17); } - public Chessboard(Serial serial) : base(serial) - { - } + // Rook + CreatePiece(new PieceBlackRook(this), 42, 5); + CreatePiece(new PieceBlackRook(this), 42, 180); - public override int LabelNumber => 1016450; // a chessboard + CreatePiece(new PieceWhiteRook(this), 216, 5); + CreatePiece(new PieceWhiteRook(this), 216, 180); - public override void CreatePieces() - { - for (var i = 0; i < 8; i++) - { - CreatePiece(new PieceBlackPawn(this), 67, 25 * i + 17); - CreatePiece(new PieceWhitePawn(this), 192, 25 * i + 17); - } + // Knight + CreatePiece(new PieceBlackKnight(this), 42, 30); + CreatePiece(new PieceBlackKnight(this), 42, 155); - // Rook - CreatePiece(new PieceBlackRook(this), 42, 5); - CreatePiece(new PieceBlackRook(this), 42, 180); + CreatePiece(new PieceWhiteKnight(this), 216, 30); + CreatePiece(new PieceWhiteKnight(this), 216, 155); - CreatePiece(new PieceWhiteRook(this), 216, 5); - CreatePiece(new PieceWhiteRook(this), 216, 180); + // Bishop + CreatePiece(new PieceBlackBishop(this), 42, 55); + CreatePiece(new PieceBlackBishop(this), 42, 130); - // Knight - CreatePiece(new PieceBlackKnight(this), 42, 30); - CreatePiece(new PieceBlackKnight(this), 42, 155); + CreatePiece(new PieceWhiteBishop(this), 216, 55); + CreatePiece(new PieceWhiteBishop(this), 216, 130); - CreatePiece(new PieceWhiteKnight(this), 216, 30); - CreatePiece(new PieceWhiteKnight(this), 216, 155); + // Queen + CreatePiece(new PieceBlackQueen(this), 42, 105); + CreatePiece(new PieceWhiteQueen(this), 216, 105); - // Bishop - CreatePiece(new PieceBlackBishop(this), 42, 55); - CreatePiece(new PieceBlackBishop(this), 42, 130); - - CreatePiece(new PieceWhiteBishop(this), 216, 55); - CreatePiece(new PieceWhiteBishop(this), 216, 130); - - // Queen - CreatePiece(new PieceBlackQueen(this), 42, 105); - CreatePiece(new PieceWhiteQueen(this), 216, 105); - - // King - CreatePiece(new PieceBlackKing(this), 42, 80); - CreatePiece(new PieceWhiteKing(this), 216, 80); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + // King + CreatePiece(new PieceBlackKing(this), 42, 80); + CreatePiece(new PieceWhiteKing(this), 216, 80); } } diff --git a/Projects/UOContent/Items/Games/Dices.cs b/Projects/UOContent/Items/Games/Dices.cs index e277a8e54..0cd9cf22e 100644 --- a/Projects/UOContent/Items/Games/Dices.cs +++ b/Projects/UOContent/Items/Games/Dices.cs @@ -1,54 +1,39 @@ +using ModernUO.Serialization; using Server.Network; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Dices : Item, ITelekinesisable { - public class Dices : Item, ITelekinesisable + [Constructible] + public Dices() : base(0xFA7) => Weight = 1.0; + + public void OnTelekinesis(Mobile from) { - [Constructible] - public Dices() : base(0xFA7) => Weight = 1.0; + Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x376A, 9, 32, 5022); + Effects.PlaySound(Location, Map, 0x1F5); - public Dices(Serial serial) : base(serial) + Roll(from); + } + + public override void OnDoubleClick(Mobile from) + { + if (!from.InRange(GetWorldLocation(), 2)) { + return; } - public void OnTelekinesis(Mobile from) - { - Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x376A, 9, 32, 5022); - Effects.PlaySound(Location, Map, 0x1F5); + Roll(from); + } - Roll(from); - } - - public override void OnDoubleClick(Mobile from) - { - if (!from.InRange(GetWorldLocation(), 2)) - { - return; - } - - Roll(from); - } - - public void Roll(Mobile from) - { - PublicOverheadMessage( - MessageType.Regular, - 0, - false, - $"*{from.Name} rolls {Utility.Random(1, 6)}, {Utility.Random(1, 6)}*" - ); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + public void Roll(Mobile from) + { + PublicOverheadMessage( + MessageType.Regular, + 0, + false, + $"*{from.Name} rolls {Utility.Random(1, 6)}, {Utility.Random(1, 6)}*" + ); } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongDealerIndicator.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongDealerIndicator.cs index 2d9d694be..3b2309e83 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongDealerIndicator.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongDealerIndicator.cs @@ -1,69 +1,62 @@ -namespace Server.Engines.Mahjong +using ModernUO.Serialization; + +namespace Server.Engines.Mahjong; + +[SerializationGenerator(1, false)] +public partial class MahjongDealerIndicator { - public class MahjongDealerIndicator + [DirtyTrackingEntity] + private readonly MahjongGame _game; + + [SerializableField(0, setter: "private")] + private Point2D _position; + + [SerializableField(1, setter: "private")] + private MahjongPieceDirection _direction; + + [SerializableField(2, setter: "private")] + private MahjongWind _wind; + + public MahjongDealerIndicator(MahjongGame game) { - public MahjongDealerIndicator(MahjongGame game, Point2D position, MahjongPieceDirection direction, MahjongWind wind) + _game = game; + } + + public MahjongDealerIndicator(MahjongGame game, Point2D position, MahjongPieceDirection direction, MahjongWind wind) + { + _game = game; + _position = position; + _direction = direction; + _wind = wind; + } + + public MahjongPieceDim Dimensions => GetDimensions(_position, _direction); + + public static MahjongPieceDim GetDimensions(Point2D position, MahjongPieceDirection direction) => + direction is MahjongPieceDirection.Up or MahjongPieceDirection.Down + ? new MahjongPieceDim(position, 40, 20) + : new MahjongPieceDim(position, 20, 40); + + public void Move(Point2D position, MahjongPieceDirection direction, MahjongWind wind) + { + var dim = GetDimensions(position, direction); + + if (!dim.IsValid()) { - Game = game; - Position = position; - Direction = direction; - Wind = wind; + return; } - public MahjongDealerIndicator(MahjongGame game, IGenericReader reader) - { - Game = game; + _position = position; + _direction = direction; + _wind = wind; - var version = reader.ReadInt(); + _game.Players.SendGeneralPacket(true, true); + } - Position = reader.ReadPoint2D(); - Direction = (MahjongPieceDirection)reader.ReadInt(); - Wind = (MahjongWind)reader.ReadInt(); - } - - public MahjongGame Game { get; } - - public Point2D Position { get; private set; } - - public MahjongPieceDirection Direction { get; private set; } - - public MahjongWind Wind { get; private set; } - - public MahjongPieceDim Dimensions => GetDimensions(Position, Direction); - - public static MahjongPieceDim GetDimensions(Point2D position, MahjongPieceDirection direction) - { - if (direction is MahjongPieceDirection.Up or MahjongPieceDirection.Down) - { - return new MahjongPieceDim(position, 40, 20); - } - - return new MahjongPieceDim(position, 20, 40); - } - - public void Move(Point2D position, MahjongPieceDirection direction, MahjongWind wind) - { - var dim = GetDimensions(position, direction); - - if (!dim.IsValid()) - { - return; - } - - Position = position; - Direction = direction; - Wind = wind; - - Game.Players.SendGeneralPacket(true, true); - } - - public void Save(IGenericWriter writer) - { - writer.Write(0); // version - - writer.Write(Position); - writer.Write((int)Direction); - writer.Write((int)Wind); - } + private void Deserialize(IGenericReader reader, int version) + { + _position = reader.ReadPoint2D(); + _direction = (MahjongPieceDirection)reader.ReadInt(); + _wind = (MahjongWind)reader.ReadInt(); } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongDices.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongDices.cs index 41666961c..80ca47512 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongDices.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongDices.cs @@ -1,52 +1,37 @@ -namespace Server.Engines.Mahjong +using ModernUO.Serialization; + +namespace Server.Engines.Mahjong; + +[SerializationGenerator(0, false)] +public partial class MahjongDices { - public class MahjongDices + [DirtyTrackingEntity] + private readonly MahjongGame _game; + + [SerializableField(0, setter: "private")] + private int _first; + + [SerializableField(1, setter: "private")] + private int _second; + + public MahjongDices(MahjongGame game) { - public MahjongDices(MahjongGame game) + _game = game; + _first = Utility.Random(1, 6); + _second = Utility.Random(1, 6); + } + + public void RollDices(Mobile from) + { + _first = Utility.Random(1, 6); + _second = Utility.Random(1, 6); + + _game.Players.SendGeneralPacket(true, true); + + if (from != null) { - Game = game; - First = Utility.Random(1, 6); - Second = Utility.Random(1, 6); - } - - public MahjongDices(MahjongGame game, IGenericReader reader) - { - Game = game; - - var version = reader.ReadInt(); - - First = reader.ReadInt(); - Second = reader.ReadInt(); - } - - public MahjongGame Game { get; } - - public int First { get; private set; } - - public int Second { get; private set; } - - public void RollDices(Mobile from) - { - First = Utility.Random(1, 6); - Second = Utility.Random(1, 6); - - Game.Players.SendGeneralPacket(true, true); - - if (from != null) - { - Game.Players.SendLocalizedMessage( - 1062695, - $"{from.Name}\t{First}\t{Second}" - ); // ~1_name~ rolls the dice and gets a ~2_number~ and a ~3_number~! - } - } - - public void Save(IGenericWriter writer) - { - writer.Write(0); // version - - writer.Write(First); - writer.Write(Second); + // ~1_name~ rolls the dice and gets a ~2_number~ and a ~3_number~! + _game.Players.SendLocalizedMessage(1062695, $"{from.Name}\t{_first}\t{_second}"); } } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongEnums.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongEnums.cs index 4dd79a98d..32d6b3ad6 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongEnums.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongEnums.cs @@ -1,56 +1,55 @@ -namespace Server.Engines.Mahjong +namespace Server.Engines.Mahjong; + +public enum MahjongPieceDirection { - 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 - } + 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 } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs index ab2fa1a63..827b135cf 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs @@ -1,347 +1,316 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Multis; -namespace Server.Engines.Mahjong +namespace Server.Engines.Mahjong; + +[SerializationGenerator(1, false)] +public partial class MahjongGame : Item, ISecurable { - public class MahjongGame : Item, ISecurable + public const int MaxPlayers = 4; + public const int BaseScore = 30000; + + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private SecureLevel _level; + + [SerializableField(1, setter: "private")] + private MahjongTile[] _tiles; + + [SerializableField(2, setter: "private")] + private MahjongDealerIndicator _dealerIndicator; + + [SerializableField(3, setter: "private")] + private MahjongWallBreakIndicator _wallBreakIndicator; + + [SerializableField(4, setter: "private")] + private MahjongDices _dices; + + [SerializableField(5, setter: "private")] + private MahjongPlayers _players; + + // Field 6 + private bool _showScores; + + // Field 7 + private bool _spectatorVision; + + private DateTime _lastReset; + + [Constructible] + public MahjongGame() : base(0xFAA) { - public const int MaxPlayers = 4; - public const int BaseScore = 30000; - private DateTime m_LastReset; + Weight = 5.0; - private bool m_ShowScores; - private bool m_SpectatorVision; + BuildWalls(); + _dealerIndicator = + new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); + _wallBreakIndicator = new MahjongWallBreakIndicator(this, new Point2D(335, 335)); + _dices = new MahjongDices(this); + _players = new MahjongPlayers(this, MaxPlayers, BaseScore); + _lastReset = Core.Now; + _level = SecureLevel.CoOwners; + } - [Constructible] - public MahjongGame() : base(0xFAA) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableField(6)] + public bool ShowScores + { + get => _showScores; + set { - Weight = 5.0; - - BuildWalls(); - DealerIndicator = - new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); - WallBreakIndicator = new MahjongWallBreakIndicator(this, new Point2D(335, 335)); - Dices = new MahjongDices(this); - Players = new MahjongPlayers(this, MaxPlayers, BaseScore); - m_LastReset = Core.Now; - Level = SecureLevel.CoOwners; - } - - public MahjongGame(Serial serial) : base(serial) - { - } - - public MahjongTile[] Tiles { get; private set; } - - public MahjongDealerIndicator DealerIndicator { get; private set; } - - public MahjongWallBreakIndicator WallBreakIndicator { get; private set; } - - public MahjongDices Dices { get; private set; } - - public MahjongPlayers Players { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool ShowScores - { - get => m_ShowScores; - set - { - if (m_ShowScores == value) - { - return; - } - - m_ShowScores = value; - - if (value) - { - Players.SendPlayersPacket(true, true); - } - - Players.SendGeneralPacket(true, true); - - Players.SendLocalizedMessage(value ? 1062777 : 1062778); // The dealer has enabled/disabled score display. - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public bool SpectatorVision - { - get => m_SpectatorVision; - set - { - if (m_SpectatorVision == value) - { - return; - } - - m_SpectatorVision = value; - - if (Players.IsInGamePlayer(Players.DealerPosition)) - { - Players.Dealer.NetState.SendMahjongGeneralInfo(this); - } - - Players.SendTilesPacket(false, true); - - Players.SendLocalizedMessage(value ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. - - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public SecureLevel Level { get; set; } - - private void BuildHorizontalWall( - ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, - MahjongTileTypeGenerator typeGenerator - ) - { - for (var i = 0; i < 17; i++) - { - var position = new Point2D(x + i * 20, y); - 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 (var i = 0; i < 17; i++) - { - var position = new Point2D(x, y + i * 20); - Tiles[index + i] = new MahjongTile( - this, - index + i, - typeGenerator.Next(), - position, - stackLevel, - direction, - false - ); - } - - index += 17; - } - - private void BuildWalls() - { - Tiles = new MahjongTile[136]; - - var typeGenerator = new MahjongTileTypeGenerator(); - - var 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); - - Players.CheckPlayers(); - - if (from.Alive && IsAccessibleTo(from) && Players.GetInGameMobiles(true, false).Count == 0) - { - list.Add(new ResetGameEntry(this)); - } - - SetSecureLevelEntry.AddTo(from, this, list); - } - - public override void OnDoubleClick(Mobile from) - { - Players.CheckPlayers(); - - Players.Join(from); - } - - public void ResetGame(Mobile from) - { - if (Core.Now - m_LastReset < TimeSpan.FromSeconds(5.0)) + if (_showScores == value) { return; } - m_LastReset = Core.Now; + _showScores = value; - if (from != null) + if (value) { - Players.SendLocalizedMessage(1062771, from.Name); // ~1_name~ has reset the game. + _players.SendPlayersPacket(true, true); } - Players.SendRelievePacket(true, true); - - BuildWalls(); - DealerIndicator = - new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); - WallBreakIndicator = new MahjongWallBreakIndicator(this, new Point2D(335, 335)); - Players = new MahjongPlayers(this, MaxPlayers, BaseScore); + _players.SendGeneralPacket(true, true); + _players.SendLocalizedMessage(value ? 1062777 : 1062778); // The dealer has enabled/disabled score display. + this.MarkDirty(); } + } - public void ResetWalls(Mobile from) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableField(7)] + public bool SpectatorVision + { + get => _spectatorVision; + set { - if (Core.Now - m_LastReset < TimeSpan.FromSeconds(5.0)) + if (_spectatorVision == value) { return; } - m_LastReset = Core.Now; + _spectatorVision = value; - BuildWalls(); - - Players.SendTilesPacket(true, true); - - if (from != null) + if (_players.IsInGamePlayer(_players.DealerPosition)) { - Players.SendLocalizedMessage(1062696); // The dealer rebuilds the wall. + _players.Dealer.NetState.SendMahjongGeneralInfo(this); + } + + _players.SendTilesPacket(false, true); + _players.SendLocalizedMessage(value ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. + InvalidateProperties(); + this.MarkDirty(); + } + } + + private void BuildHorizontalWall( + ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, + MahjongTileTypeGenerator typeGenerator + ) + { + for (var i = 0; i < 17; i++) + { + var position = new Point2D(x + i * 20, y); + Tiles[index + i] = new MahjongTile( + this, + index + i, + typeGenerator.Next(), + position, + stackLevel, + direction, + false + ); + } + + index += 17; + this.MarkDirty(); + } + + private void BuildVerticalWall( + ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, + MahjongTileTypeGenerator typeGenerator + ) + { + for (var i = 0; i < 17; i++) + { + var position = new Point2D(x, y + i * 20); + Tiles[index + i] = new MahjongTile( + this, + index + i, + typeGenerator.Next(), + position, + stackLevel, + direction, + false + ); + } + + index += 17; + this.MarkDirty(); + } + + private void BuildWalls() + { + Tiles = new MahjongTile[136]; + + var typeGenerator = new MahjongTileTypeGenerator(); + + var 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(IPropertyList list) + { + base.GetProperties(list); + + if (_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); + + _players.CheckPlayers(); + + if (from.Alive && IsAccessibleTo(from) && _players.GetInGameMobiles(true, false).Count == 0) + { + list.Add(new ResetGameEntry(this)); + } + + SetSecureLevelEntry.AddTo(from, this, list); + } + + public override void OnDoubleClick(Mobile from) + { + _players.CheckPlayers(); + _players.Join(from); + } + + public void ResetGame(Mobile from) + { + if (Core.Now - _lastReset < TimeSpan.FromSeconds(5.0)) + { + return; + } + + _lastReset = Core.Now; + + if (from != null) + { + _players.SendLocalizedMessage(1062771, from.Name); // ~1_name~ has reset the game. + } + + _players.SendRelievePacket(true, true); + + BuildWalls(); + DealerIndicator = + new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); + WallBreakIndicator = new MahjongWallBreakIndicator(this, new Point2D(335, 335)); + Players = new MahjongPlayers(this, MaxPlayers, BaseScore); + } + + public void ResetWalls(Mobile from) + { + if (Core.Now - _lastReset < TimeSpan.FromSeconds(5.0)) + { + return; + } + + _lastReset = Core.Now; + + BuildWalls(); + + _players.SendTilesPacket(true, true); + + if (from != null) + { + _players.SendLocalizedMessage(1062696); // The dealer rebuilds the wall. + } + } + + public int GetStackLevel(MahjongPieceDim dim) + { + var level = -1; + foreach (var tile in _tiles) + { + if (tile.StackLevel > level && dim.IsOverlapping(tile.Dimensions)) + { + level = tile.StackLevel; } } - public int GetStackLevel(MahjongPieceDim dim) - { - var level = -1; - foreach (var tile in Tiles) - { - if (tile.StackLevel > level && dim.IsOverlapping(tile.Dimensions)) - { - level = tile.StackLevel; - } - } + return level; + } - return level; + private void Deserialize(IGenericReader reader, int number) + { + _level = (SecureLevel)reader.ReadInt(); + var length = reader.ReadInt(); + _tiles = new MahjongTile[length]; + + for (var i = 0; i < length; i++) + { + var tile = _tiles[i] = new MahjongTile(this); + tile.Deserialize(reader); } - public override void Serialize(IGenericWriter writer) + _dealerIndicator = new MahjongDealerIndicator(this); + _dealerIndicator.Deserialize(reader); + + _wallBreakIndicator = new MahjongWallBreakIndicator(this); + _wallBreakIndicator.Deserialize(reader); + + _dices = new MahjongDices(this); + _dices.Deserialize(reader); + + _players = new MahjongPlayers(this); + _players.Deserialize(reader); + + _showScores = reader.ReadBool(); + _spectatorVision = reader.ReadBool(); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + _lastReset = Core.Now; + } + + private class ResetGameEntry : ContextMenuEntry + { + private readonly MahjongGame _game; + + public ResetGameEntry(MahjongGame game) : base(6162) => _game = game; + + public override void OnClick() { - base.Serialize(writer); + var from = Owner.From; - writer.Write(1); // version - - writer.Write((int)Level); - - writer.Write(Tiles.Length); - - for (var i = 0; i < Tiles.Length; i++) + if (from.CheckAlive() && !_game.Deleted && _game.IsAccessibleTo(from) && + _game.Players.GetInGameMobiles(true, false).Count == 0) { - Tiles[i].Save(writer); - } - - DealerIndicator.Save(writer); - - WallBreakIndicator.Save(writer); - - Dices.Save(writer); - - Players.Save(writer); - - writer.Write(m_ShowScores); - writer.Write(m_SpectatorVision); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 1: - { - Level = (SecureLevel)reader.ReadInt(); - - goto case 0; - } - case 0: - { - if (version < 1) - { - Level = SecureLevel.CoOwners; - } - - var length = reader.ReadInt(); - Tiles = new MahjongTile[length]; - - for (var i = 0; i < length; i++) - { - Tiles[i] = new MahjongTile(this, reader); - } - - DealerIndicator = new MahjongDealerIndicator(this, reader); - - WallBreakIndicator = new MahjongWallBreakIndicator(this, reader); - - Dices = new MahjongDices(this, reader); - - Players = new MahjongPlayers(this, reader); - - m_ShowScores = reader.ReadBool(); - m_SpectatorVision = reader.ReadBool(); - - m_LastReset = Core.Now; - - break; - } - } - } - - private class ResetGameEntry : ContextMenuEntry - { - private readonly MahjongGame m_Game; - - public ResetGameEntry(MahjongGame game) : base(6162) => m_Game = game; - - public override void OnClick() - { - var 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); - } + _game.ResetGame(from); } } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs index 73da28c08..8d6dfe42e 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs @@ -43,9 +43,9 @@ namespace Server.Engines.Mahjong return null; } - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0xDA, 0, true, OnPacket); + IncomingPackets.Register(0xDA, 0, true, &OnPacket); RegisterSubCommand(0x6, ExitGame); RegisterSubCommand(0xA, GivePoints); @@ -61,7 +61,7 @@ namespace Server.Engines.Mahjong RegisterSubCommand(0x18, MoveDealerIndicator); } - public static void OnPacket(NetState state, CircularBufferReader reader, ref int packetLength) + public static void OnPacket(NetState state, CircularBufferReader reader, int packetLength) { var game = World.FindItem((Serial)reader.ReadUInt32()) as MahjongGame; @@ -287,7 +287,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongJoinGame(this NetState ns, Serial game) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -303,7 +303,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongPlayersInfo(this NetState ns, MahjongGame game, Mobile to) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -361,7 +361,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongTileInfo(this NetState ns, MahjongTile tile, Mobile to) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -407,7 +407,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongTilesInfo(this NetState ns, MahjongGame game, Mobile to) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -460,7 +460,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongGeneralInfo(this NetState ns, MahjongGame game) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -503,7 +503,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongRelieve(this NetState ns, Serial game) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPieceDim.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPieceDim.cs index 932f941b3..d93df6372 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPieceDim.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPieceDim.cs @@ -1,50 +1,49 @@ -namespace Server.Engines.Mahjong +namespace Server.Engines.Mahjong; + +public struct MahjongPieceDim { - public struct MahjongPieceDim + public Point2D Position { get; } + + public int Width { get; } + + public int Height { get; } + + public MahjongPieceDim(Point2D position, int width, int height) { - public Point2D Position { get; } + Position = position; + Width = width; + Height = height; + } - public int Width { get; } + public bool IsValid() => + Position.X >= 0 && Position.Y >= 0 && Position.X + Width <= 670 && Position.Y + Height <= 670; - public int Height { get; } + public bool IsOverlapping(MahjongPieceDim dim) => + Position.X < dim.Position.X + dim.Width && Position.Y < dim.Position.Y + dim.Height && + Position.X + Width > dim.Position.X && Position.Y + Height > dim.Position.Y; - public MahjongPieceDim(Point2D position, int width, int height) + public int GetHandArea() + { + if (Position.X + Width > 150 && Position.X < 520 && Position.Y < 35) { - Position = position; - Width = width; - Height = height; + return 0; } - public bool IsValid() => - Position.X >= 0 && Position.Y >= 0 && Position.X + Width <= 670 && Position.Y + Height <= 670; - - public bool IsOverlapping(MahjongPieceDim dim) => - Position.X < dim.Position.X + dim.Width && Position.Y < dim.Position.Y + dim.Height && - Position.X + Width > dim.Position.X && Position.Y + Height > dim.Position.Y; - - public int GetHandArea() + if (Position.X + Width > 635 && Position.Y + Height > 150 && Position.Y < 520) { - if (Position.X + Width > 150 && Position.X < 520 && Position.Y < 35) - { - return 0; - } - - if (Position.X + Width > 635 && Position.Y + Height > 150 && Position.Y < 520) - { - return 1; - } - - if (Position.X + Width > 150 && Position.X < 520 && Position.Y + Height > 635) - { - return 2; - } - - if (Position.X < 35 && Position.Y + Height > 150 && Position.Y < 520) - { - return 3; - } - - return -1; + return 1; } + + if (Position.X + Width > 150 && Position.X < 520 && Position.Y + Height > 635) + { + return 2; + } + + if (Position.X < 35 && Position.Y + Height > 150 && Position.Y < 520) + { + return 3; + } + + return -1; } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs index 898b64009..2627ec27e 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs @@ -1,607 +1,604 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Network; -namespace Server.Engines.Mahjong +namespace Server.Engines.Mahjong; + +[SerializationGenerator(1, false)] +public partial class MahjongPlayers { - public class MahjongPlayers + [SerializableField(0, setter: "private")] + private Mobile[] _players; + + [SerializableField(1, setter: "private")] + private bool[] _inGame; + + [SerializableField(2, setter: "private")] + private bool[] _publicHand; + + [SerializableField(3, setter: "private")] + private int[] _scores; + + [SerializableField(4, setter: "private")] + private int _dealerPosition; + + private List _spectators; + + [DirtyTrackingEntity] + private readonly MahjongGame _game; + + public MahjongPlayers(MahjongGame game, int maxPlayers, int baseScore) { - private readonly bool[] m_InGame; - private readonly Mobile[] m_Players; - private readonly bool[] m_PublicHand; - private readonly int[] m_Scores; - private readonly List m_Spectators; + _game = game; + _spectators = new List(); - public MahjongPlayers(MahjongGame game, int maxPlayers, int baseScore) + _players = new Mobile[maxPlayers]; + _inGame = new bool[maxPlayers]; + _publicHand = new bool[maxPlayers]; + _scores = new int[maxPlayers]; + + for (var i = 0; i < _scores.Length; i++) { - Game = game; - m_Spectators = new List(); + _scores[i] = baseScore; + } + } - m_Players = new Mobile[maxPlayers]; - m_InGame = new bool[maxPlayers]; - m_PublicHand = new bool[maxPlayers]; - m_Scores = new int[maxPlayers]; + public MahjongPlayers(MahjongGame game) + { + _game = game; + _spectators = new List(); + } - for (var i = 0; i < m_Scores.Length; i++) + private void Deserialize(IGenericReader reader, int version) + { + var seats = reader.ReadInt(); + _players = new Mobile[seats]; + _inGame = new bool[seats]; + _publicHand = new bool[seats]; + _scores = new int[seats]; + + for (var i = 0; i < seats; i++) + { + _players[i] = reader.ReadEntity(); + _publicHand[i] = reader.ReadBool(); + _scores[i] = reader.ReadInt(); + } + + _dealerPosition = reader.ReadInt(); + } + + public int Seats => _players.Length; + public Mobile Dealer => _players[DealerPosition]; + + public Mobile GetPlayer(int index) + { + if (index < 0 || index >= _players.Length) + { + return null; + } + + return _players[index]; + } + + public int GetPlayerIndex(Mobile mobile) + { + for (var i = 0; i < _players.Length; i++) + { + if (_players[i] == mobile) { - m_Scores[i] = baseScore; + return i; } } - public MahjongPlayers(MahjongGame game, IGenericReader reader) + return -1; + } + + public bool IsInGameDealer(Mobile mobile) + { + if (Dealer != mobile) { - Game = game; - m_Spectators = new List(); - - var version = reader.ReadInt(); - - var seats = reader.ReadInt(); - m_Players = new Mobile[seats]; - m_InGame = new bool[seats]; - m_PublicHand = new bool[seats]; - m_Scores = new int[seats]; - - for (var i = 0; i < seats; i++) - { - m_Players[i] = reader.ReadEntity(); - m_PublicHand[i] = reader.ReadBool(); - m_Scores[i] = reader.ReadInt(); - } - - DealerPosition = reader.ReadInt(); + return false; } - public MahjongGame Game { get; } + return _inGame[DealerPosition]; + } - public int Seats => m_Players.Length; - public Mobile Dealer => m_Players[DealerPosition]; - public int DealerPosition { get; private set; } - - public Mobile GetPlayer(int index) + public bool IsInGamePlayer(int index) + { + if (index < 0 || index >= _players.Length || _players[index] == null) { - if (index < 0 || index >= m_Players.Length) - { - return null; - } - - return m_Players[index]; + return false; } - public int GetPlayerIndex(Mobile mobile) + return _inGame[index]; + } + + public bool IsInGamePlayer(Mobile mobile) + { + var index = GetPlayerIndex(mobile); + + return IsInGamePlayer(index); + } + + public bool IsSpectator(Mobile mobile) => _spectators.Contains(mobile); + + public int GetScore(int index) + { + if (index < 0 || index >= _scores.Length) { - for (var i = 0; i < m_Players.Length; i++) + return 0; + } + + return _scores[index]; + } + + public bool IsPublic(int index) + { + if (index < 0 || index >= _publicHand.Length) + { + return false; + } + + return _publicHand[index]; + } + + public void SetPublic(int index, bool value) + { + if (index < 0 || index >= _publicHand.Length || _publicHand[index] == value) + { + return; + } + + _publicHand[index] = value; + + SendTilesPacket(true, !_game.SpectatorVision); + + if (IsInGamePlayer(index)) + { + _players[index].SendLocalizedMessage(value ? 1062775 : 1062776); // Your hand is [not] publicly viewable. + } + } + + public List GetInGameMobiles(bool players, bool spectators) + { + var list = new List(); + + if (players) + { + for (var i = 0; i < _players.Length; i++) { - if (m_Players[i] == mobile) + if (IsInGamePlayer(i)) { - return i; + list.Add(_players[i]); } } - - return -1; } - public bool IsInGameDealer(Mobile mobile) + if (spectators) { - if (Dealer != mobile) + list.AddRange(_spectators); + } + + return list; + } + + public void CheckPlayers() + { + var removed = false; + + Span relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); + + for (var i = 0; i < _players.Length; i++) + { + var player = _players[i]; + + if (player == null) { - return false; + continue; } - return m_InGame[DealerPosition]; - } - - public bool IsInGamePlayer(int index) - { - if (index < 0 || index >= m_Players.Length || m_Players[index] == null) + if (player.Deleted) { - return false; + _players[i] = null; + + SendPlayerExitMessage(player); + UpdateDealer(true); + + removed = true; } - - return m_InGame[index]; - } - - public bool IsInGamePlayer(Mobile mobile) - { - var index = GetPlayerIndex(mobile); - - return IsInGamePlayer(index); - } - - public bool IsSpectator(Mobile mobile) => m_Spectators.Contains(mobile); - - public int GetScore(int index) - { - if (index < 0 || index >= m_Scores.Length) + else if (_inGame[i]) { - return 0; - } - - return m_Scores[index]; - } - - public bool IsPublic(int index) - { - if (index < 0 || index >= m_PublicHand.Length) - { - return false; - } - - 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, !Game.SpectatorVision); - - if (IsInGamePlayer(index)) - { - m_Players[index].SendLocalizedMessage(value ? 1062775 : 1062776); // Your hand is [not] publicly viewable. - } - } - - public List GetInGameMobiles(bool players, bool spectators) - { - var list = new List(); - - if (players) - { - for (var i = 0; i < m_Players.Length; i++) + if (player.NetState == null) { - if (IsInGamePlayer(i)) - { - list.Add(m_Players[i]); - } - } - } - - if (spectators) - { - list.AddRange(m_Spectators); - } - - return list; - } - - public void CheckPlayers() - { - var removed = false; - - Span relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); - - for (var i = 0; i < m_Players.Length; i++) - { - var player = m_Players[i]; - - if (player == null) - { - continue; - } - - if (player.Deleted) - { - m_Players[i] = null; + _inGame[i] = false; SendPlayerExitMessage(player); UpdateDealer(true); removed = true; } - else if (m_InGame[i]) + else if (!_game.IsAccessibleTo(player) || player.Map != _game.Map || + !player.InRange(_game.GetWorldLocation(), 5)) { - if (player.NetState == null) - { - m_InGame[i] = false; + _inGame[i] = false; - SendPlayerExitMessage(player); - UpdateDealer(true); + MahjongPackets.CreateMahjongRelieve(relievePacket, _game.Serial); + player.NetState?.Send(relievePacket); - removed = true; - } - else if (!Game.IsAccessibleTo(player) || player.Map != Game.Map || - !player.InRange(Game.GetWorldLocation(), 5)) - { - m_InGame[i] = false; + SendPlayerExitMessage(player); + UpdateDealer(true); - MahjongPackets.CreateMahjongRelieve(relievePacket, Game.Serial); - player.NetState?.Send(relievePacket); - - SendPlayerExitMessage(player); - UpdateDealer(true); - - removed = true; - } + removed = true; } } - - for (var i = 0; i < m_Spectators.Count;) - { - var mobile = m_Spectators[i]; - - if (mobile.NetState == null || mobile.Deleted) - { - m_Spectators.RemoveAt(i); - } - else if (!Game.IsAccessibleTo(mobile) || mobile.Map != Game.Map || - !mobile.InRange(Game.GetWorldLocation(), 5)) - { - m_Spectators.RemoveAt(i); - - MahjongPackets.CreateMahjongRelieve(relievePacket, Game.Serial); - mobile.NetState?.Send(relievePacket); - } - else - { - i++; - } - } - - if (removed && !UpdateSpectators()) - { - SendPlayersPacket(true, true); - } } - private void UpdateDealer(bool message) + for (var i = 0; i < _spectators.Count;) { - if (IsInGamePlayer(DealerPosition)) + var mobile = _spectators[i]; + + if (mobile.NetState == null || mobile.Deleted) { + _spectators.RemoveAt(i); + } + else if (!_game.IsAccessibleTo(mobile) || mobile.Map != _game.Map || + !mobile.InRange(_game.GetWorldLocation(), 5)) + { + _spectators.RemoveAt(i); + + MahjongPackets.CreateMahjongRelieve(relievePacket, _game.Serial); + mobile.NetState?.Send(relievePacket); + } + else + { + i++; + } + } + + if (removed && !UpdateSpectators()) + { + SendPlayersPacket(true, true); + } + } + + private void UpdateDealer(bool message) + { + if (IsInGamePlayer(DealerPosition)) + { + return; + } + + for (var i = DealerPosition + 1; i < _players.Length; i++) + { + if (IsInGamePlayer(i)) + { + DealerPosition = i; + + if (message) + { + SendDealerChangedMessage(); + } + return; } + } - for (var i = DealerPosition + 1; i < m_Players.Length; i++) + for (var i = 0; i < DealerPosition; i++) + { + if (IsInGamePlayer(i)) { - if (IsInGamePlayer(i)) + DealerPosition = i; + + if (message) { - DealerPosition = i; - - if (message) - { - SendDealerChangedMessage(); - } - - return; + SendDealerChangedMessage(); } + + return; } + } + } - for (var i = 0; i < DealerPosition; i++) + private int GetNextSeat() + { + for (var i = DealerPosition; i < _players.Length; i++) + { + if (_players[i] == null) { - if (IsInGamePlayer(i)) - { - DealerPosition = i; - - if (message) - { - SendDealerChangedMessage(); - } - - return; - } + return i; } } - private int GetNextSeat() + for (var i = 0; i < DealerPosition; i++) { - for (var i = DealerPosition; i < m_Players.Length; i++) + if (_players[i] == null) { - if (m_Players[i] == null) - { - return i; - } + return i; } - - for (var i = 0; i < DealerPosition; i++) - { - if (m_Players[i] == null) - { - return i; - } - } - - return -1; } - private bool UpdateSpectators() + return -1; + } + + private bool UpdateSpectators() + { + if (_spectators.Count == 0) { - if (m_Spectators.Count == 0) - { - return false; - } - - var nextSeat = GetNextSeat(); - - if (nextSeat >= 0) - { - var newPlayer = m_Spectators[0]; - - m_Spectators.RemoveAt(0); - - AddPlayer(newPlayer, nextSeat, false); - - UpdateSpectators(); - - return true; - } - return false; } - private void AddPlayer(Mobile player, int index, bool sendJoinGame) + var nextSeat = GetNextSeat(); + + if (nextSeat >= 0) { - m_Players[index] = player; - m_InGame[index] = true; + var newPlayer = _spectators[0]; - UpdateDealer(false); + _spectators.RemoveAt(0); - if (sendJoinGame) - { - player.NetState.SendMahjongJoinGame(Game.Serial); - } + AddPlayer(newPlayer, nextSeat, false); - SendPlayersPacket(true, true); + UpdateSpectators(); - player.NetState.SendMahjongGeneralInfo(Game); - player.NetState.SendMahjongTilesInfo(Game, player); - - if (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. - } + return true; } - private void AddSpectator(Mobile mobile) - { - if (!IsSpectator(mobile)) - { - m_Spectators.Add(mobile); - } + return false; + } - mobile.NetState.SendMahjongJoinGame(Game.Serial); - mobile.NetState.SendMahjongPlayersInfo(Game, mobile); - mobile.NetState.SendMahjongGeneralInfo(Game); - mobile.NetState.SendMahjongTilesInfo(Game, mobile); + private void AddPlayer(Mobile player, int index, bool sendJoinGame) + { + _players[index] = player; + _inGame[index] = true; + + UpdateDealer(false); + + if (sendJoinGame) + { + player.NetState.SendMahjongJoinGame(_game.Serial); } - public void Join(Mobile mobile) + SendPlayersPacket(true, true); + + player.NetState.SendMahjongGeneralInfo(_game); + player.NetState.SendMahjongTilesInfo(_game, player); + + if (DealerPosition == index) { - var index = GetPlayerIndex(mobile); + 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. + } + } - if (index >= 0) - { - AddPlayer(mobile, index, true); - return; - } - - var nextSeat = GetNextSeat(); - - if (nextSeat >= 0) - { - AddPlayer(mobile, nextSeat, true); - } - else - { - AddSpectator(mobile); - } + private void AddSpectator(Mobile mobile) + { + if (!IsSpectator(mobile)) + { + _spectators.Add(mobile); } - public void LeaveGame(Mobile player) + mobile.NetState.SendMahjongJoinGame(_game.Serial); + mobile.NetState.SendMahjongPlayersInfo(_game, mobile); + mobile.NetState.SendMahjongGeneralInfo(_game); + mobile.NetState.SendMahjongTilesInfo(_game, mobile); + } + + public void Join(Mobile mobile) + { + var index = GetPlayerIndex(mobile); + + if (index >= 0) { - var index = GetPlayerIndex(player); - if (index >= 0) - { - m_InGame[index] = false; - - SendPlayerExitMessage(player); - UpdateDealer(true); - - SendPlayersPacket(true, true); - } - else - { - m_Spectators.Remove(player); - } + AddPlayer(mobile, index, true); + return; } - public void ResetScores(int value) + var nextSeat = GetNextSeat(); + + if (nextSeat >= 0) { - for (var i = 0; i < m_Scores.Length; i++) - { - m_Scores[i] = value; - } - - SendPlayersPacket(true, Game.ShowScores); - - SendLocalizedMessage(1062697); // The dealer redistributes the score sticks evenly. + AddPlayer(mobile, nextSeat, true); } - - public void TransferScore(Mobile from, int toPosition, int amount) + else { - var fromPosition = GetPlayerIndex(from); - var to = GetPlayer(toPosition); - - if (fromPosition < 0 || to == null || m_Scores[fromPosition] < amount) - { - return; - } - - m_Scores[fromPosition] -= amount; - m_Scores[toPosition] += amount; - - if (Game.ShowScores) - { - SendPlayersPacket(true, true); - } - else - { - from.NetState.SendMahjongPlayersInfo(Game, from); - to.NetState.SendMahjongPlayersInfo(Game, to); - } - - // ~1_giver~ gives ~2_receiver~ ~3_number~ points. - SendLocalizedMessage(1062774, $"{from.Name}\t{to.Name}\t{amount}"); + AddSpectator(mobile); } + } - public void OpenSeat(int index) + public void LeaveGame(Mobile player) + { + var index = GetPlayerIndex(player); + if (index >= 0) { - var player = GetPlayer(index); - if (player == null) - { - return; - } - - if (m_InGame[index]) - { - player.NetState.SendMahjongRelieve(Game.Serial); - } - - m_Players[index] = null; - - SendLocalizedMessage(1062699, player.Name); // ~1_name~ is relieved from the game by the dealer. + _inGame[index] = false; + SendPlayerExitMessage(player); UpdateDealer(true); - if (!UpdateSpectators()) - { - SendPlayersPacket(true, true); - } + SendPlayersPacket(true, true); + } + else + { + _spectators.Remove(player); + } + } + + public void ResetScores(int value) + { + for (var i = 0; i < _scores.Length; i++) + { + _scores[i] = value; } - public void AssignDealer(int index) + SendPlayersPacket(true, _game.ShowScores); + + SendLocalizedMessage(1062697); // The dealer redistributes the score sticks evenly. + } + + public void TransferScore(Mobile from, int toPosition, int amount) + { + var fromPosition = GetPlayerIndex(from); + var to = GetPlayer(toPosition); + + if (fromPosition < 0 || to == null || _scores[fromPosition] < amount) { - var to = GetPlayer(index); - - if (to == null || !m_InGame[index]) - { - return; - } - - var oldDealer = DealerPosition; - - DealerPosition = index; - - if (IsInGamePlayer(oldDealer)) - { - m_Players[oldDealer].NetState.SendMahjongPlayersInfo(Game, m_Players[oldDealer]); - } - - to.NetState.SendMahjongPlayersInfo(Game, to); - - SendDealerChangedMessage(); + return; } - private void SendDealerChangedMessage() + _scores[fromPosition] -= amount; + _scores[toPosition] += amount; + + if (_game.ShowScores) { - if (Dealer != null) - { - SendLocalizedMessage(1062698, Dealer.Name); // ~1_name~ is assigned the dealer. - } + SendPlayersPacket(true, true); + } + else + { + from.NetState.SendMahjongPlayersInfo(_game, from); + to.NetState.SendMahjongPlayersInfo(_game, to); } - private void SendPlayerExitMessage(Mobile who) + // ~1_giver~ gives ~2_receiver~ ~3_number~ points. + SendLocalizedMessage(1062774, $"{from.Name}\t{to.Name}\t{amount}"); + } + + public void OpenSeat(int index) + { + var player = GetPlayer(index); + if (player == null) { - SendLocalizedMessage(1062762, who.Name); // ~1_name~ has left the game. + return; } - public void SendPlayersPacket(bool players, bool spectators) + if (_inGame[index]) { - foreach (var mobile in GetInGameMobiles(players, spectators)) - { - mobile.NetState.SendMahjongPlayersInfo(Game, mobile); - } + player.NetState.SendMahjongRelieve(_game.Serial); } - public void SendGeneralPacket(bool players, bool spectators) + _players[index] = null; + + SendLocalizedMessage(1062699, player.Name); // ~1_name~ is relieved from the game by the dealer. + + UpdateDealer(true); + + if (!UpdateSpectators()) { - var mobiles = GetInGameMobiles(players, spectators); + SendPlayersPacket(true, true); + } + } - if (mobiles.Count == 0) - { - return; - } + public void AssignDealer(int index) + { + var to = GetPlayer(index); - Span generalInfo = stackalloc byte[MahjongPackets.MahjongGeneralInfoPacketLength].InitializePacket(); - - foreach (var mobile in mobiles) - { - MahjongPackets.CreateMahjongGeneralInfo(generalInfo, Game); - mobile.NetState?.Send(generalInfo); - } + if (to == null || !_inGame[index]) + { + return; } - public void SendTilesPacket(bool players, bool spectators) + var oldDealer = DealerPosition; + + DealerPosition = index; + + if (IsInGamePlayer(oldDealer)) { - foreach (var mobile in GetInGameMobiles(players, spectators)) - { - mobile.NetState.SendMahjongTilesInfo(Game, mobile); - } + _players[oldDealer].NetState.SendMahjongPlayersInfo(_game, _players[oldDealer]); } - public void SendTilePacket(MahjongTile tile, bool players, bool spectators) + to.NetState.SendMahjongPlayersInfo(_game, to); + + SendDealerChangedMessage(); + } + + private void SendDealerChangedMessage() + { + if (Dealer != null) { - foreach (var mobile in GetInGameMobiles(players, spectators)) - { - mobile.NetState.SendMahjongTileInfo(tile, mobile); - } + 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 (var mobile in GetInGameMobiles(players, spectators)) + { + mobile.NetState.SendMahjongPlayersInfo(_game, mobile); + } + } + + public void SendGeneralPacket(bool players, bool spectators) + { + var mobiles = GetInGameMobiles(players, spectators); + + if (mobiles.Count == 0) + { + return; } - public void SendRelievePacket(bool players, bool spectators) + Span generalInfo = stackalloc byte[MahjongPackets.MahjongGeneralInfoPacketLength].InitializePacket(); + + foreach (var mobile in mobiles) { - var mobiles = GetInGameMobiles(players, spectators); + MahjongPackets.CreateMahjongGeneralInfo(generalInfo, _game); + mobile.NetState?.Send(generalInfo); + } + } - if (mobiles.Count == 0) - { - return; - } + public void SendTilesPacket(bool players, bool spectators) + { + foreach (var mobile in GetInGameMobiles(players, spectators)) + { + mobile.NetState.SendMahjongTilesInfo(_game, mobile); + } + } - Span relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); + public void SendTilePacket(MahjongTile tile, bool players, bool spectators) + { + foreach (var mobile in GetInGameMobiles(players, spectators)) + { + mobile.NetState.SendMahjongTileInfo(tile, mobile); + } + } - foreach (var mobile in mobiles) - { - MahjongPackets.CreateMahjongRelieve(relievePacket, Game.Serial); - mobile.NetState?.Send(relievePacket); - } + public void SendRelievePacket(bool players, bool spectators) + { + var mobiles = GetInGameMobiles(players, spectators); + + if (mobiles.Count == 0) + { + return; } - public void SendLocalizedMessage(int number) + Span relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); + + foreach (var mobile in mobiles) { - foreach (var mobile in GetInGameMobiles(true, true)) - { - mobile.SendLocalizedMessage(number); - } + MahjongPackets.CreateMahjongRelieve(relievePacket, _game.Serial); + mobile.NetState?.Send(relievePacket); } + } - public void SendLocalizedMessage(int number, string args) + public void SendLocalizedMessage(int number) + { + foreach (var mobile in GetInGameMobiles(true, true)) { - foreach (var mobile in GetInGameMobiles(true, true)) - { - mobile.SendLocalizedMessage(number, args); - } + mobile.SendLocalizedMessage(number); } + } - public void Save(IGenericWriter writer) + public void SendLocalizedMessage(int number, string args) + { + foreach (var mobile in GetInGameMobiles(true, true)) { - writer.Write(0); // version - - writer.Write(Seats); - - for (var i = 0; i < Seats; i++) - { - writer.Write(m_Players[i]); - writer.Write(m_PublicHand[i]); - writer.Write(m_Scores[i]); - } - - writer.Write(DealerPosition); + mobile.SendLocalizedMessage(number, args); } } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongTile.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongTile.cs index e9d3bf628..61c684d17 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongTile.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongTile.cs @@ -1,95 +1,86 @@ -namespace Server.Engines.Mahjong +using ModernUO.Serialization; + +namespace Server.Engines.Mahjong; + +[SerializationGenerator(1, false)] +public partial class MahjongTile { - public class MahjongTile + [DirtyTrackingEntity] + private readonly MahjongGame _game; + + [SerializableField(0, setter: "private")] + private int _number; + + [SerializableField(1, setter: "private")] + private MahjongTileType _value; + + [SerializableField(2, setter: "private")] + private Point2D _position; + + [SerializableField(3, setter: "private")] + private int _stackLevel; + + [SerializableField(4, setter: "private")] + private MahjongPieceDirection _direction; + + [SerializableField(5, setter: "private")] + private bool _flipped; + + public MahjongTile(MahjongGame game) => _game = game; + + public MahjongTile( + MahjongGame game, int number, MahjongTileType value, Point2D position, int stackLevel, + MahjongPieceDirection direction, bool flipped + ) { - protected Point2D m_Position; + _game = game; + _number = number; + _value = value; + _position = position; + _stackLevel = stackLevel; + _direction = direction; + _flipped = flipped; + } - public MahjongTile( - MahjongGame game, int number, MahjongTileType value, Point2D position, int stackLevel, - MahjongPieceDirection direction, bool flipped - ) + public MahjongGame Game => _game; + + private void Deserialize(IGenericReader reader, int version) + { + _number = reader.ReadInt(); + _value = (MahjongTileType)reader.ReadInt(); + _position = reader.ReadPoint2D(); + _stackLevel = reader.ReadInt(); + _direction = (MahjongPieceDirection)reader.ReadInt(); + _flipped = reader.ReadBool(); + } + + public MahjongPieceDim Dimensions => GetDimensions(_position, _direction); + + public bool IsMovable => _game.GetStackLevel(Dimensions) <= _stackLevel; + + public static MahjongPieceDim GetDimensions(Point2D position, MahjongPieceDirection direction) => + direction is MahjongPieceDirection.Up or MahjongPieceDirection.Down + ? new MahjongPieceDim(position, 20, 30) + : new MahjongPieceDim(position, 30, 20); + + public void Move(Point2D position, MahjongPieceDirection direction, bool flip, int validHandArea) + { + var dim = GetDimensions(position, direction); + var curHandArea = Dimensions.GetHandArea(); + var newHandArea = dim.GetHandArea(); + + if (!IsMovable || !dim.IsValid() || validHandArea >= 0 && + (curHandArea >= 0 && curHandArea != validHandArea || newHandArea >= 0 && newHandArea != validHandArea)) { - Game = game; - Number = number; - Value = value; - m_Position = position; - StackLevel = stackLevel; - Direction = direction; - Flipped = flipped; + return; } - public MahjongTile(MahjongGame game, IGenericReader reader) - { - Game = game; + Position = position; + Direction = direction; + StackLevel = -1; // Avoid self interference + StackLevel = _game.GetStackLevel(dim) + 1; + Flipped = flip; - var version = reader.ReadInt(); - - Number = reader.ReadInt(); - Value = (MahjongTileType)reader.ReadInt(); - m_Position = reader.ReadPoint2D(); - StackLevel = reader.ReadInt(); - Direction = (MahjongPieceDirection)reader.ReadInt(); - Flipped = reader.ReadBool(); - } - - public MahjongGame Game { get; } - - public int Number { get; } - - public MahjongTileType Value { get; } - - public Point2D Position => m_Position; - public int StackLevel { get; private set; } - - public MahjongPieceDirection Direction { get; private set; } - - public bool Flipped { get; private set; } - - public MahjongPieceDim Dimensions => GetDimensions(m_Position, Direction); - - public bool IsMovable => Game.GetStackLevel(Dimensions) <= StackLevel; - - public static MahjongPieceDim GetDimensions(Point2D position, MahjongPieceDirection direction) - { - if (direction is MahjongPieceDirection.Up or MahjongPieceDirection.Down) - { - return new MahjongPieceDim(position, 20, 30); - } - - return new MahjongPieceDim(position, 30, 20); - } - - public void Move(Point2D position, MahjongPieceDirection direction, bool flip, int validHandArea) - { - var dim = GetDimensions(position, direction); - var curHandArea = Dimensions.GetHandArea(); - var newHandArea = dim.GetHandArea(); - - if (!IsMovable || !dim.IsValid() || validHandArea >= 0 && - (curHandArea >= 0 && curHandArea != validHandArea || newHandArea >= 0 && newHandArea != validHandArea)) - { - return; - } - - m_Position = position; - Direction = direction; - StackLevel = -1; // Avoid self interference - StackLevel = Game.GetStackLevel(dim) + 1; - Flipped = flip; - - Game.Players.SendTilePacket(this, true, true); - } - - public void Save(IGenericWriter writer) - { - writer.Write(0); // version - - writer.Write(Number); - writer.Write((int)Value); - writer.Write(m_Position); - writer.Write(StackLevel); - writer.Write((int)Direction); - writer.Write(Flipped); - } + _game.Players.SendTilePacket(this, true, true); } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongTileTypeGenerator.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongTileTypeGenerator.cs index 099e3b610..90b260f43 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongTileTypeGenerator.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongTileTypeGenerator.cs @@ -1,31 +1,28 @@ -using System.Collections.Generic; +namespace Server.Engines.Mahjong; -namespace Server.Engines.Mahjong +public class MahjongTileTypeGenerator { - public class MahjongTileTypeGenerator + private MahjongTileType[] _leftTileTypes; + private int _nextTile; + + public MahjongTileTypeGenerator() { - public MahjongTileTypeGenerator() - { - LeftTileTypes = new List(136); + _leftTileTypes = new MahjongTileType[136]; - for (var i = 1; i <= 34; i++) - { - var tile = (MahjongTileType)i; - LeftTileTypes.Add(tile); - LeftTileTypes.Add(tile); - LeftTileTypes.Add(tile); - LeftTileTypes.Add(tile); - } + for (int i = 1, j = 0; i <= 34; i++) + { + var tile = (MahjongTileType)i; + _leftTileTypes[j++] = tile; + _leftTileTypes[j++] = tile; + _leftTileTypes[j++] = tile; + _leftTileTypes[j++] = tile; } - public List LeftTileTypes { get; } - - public MahjongTileType Next() - { - var next = LeftTileTypes.RandomElement(); - LeftTileTypes.Remove(next); - - return next; - } + _leftTileTypes.Shuffle(); + _leftTileTypes.Shuffle(); + _leftTileTypes.Shuffle(); + _leftTileTypes.Shuffle(); } + + public MahjongTileType Next() => _leftTileTypes[_nextTile++]; } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongWallBreakIndicator.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongWallBreakIndicator.cs index febd3c228..c73613aa9 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongWallBreakIndicator.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongWallBreakIndicator.cs @@ -1,49 +1,39 @@ -namespace Server.Engines.Mahjong +using ModernUO.Serialization; + +namespace Server.Engines.Mahjong; + +[SerializationGenerator(0, false)] +public partial class MahjongWallBreakIndicator { - public class MahjongWallBreakIndicator + [DirtyTrackingEntity] + private readonly MahjongGame _game; + + [SerializableField(0, setter: "private")] + private Point2D _position; + + public MahjongWallBreakIndicator(MahjongGame game) => _game = game; + + public MahjongWallBreakIndicator(MahjongGame game, Point2D position) { - public MahjongWallBreakIndicator(MahjongGame game, Point2D position) + _game = game; + _position = position; + } + + public MahjongPieceDim Dimensions => GetDimensions(_position); + + public static MahjongPieceDim GetDimensions(Point2D position) => new(position, 20, 20); + + public void Move(Point2D position) + { + var dim = GetDimensions(position); + + if (!dim.IsValid()) { - Game = game; - Position = position; + return; } - public MahjongWallBreakIndicator(MahjongGame game, IGenericReader reader) - { - Game = game; + _position = position; - var version = reader.ReadInt(); - - Position = reader.ReadPoint2D(); - } - - public MahjongGame Game { get; } - - public Point2D Position { get; private set; } - - public MahjongPieceDim Dimensions => GetDimensions(Position); - - public static MahjongPieceDim GetDimensions(Point2D position) => new(position, 20, 20); - - public void Move(Point2D position) - { - var dim = GetDimensions(position); - - if (!dim.IsValid()) - { - return; - } - - Position = position; - - Game.Players.SendGeneralPacket(true, true); - } - - public void Save(IGenericWriter writer) - { - writer.Write(0); // version - - writer.Write(Position); - } + _game.Players.SendGeneralPacket(true, true); } } diff --git a/Projects/UOContent/Items/Gems/Amber.cs b/Projects/UOContent/Items/Gems/Amber.cs index d811421cf..99760ef19 100644 --- a/Projects/UOContent/Items/Gems/Amber.cs +++ b/Projects/UOContent/Items/Gems/Amber.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Amber : Item { - public class Amber : Item + [Constructible] + public Amber(int amount = 1) : base(0xF25) { - [Constructible] - public Amber(int amount = 1) : base(0xF25) - { - Stackable = true; - Amount = amount; - } - - public Amber(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Amethyst.cs b/Projects/UOContent/Items/Gems/Amethyst.cs index 99f684cc7..4e1cefd0d 100644 --- a/Projects/UOContent/Items/Gems/Amethyst.cs +++ b/Projects/UOContent/Items/Gems/Amethyst.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Amethyst : Item { - public class Amethyst : Item + [Constructible] + public Amethyst(int amount = 1) : base(0xF16) { - [Constructible] - public Amethyst(int amount = 1) : base(0xF16) - { - Stackable = true; - Amount = amount; - } - - public Amethyst(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Citrine.cs b/Projects/UOContent/Items/Gems/Citrine.cs index 2f87a0462..cd06a3713 100644 --- a/Projects/UOContent/Items/Gems/Citrine.cs +++ b/Projects/UOContent/Items/Gems/Citrine.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Citrine : Item { - public class Citrine : Item + [Constructible] + public Citrine(int amount = 1) : base(0xF15) { - [Constructible] - public Citrine(int amount = 1) : base(0xF15) - { - Stackable = true; - Amount = amount; - } - - public Citrine(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Diamond.cs b/Projects/UOContent/Items/Gems/Diamond.cs index 22802ba8e..22194b60c 100644 --- a/Projects/UOContent/Items/Gems/Diamond.cs +++ b/Projects/UOContent/Items/Gems/Diamond.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Diamond : Item { - public class Diamond : Item + [Constructible] + public Diamond(int amount = 1) : base(0xF26) { - [Constructible] - public Diamond(int amount = 1) : base(0xF26) - { - Stackable = true; - Amount = amount; - } - - public Diamond(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Emerald.cs b/Projects/UOContent/Items/Gems/Emerald.cs index 90bde6ffc..de02a09dd 100644 --- a/Projects/UOContent/Items/Gems/Emerald.cs +++ b/Projects/UOContent/Items/Gems/Emerald.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Emerald : Item { - public class Emerald : Item + [Constructible] + public Emerald(int amount = 1) : base(0xF10) { - [Constructible] - public Emerald(int amount = 1) : base(0xF10) - { - Stackable = true; - Amount = amount; - } - - public Emerald(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Ruby.cs b/Projects/UOContent/Items/Gems/Ruby.cs index 536f2a06b..9d2712818 100644 --- a/Projects/UOContent/Items/Gems/Ruby.cs +++ b/Projects/UOContent/Items/Gems/Ruby.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Ruby : Item { - public class Ruby : Item + [Constructible] + public Ruby(int amount = 1) : base(0xF13) { - [Constructible] - public Ruby(int amount = 1) : base(0xF13) - { - Stackable = true; - Amount = amount; - } - - public Ruby(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Sapphire.cs b/Projects/UOContent/Items/Gems/Sapphire.cs index a43a7b7f5..870e4346a 100644 --- a/Projects/UOContent/Items/Gems/Sapphire.cs +++ b/Projects/UOContent/Items/Gems/Sapphire.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Sapphire : Item { - public class Sapphire : Item + [Constructible] + public Sapphire(int amount = 1) : base(0xF19) { - [Constructible] - public Sapphire(int amount = 1) : base(0xF19) - { - Stackable = true; - Amount = amount; - } - - public Sapphire(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/StarSapphire.cs b/Projects/UOContent/Items/Gems/StarSapphire.cs index 04f2aa463..06e464600 100644 --- a/Projects/UOContent/Items/Gems/StarSapphire.cs +++ b/Projects/UOContent/Items/Gems/StarSapphire.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class StarSapphire : Item { - public class StarSapphire : Item + [Constructible] + public StarSapphire(int amount = 1) : base(0xF21) { - [Constructible] - public StarSapphire(int amount = 1) : base(0xF21) - { - Stackable = true; - Amount = amount; - } - - public StarSapphire(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Gems/Tourmaline.cs b/Projects/UOContent/Items/Gems/Tourmaline.cs index c17503f80..8ead4268e 100644 --- a/Projects/UOContent/Items/Gems/Tourmaline.cs +++ b/Projects/UOContent/Items/Gems/Tourmaline.cs @@ -1,32 +1,16 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Tourmaline : Item { - public class Tourmaline : Item + [Constructible] + public Tourmaline(int amount = 1) : base(0xF2D) { - [Constructible] - public Tourmaline(int amount = 1) : base(0xF2D) - { - Stackable = true; - Amount = amount; - } - - public Tourmaline(Serial serial) : base(serial) - { - } - - public override double DefaultWeight => 0.1; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = true; + Amount = amount; } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Guilds/GuildDeed.cs b/Projects/UOContent/Items/Guilds/GuildDeed.cs index 4413b4bbc..ac0869a71 100644 --- a/Projects/UOContent/Items/Guilds/GuildDeed.cs +++ b/Projects/UOContent/Items/Guilds/GuildDeed.cs @@ -1,47 +1,71 @@ +using ModernUO.Serialization; using Server.Guilds; using Server.Multis; using Server.Prompts; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class GuildDeed : Item { - public class GuildDeed : Item + [Constructible] + public GuildDeed() : base(0x14F0) => Weight = 1.0; + + public override int LabelNumber => 1041055; // a guild deed + + public override void OnDoubleClick(Mobile from) { - [Constructible] - public GuildDeed() : base(0x14F0) => Weight = 1.0; - - public GuildDeed(Serial serial) : base(serial) + if (Guild.NewGuildSystem) { + return; } - public override int LabelNumber => 1041055; // a guild deed - - public override void Serialize(IGenericWriter writer) + if (!IsChildOf(from.Backpack)) { - base.Serialize(writer); - - writer.Write(0); // version + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - - public override void Deserialize(IGenericReader reader) + else if (from.Guild != null) { - base.Deserialize(reader); + from.SendLocalizedMessage(501137); // You must resign from your current guild before founding another! + } + else + { + var house = BaseHouse.FindHouseAt(from); - var version = reader.ReadInt(); - - if (Weight == 0.0) + if (house == null) { - Weight = 1.0; + 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); } } + } - public override void OnDoubleClick(Mobile from) + private class InternalPrompt : Prompt + { + private readonly GuildDeed m_Deed; + + public InternalPrompt(GuildDeed deed) => m_Deed = deed; + + public override void OnResponse(Mobile from, string text) { - if (Guild.NewGuildSystem) + if (m_Deed.Deleted) { return; } - if (!IsChildOf(from.Backpack)) + if (!m_Deed.IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } @@ -67,76 +91,30 @@ namespace Server.Items } else { - from.SendLocalizedMessage(1013060); // Enter new guild name (40 characters max): - from.Prompt = new InternalPrompt(this); + m_Deed.Delete(); + + if (text.Length > 40) + { + text = text[..40]; + } + + var guild = new Guild(from, text, "none"); + + from.Guild = guild; + from.GuildTitle = "Guildmaster"; + + var stone = new Guildstone(guild); + + stone.MoveToWorld(from.Location, from.Map); + + guild.Guildstone = stone; } } } - private class InternalPrompt : Prompt + public override void OnCancel(Mobile from) { - private readonly 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 - { - var 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[..40]; - } - - var guild = new Guild(from, text, "none"); - - from.Guild = guild; - from.GuildTitle = "Guildmaster"; - - var 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. - } + from.SendLocalizedMessage(501145); // Placement of guildstone cancelled. } } } diff --git a/Projects/UOContent/Items/Guilds/GuildTeleporter.cs b/Projects/UOContent/Items/Guilds/GuildTeleporter.cs index 8e2d0555f..81d7efa12 100644 --- a/Projects/UOContent/Items/Guilds/GuildTeleporter.cs +++ b/Projects/UOContent/Items/Guilds/GuildTeleporter.cs @@ -1,101 +1,65 @@ +using ModernUO.Serialization; using Server.Guilds; using Server.Multis; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class GuildTeleporter : Item { - public class GuildTeleporter : Item + [SerializableField(0)] + private Item _stone; + + [Constructible] + public GuildTeleporter(Item stone = null) : base(0x1869) { - private Item m_Stone; + Weight = 1.0; + LootType = LootType.Blessed; + _stone = stone; + } - [Constructible] - public GuildTeleporter(Item stone = null) : base(0x1869) + public override int LabelNumber => 1041054; // guildstone teleporter + + public override bool DisplayLootType => false; + + public override void OnDoubleClick(Mobile from) + { + if (Guild.NewGuildSystem) { - Weight = 1.0; - LootType = LootType.Blessed; - - m_Stone = stone; + return; } - public GuildTeleporter(Serial serial) : base(serial) + if (!IsChildOf(from.Backpack)) { + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + return; } - public override int LabelNumber => 1041054; // guildstone teleporter - - public override bool DisplayLootType => false; - - public override void Serialize(IGenericWriter writer) + if (_stone is not Guildstone gs || gs.Deleted || gs.Guild?.Teleporter != this) { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Stone); + from.SendLocalizedMessage(501197); // This teleporting object can not determine what guildstone to teleport + return; } - public override void Deserialize(IGenericReader reader) + var house = BaseHouse.FindHouseAt(from); + + if (house == null) { - base.Deserialize(reader); - LootType = LootType.Blessed; - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - m_Stone = reader.ReadEntity(); - - break; - } - } - - if (Weight == 0.0) - { - Weight = 1.0; - } + from.SendLocalizedMessage(501138); // You can only place a guildstone in a house. } - - public override void OnDoubleClick(Mobile from) + else if (!house.IsOwner(from)) { - if (Guild.NewGuildSystem) - { - return; - } - - var 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?.Deleted != false || stone.Guild?.Teleporter != this) - { - from.SendLocalizedMessage(501197); // This teleporting object can not determine what guildstone to teleport - } - else - { - var 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; - } - } + 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 + { + gs.MoveToWorld(from.Location, from.Map); + Delete(); + gs.Guild.Teleporter = null; } } } diff --git a/Projects/UOContent/Items/Guilds/Guildstone.cs b/Projects/UOContent/Items/Guilds/Guildstone.cs index f002a89b2..adce68a6c 100644 --- a/Projects/UOContent/Items/Guilds/Guildstone.cs +++ b/Projects/UOContent/Items/Guilds/Guildstone.cs @@ -1,3 +1,4 @@ +using ModernUO.Serialization; using Server.Factions; using Server.Guilds; using Server.Gumps; @@ -5,461 +6,311 @@ using Server.Multis; using Server.Network; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(4, false)] +public partial class Guildstone : Item, IAddon, IChoppable { - public class Guildstone : Item, IAddon, IChoppable + [InvalidateProperties] + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private string _guildName; + + [InvalidateProperties] + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private string _guildAbbrev; + + [InvalidateProperties] + [SerializableField(2, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Guild _guild; + + public Guildstone(Guild g) : this(g, g.Name, g.Abbreviation) { - private bool m_BeforeChangeover; - private string m_GuildAbbrev; - private string m_GuildName; + } - public Guildstone(Guild g) : this(g, g.Name, g.Abbreviation) + public Guildstone(Guild g, string guildName, string abbrev) : base(Guild.NewGuildSystem ? 0xED6 : 0xED4) + { + _guild = g; + _guildName = guildName; + _guildAbbrev = abbrev; + + Movable = false; + } + + public override int LabelNumber => 1041429; // a guildstone + + public Item Deed => new GuildstoneDeed(Guild, _guildName, _guildAbbrev); + + public bool CouldFit(IPoint3D p, Map map) => map.CanFit(p.X, p.Y, p.Z, ItemData.Height); + + public void OnChop(Mobile from) + { + if (!Guild.NewGuildSystem) { + return; } - public Guildstone(Guild g, string guildName, string abbrev) : base(Guild.NewGuildSystem ? 0xED6 : 0xED4) + var house = BaseHouse.FindHouseAt(this); + + if (house?.IsOwner(from) == true && house.Addons.Contains(this)) { - Guild = g; - m_GuildName = guildName; - m_GuildAbbrev = abbrev; + Effects.PlaySound(GetWorldLocation(), Map, 0x3B3); + from.SendLocalizedMessage(500461); // You destroy the item. - Movable = false; - } + Delete(); - public Guildstone(Serial serial) : base(serial) - { - } + house.Addons.Remove(this); - [CommandProperty(AccessLevel.GameMaster)] - public string GuildName - { - get => m_GuildName; - set + var deed = Deed; + + if (deed != null) { - m_GuildName = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public string GuildAbbrev - { - get => m_GuildAbbrev; - set - { - m_GuildAbbrev = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public Guild Guild { get; private set; } - - public override int LabelNumber => 1041429; // a guildstone - - public Item Deed => new GuildstoneDeed(Guild, m_GuildName, m_GuildAbbrev); - - public bool CouldFit(IPoint3D p, Map map) => map.CanFit(p.X, p.Y, p.Z, ItemData.Height); - - public void OnChop(Mobile from) - { - if (!Guild.NewGuildSystem) - { - return; - } - - var house = BaseHouse.FindHouseAt(this); - - var contains = false; - - if (house == null && m_BeforeChangeover || - house?.IsOwner(from) == true && (contains = house.Addons.Contains(this))) - { - Effects.PlaySound(GetWorldLocation(), Map, 0x3B3); - from.SendLocalizedMessage(500461); // You destroy the item. - - Delete(); - - if (contains) - { - house.Addons.Remove(this); - } - - var deed = Deed; - - if (deed != null) - { - from.AddToBackpack(deed); - } - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - if (Guild?.Disbanded == false) - { - m_GuildName = Guild.Name; - m_GuildAbbrev = Guild.Abbreviation; - } - - writer.Write(3); // version - - writer.Write(m_BeforeChangeover); - - writer.Write(m_GuildName); - writer.Write(m_GuildAbbrev); - - writer.Write(Guild); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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: - { - Guild = reader.ReadEntity(); - - goto case 0; - } - case 0: - { - break; - } - } - - if (Guild.NewGuildSystem && ItemID == 0xED4) - { - ItemID = 0xED6; - } - - if (version <= 2) - { - m_BeforeChangeover = true; - } - - if (Guild.NewGuildSystem && m_BeforeChangeover) - { - Timer.StartTimer(AddToHouse); - } - - if (!Guild.NewGuildSystem && Guild == null) - { - Delete(); - } - } - - private void AddToHouse() - { - var house = BaseHouse.FindHouseAt(this); - - if (Guild.NewGuildSystem && m_BeforeChangeover && house?.Addons.Contains(this) == false) - { - house.Addons.Add(this); - m_BeforeChangeover = false; - } - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - - if (Guild?.Disbanded == false) - { - string name; - string abbr; - - if ((name = Guild.Name) == null || (name = name.Trim()).Length <= 0) - { - name = "(unnamed)"; - } - - if ((abbr = Guild.Abbreviation) == null || (abbr = abbr.Trim()).Length <= 0) - { - abbr = ""; - } - - // list.Add( 1060802, Utility.FixHtml( name ) ); // Guild name: ~1_val~ - list.Add(1060802, $"{Utility.FixHtml(name)} [{Utility.FixHtml(abbr)}]"); - } - else if (m_GuildName != null && m_GuildAbbrev != null) - { - list.Add(1060802, $"{Utility.FixHtml(m_GuildName)} [{Utility.FixHtml(m_GuildAbbrev)}]"); - } - } - - public override void OnSingleClick(Mobile from) - { - base.OnSingleClick(from); - - if (Guild?.Disbanded == false) - { - string name; - - if ((name = Guild.Name) == null || (name = name.Trim()).Length <= 0) - { - name = "(unnamed)"; - } - - LabelTo(from, name); - } - else if (m_GuildName != null) - { - LabelTo(from, m_GuildName); - } - } - - public override void OnAfterDelete() - { - if (!Guild.NewGuildSystem && Guild?.Disbanded == false) - { - Guild.Disband(); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (Guild.NewGuildSystem) - { - return; - } - - if (Guild?.Disbanded != false) - { - Delete(); - } - else if (!from.InRange(GetWorldLocation(), 2)) - { - from.SendLocalizedMessage(500446); // That is too far away. - } - else if (Guild.Accepted.Contains(from)) - { - var guildState = PlayerState.Find(Guild.Leader); - var targetState = PlayerState.Find(from); - - var guildFaction = guildState?.Faction; - var targetFaction = targetState?.Faction; - - if (guildFaction != targetFaction || targetState?.IsLeaving == true) - { - return; - } - - if (guildState != null && targetState != null) - { - targetState.Leaving = guildState.Leaving; - } - - Guild.Accepted.Remove(from); - Guild.AddMember(from); - - GuildGump.EnsureClosed(from); - from.SendGump(new GuildGump(from, Guild)); - } - else if (from.AccessLevel < AccessLevel.GameMaster && !Guild.IsMember(from)) - { - from.NetState.SendMessageLocalized( - Serial, - ItemID, - MessageType.Regular, - 0x3B2, - 3, - 501158 - ); // You are not a member ... - } - else - { - GuildGump.EnsureClosed(from); - from.SendGump(new GuildGump(from, Guild)); + from.AddToBackpack(deed); } } } - [Flippable(0x14F0, 0x14EF)] - public class GuildstoneDeed : Item + private void Deserialize(IGenericReader reader, int version) { - private string m_GuildAbbrev; + reader.ReadBool(); // Before Change Over + _guildName = reader.ReadString(); + _guildAbbrev = reader.ReadString(); + _guild = reader.ReadEntity(); + } - private string m_GuildName; - - [Constructible] - public GuildstoneDeed(Guild g = null, string guildName = null, string abbrev = null) : base(0x14F0) + [AfterDeserialization(false)] + private void AfterDeserialization() + { + if (Guild.NewGuildSystem && ItemID == 0xED4) { - Guild = g; - m_GuildName = guildName; - m_GuildAbbrev = abbrev; - - Weight = 1.0; + ItemID = 0xED6; } - public GuildstoneDeed(Serial serial) : base(serial) + if (!Guild.NewGuildSystem && Guild == null) { + Delete(); + } + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (_guild?.Disbanded == false) + { + string name; + string abbr; + + if ((name = _guild.Name) == null || (name = name.Trim()).Length <= 0) + { + name = "(unnamed)"; + } + + if ((abbr = _guild.Abbreviation) == null || (abbr = abbr.Trim()).Length <= 0) + { + abbr = ""; + } + + // list.Add( 1060802, Utility.FixHtml( name ) ); // Guild name: ~1_val~ + list.Add(1060802, $"{Utility.FixHtml(name)} [{Utility.FixHtml(abbr)}]"); + } + else if (_guildName != null && _guildAbbrev != null) + { + list.Add(1060802, $"{Utility.FixHtml(_guildName)} [{Utility.FixHtml(_guildAbbrev)}]"); + } + } + + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick(from); + + if (_guild?.Disbanded == false) + { + string name; + + if ((name = _guild.Name) == null || (name = name.Trim()).Length <= 0) + { + name = "(unnamed)"; + } + + LabelTo(from, name); + } + else if (_guildName != null) + { + LabelTo(from, _guildName); + } + } + + public override void OnAfterDelete() + { + if (!Guild.NewGuildSystem && _guild?.Disbanded == false) + { + _guild.Disband(); + } + } + + public override void OnDoubleClick(Mobile from) + { + if (Guild.NewGuildSystem) + { + return; } - public override int LabelNumber => 1041233; // deed to a guildstone - - [CommandProperty(AccessLevel.GameMaster)] - public string GuildName + if (_guild?.Disbanded != false) { - get => m_GuildName; - set - { - m_GuildName = value; - InvalidateProperties(); - } + Delete(); } - - [CommandProperty(AccessLevel.GameMaster)] - public string GuildAbbrev + else if (!from.InRange(GetWorldLocation(), 2)) { - get => m_GuildAbbrev; - set - { - m_GuildAbbrev = value; - InvalidateProperties(); - } + from.SendLocalizedMessage(500446); // That is too far away. } - - [CommandProperty(AccessLevel.GameMaster)] - public Guild Guild { get; private set; } - - public override void Serialize(IGenericWriter writer) + else if (_guild.Accepted.Contains(from)) { - base.Serialize(writer); + var guildState = PlayerState.Find(_guild.Leader); + var targetState = PlayerState.Find(from); - if (Guild?.Disbanded == false) - { - m_GuildName = Guild.Name; - m_GuildAbbrev = Guild.Abbreviation; - } + var guildFaction = guildState?.Faction; + var targetFaction = targetState?.Faction; - writer.Write(1); // version - - writer.Write(m_GuildName); - writer.Write(m_GuildAbbrev); - - writer.Write(Guild); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 1: - { - m_GuildName = reader.ReadString(); - m_GuildAbbrev = reader.ReadString(); - - Guild = reader.ReadEntity(); - - break; - } - } - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - - if (Guild?.Disbanded == false) - { - string name; - string abbr; - - if ((name = Guild.Name) == null || (name = name.Trim()).Length <= 0) - { - name = "(unnamed)"; - } - - if ((abbr = Guild.Abbreviation) == null || (abbr = abbr.Trim()).Length <= 0) - { - abbr = ""; - } - - // list.Add( 1060802, Utility.FixHtml( name ) ); // Guild name: ~1_val~ - list.Add(1060802, $"{Utility.FixHtml(name)} [{Utility.FixHtml(abbr)}]"); - } - else if (m_GuildName != null && m_GuildAbbrev != null) - { - list.Add(1060802, $"{Utility.FixHtml(m_GuildName)} [{Utility.FixHtml(m_GuildAbbrev)}]"); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (IsChildOf(from.Backpack)) - { - var house = BaseHouse.FindHouseAt(from); - - if (house?.IsOwner(from) == true) - { - from.SendLocalizedMessage(1062838); // Where would you like to place this decoration? - from.BeginTarget(-1, true, TargetFlags.None, Placement_OnTarget); - } - 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) - { - if (targeted is not IPoint3D p || Deleted) + if (guildFaction != targetFaction || targetState?.IsLeaving == true) { return; } - var loc = new Point3D(p); - - var house = BaseHouse.FindHouseAt(loc, from.Map, 16); - - if (IsChildOf(from.Backpack)) + if (guildState != null && targetState != null) { - if (house?.IsOwner(from) == true) - { - Item addon = new Guildstone(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. + targetState.Leaving = guildState.Leaving; } + + _guild.Remove(_guild.Accepted, from); + _guild.AddMember(from); + + GuildGump.EnsureClosed(from); + from.SendGump(new GuildGump(from, Guild)); + } + else if (from.AccessLevel < AccessLevel.GameMaster && !_guild.IsMember(from)) + { + // You are not a member ... + from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, 501158); + } + else + { + GuildGump.EnsureClosed(from); + from.SendGump(new GuildGump(from, _guild)); + } + } +} + +[Flippable(0x14F0, 0x14EF)] +[SerializationGenerator(0, false)] +public partial class GuildstoneDeed : Item +{ + [InvalidateProperties] + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private string _guildName; + + [InvalidateProperties] + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private string _guildAbbrev; + + [InvalidateProperties] + [SerializableField(2, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Guild _guild; + + [Constructible] + public GuildstoneDeed(Guild g = null, string guildName = null, string abbrev = null) : base(0x14F0) + { + _guild = g; + _guildName = guildName; + _guildAbbrev = abbrev; + + Weight = 1.0; + } + + public override int LabelNumber => 1041233; // deed to a guildstone + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (_guild?.Disbanded == false) + { + string name; + string abbr; + + if ((name = _guild.Name) == null || (name = name.Trim()).Length <= 0) + { + name = "(unnamed)"; + } + + if ((abbr = _guild.Abbreviation) == null || (abbr = abbr.Trim()).Length <= 0) + { + abbr = ""; + } + + // list.Add( 1060802, Utility.FixHtml( name ) ); // Guild name: ~1_val~ + list.Add(1060802, $"{Utility.FixHtml(name)} [{Utility.FixHtml(abbr)}]"); + } + else if (_guildName != null && _guildAbbrev != null) + { + list.Add(1060802, $"{Utility.FixHtml(_guildName)} [{Utility.FixHtml(_guildAbbrev)}]"); + } + } + + 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; + } + + var house = BaseHouse.FindHouseAt(from); + + if (house?.IsOwner(from) == true) + { + from.SendLocalizedMessage(1062838); // Where would you like to place this decoration? + from.BeginTarget(-1, true, TargetFlags.None, Placement_OnTarget); + } + else + { + from.SendLocalizedMessage(502092); // You must be in your house to do this. + } + } + + public void Placement_OnTarget(Mobile from, object targeted) + { + if (targeted is not IPoint3D p || Deleted) + { + return; + } + + if (!IsChildOf(from.Backpack)) + { + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + return; + } + + var loc = new Point3D(p); + + var house = BaseHouse.FindHouseAt(loc, from.Map, 16); + if (house?.IsOwner(from) == true) + { + Item addon = new Guildstone(_guild, _guildName, _guildAbbrev); + + addon.MoveToWorld(loc, from.Map); + + house.Add(house.Addons, addon); + Delete(); + } + else + { + from.SendLocalizedMessage(1042036); // That location is not in your house. } } } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/BraceletOfHealth.cs b/Projects/UOContent/Items/Jewels/Artifacts/BraceletOfHealth.cs index 6aaf3a314..249e6b333 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/BraceletOfHealth.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/BraceletOfHealth.cs @@ -1,34 +1,18 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class BraceletOfHealth : GoldBracelet { - public class BraceletOfHealth : GoldBracelet + [Constructible] + public BraceletOfHealth() { - [Constructible] - public BraceletOfHealth() - { - Hue = 0x21; - Attributes.BonusHits = 5; - Attributes.RegenHits = 10; - } - - public BraceletOfHealth(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1061103; // Bracelet of Health - public override int ArtifactRarity => 11; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Hue = 0x21; + Attributes.BonusHits = 5; + Attributes.RegenHits = 10; } + + public override int LabelNumber => 1061103; // Bracelet of Health + public override int ArtifactRarity => 11; } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs b/Projects/UOContent/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs index 779ba556e..f089442a0 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs @@ -1,34 +1,18 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class EssenceOfBattle : GoldRing { - public class EssenceOfBattle : GoldRing + [Constructible] + public EssenceOfBattle() { - [Constructible] - public EssenceOfBattle() - { - Hue = 0x550; - Attributes.BonusDex = 7; - Attributes.BonusStr = 7; - Attributes.WeaponDamage = 30; - } - - public EssenceOfBattle(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1072935; // Essence of Battle - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } + Hue = 0x550; + Attributes.BonusDex = 7; + Attributes.BonusStr = 7; + Attributes.WeaponDamage = 30; } + + public override int LabelNumber => 1072935; // Essence of Battle } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs b/Projects/UOContent/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs index 180930246..e32120647 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs @@ -1,36 +1,20 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class PendantOfTheMagi : GoldNecklace { - public class PendantOfTheMagi : GoldNecklace + [Constructible] + public PendantOfTheMagi() { - [Constructible] - 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 int LabelNumber => 1072937; // Pendant of the Magi - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } + Hue = 0x48D; + Attributes.BonusInt = 10; + Attributes.RegenMana = 3; + Attributes.SpellDamage = 5; + Attributes.LowerManaCost = 10; + Attributes.LowerRegCost = 30; } + + public override int LabelNumber => 1072937; // Pendant of the Magi } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs b/Projects/UOContent/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs index 7e0edd560..907ac1e7d 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs @@ -1,39 +1,23 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class ResilientBracer : GoldBracelet { - public class ResilientBracer : GoldBracelet + [Constructible] + public ResilientBracer() { - [Constructible] - public ResilientBracer() - { - Hue = 0x488; + Hue = 0x488; - SkillBonuses.SetValues(0, SkillName.MagicResist, 15.0); + SkillBonuses.SetValues(0, SkillName.MagicResist, 15.0); - Attributes.BonusHits = 5; - Attributes.RegenHits = 2; - Attributes.DefendChance = 10; - } - - public ResilientBracer(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1072933; // Resillient Bracer - - public override int PhysicalResistance => 20; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } + Attributes.BonusHits = 5; + Attributes.RegenHits = 2; + Attributes.DefendChance = 10; } + + public override int LabelNumber => 1072933; // Resillient Bracer + + public override int PhysicalResistance => 20; } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs b/Projects/UOContent/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs index 623f99815..88f6afd7b 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs @@ -1,42 +1,21 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class OrnamentOfTheMagician : GoldBracelet { - public class OrnamentOfTheMagician : GoldBracelet + [Constructible] + public OrnamentOfTheMagician() { - [Constructible] - 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 int LabelNumber => 1061105; // Ornament of the Magician - public override int ArtifactRarity => 11; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Hue == 0x12B) - { - Hue = 0x554; - } - } + Hue = 0x554; + Attributes.CastRecovery = 3; + Attributes.CastSpeed = 2; + Attributes.LowerManaCost = 10; + Attributes.LowerRegCost = 20; + Resistances.Energy = 15; } + + public override int LabelNumber => 1061105; // Ornament of the Magician + public override int ArtifactRarity => 11; } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheElements.cs b/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheElements.cs index 25ffd9f83..6bd99bf4b 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheElements.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheElements.cs @@ -1,37 +1,21 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class RingOfTheElements : GoldRing { - public class RingOfTheElements : GoldRing + [Constructible] + public RingOfTheElements() { - [Constructible] - 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 int LabelNumber => 1061104; // Ring of the Elements - public override int ArtifactRarity => 11; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Hue = 0x4E9; + Attributes.Luck = 100; + Resistances.Fire = 16; + Resistances.Cold = 16; + Resistances.Poison = 16; + Resistances.Energy = 16; } + + public override int LabelNumber => 1061104; // Ring of the Elements + public override int ArtifactRarity => 11; } diff --git a/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheVile.cs b/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheVile.cs index 4a361064b..3a3613777 100644 --- a/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheVile.cs +++ b/Projects/UOContent/Items/Jewels/Artifacts/RingOfTheVile.cs @@ -1,41 +1,20 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class RingOfTheVile : GoldRing { - public class RingOfTheVile : GoldRing + [Constructible] + public RingOfTheVile() { - [Constructible] - public RingOfTheVile() - { - Hue = 0x4F7; - Attributes.BonusDex = 8; - Attributes.RegenStam = 6; - Attributes.AttackChance = 15; - Resistances.Poison = 20; - } - - public RingOfTheVile(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1061102; // Ring of the Vile - public override int ArtifactRarity => 11; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (Hue == 0x4F4) - { - Hue = 0x4F7; - } - } + Hue = 0x4F7; + Attributes.BonusDex = 8; + Attributes.RegenStam = 6; + Attributes.AttackChance = 15; + Resistances.Poison = 20; } + + public override int LabelNumber => 1061102; // Ring of the Vile + public override int ArtifactRarity => 11; } diff --git a/Projects/UOContent/Items/Jewels/BaseJewel.cs b/Projects/UOContent/Items/Jewels/BaseJewel.cs index ac7503be4..c15c2d6e0 100644 --- a/Projects/UOContent/Items/Jewels/BaseJewel.cs +++ b/Projects/UOContent/Items/Jewels/BaseJewel.cs @@ -1,499 +1,444 @@ using System; +using ModernUO.Serialization; using Server.Engines.Craft; -namespace Server.Items +namespace Server.Items; + +public enum GemType { - public enum GemType + None, + StarSapphire, + Emerald, + Sapphire, + Ruby, + Citrine, + Amethyst, + Tourmaline, + Amber, + Diamond +} + +[SerializationGenerator(4, false)] +public abstract partial class BaseJewel : Item, ICraftable +{ + [EncodedInt] + [InvalidateProperties] + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _maxHitPoints; + + // Field 1 + private int _hitPoints; + + [SerializableField(2, "private", "private")] + private CraftResource _rawResource; + + [SerializableField(3)] + [InvalidateProperties] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private GemType _gemType; + + [SerializableField(4, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster, canModify: true)]")] + private AosAttributes _attributes; + + [SerializableField(5, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster, canModify: true)]")] + private AosElementAttributes _resistances; + + [SerializableField(6, setter: "private")] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster, canModify: true)]")] + private AosSkillBonuses _skillBonuses; + + public BaseJewel(int itemID, Layer layer) : base(itemID) { - None, - StarSapphire, - Emerald, - Sapphire, - Ruby, - Citrine, - Amethyst, - Tourmaline, - Amber, - Diamond + _attributes = new AosAttributes(this); + _resistances = new AosElementAttributes(this); + _skillBonuses = new AosSkillBonuses(this); + _rawResource = CraftResource.Iron; + Hue = CraftResources.GetHue(_rawResource); + _gemType = GemType.None; + + Layer = layer; + + _hitPoints = _maxHitPoints = Utility.RandomMinMax(InitMinHits, InitMaxHits); } - public abstract class BaseJewel : Item, ICraftable + [EncodedInt] + [SerializableField(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int HitPoints { - private GemType m_GemType; - private int m_HitPoints; - private int m_MaxHitPoints; - private CraftResource m_Resource; - - public BaseJewel(int itemID, Layer layer) : base(itemID) + get => _hitPoints; + set { - Attributes = new AosAttributes(this); - Resistances = new AosElementAttributes(this); - SkillBonuses = new AosSkillBonuses(this); - m_Resource = CraftResource.Iron; - m_GemType = GemType.None; - - Layer = layer; - - m_HitPoints = m_MaxHitPoints = Utility.RandomMinMax(InitMinHits, InitMaxHits); - } - - public BaseJewel(Serial serial) : base(serial) - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public int MaxHitPoints - { - get => m_MaxHitPoints; - set + if (value != _hitPoints && _maxHitPoints > 0) { - m_MaxHitPoints = value; + _hitPoints = value; + + if (_hitPoints < 0) + { + Delete(); + } + else if (_hitPoints > _maxHitPoints) + { + _hitPoints = _maxHitPoints; + } + InvalidateProperties(); + this.MarkDirty(); } } + } - [CommandProperty(AccessLevel.GameMaster)] - public int HitPoints + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource + { + get => _rawResource; + set { - get => 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(); - } - } + _rawResource = value; + Hue = CraftResources.GetHue(_rawResource); } + } - [CommandProperty(AccessLevel.GameMaster, canModify: true)] - public AosAttributes Attributes { get; private set; } + public override int PhysicalResistance => Resistances.Physical; + public override int FireResistance => Resistances.Fire; + public override int ColdResistance => Resistances.Cold; + public override int PoisonResistance => Resistances.Poison; + public override int EnergyResistance => Resistances.Energy; + public virtual int BaseGemTypeNumber => 0; - [CommandProperty(AccessLevel.GameMaster, canModify: true)] - public AosElementAttributes Resistances { get; private set; } + public virtual int InitMinHits => 0; + public virtual int InitMaxHits => 0; - [CommandProperty(AccessLevel.GameMaster, canModify: true)] - public AosSkillBonuses SkillBonuses { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource + public override int LabelNumber + { + get { - get => m_Resource; - set + if (_gemType == GemType.None) { - m_Resource = value; - Hue = CraftResources.GetHue(m_Resource); + return base.LabelNumber; } - } - [CommandProperty(AccessLevel.GameMaster)] - public GemType GemType + return BaseGemTypeNumber + (int)_gemType - 1; + } + } + + public virtual int ArtifactRarity => 0; + + public int OnCraft( + int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, + CraftItem craftItem, int resHue + ) + { + var resourceType = typeRes ?? craftItem.Resources[0].ItemType; + + Resource = CraftResources.GetFromType(resourceType); + + var context = craftSystem.GetContext(from); + + if (context?.DoNotColor == true) { - get => m_GemType; - set - { - m_GemType = value; - InvalidateProperties(); - } + Hue = 0; } - public override int PhysicalResistance => Resistances.Physical; - public override int FireResistance => Resistances.Fire; - public override int ColdResistance => Resistances.Cold; - public override int PoisonResistance => Resistances.Poison; - public override int EnergyResistance => Resistances.Energy; - public virtual int BaseGemTypeNumber => 0; - - public virtual int InitMinHits => 0; - public virtual int InitMaxHits => 0; - - public override int LabelNumber + if (craftItem.Resources.Count > 1) { - get - { - if (m_GemType == GemType.None) - { - return base.LabelNumber; - } + resourceType = craftItem.Resources[1].ItemType; - return BaseGemTypeNumber + (int)m_GemType - 1; + 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; } } - public virtual int ArtifactRarity => 0; + return 1; + } - public int OnCraft( - int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, - CraftItem craftItem, int resHue - ) + public override void OnAfterDuped(Item newItem) + { + if (newItem is not BaseJewel jewel) { - var resourceType = typeRes ?? craftItem.Resources[0].ItemType; - - Resource = CraftResources.GetFromType(resourceType); - - var context = craftSystem.GetContext(from); - - if (context?.DoNotColor == true) - { - Hue = 0; - } - - if (craftItem.Resources.Count > 1) - { - resourceType = craftItem.Resources[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; + return; } - public override void OnAfterDuped(Item newItem) + jewel.Attributes = new AosAttributes(newItem, Attributes); + jewel.Resistances = new AosElementAttributes(newItem, Resistances); + jewel.SkillBonuses = new AosSkillBonuses(newItem, SkillBonuses); + } + + public override void OnAdded(IEntity parent) + { + if (Core.AOS && parent is Mobile from) { - if (newItem is not BaseJewel jewel) + SkillBonuses.AddTo(from); + + var strBonus = Attributes.BonusStr; + var dexBonus = Attributes.BonusDex; + var intBonus = Attributes.BonusInt; + + if (strBonus != 0 || dexBonus != 0 || intBonus != 0) { - return; - } + var serial = Serial; - jewel.Attributes = new AosAttributes(newItem, Attributes); - jewel.Resistances = new AosElementAttributes(newItem, Resistances); - jewel.SkillBonuses = new AosSkillBonuses(newItem, SkillBonuses); - } - - public override void OnAdded(IEntity parent) - { - if (Core.AOS && parent is Mobile from) - { - SkillBonuses.AddTo(from); - - var strBonus = Attributes.BonusStr; - var dexBonus = Attributes.BonusDex; - var intBonus = Attributes.BonusInt; - - if (strBonus != 0 || dexBonus != 0 || intBonus != 0) + if (strBonus != 0) { - var modName = 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.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } - from.CheckStatTimers(); - } - } + if (dexBonus != 0) + { + from.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); + } - public override void OnRemoved(IEntity parent) + if (intBonus != 0) + { + from.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); + } + } + + from.CheckStatTimers(); + } + } + + public override void OnRemoved(IEntity parent) + { + if (Core.AOS && parent is Mobile from) { - if (Core.AOS && parent is Mobile from) - { - SkillBonuses.Remove(); + SkillBonuses.Remove(); - var modName = Serial.ToString(); + var serial = Serial; - from.RemoveStatMod($"{modName}Str"); - from.RemoveStatMod($"{modName}Dex"); - from.RemoveStatMod($"{modName}Int"); + from.RemoveStatMod($"{serial}Str"); + from.RemoveStatMod($"{serial}Dex"); + from.RemoveStatMod($"{serial}Int"); - from.CheckStatTimers(); - } + from.CheckStatTimers(); } + } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + SkillBonuses.GetProperties(list); + + int prop; + + if ((prop = ArtifactRarity) > 0) { - base.GetProperties(list); - - SkillBonuses.GetProperties(list); - - int prop; - - if ((prop = ArtifactRarity) > 0) - { - list.Add(1061078, prop.ToString()); // artifact rarity ~1_val~ - } - - if ((prop = Attributes.WeaponDamage) != 0) - { - list.Add(1060401, prop.ToString()); // damage increase ~1_val~% - } - - if ((prop = Attributes.DefendChance) != 0) - { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% - } - - if ((prop = Attributes.BonusDex) != 0) - { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ - } - - if ((prop = Attributes.EnhancePotions) != 0) - { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% - } - - if ((prop = Attributes.CastRecovery) != 0) - { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ - } - - if ((prop = Attributes.CastSpeed) != 0) - { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ - } - - if ((prop = Attributes.AttackChance) != 0) - { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% - } - - if ((prop = Attributes.BonusHits) != 0) - { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ - } - - if ((prop = Attributes.BonusInt) != 0) - { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ - } - - if ((prop = Attributes.LowerManaCost) != 0) - { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% - } - - if ((prop = Attributes.LowerRegCost) != 0) - { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% - } - - if ((prop = Attributes.Luck) != 0) - { - list.Add(1060436, prop.ToString()); // luck ~1_val~ - } - - if ((prop = Attributes.BonusMana) != 0) - { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ - } - - if ((prop = Attributes.RegenMana) != 0) - { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ - } - - if (Attributes.NightSight != 0) - { - list.Add(1060441); // night sight - } - - if ((prop = Attributes.ReflectPhysical) != 0) - { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% - } - - if ((prop = Attributes.RegenStam) != 0) - { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ - } - - if ((prop = Attributes.RegenHits) != 0) - { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ - } - - if (Attributes.SpellChanneling != 0) - { - list.Add(1060482); // spell channeling - } - - if ((prop = Attributes.SpellDamage) != 0) - { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% - } - - if ((prop = Attributes.BonusStam) != 0) - { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ - } - - if ((prop = Attributes.BonusStr) != 0) - { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ - } - - if ((prop = Attributes.WeaponSpeed) != 0) - { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% - } - - if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) - { - list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~% - } - - AddResistanceProperties(list); - - if (m_HitPoints >= 0 && m_MaxHitPoints > 0) - { - list.Add(1060639, "{0}\t{1}", m_HitPoints, m_MaxHitPoints); // durability ~1_val~ / ~2_val~ - } + list.Add(1061078, prop); // artifact rarity ~1_val~ } - public override void Serialize(IGenericWriter writer) + if ((prop = Attributes.WeaponDamage) != 0) { - base.Serialize(writer); - - writer.Write(3); // version - - writer.WriteEncodedInt(m_MaxHitPoints); - writer.WriteEncodedInt(m_HitPoints); - - writer.WriteEncodedInt((int)m_Resource); - writer.WriteEncodedInt((int)m_GemType); - - Attributes.Serialize(writer); - Resistances.Serialize(writer); - SkillBonuses.Serialize(writer); + list.Add(1060401, prop); // damage increase ~1_val~% } - public override void Deserialize(IGenericReader reader) + if ((prop = Attributes.DefendChance) != 0) { - base.Deserialize(reader); + list.Add(1060408, prop); // defense chance increase ~1_val~% + } - var version = reader.ReadInt(); + if ((prop = Attributes.BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } - switch (version) + if ((prop = Attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = Attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = Attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = Attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = Attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = Attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = Attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = Attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = Attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if ((prop = Attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = Attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (Attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = Attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = Attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = Attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if (Attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = Attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = Attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = Attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = Attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } + + AddResistanceProperties(list); + + if (_hitPoints >= 0 && _maxHitPoints > 0) + { + list.Add(1060639, $"{_hitPoints}\t{_maxHitPoints}"); // durability ~1_val~ / ~2_val~ + } + } + + private void Deserialize(IGenericReader reader, int version) + { + _maxHitPoints = reader.ReadEncodedInt(); + _hitPoints = reader.ReadEncodedInt(); + _rawResource = (CraftResource)reader.ReadEncodedInt(); + _gemType = (GemType)reader.ReadEncodedInt(); + _attributes = new AosAttributes(this); + _attributes.Deserialize(reader); + _resistances = new AosElementAttributes(this); + _resistances.Deserialize(reader); + _skillBonuses = new AosSkillBonuses(this); + _skillBonuses.Deserialize(reader); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + var m = Parent as Mobile; + + if (Core.AOS && m != null) + { + SkillBonuses.AddTo(m); + } + + var strBonus = Attributes.BonusStr; + var dexBonus = Attributes.BonusDex; + var intBonus = Attributes.BonusInt; + + if (m != null && (strBonus != 0 || dexBonus != 0 || intBonus != 0)) + { + var serial = Serial; + + if (strBonus != 0) { - case 3: - { - m_MaxHitPoints = reader.ReadEncodedInt(); - m_HitPoints = reader.ReadEncodedInt(); - - goto case 2; - } - case 2: - { - m_Resource = (CraftResource)reader.ReadEncodedInt(); - m_GemType = (GemType)reader.ReadEncodedInt(); - - goto case 1; - } - case 1: - { - Attributes = new AosAttributes(this); - Attributes.Deserialize(reader); - Resistances = new AosElementAttributes(this); - Resistances.Deserialize(reader); - SkillBonuses = new AosSkillBonuses(this); - SkillBonuses.Deserialize(reader); - - var m = Parent as Mobile; - - if (Core.AOS && m != null) - { - SkillBonuses.AddTo(m); - } - - var strBonus = Attributes.BonusStr; - var dexBonus = Attributes.BonusDex; - var intBonus = Attributes.BonusInt; - - if (m != null && (strBonus != 0 || dexBonus != 0 || intBonus != 0)) - { - var 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)); - } - } - - m?.CheckStatTimers(); - - break; - } - case 0: - { - Attributes = new AosAttributes(this); - Resistances = new AosElementAttributes(this); - SkillBonuses = new AosSkillBonuses(this); - - break; - } + m.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } - if (version < 2) + if (dexBonus != 0) { - m_Resource = CraftResource.Iron; - m_GemType = GemType.None; + m.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); + } + + if (intBonus != 0) + { + m.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } + + m?.CheckStatTimers(); } } diff --git a/Projects/UOContent/Items/Jewels/Beads.cs b/Projects/UOContent/Items/Jewels/Beads.cs index da34bfca4..e60572be2 100644 --- a/Projects/UOContent/Items/Jewels/Beads.cs +++ b/Projects/UOContent/Items/Jewels/Beads.cs @@ -1,24 +1,10 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Beads : Item { - public class Beads : Item - { - [Constructible] - public Beads() : base(0x108B) => Weight = 1.0; - - public Beads(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } + [Constructible] + public Beads() : base(0x108B) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Jewels/Bracelet.cs b/Projects/UOContent/Items/Jewels/Bracelet.cs index f300bd582..7a5aa44f8 100644 --- a/Projects/UOContent/Items/Jewels/Bracelet.cs +++ b/Projects/UOContent/Items/Jewels/Bracelet.cs @@ -1,77 +1,27 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class BaseBracelet : BaseJewel { - public abstract class BaseBracelet : BaseJewel + public BaseBracelet(int itemID) : base(itemID, Layer.Bracelet) { - public BaseBracelet(int itemID) : base(itemID, Layer.Bracelet) - { - } - - public BaseBracelet(Serial serial) : base(serial) - { - } - - public override int BaseGemTypeNumber => 1044221; // star sapphire bracelet - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } } - public class GoldBracelet : BaseBracelet - { - [Constructible] - public GoldBracelet() : base(0x1086) => Weight = 0.1; - - public GoldBracelet(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SilverBracelet : BaseBracelet - { - [Constructible] - public SilverBracelet() : base(0x1F06) => Weight = 0.1; - - public SilverBracelet(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } + public override int BaseGemTypeNumber => 1044221; // star sapphire bracelet +} + +[SerializationGenerator(0, false)] +public partial class GoldBracelet : BaseBracelet +{ + [Constructible] + public GoldBracelet() : base(0x1086) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class SilverBracelet : BaseBracelet +{ + [Constructible] + public SilverBracelet() : base(0x1F06) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Earrings.cs b/Projects/UOContent/Items/Jewels/Earrings.cs index f99ae91dd..73a6ccd09 100644 --- a/Projects/UOContent/Items/Jewels/Earrings.cs +++ b/Projects/UOContent/Items/Jewels/Earrings.cs @@ -1,77 +1,27 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class BaseEarrings : BaseJewel { - public abstract class BaseEarrings : BaseJewel + public BaseEarrings(int itemID) : base(itemID, Layer.Earrings) { - public BaseEarrings(int itemID) : base(itemID, Layer.Earrings) - { - } - - public BaseEarrings(Serial serial) : base(serial) - { - } - - public override int BaseGemTypeNumber => 1044203; // star sapphire earrings - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } } - public class GoldEarrings : BaseEarrings - { - [Constructible] - public GoldEarrings() : base(0x1087) => Weight = 0.1; - - public GoldEarrings(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SilverEarrings : BaseEarrings - { - [Constructible] - public SilverEarrings() : base(0x1F07) => Weight = 0.1; - - public SilverEarrings(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } + public override int BaseGemTypeNumber => 1044203; // star sapphire earrings +} + +[SerializationGenerator(0, false)] +public partial class GoldEarrings : BaseEarrings +{ + [Constructible] + public GoldEarrings() : base(0x1087) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class SilverEarrings : BaseEarrings +{ + [Constructible] + public SilverEarrings() : base(0x1F07) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Necklace.cs b/Projects/UOContent/Items/Jewels/Necklace.cs index baa9cc827..d4cd1eb9e 100644 --- a/Projects/UOContent/Items/Jewels/Necklace.cs +++ b/Projects/UOContent/Items/Jewels/Necklace.cs @@ -1,149 +1,48 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class BaseNecklace : BaseJewel { - public abstract class BaseNecklace : BaseJewel + public BaseNecklace(int itemID) : base(itemID, Layer.Neck) { - public BaseNecklace(int itemID) : base(itemID, Layer.Neck) - { - } - - public BaseNecklace(Serial serial) : base(serial) - { - } - - public override int BaseGemTypeNumber => 1044241; // star sapphire necklace - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } } - public class Necklace : BaseNecklace - { - [Constructible] - public Necklace() : base(0x1085) => Weight = 0.1; - - public Necklace(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GoldNecklace : BaseNecklace - { - [Constructible] - public GoldNecklace() : base(0x1088) => Weight = 0.1; - - public GoldNecklace(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GoldBeadNecklace : BaseNecklace - { - [Constructible] - public GoldBeadNecklace() : base(0x1089) => Weight = 0.1; - - public GoldBeadNecklace(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SilverNecklace : BaseNecklace - { - [Constructible] - public SilverNecklace() : base(0x1F08) => Weight = 0.1; - - public SilverNecklace(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SilverBeadNecklace : BaseNecklace - { - [Constructible] - public SilverBeadNecklace() : base(0x1F05) => Weight = 0.1; - - public SilverBeadNecklace(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } + public override int BaseGemTypeNumber => 1044241; // star sapphire necklace +} + +[SerializationGenerator(0, false)] +public partial class Necklace : BaseNecklace +{ + [Constructible] + public Necklace() : base(0x1085) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class GoldNecklace : BaseNecklace +{ + [Constructible] + public GoldNecklace() : base(0x1088) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class GoldBeadNecklace : BaseNecklace +{ + [Constructible] + public GoldBeadNecklace() : base(0x1089) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class SilverNecklace : BaseNecklace +{ + [Constructible] + public SilverNecklace() : base(0x1F08) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class SilverBeadNecklace : BaseNecklace +{ + [Constructible] + public SilverBeadNecklace() : base(0x1F05) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Ring.cs b/Projects/UOContent/Items/Jewels/Ring.cs index 140840ace..9c142f099 100644 --- a/Projects/UOContent/Items/Jewels/Ring.cs +++ b/Projects/UOContent/Items/Jewels/Ring.cs @@ -1,77 +1,27 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class BaseRing : BaseJewel { - public abstract class BaseRing : BaseJewel + public BaseRing(int itemID) : base(itemID, Layer.Ring) { - public BaseRing(int itemID) : base(itemID, Layer.Ring) - { - } - - public BaseRing(Serial serial) : base(serial) - { - } - - public override int BaseGemTypeNumber => 1044176; // star sapphire ring - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } } - public class GoldRing : BaseRing - { - [Constructible] - public GoldRing() : base(0x108a) => Weight = 0.1; - - public GoldRing(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SilverRing : BaseRing - { - [Constructible] - public SilverRing() : base(0x1F09) => Weight = 0.1; - - public SilverRing(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } + public override int BaseGemTypeNumber => 1044176; // star sapphire ring +} + +[SerializationGenerator(0, false)] +public partial class GoldRing : BaseRing +{ + [Constructible] + public GoldRing() : base(0x108a) => Weight = 0.1; +} + +[SerializationGenerator(0, false)] +public partial class SilverRing : BaseRing +{ + [Constructible] + public SilverRing() : base(0x1F09) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Lights/BaseEquipableLight.cs b/Projects/UOContent/Items/Lights/BaseEquipableLight.cs new file mode 100644 index 000000000..8e0482cf9 --- /dev/null +++ b/Projects/UOContent/Items/Lights/BaseEquipableLight.cs @@ -0,0 +1,109 @@ +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public abstract partial class BaseEquipableLight : BaseLight +{ + [Constructible] + public BaseEquipableLight(int itemID) : base(itemID) => Layer = Layer.TwoHanded; + + private BaseEquipableLight SplitStack() + { + if (!Stackable || Amount < 2) + { + return null; + } + + var stack = Mobile.LiftItemDupe(this, 1); + stack.BurntOut = BurntOut; + stack.Duration = Duration; + stack.Light = Light; + stack.Protected = Protected; + + return stack; + } + + public override bool OnEquip(Mobile from) + { + if (!base.OnEquip(from)) + { + return false; + } + + var stack = SplitStack(); + if (stack != null && stack.Parent != from.Backpack) + { + if (from.AddToBackpack(stack)) + { + if (this is Candle) + { + from.SendLocalizedMessage(502967); // You put the remaining unlit candles into your backpack. + } + else if (this is Torch) + { + from.SendLocalizedMessage(502970); // You put the remaining unlit torches into your backpack. + } + } + else + { + stack.MoveToWorld(from.Location, from.Map); + } + } + + return true; + } + + public override void Ignite() + { + if (Parent is not Mobile && RootParent is Mobile holder) + { + 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(502972); // You put the torch in your left hand. + } + + // No message for lanterns? + } + else + { + SplitStack(); + MoveToWorld(holder.Location, holder.Map); + + if (this is Candle) + { + holder.SendLocalizedMessage(502968); // You cannot hold the candle, so it has been placed at your feet. + } + else if (this is Torch) + { + // 502971 has the wrong message + holder.SendMessage("You cannot hold the torch, so it has been placed at your feet."); + } + + // No message for lanterns? + } + } + else + { + SplitStack(); + } + + base.Ignite(); + } + + public override void OnAdded(IEntity parent) + { + if (Burning && parent is Container) + { + Douse(); + } + + base.OnAdded(parent); + } +} diff --git a/Projects/UOContent/Items/Lights/BaseEquippableLight.cs b/Projects/UOContent/Items/Lights/BaseEquippableLight.cs deleted file mode 100644 index 5310b457c..000000000 --- a/Projects/UOContent/Items/Lights/BaseEquippableLight.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace Server.Items -{ - public abstract class BaseEquipableLight : BaseLight - { - [Constructible] - public BaseEquipableLight(int itemID) : base(itemID) => Layer = Layer.TwoHanded; - - public BaseEquipableLight(Serial serial) : base(serial) - { - } - - public override void Ignite() - { - if (Parent is not Mobile && RootParent is Mobile holder) - { - 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(IEntity parent) - { - if (Burning && parent is Container) - { - Douse(); - } - - base.OnAdded(parent); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } -} diff --git a/Projects/UOContent/Items/Lights/BaseLight.cs b/Projects/UOContent/Items/Lights/BaseLight.cs index eb83b594b..7aa6742d4 100644 --- a/Projects/UOContent/Items/Lights/BaseLight.cs +++ b/Projects/UOContent/Items/Lights/BaseLight.cs @@ -1,233 +1,213 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(1, false)] +public abstract partial class BaseLight : Item { - public abstract class BaseLight : Item + public static readonly bool Burnout = false; + + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _burntOut; + + // Field 1 + private bool _burning; + + // Field 2 + private TimeSpan _duration = TimeSpan.Zero; + + [SerializableField(3)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _protected; + + [TimerDrift] + [SerializableField(4, getter: "private", setter: "private")] + private Timer _burnTimer; + + [DeserializeTimerField(4)] + private void DeserializeTimer(TimeSpan delay) { - public static readonly bool Burnout = false; - private bool m_Burning; - private TimeSpan m_Duration = TimeSpan.Zero; - private DateTime m_End; - private Timer m_Timer; - - [Constructible] - public BaseLight(int itemID) : base(itemID) + if (_burning && _duration != TimeSpan.Zero) { + DoTimer(delay); } + } - public BaseLight(Serial serial) : base(serial) + [Constructible] + public BaseLight(int itemID) : base(itemID) + { + } + + public abstract int LitItemID { get; } + + public virtual int UnlitItemID => 0; + public virtual int BurntOutItemID => 0; + + public virtual int LitSound => 0x47; + public virtual int UnlitSound => 0x3be; + public virtual int BurntOutSound => 0x4b8; + + [SerializableField(1)] + [CommandProperty(AccessLevel.GameMaster)] + public bool Burning + { + get => _burning; + set { - } - - public abstract int LitItemID { get; } - - public virtual int UnlitItemID => 0; - public virtual int BurntOutItemID => 0; - - public virtual int LitSound => 0x47; - public virtual int UnlitSound => 0x3be; - public virtual int BurntOutSound => 0x4b8; - - [CommandProperty(AccessLevel.GameMaster)] - public bool Burning - { - get => m_Burning; - set + if (_burning != value) { - if (m_Burning != value) - { - m_Burning = true; - DoTimer(m_Duration); - } + _burning = true; + DoTimer(_duration); + this.MarkDirty(); } } + } - [CommandProperty(AccessLevel.GameMaster)] - public bool BurntOut { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool Protected { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public TimeSpan Duration + [SerializableField(2)] + [CommandProperty(AccessLevel.GameMaster)] + public TimeSpan Duration + { + get => _duration != TimeSpan.Zero && _burning && _burnTimer != null ? _burnTimer.Next - Core.Now : _duration; + set { - get => m_Duration != TimeSpan.Zero && m_Burning ? m_End - Core.Now : m_Duration; - set => m_Duration = value; + _duration = value; + this.MarkDirty(); + } + } + + public virtual void PlayLitSound() + { + if (LitSound != 0) + { + var loc = GetWorldLocation(); + Effects.PlaySound(loc, Map, LitSound); + } + } + + public virtual void PlayUnlitSound() + { + var sound = UnlitSound; + + if (BurntOut && BurntOutSound != 0) + { + sound = BurntOutSound; } - public virtual void PlayLitSound() + if (sound != 0) { - if (LitSound != 0) - { - var loc = GetWorldLocation(); - Effects.PlaySound(loc, Map, LitSound); - } + var loc = GetWorldLocation(); + Effects.PlaySound(loc, Map, sound); + } + } + + public virtual void Ignite() + { + if (!BurntOut) + { + PlayLitSound(); + + _burning = true; + ItemID = LitItemID; + DoTimer(_duration); + } + } + + public virtual void Douse() + { + _burning = false; + + ItemID = BurntOut && BurntOutItemID != 0 ? BurntOutItemID : UnlitItemID; + + if (BurntOut) + { + _duration = TimeSpan.Zero; + } + else if (_duration != TimeSpan.Zero) + { + _duration = _burnTimer.Next - Core.Now; } - public virtual void PlayUnlitSound() + _burnTimer?.Stop(); + this.MarkDirty(); + + PlayUnlitSound(); + } + + public virtual void Burn() + { + BurntOut = true; + Douse(); + } + + private void DoTimer(TimeSpan delay) + { + _duration = delay; + _burnTimer?.Stop(); + this.MarkDirty(); + + if (delay == TimeSpan.Zero) { - var sound = UnlitSound; - - if (BurntOut && BurntOutSound != 0) - { - sound = BurntOutSound; - } - - if (sound != 0) - { - var loc = GetWorldLocation(); - Effects.PlaySound(loc, Map, sound); - } + return; } - public virtual void Ignite() - { - if (!BurntOut) - { - PlayLitSound(); + _burnTimer = new InternalTimer(this, delay); + _burnTimer.Start(); + this.MarkDirty(); + } - m_Burning = true; - ItemID = LitItemID; - DoTimer(m_Duration); - } + public override void OnDoubleClick(Mobile from) + { + if (_burntOut) + { + return; } - public virtual void Douse() + if (_protected && from.AccessLevel == AccessLevel.Player) { - m_Burning = false; - - if (BurntOut && BurntOutItemID != 0) - { - ItemID = BurntOutItemID; - } - else - { - ItemID = UnlitItemID; - } - - if (BurntOut) - { - m_Duration = TimeSpan.Zero; - } - else if (m_Duration != TimeSpan.Zero) - { - m_Duration = m_End - Core.Now; - } - - m_Timer?.Stop(); - - PlayUnlitSound(); + return; } - public virtual void Burn() + if (!from.InRange(GetWorldLocation(), 2)) + { + return; + } + + if (!_burning) + { + Ignite(); + } + else if (UnlitItemID != 0) { - BurntOut = true; Douse(); } + } - private void DoTimer(TimeSpan delay) + private void Deserialize(IGenericReader reader, int version) + { + _burntOut = reader.ReadBool(); + _burning = reader.ReadBool(); + _duration = reader.ReadTimeSpan(); + _protected = reader.ReadBool(); + + if (_burning && _duration != TimeSpan.Zero) { - m_Duration = delay; - - m_Timer?.Stop(); - - if (delay == TimeSpan.Zero) - { - return; - } - - m_End = Core.Now + delay; - - m_Timer = new InternalTimer(this, delay); - m_Timer.Start(); + DoTimer(reader.ReadDeltaTime() - Core.Now); } + } - public override void OnDoubleClick(Mobile from) + private class InternalTimer : Timer + { + private readonly BaseLight m_Light; + + public InternalTimer(BaseLight light, TimeSpan delay) : base(delay) => m_Light = light; + + protected override void OnTick() { - if (BurntOut) + if (m_Light?.Deleted == false) { - return; - } - - if (Protected && from.AccessLevel == AccessLevel.Player) - { - return; - } - - if (!from.InRange(GetWorldLocation(), 2)) - { - return; - } - - if (m_Burning) - { - if (UnlitItemID != 0) - { - Douse(); - } - } - else - { - Ignite(); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - writer.Write(BurntOut); - writer.Write(m_Burning); - writer.Write(m_Duration); - writer.Write(Protected); - - if (m_Burning && m_Duration != TimeSpan.Zero) - { - writer.WriteDeltaTime(m_End); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - BurntOut = reader.ReadBool(); - m_Burning = reader.ReadBool(); - m_Duration = reader.ReadTimeSpan(); - Protected = reader.ReadBool(); - - if (m_Burning && m_Duration != TimeSpan.Zero) - { - DoTimer(reader.ReadDeltaTime() - Core.Now); - } - - break; - } - } - } - - private class InternalTimer : Timer - { - private readonly BaseLight m_Light; - - public InternalTimer(BaseLight light, TimeSpan delay) : base(delay) - { - m_Light = light; - } - - protected override void OnTick() - { - if (m_Light?.Deleted == false) - { - m_Light.Burn(); - } + m_Light.Burn(); } } } diff --git a/Projects/UOContent/Items/Lights/Brazier.cs b/Projects/UOContent/Items/Lights/Brazier.cs index 65b6f8066..56f834c3b 100644 --- a/Projects/UOContent/Items/Lights/Brazier.cs +++ b/Projects/UOContent/Items/Lights/Brazier.cs @@ -1,35 +1,20 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Brazier : BaseLight { - public class Brazier : BaseLight + [Constructible] + public Brazier() : base(0xE31) { - [Constructible] - 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 int LitItemID => 0xE31; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = true; + Light = LightType.Circle225; + Weight = 20.0; } + + public override int LitItemID => 0xE31; } diff --git a/Projects/UOContent/Items/Lights/BrazierTall.cs b/Projects/UOContent/Items/Lights/BrazierTall.cs index 0774217a8..c23945c50 100644 --- a/Projects/UOContent/Items/Lights/BrazierTall.cs +++ b/Projects/UOContent/Items/Lights/BrazierTall.cs @@ -1,35 +1,20 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class BrazierTall : BaseLight { - public class BrazierTall : BaseLight + [Constructible] + public BrazierTall() : base(0x19AA) { - [Constructible] - 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 int LitItemID => 0x19AA; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = true; + Light = LightType.Circle300; + Weight = 25.0; } + + public override int LitItemID => 0x19AA; } diff --git a/Projects/UOContent/Items/Lights/Candelabra.cs b/Projects/UOContent/Items/Lights/Candelabra.cs index 81889c01e..823313f2d 100644 --- a/Projects/UOContent/Items/Lights/Candelabra.cs +++ b/Projects/UOContent/Items/Lights/Candelabra.cs @@ -1,69 +1,44 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Candelabra : BaseLight, IShipwreckedItem { - public class Candelabra : BaseLight, IShipwreckedItem + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _isShipwreckedItem; + + [Constructible] + public Candelabra() : base(0xA27) { - [Constructible] - public Candelabra() : base(0xA27) + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle225; + Weight = 3.0; + } + + public override int LitItemID => 0xB1D; + public override int UnlitItemID => 0xA27; + + public override void AddNameProperties(IPropertyList list) + { + base.AddNameProperties(list); + + if (IsShipwreckedItem) { - Duration = TimeSpan.Zero; // Never burnt out - Burning = false; - Light = LightType.Circle225; - Weight = 3.0; + list.Add(1041645); // recovered from a shipwreck } + } - public Candelabra(Serial serial) : base(serial) + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick(from); + + if (IsShipwreckedItem) { - } - - public override int LitItemID => 0xB1D; - public override int UnlitItemID => 0xA27; - - [CommandProperty(AccessLevel.GameMaster)] - public bool IsShipwreckedItem { get; set; } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(1); - - writer.Write(IsShipwreckedItem); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - switch (version) - { - case 1: - { - IsShipwreckedItem = reader.ReadBool(); - break; - } - } - } - - public override void AddNameProperties(ObjectPropertyList list) - { - base.AddNameProperties(list); - - if (IsShipwreckedItem) - { - list.Add(1041645); // recovered from a shipwreck - } - } - - public override void OnSingleClick(Mobile from) - { - base.OnSingleClick(from); - - if (IsShipwreckedItem) - { - LabelTo(from, 1041645); // recovered from a shipwreck - } + LabelTo(from, 1041645); // recovered from a shipwreck } } } diff --git a/Projects/UOContent/Items/Lights/CandelabraStand.cs b/Projects/UOContent/Items/Lights/CandelabraStand.cs index 4f32f5d39..00e49e521 100644 --- a/Projects/UOContent/Items/Lights/CandelabraStand.cs +++ b/Projects/UOContent/Items/Lights/CandelabraStand.cs @@ -1,35 +1,20 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CandelabraStand : BaseLight { - public class CandelabraStand : BaseLight + [Constructible] + public CandelabraStand() : base(0xA29) { - [Constructible] - 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 int LitItemID => 0xB26; - public override int UnlitItemID => 0xA29; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle225; + Weight = 20.0; } + + public override int LitItemID => 0xB26; + public override int UnlitItemID => 0xA29; } diff --git a/Projects/UOContent/Items/Lights/Candle.cs b/Projects/UOContent/Items/Lights/Candle.cs index b38bcef4a..f0d5bd42e 100644 --- a/Projects/UOContent/Items/Lights/Candle.cs +++ b/Projects/UOContent/Items/Lights/Candle.cs @@ -1,43 +1,22 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Candle : BaseEquipableLight { - public class Candle : BaseEquipableLight + [Constructible] + public Candle() : base(0xA28) { - [Constructible] - public Candle() : base(0xA28) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(20); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(20) : TimeSpan.Zero; + Burning = false; - Burning = false; - Light = LightType.Circle150; - Weight = 1.0; - } - - public Candle(Serial serial) : base(serial) - { - } - - public override int LitItemID => 0xA0F; - public override int UnlitItemID => 0xA28; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Stackable = true; + Light = LightType.Circle150; + Weight = 1.0; } + + public override int LitItemID => 0xA0F; + public override int UnlitItemID => 0xA28; } diff --git a/Projects/UOContent/Items/Lights/CandleLarge.cs b/Projects/UOContent/Items/Lights/CandleLarge.cs index 9cf17c014..12dfdf188 100644 --- a/Projects/UOContent/Items/Lights/CandleLarge.cs +++ b/Projects/UOContent/Items/Lights/CandleLarge.cs @@ -1,43 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CandleLarge : BaseLight { - public class CandleLarge : BaseLight + [Constructible] + public CandleLarge() : base(0xA26) { - [Constructible] - public CandleLarge() : base(0xA26) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(25); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; - Light = LightType.Circle150; - Weight = 2.0; - } - - public CandleLarge(Serial serial) : base(serial) - { - } - - public override int LitItemID => 0xB1A; - public override int UnlitItemID => 0xA26; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Burning = false; + Light = LightType.Circle150; + Weight = 2.0; } + + public override int LitItemID => 0xB1A; + public override int UnlitItemID => 0xA26; } diff --git a/Projects/UOContent/Items/Lights/CandleLong.cs b/Projects/UOContent/Items/Lights/CandleLong.cs index 540550be1..39daca623 100644 --- a/Projects/UOContent/Items/Lights/CandleLong.cs +++ b/Projects/UOContent/Items/Lights/CandleLong.cs @@ -1,43 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CandleLong : BaseLight { - public class CandleLong : BaseLight + [Constructible] + public CandleLong() : base(0x1433) { - [Constructible] - public CandleLong() : base(0x1433) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(30); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(30) : TimeSpan.Zero; - Burning = false; - Light = LightType.Circle150; - Weight = 1.0; - } - - public CandleLong(Serial serial) : base(serial) - { - } - - public override int LitItemID => 0x1430; - public override int UnlitItemID => 0x1433; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Burning = false; + Light = LightType.Circle150; + Weight = 1.0; } + + public override int LitItemID => 0x1430; + public override int UnlitItemID => 0x1433; } diff --git a/Projects/UOContent/Items/Lights/CandleShort.cs b/Projects/UOContent/Items/Lights/CandleShort.cs index fd0826f5a..942478f35 100644 --- a/Projects/UOContent/Items/Lights/CandleShort.cs +++ b/Projects/UOContent/Items/Lights/CandleShort.cs @@ -1,43 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CandleShort : BaseLight { - public class CandleShort : BaseLight + [Constructible] + public CandleShort() : base(0x142F) { - [Constructible] - public CandleShort() : base(0x142F) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(25); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; - Light = LightType.Circle150; - Weight = 1.0; - } - - public CandleShort(Serial serial) : base(serial) - { - } - - public override int LitItemID => 0x142C; - public override int UnlitItemID => 0x142F; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Burning = false; + Light = LightType.Circle150; + Weight = 1.0; } + + public override int LitItemID => 0x142C; + public override int UnlitItemID => 0x142F; } diff --git a/Projects/UOContent/Items/Lights/CandleSkull.cs b/Projects/UOContent/Items/Lights/CandleSkull.cs index 049a426a3..8cd3a9f21 100644 --- a/Projects/UOContent/Items/Lights/CandleSkull.cs +++ b/Projects/UOContent/Items/Lights/CandleSkull.cs @@ -1,66 +1,22 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class CandleSkull : BaseLight { - public class CandleSkull : BaseLight + [Constructible] + public CandleSkull() : base(0x1853) { - [Constructible] - public CandleSkull() : base(0x1853) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(25); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; - Light = LightType.Circle150; - Weight = 5.0; - } - - public CandleSkull(Serial serial) : base(serial) - { - } - - public override int LitItemID - { - get - { - if (ItemID is 0x1583 or 0x1854) - { - return 0x1854; - } - - return 0x1858; - } - } - - public override int UnlitItemID - { - get - { - if (ItemID is 0x1853 or 0x1584) - { - return 0x1853; - } - - return 0x1857; - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Burning = false; + Light = LightType.Circle150; + Weight = 5.0; } + + public override int LitItemID => ItemID is 0x1583 or 0x1854 ? 0x1854 : 0x1858; + + public override int UnlitItemID => ItemID is 0x1853 or 0x1584 ? 0x1853 : 0x1857; } diff --git a/Projects/UOContent/Items/Lights/DarkSource.cs b/Projects/UOContent/Items/Lights/DarkSource.cs index 6cf443480..946a6bb12 100644 --- a/Projects/UOContent/Items/Lights/DarkSource.cs +++ b/Projects/UOContent/Items/Lights/DarkSource.cs @@ -1,28 +1,14 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class DarkSource : Item { - public class DarkSource : Item + [Constructible] + public DarkSource() : base(0x1646) { - [Constructible] - public DarkSource() : base(0x1646) - { - Layer = Layer.TwoHanded; - Movable = false; - } - - public DarkSource(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Layer = Layer.TwoHanded; + Movable = false; } } diff --git a/Projects/UOContent/Items/Lights/HangingLantern.cs b/Projects/UOContent/Items/Lights/HangingLantern.cs index cf4e2fba1..1ae33dcf9 100644 --- a/Projects/UOContent/Items/Lights/HangingLantern.cs +++ b/Projects/UOContent/Items/Lights/HangingLantern.cs @@ -1,36 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class HangingLantern : BaseLight { - public class HangingLantern : BaseLight + [Constructible] + public HangingLantern() : base(0xA1D) { - [Constructible] - 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 int LitItemID => 0xA1A; - public override int UnlitItemID => 0xA1D; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; } + + public override int LitItemID => 0xA1A; + public override int UnlitItemID => 0xA1D; } diff --git a/Projects/UOContent/Items/Lights/HeatingStand.cs b/Projects/UOContent/Items/Lights/HeatingStand.cs index bfc55fcb1..1b1abe044 100644 --- a/Projects/UOContent/Items/Lights/HeatingStand.cs +++ b/Projects/UOContent/Items/Lights/HeatingStand.cs @@ -1,71 +1,49 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class HeatingStand : BaseLight { - public class HeatingStand : BaseLight + [Constructible] + public HeatingStand() : base(0x1849) { - [Constructible] - public HeatingStand() : base(0x1849) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(25); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; + Burning = false; + Light = LightType.Empty; + Weight = 1.0; + } + + public override int LitItemID => 0x184A; + public override int UnlitItemID => 0x1849; + + public override void Ignite() + { + base.Ignite(); + + if (ItemID == LitItemID) + { + Light = LightType.Circle150; + } + else if (ItemID == UnlitItemID) + { Light = LightType.Empty; - Weight = 1.0; } + } - public HeatingStand(Serial serial) : base(serial) + public override void Douse() + { + base.Douse(); + + if (ItemID == LitItemID) { + Light = LightType.Circle150; } - - public override int LitItemID => 0x184A; - public override int UnlitItemID => 0x1849; - - public override void Ignite() + else if (ItemID == UnlitItemID) { - 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 override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); + Light = LightType.Empty; } } } diff --git a/Projects/UOContent/Items/Lights/LampPost1.cs b/Projects/UOContent/Items/Lights/LampPost1.cs index 8d9c2bae0..985e6e7b1 100644 --- a/Projects/UOContent/Items/Lights/LampPost1.cs +++ b/Projects/UOContent/Items/Lights/LampPost1.cs @@ -1,36 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class LampPost1 : BaseLight { - public class LampPost1 : BaseLight + [Constructible] + public LampPost1() : base(0xB21) { - [Constructible] - 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 int LitItemID => 0xB20; - public override int UnlitItemID => 0xB21; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; } + + public override int LitItemID => 0xB20; + public override int UnlitItemID => 0xB21; } diff --git a/Projects/UOContent/Items/Lights/LampPost2.cs b/Projects/UOContent/Items/Lights/LampPost2.cs index e041ea02d..f36bdf4db 100644 --- a/Projects/UOContent/Items/Lights/LampPost2.cs +++ b/Projects/UOContent/Items/Lights/LampPost2.cs @@ -1,36 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class LampPost2 : BaseLight { - public class LampPost2 : BaseLight + [Constructible] + public LampPost2() : base(0xB23) { - [Constructible] - 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 int LitItemID => 0xB22; - public override int UnlitItemID => 0xB23; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; } + + public override int LitItemID => 0xB22; + public override int UnlitItemID => 0xB23; } diff --git a/Projects/UOContent/Items/Lights/LampPost3.cs b/Projects/UOContent/Items/Lights/LampPost3.cs index 16000c03b..c9cb237a0 100644 --- a/Projects/UOContent/Items/Lights/LampPost3.cs +++ b/Projects/UOContent/Items/Lights/LampPost3.cs @@ -1,36 +1,21 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class LampPost3 : BaseLight { - public class LampPost3 : BaseLight + [Constructible] + public LampPost3() : base(0xb25) { - [Constructible] - 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 int LitItemID => 0xB24; - public override int UnlitItemID => 0xB25; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; } + + public override int LitItemID => 0xB24; + public override int UnlitItemID => 0xB25; } diff --git a/Projects/UOContent/Items/Lights/Lantern.cs b/Projects/UOContent/Items/Lights/Lantern.cs index 50635bda3..84c964ad5 100644 --- a/Projects/UOContent/Items/Lights/Lantern.cs +++ b/Projects/UOContent/Items/Lights/Lantern.cs @@ -1,90 +1,31 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Lantern : BaseEquipableLight { - public class Lantern : BaseEquipableLight + [Constructible] + public Lantern() : base(0xA25) { - [Constructible] - public Lantern() : base(0xA25) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(20); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(20) : TimeSpan.Zero; - Burning = false; - Light = LightType.Circle300; - Weight = 2.0; - } - - public Lantern(Serial serial) : base(serial) - { - } - - public override int LitItemID - { - get - { - if (ItemID is 0xA15 or 0xA17) - { - return ItemID; - } - - return 0xA22; - } - } - - public override int UnlitItemID - { - get - { - if (ItemID == 0xA18) - { - return ItemID; - } - - return 0xA25; - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Burning = false; + Light = LightType.Circle300; + Weight = 2.0; } - public class LanternOfSouls : Lantern - { - [Constructible] - public LanternOfSouls() => Hue = 0x482; + public override int LitItemID => ItemID is 0xA15 or 0xA17 ? ItemID : 0xA22; - public LanternOfSouls(Serial serial) : base(serial) - { - } - - public override int LabelNumber => 1061618; // Lantern of Souls - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } - } + public override int UnlitItemID => ItemID == 0xA18 ? ItemID : 0xA25; +} + +[SerializationGenerator(0)] +public partial class LanternOfSouls : Lantern +{ + [Constructible] + public LanternOfSouls() => Hue = 0x482; + + public override int LabelNumber => 1061618; // Lantern of Souls } diff --git a/Projects/UOContent/Items/Lights/LightSource.cs b/Projects/UOContent/Items/Lights/LightSource.cs index fb6061ae3..b3a9d1df4 100644 --- a/Projects/UOContent/Items/Lights/LightSource.cs +++ b/Projects/UOContent/Items/Lights/LightSource.cs @@ -1,28 +1,14 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class LightSource : Item { - public class LightSource : Item + [Constructible] + public LightSource() : base(0x1647) { - [Constructible] - public LightSource() : base(0x1647) - { - Layer = Layer.TwoHanded; - Movable = false; - } - - public LightSource(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Layer = Layer.TwoHanded; + Movable = false; } } diff --git a/Projects/UOContent/Items/Lights/PaperLantern.cs b/Projects/UOContent/Items/Lights/PaperLantern.cs index eabaaa561..681bf7705 100644 --- a/Projects/UOContent/Items/Lights/PaperLantern.cs +++ b/Projects/UOContent/Items/Lights/PaperLantern.cs @@ -1,37 +1,22 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class PaperLantern : BaseLight { - [Flippable] - public class PaperLantern : BaseLight + [Constructible] + public PaperLantern() : base(0x24BE) { - [Constructible] - 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 int LitItemID => 0x24BD; - public override int UnlitItemID => 0x24BE; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle150; + Weight = 3.0; } + + public override int LitItemID => 0x24BD; + public override int UnlitItemID => 0x24BE; } diff --git a/Projects/UOContent/Items/Lights/RedHangingLantern.cs b/Projects/UOContent/Items/Lights/RedHangingLantern.cs index 216fbd1be..3643c2e12 100644 --- a/Projects/UOContent/Items/Lights/RedHangingLantern.cs +++ b/Projects/UOContent/Items/Lights/RedHangingLantern.cs @@ -1,74 +1,37 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class RedHangingLantern : BaseLight { - [Flippable] - public class RedHangingLantern : BaseLight + [Constructible] + public RedHangingLantern() : base(0x24C2) { - [Constructible] - public RedHangingLantern() : base(0x24C2) + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 3.0; + } + + public override int LitItemID => ItemID == 0x24C2 ? 0x24C1 : 0x24C3; + + public override int UnlitItemID => ItemID == 0x24C1 ? 0x24C2 : 0x24C4; + + public void Flip() + { + Light = LightType.Circle300; + + ItemID = ItemID switch { - Movable = true; - Duration = TimeSpan.Zero; // Never burnt out - Burning = false; - Light = LightType.Circle300; - Weight = 3.0; - } - - public RedHangingLantern(Serial serial) : base(serial) - { - } - - public override int LitItemID - { - get - { - if (ItemID == 0x24C2) - { - return 0x24C1; - } - - return 0x24C3; - } - } - - public override int UnlitItemID - { - get - { - if (ItemID == 0x24C1) - { - return 0x24C2; - } - - return 0x24C4; - } - } - - public void Flip() - { - Light = LightType.Circle300; - - ItemID = ItemID switch - { - 0x24C2 => 0x24C4, - 0x24C1 => 0x24C3, - 0x24C4 => 0x24C2, - 0x24C3 => 0x24C1, - _ => ItemID - }; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + 0x24C2 => 0x24C4, + 0x24C1 => 0x24C3, + 0x24C4 => 0x24C2, + 0x24C3 => 0x24C1, + _ => ItemID + }; } } diff --git a/Projects/UOContent/Items/Lights/RoundPaperLantern.cs b/Projects/UOContent/Items/Lights/RoundPaperLantern.cs index 54c732995..3b028a0d3 100644 --- a/Projects/UOContent/Items/Lights/RoundPaperLantern.cs +++ b/Projects/UOContent/Items/Lights/RoundPaperLantern.cs @@ -1,37 +1,22 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class RoundPaperLantern : BaseLight { - [Flippable] - public class RoundPaperLantern : BaseLight + [Constructible] + public RoundPaperLantern() : base(0x24CA) { - [Constructible] - 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 int LitItemID => 0x24C9; - public override int UnlitItemID => 0x24CA; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle150; + Weight = 3.0; } + + public override int LitItemID => 0x24C9; + public override int UnlitItemID => 0x24CA; } diff --git a/Projects/UOContent/Items/Lights/ShojiLantern.cs b/Projects/UOContent/Items/Lights/ShojiLantern.cs index 979566251..56aa5d81e 100644 --- a/Projects/UOContent/Items/Lights/ShojiLantern.cs +++ b/Projects/UOContent/Items/Lights/ShojiLantern.cs @@ -1,37 +1,22 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class ShojiLantern : BaseLight { - [Flippable] - public class ShojiLantern : BaseLight + [Constructible] + public ShojiLantern() : base(0x24BC) { - [Constructible] - 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 int LitItemID => 0x24BB; - public override int UnlitItemID => 0x24BC; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle150; + Weight = 3.0; } + + public override int LitItemID => 0x24BB; + public override int UnlitItemID => 0x24BC; } diff --git a/Projects/UOContent/Items/Lights/Torch.cs b/Projects/UOContent/Items/Lights/Torch.cs index 8e4eecbf5..75c5f0967 100644 --- a/Projects/UOContent/Items/Lights/Torch.cs +++ b/Projects/UOContent/Items/Lights/Torch.cs @@ -1,72 +1,46 @@ using System; +using ModernUO.Serialization; using Server.Mobiles; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Torch : BaseEquipableLight { - public class Torch : BaseEquipableLight + [Constructible] + public Torch() : base(0xF6B) { - [Constructible] - public Torch() : base(0xF6B) - { - if (Burnout) - { - Duration = TimeSpan.FromMinutes(30); - } - else - { - Duration = TimeSpan.Zero; - } + Duration = Burnout ? TimeSpan.FromMinutes(30) : TimeSpan.Zero; + Burning = false; - Burning = false; - Light = LightType.Circle300; - Weight = 1.0; + Stackable = true; + Light = LightType.Circle300; + Weight = 1.0; + } + + public override int LitItemID => 0xA12; + public override int UnlitItemID => 0xF6B; + + public override int LitSound => 0x54; + public override int UnlitSound => 0x4BB; + + public override void OnAdded(IEntity parent) + { + base.OnAdded(parent); + + if (parent is Mobile mobile && Burning) + { + MeerMage.StopEffect(mobile, true); } + } - public Torch(Serial serial) : base(serial) + public override void Ignite() + { + base.Ignite(); + + if (Parent is Mobile mobile && Burning) { - } - - public override int LitItemID => 0xA12; - public override int UnlitItemID => 0xF6B; - - public override int LitSound => 0x54; - public override int UnlitSound => 0x4BB; - - public override void OnAdded(IEntity parent) - { - base.OnAdded(parent); - - if (parent is Mobile mobile && Burning) - { - MeerMage.StopEffect(mobile, true); - } - } - - public override void Ignite() - { - base.Ignite(); - - if (Parent is Mobile mobile && Burning) - { - MeerMage.StopEffect(mobile, true); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - - if (Weight == 2.0) - { - Weight = 1.0; - } + MeerMage.StopEffect(mobile, true); } } } diff --git a/Projects/UOContent/Items/Lights/WallSconce.cs b/Projects/UOContent/Items/Lights/WallSconce.cs index 6967d91f0..3268efcaa 100644 --- a/Projects/UOContent/Items/Lights/WallSconce.cs +++ b/Projects/UOContent/Items/Lights/WallSconce.cs @@ -1,81 +1,42 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class WallSconce : BaseLight { - [Flippable] - public class WallSconce : BaseLight + [Constructible] + public WallSconce() : base(0x9FB) { - [Constructible] - public WallSconce() : base(0x9FB) + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.WestBig; + Weight = 3.0; + } + + public override int LitItemID => ItemID == 0x9FB ? 0x9FD : 0xA02; + + public override int UnlitItemID => ItemID == 0x9FD ? 0x9FB : 0xA00; + + public void Flip() + { + Light = Light switch { - Movable = false; - Duration = TimeSpan.Zero; // Never burnt out - Burning = false; - Light = LightType.WestBig; - Weight = 3.0; - } + LightType.WestBig => LightType.NorthBig, + LightType.NorthBig => LightType.WestBig, + _ => Light + }; - public WallSconce(Serial serial) : base(serial) + ItemID = ItemID switch { - } - - public override int LitItemID - { - get - { - if (ItemID == 0x9FB) - { - return 0x9FD; - } - - return 0xA02; - } - } - - public override int UnlitItemID - { - get - { - if (ItemID == 0x9FD) - { - return 0x9FB; - } - - return 0xA00; - } - } - - public void Flip() - { - if (Light == LightType.WestBig) - { - Light = LightType.NorthBig; - } - else if (Light == LightType.NorthBig) - { - Light = LightType.WestBig; - } - - ItemID = ItemID switch - { - 0x9FB => 0xA00, - 0x9FD => 0xA02, - 0xA00 => 0x9FB, - 0xA02 => 0x9FD, - _ => ItemID - }; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + 0x9FB => 0xA00, + 0x9FD => 0xA02, + 0xA00 => 0x9FB, + 0xA02 => 0x9FD, + _ => ItemID + }; } } diff --git a/Projects/UOContent/Items/Lights/WallTorch.cs b/Projects/UOContent/Items/Lights/WallTorch.cs index 084ac8ce5..968b0bbae 100644 --- a/Projects/UOContent/Items/Lights/WallTorch.cs +++ b/Projects/UOContent/Items/Lights/WallTorch.cs @@ -1,81 +1,42 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class WallTorch : BaseLight { - [Flippable] - public class WallTorch : BaseLight + [Constructible] + public WallTorch() : base(0xA05) { - [Constructible] - public WallTorch() : base(0xA05) + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.WestBig; + Weight = 3.0; + } + + public override int LitItemID => ItemID == 0xA05 ? 0xA07 : 0xA0C; + + public override int UnlitItemID => ItemID == 0xA07 ? 0xA05 : 0xA0A; + + public void Flip() + { + Light = Light switch { - Movable = false; - Duration = TimeSpan.Zero; // Never burnt out - Burning = false; - Light = LightType.WestBig; - Weight = 3.0; - } + LightType.WestBig => LightType.NorthBig, + LightType.NorthBig => LightType.WestBig, + _ => Light + }; - public WallTorch(Serial serial) : base(serial) + ItemID = ItemID switch { - } - - public override int LitItemID - { - get - { - if (ItemID == 0xA05) - { - return 0xA07; - } - - return 0xA0C; - } - } - - public override int UnlitItemID - { - get - { - if (ItemID == 0xA07) - { - return 0xA05; - } - - return 0xA0A; - } - } - - public void Flip() - { - if (Light == LightType.WestBig) - { - Light = LightType.NorthBig; - } - else if (Light == LightType.NorthBig) - { - Light = LightType.WestBig; - } - - ItemID = ItemID switch - { - 0xA05 => 0xA0A, - 0xA07 => 0xA0C, - 0xA0A => 0xA05, - 0xA0C => 0xA07, - _ => ItemID - }; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + 0xA05 => 0xA0A, + 0xA07 => 0xA0C, + 0xA0A => 0xA05, + 0xA0C => 0xA07, + _ => ItemID + }; } } diff --git a/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs b/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs index ee21e7458..7d62bde96 100644 --- a/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs +++ b/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs @@ -1,74 +1,37 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[Flippable] +[SerializationGenerator(0, false)] +public partial class WhiteHangingLantern : BaseLight { - [Flippable] - public class WhiteHangingLantern : BaseLight + [Constructible] + public WhiteHangingLantern() : base(0x24C6) { - [Constructible] - public WhiteHangingLantern() : base(0x24C6) + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 3.0; + } + + public override int LitItemID => ItemID == 0x24C6 ? 0x24C5 : 0x24C7; + + public override int UnlitItemID => ItemID == 0x24C5 ? 0x24C6 : 0x24C8; + + public void Flip() + { + Light = LightType.Circle300; + + ItemID = ItemID switch { - Movable = true; - Duration = TimeSpan.Zero; // Never burnt out - Burning = false; - Light = LightType.Circle300; - Weight = 3.0; - } - - public WhiteHangingLantern(Serial serial) : base(serial) - { - } - - public override int LitItemID - { - get - { - if (ItemID == 0x24C6) - { - return 0x24C5; - } - - return 0x24C7; - } - } - - public override int UnlitItemID - { - get - { - if (ItemID == 0x24C5) - { - return 0x24C6; - } - - return 0x24C8; - } - } - - public void Flip() - { - Light = LightType.Circle300; - - ItemID = ItemID switch - { - 0x24C6 => 0x24C8, - 0x24C5 => 0x24C7, - 0x24C8 => 0x24C6, - 0x24C7 => 0x24C5, - _ => ItemID - }; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.Write(0); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); - } + 0x24C6 => 0x24C8, + 0x24C5 => 0x24C7, + 0x24C8 => 0x24C6, + 0x24C7 => 0x24C5, + _ => ItemID + }; } } diff --git a/Projects/UOContent/Items/Maps/MapItemPackets.cs b/Projects/UOContent/Items/Maps/MapItemPackets.cs index 08229830c..935331792 100644 --- a/Projects/UOContent/Items/Maps/MapItemPackets.cs +++ b/Projects/UOContent/Items/Maps/MapItemPackets.cs @@ -20,12 +20,12 @@ namespace Server.Network { public static class MapItemPackets { - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0x56, 11, true, OnMapCommand); + IncomingPackets.Register(0x56, 11, true, &OnMapCommand); } - private static void OnMapCommand(NetState state, CircularBufferReader reader, ref int packetLength) + private static void OnMapCommand(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; @@ -65,7 +65,7 @@ namespace Server.Network public static void SendMapDetails(this NetState ns, MapItem map) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -93,7 +93,7 @@ namespace Server.Network public static void SendMapCommand(this NetState ns, MapItem map, int command, int x = 0, int y = 0, bool editable = false) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Maps/TreasureMap.cs b/Projects/UOContent/Items/Maps/TreasureMap.cs index be5ca3039..ed4c61ca3 100644 --- a/Projects/UOContent/Items/Maps/TreasureMap.cs +++ b/Projects/UOContent/Items/Maps/TreasureMap.cs @@ -523,7 +523,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/ArcaneGem.cs b/Projects/UOContent/Items/Misc/ArcaneGem.cs index 5da64655f..c287efe35 100644 --- a/Projects/UOContent/Items/Misc/ArcaneGem.cs +++ b/Projects/UOContent/Items/Misc/ArcaneGem.cs @@ -126,12 +126,12 @@ namespace Server.Items if (clothing != null) { clothing.Quality = ClothingQuality.Regular; - clothing.Crafter = from; + clothing.Crafter = from.RawName; } else if (armor != null) { armor.Quality = ArmorQuality.Regular; - armor.Crafter = from; + armor.Crafter = from.RawName; armor.PhysicalBonus = armor.FireBonus = armor.ColdBonus = diff --git a/Projects/UOContent/Items/Misc/BankCheck.cs b/Projects/UOContent/Items/Misc/BankCheck.cs index 5683d9d5f..d7396374f 100644 --- a/Projects/UOContent/Items/Misc/BankCheck.cs +++ b/Projects/UOContent/Items/Misc/BankCheck.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using Server.Accounting; using Server.Engines.Quests; using Server.Engines.Quests.Haven; @@ -69,22 +68,17 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - - string worth; - if (Core.ML) { - worth = m_Worth.ToString("N0", CultureInfo.GetCultureInfo("en-US")); + list.Add(1060738, $"{m_Worth:N0}"); // value: ~1_val~ } else { - worth = m_Worth.ToString(); + list.Add(1060738, m_Worth); // value: ~1_val~ } - - list.Add(1060738, worth); // value: ~1_val~ } public override void OnAdded(IEntity parent) @@ -144,7 +138,7 @@ namespace Server.Items tradeInfo.VirtualCheck?.UpdateTrade(tradeInfo.Mobile); } - owner.SendLocalizedMessage(1042763, Worth.ToString("#,0")); + owner.SendLocalizedMessage(1042763, $"{m_Worth:N0}"); Delete(); @@ -159,18 +153,17 @@ namespace Server.Items MessageType.Label, 0x3B2, 3, - 1041361, + 1041361, // A bank check: "", AffixType.Append, $" {m_Worth}" - ); // A bank check: + ); } public override void OnDoubleClick(Mobile from) { // This probably isn't OSI accurate, but we can't just make the quests redundant. // Double-clicking the BankCheck in your pack will now credit your account. - var box = AccountGold.Enabled ? from.Backpack : from.FindBankNoCreate(); if (box == null || !IsChildOf(box)) @@ -180,82 +173,68 @@ namespace Server.Items return; } - Delete(); - var deposited = 0; var toAdd = m_Worth; if (AccountGold.Enabled && from.Account?.DepositGold(toAdd) == true) { deposited = toAdd; - toAdd = 0; } - if (toAdd > 0) + while (toAdd > 0) { - Gold gold; + var amount = Math.Min(toAdd, 60000); - while (toAdd > 60000) + var gold = new Gold(amount); + + if (box.TryDropItem(from, gold, false)) { - 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; - } + toAdd -= amount; + deposited += amount; } - - if (toAdd > 0) + else { - gold = new Gold(toAdd); - - if (box.TryDropItem(from, gold, false)) - { - deposited += toAdd; - } - else - { - gold.Delete(); - - from.AddToBackpack(new BankCheck(toAdd)); - } + gold.Delete(); + break; } } - // Gold was deposited in your account: - from.SendLocalizedMessage(1042672, true, deposited.ToString("#,0")); - - if (from is PlayerMobile pm) + if (deposited >= m_Worth) { - var qs = pm.Quest; + Delete(); + } + else + { + Worth -= deposited; + } - if (qs is DarkTidesQuest) + if (deposited > 0) + { + // Gold was deposited in your account: + from.SendLocalizedMessage(1042672, true, $"{deposited:N0}"); + + if (from is PlayerMobile pm) { - QuestObjective obj = qs.FindObjective(); + var qs = pm.Quest; - if (obj?.Completed == false) + if (qs is DarkTidesQuest) { - obj.Complete(); + QuestObjective obj = qs.FindObjective(); + + if (obj?.Completed == false) + { + obj.Complete(); + } } - } - if (qs is UzeraanTurmoilQuest) - { - var obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective)); - - if (obj?.Completed == false) + if (qs is UzeraanTurmoilQuest) { - obj.Complete(); + var obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective)); + + if (obj?.Completed == false) + { + obj.Complete(); + } } } } diff --git a/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesFermentedWine.cs b/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesFermentedWine.cs index 4a292c615..080783aab 100644 --- a/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesFermentedWine.cs +++ b/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesFermentedWine.cs @@ -24,7 +24,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesHairDye.cs b/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesHairDye.cs index 24a51b8f8..c8a40859b 100644 --- a/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesHairDye.cs +++ b/Projects/UOContent/Items/Misc/Blighted Grove/MelisandesHairDye.cs @@ -28,7 +28,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/CommunicationCrystals.cs b/Projects/UOContent/Items/Misc/CommunicationCrystals.cs index 357720994..35955907d 100644 --- a/Projects/UOContent/Items/Misc/CommunicationCrystals.cs +++ b/Projects/UOContent/Items/Misc/CommunicationCrystals.cs @@ -91,17 +91,17 @@ namespace Server.Items public override bool HandlesOnSpeech => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); list.Add(Active ? 1060742 : 1060743); // active / inactive list.Add(1060745); // broadcast - list.Add(1060741, Charges.ToString()); // charges: ~1_val~ + list.Add(1060741, Charges); // charges: ~1_val~ if (Receivers.Count > 0) { - list.Add(1060746, Receivers.Count.ToString()); // links: ~1_val~ + list.Add(1060746, Receivers.Count); // links: ~1_val~ } } @@ -338,7 +338,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs index f3ea9e7b6..17f686976 100644 --- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs @@ -799,9 +799,9 @@ namespace Server.Items return m_Devourer.Devour(this); // Devour the corpse if it hasn't } - public override void SendInfoTo(NetState ns, ReadOnlySpan world = default, Span opl = default) + public override void SendInfoTo(NetState ns, ReadOnlySpan world = default) { - base.SendInfoTo(ns, world, opl); + base.SendInfoTo(ns, world); if (((Body)Amount).IsHuman && ItemID == 0x2006) { @@ -1130,7 +1130,7 @@ namespace Server.Items public override bool CheckContentDisplay(Mobile from) => false; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (ItemID == 0x2006) // Corpse form { diff --git a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs index 7de62807b..b78300f70 100644 --- a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs +++ b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs @@ -23,7 +23,7 @@ namespace Server.Network { public static void SendCorpseEquip(this NetState ns, Mobile beholder, Corpse beheld) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -67,7 +67,7 @@ namespace Server.Network public static void SendCorpseContent(this NetState ns, Mobile beholder, Corpse beheld) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs index 9eb2da8ac..a38c910aa 100644 --- a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs @@ -43,7 +43,7 @@ namespace Server.Items // Do not display (x items, y stones) public override bool CheckContentDisplay(Mobile from) => false; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add(1046414, Name); // the remains of ~1_NAME~ } diff --git a/Projects/UOContent/Items/Misc/Firebomb.cs b/Projects/UOContent/Items/Misc/Firebomb.cs index 60433972e..1c7f9a602 100644 --- a/Projects/UOContent/Items/Misc/Firebomb.cs +++ b/Projects/UOContent/Items/Misc/Firebomb.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; +using Server.Collections; using Server.Network; using Server.Spells; +using Server.Spells.Fourth; using Server.Targeting; namespace Server.Items @@ -10,6 +11,7 @@ namespace Server.Items public class Firebomb : Item { private Mobile m_LitBy; + private Point3D _thrownFromLocation; private int m_Ticks; private TimerExecutionToken _timerToken; private List m_Users; @@ -138,24 +140,34 @@ namespace Server.Items else if (RootParent == null) { var eable = Map.GetMobilesInRange(Location, 1); - var toDamage = eable.ToList(); - - eable.Free(); - - for (var i = 0; i < toDamage.Count; ++i) + using var targets = PooledRefQueue.Create(); + foreach (var m in eable) { - var victim = toDamage[i]; - - if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, victim) && - m_LitBy.CanBeHarmful(victim, false)) + if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, m) && + m_LitBy.CanBeHarmful(m, false)) { - m_LitBy?.DoHarmful(victim); - - AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0); + targets.Enqueue(m); } } + eable.Free(); - new FirebombField(m_LitBy, toDamage).MoveToWorld(Location, Map); + while (targets.Count > 0) + { + var victim = targets.Dequeue(); + m_LitBy?.DoHarmful(victim); + AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0); + } + + var loc = _thrownFromLocation; + var eastToWest = SpellHelper.GetEastToWest(loc, Location); + Effects.PlaySound(loc, Map, 0x20C); + var itemID = eastToWest ? 0x398C : 0x3996; + + for (var i = -2; i <= 2; ++i) + { + var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z); + new FireFieldSpell.FireFieldItem(itemID, targetLoc, m_LitBy, Map, TimeSpan.FromSeconds(9), i); + } } _timerToken.Cancel(); @@ -178,12 +190,12 @@ namespace Server.Items } SpellHelper.GetSurfaceTop(ref p); - var loc = new Point3D(p); + _thrownFromLocation = new Point3D(p); var map = Map; from.RevealingAction(); - var to = p as IEntity ?? new Entity(Serial.Zero, loc, map); + var to = p as IEntity ?? new Entity(Serial.Zero, _thrownFromLocation, map); Effects.SendMovingEffect(from, to, ItemID, 7, 0, false, false, Hue); @@ -195,7 +207,7 @@ namespace Server.Items return; } - MoveToWorld(loc, map); + MoveToWorld(_thrownFromLocation, map); } ); Internalize(); @@ -203,9 +215,7 @@ namespace Server.Items private class ThrowTarget : Target { - public ThrowTarget(Firebomb bomb) - : base(12, true, TargetFlags.None) => - Bomb = bomb; + public ThrowTarget(Firebomb bomb) : base(12, true, TargetFlags.None) => Bomb = bomb; public Firebomb Bomb { get; } @@ -215,93 +225,4 @@ namespace Server.Items } } } - - public class FirebombField : Item - { - private readonly List m_Burning; - private readonly DateTime m_Expire; - private readonly Mobile m_LitBy; - private TimerExecutionToken _timerToken; - - public FirebombField(Mobile litBy, List toDamage) : base(0x376A) - { - Movable = false; - m_LitBy = litBy; - m_Expire = Core.Now + TimeSpan.FromSeconds(10); - m_Burning = toDamage; - Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnFirebombFieldTimerTick, out _timerToken); - } - - public FirebombField(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - // Don't serialize these... - } - - public override void Deserialize(IGenericReader reader) - { - } - - public override bool OnMoveOver(Mobile m) - { - if (ItemID == 0x398C && m_LitBy == null || - SpellHelper.ValidIndirectTarget(m_LitBy, m) && m_LitBy.CanBeHarmful(m, false)) - { - m_LitBy?.DoHarmful(m); - - AOS.Damage(m, m_LitBy, 2, 0, 100, 0, 0, 0); - m.PlaySound(0x208); - - if (!m_Burning.Contains(m)) - { - m_Burning.Add(m); - } - } - - return true; - } - - private void OnFirebombFieldTimerTick() - { - if (Deleted) - { - _timerToken.Cancel(); - return; - } - - if (ItemID == 0x376A) - { - ItemID = 0x398C; - return; - } - - for (var i = 0; i < m_Burning.Count;) - { - var victim = m_Burning[i]; - - if (victim.Location == Location && victim.Map == Map && - (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, victim) && - m_LitBy.CanBeHarmful(victim, false))) - { - m_LitBy?.DoHarmful(victim); - - AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0); - ++i; - } - else - { - m_Burning.RemoveAt(i); - } - } - - if (Core.Now >= m_Expire) - { - _timerToken.Cancel(); - Delete(); - } - } - } } diff --git a/Projects/UOContent/Items/Misc/Gold.cs b/Projects/UOContent/Items/Misc/Gold.cs index faa7283d1..51e507260 100644 --- a/Projects/UOContent/Items/Misc/Gold.cs +++ b/Projects/UOContent/Items/Misc/Gold.cs @@ -102,7 +102,7 @@ namespace Server.Items tradeInfo.VirtualCheck?.UpdateTrade(tradeInfo.Mobile); } - owner.SendLocalizedMessage(1042763, Amount.ToString("#,0")); + owner.SendLocalizedMessage(1042763, $"{Amount:N0}"); Delete(); diff --git a/Projects/UOContent/Items/Misc/InteriorDecorator.cs b/Projects/UOContent/Items/Misc/InteriorDecorator.cs index 493816a5e..953b19c21 100644 --- a/Projects/UOContent/Items/Misc/InteriorDecorator.cs +++ b/Projects/UOContent/Items/Misc/InteriorDecorator.cs @@ -41,7 +41,7 @@ namespace Server.Items public override int LabelNumber => 1041280; // an interior decorator - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -320,7 +320,7 @@ namespace Server.Items if (floorZ > int.MinValue && item.Z < floorZ + 15) // Confirmed : no height checks here { - item.Location = new Point3D(item.Location, item.Z + 1); + item.Location = new Point3D(item.Location.X, item.Location.Y, item.Z + 1); } else { @@ -334,7 +334,7 @@ namespace Server.Items if (floorZ > int.MinValue && item.Z > GetFloorZ(item)) { - item.Location = new Point3D(item.Location, item.Z - 1); + item.Location = new Point3D(item.Location.X, item.Location.Y, item.Z - 1); } else { diff --git a/Projects/UOContent/Items/Misc/Key.cs b/Projects/UOContent/Items/Misc/Key.cs index 758f8c1ad..0f362d47b 100644 --- a/Projects/UOContent/Items/Misc/Key.cs +++ b/Projects/UOContent/Items/Misc/Key.cs @@ -2,295 +2,294 @@ using Server.Network; using Server.Prompts; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +public enum KeyType { - 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; + + [Constructible] + public Key(uint val = 0) : this(KeyType.Iron, val) { - Copper = 0x100E, - Gold = 0x100F, - Iron = 0x1010, - Rusty = 0x1013 } - public interface ILockable + public Key(KeyType type, uint val = 0, Item link = null) : base((int)type) { - bool Locked { get; set; } - uint KeyValue { get; set; } + Weight = 1.0; + + MaxRange = 3; + m_KeyVal = val; + Link = link; } - public class Key : Item + public Key(Serial serial) : base(serial) { - private string m_Description; - private uint m_KeyVal; + } - [Constructible] - public Key(uint val = 0) : this(KeyType.Iron, val) + [CommandProperty(AccessLevel.GameMaster)] + public string Description + { + get => m_Description; + set { + m_Description = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public int MaxRange { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public uint KeyValue + { + get => m_KeyVal; + + set + { + m_KeyVal = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public Item Link { get; set; } + + public static uint RandomValue() => (uint)(0xFFFFFFFE * Utility.RandomDouble()) + 1; + + public static void RemoveKeys(Mobile m, uint keyValue) + { + if (keyValue == 0) + { + return; } - public Key(KeyType type, uint val = 0, Item link = null) : base((int)type) - { - Weight = 1.0; + RemoveKeys(m.Backpack, keyValue); + RemoveKeys(m.BankBox, keyValue); + } - MaxRange = 3; - m_KeyVal = val; - Link = link; + public static void RemoveKeys(Container cont, uint keyValue) + { + if (cont == null || keyValue == 0) + { + return; } - public Key(Serial serial) : base(serial) - { - } + var items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) }); - [CommandProperty(AccessLevel.GameMaster)] - public string Description + foreach (var item in items) { - get => m_Description; - set + if (item is Key key) { - m_Description = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int MaxRange { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public uint KeyValue - { - get => m_KeyVal; - - set - { - m_KeyVal = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public Item Link { get; set; } - - public static uint RandomValue() => (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; - } - - var items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) }); - - foreach (var item in items) - { - if (item is Key key) + if (key.KeyValue == keyValue) { - if (key.KeyValue == keyValue) + key.Delete(); + } + } + else + { + var keyRing = (KeyRing)item; + + keyRing.RemoveKeys(keyValue); + } + } + } + + public static bool ContainsKey(Container cont, uint keyValue) + { + if (cont == null) + { + return false; + } + + var items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) }); + + foreach (var item in items) + { + if (item is Key key) + { + if (key.KeyValue == keyValue) + { + return true; + } + } + else + { + var keyRing = (KeyRing)item; + + if (keyRing.ContainsKey(keyValue)) + { + return true; + } + } + } + + return false; + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(2); // version + + writer.Write(MaxRange); + + writer.Write(Link); + + writer.Write(m_Description); + writer.Write(m_KeyVal); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 2: + { + MaxRange = reader.ReadInt(); + + goto case 1; + } + case 1: + { + Link = reader.ReadEntity(); + + goto case 0; + } + case 0: + { + if (version < 2 || MaxRange == 0) { - key.Delete(); + MaxRange = 3; } - } - else - { - var keyRing = (KeyRing)item; - keyRing.RemoveKeys(keyValue); + m_Description = reader.ReadString(); + + m_KeyVal = reader.ReadUInt(); + + break; } - } + } + } + + public override void OnDoubleClick(Mobile from) + { + if (!IsChildOf(from.Backpack)) + { + from.SendLocalizedMessage(501661); // That key is unreachable. + return; } - public static bool ContainsKey(Container cont, uint keyValue) + Target t; + int number; + + if (m_KeyVal != 0) { - if (cont == null) + 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(IPropertyList 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 + { + desc = m_Description?.Trim() ?? ""; + } + + if (desc.Length > 0) + { + from.NetState.SendMessage(Serial, ItemID, MessageType.Regular, 0x3B2, 3, false, "ENU", "", desc); + } + } + + public bool UseOn(Mobile from, ILockable o) + { + if (o.KeyValue == KeyValue) + { + if (o is BaseDoor door && !door.UseLocks()) { return false; } - var items = cont.FindItemsByType(new[] { typeof(Key), typeof(KeyRing) }); + o.Locked = !o.Locked; - foreach (var item in items) + if (o is Item item) { - if (item is Key key) + if (o.Locked) { - if (key.KeyValue == keyValue) - { - return true; - } + item.SendLocalizedMessageTo(from, 1048000); // You lock it. } else { - var keyRing = (KeyRing)item; - - if (keyRing.ContainsKey(keyValue)) - { - return true; - } - } - } - - return false; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(2); // version - - writer.Write(MaxRange); - - writer.Write(Link); - - writer.Write(m_Description); - writer.Write(m_KeyVal); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 2: - { - MaxRange = reader.ReadInt(); - - goto case 1; - } - case 1: - { - Link = reader.ReadEntity(); - - goto case 0; - } - case 0: - { - if (version < 2 || MaxRange == 0) - { - MaxRange = 3; - } - - m_Description = reader.ReadString(); - - m_KeyVal = reader.ReadUInt(); - - break; - } - } - } - - public override void OnDoubleClick(Mobile from) - { - if (!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 - { - desc = m_Description?.Trim() ?? ""; - } - - if (desc.Length > 0) - { - from.NetState.SendMessage(Serial, ItemID, MessageType.Regular, 0x3B2, 3, false, "ENU", "", desc); - } - } - - public bool UseOn(Mobile from, ILockable o) - { - if (o.KeyValue == KeyValue) - { - if (o is BaseDoor door && !door.UseLocks()) - { - return false; + item.SendLocalizedMessageTo(from, 1048001); // You unlock it. } - o.Locked = !o.Locked; - - if (o is LockableContainer cont1) + if (item is LockableContainer cont) { - if (cont1.LockLevel == -255) + if (cont.LockLevel == ILockpickable.MagicLock) { - cont1.LockLevel = cont1.RequiredSkill - 10; - } - } - - if (o is Item item) - { - if (o.Locked) - { - item.SendLocalizedMessageTo(from, 1048000); // You lock it. - } - else - { - item.SendLocalizedMessageTo(from, 1048001); // You unlock it. + cont.LockLevel = cont.RequiredSkill - 10; } - if (item is LockableContainer cont && cont.TrapType != TrapType.None && cont.TrapOnLockpick) + if (cont.TrapType != TrapType.None && cont.TrapOnLockpick) { if (o.Locked) { @@ -298,138 +297,136 @@ namespace Server.Items } else { - cont.SendLocalizedMessageTo( - from, - 501672 - ); // You disable the trap temporarily. Lock it again to re-enable it. + // You disable the trap temporarily. Lock it again to re-enable it. + cont.SendLocalizedMessageTo(from, 501672); } } } - - return true; } - return false; + return true; } - private class RenamePrompt : Prompt + return false; + } + + private class RenamePrompt : Prompt + { + private readonly Key m_Key; + + public RenamePrompt(Key key) => m_Key = key; + + public override void OnResponse(Mobile from, string text) { - private readonly 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)) { - if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(501661); // That key is unreachable. - return; - } - - m_Key.Description = Utility.FixHtml(text); + from.SendLocalizedMessage(501661); // That key is unreachable. + return; } + + m_Key.Description = Utility.FixHtml(text); + } + } + + private class UnlockTarget : Target + { + private readonly Key m_Key; + + public UnlockTarget(Key key) : base(key.MaxRange, false, TargetFlags.None) + { + m_Key = key; + CheckLOS = false; } - private class UnlockTarget : Target + protected override void OnTarget(Mobile from, object targeted) { - private readonly Key m_Key; - - public UnlockTarget(Key key) : base(key.MaxRange, false, TargetFlags.None) + if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack)) { - m_Key = key; - CheckLOS = false; + from.SendLocalizedMessage(501661); // That key is unreachable. + return; } - protected override void OnTarget(Mobile from, object targeted) + int number; + + if (targeted == m_Key) { - if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(501661); // That key is unreachable. - return; - } + number = 501665; // Enter a description for this key. - int number; - - if (targeted == m_Key) + from.Prompt = new RenamePrompt(m_Key); + } + else if (targeted is ILockable lockable) + { + if (m_Key.UseOn(from, lockable)) { - number = 501665; // Enter a description for this key. - - from.Prompt = new RenamePrompt(m_Key); - } - else if (targeted is ILockable lockable) - { - if (m_Key.UseOn(from, lockable)) - { - number = -1; - } - else - { - number = 501668; // This key doesn't seem to unlock that. - } + number = -1; } else { - number = 501666; // You can't unlock that! - } - - if (number != -1) - { - from.SendLocalizedMessage(number); + number = 501668; // This key doesn't seem to unlock that. } } - } - - private class CopyTarget : Target - { - private readonly Key m_Key; - - public CopyTarget(Key key) : base(3, false, TargetFlags.None) => m_Key = key; - - protected override void OnTarget(Mobile from, object targeted) + else { - if (m_Key.Deleted || !m_Key.IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(501661); // That key is unreachable. - return; - } - - int number; - - if (targeted is Key k) - { - 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. - } + number = 501666; // You can't unlock that! + } + if (number != -1) + { from.SendLocalizedMessage(number); } } } + + private class CopyTarget : Target + { + private readonly 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 k) + { + 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); + } + } } diff --git a/Projects/UOContent/Items/Misc/PoolOfAcid.cs b/Projects/UOContent/Items/Misc/PoolOfAcid.cs index db2511df9..a4941b542 100644 --- a/Projects/UOContent/Items/Misc/PoolOfAcid.cs +++ b/Projects/UOContent/Items/Misc/PoolOfAcid.cs @@ -1,102 +1,97 @@ using System; -using System.Collections.Generic; +using Server.Collections; using Server.Mobiles; -namespace Server.Items +namespace Server.Items; + +[ManualDirtyChecking] +[TypeAlias("Server.Items.AcidSlime")] +public class PoolOfAcid : Item { - [TypeAlias("Server.Items.AcidSlime")] - public class PoolOfAcid : Item + private readonly TimeSpan _duration; + private readonly int _maxDamage; + private readonly int _minDamage; + private TimerExecutionToken _timerToken; + private bool _drying; + + [Constructible] + public PoolOfAcid() : this(TimeSpan.FromSeconds(10.0), 2, 5) { - private readonly DateTime m_Created; - private readonly TimeSpan m_Duration; - private readonly int m_MaxDamage; - private readonly int m_MinDamage; - private TimerExecutionToken _timerToken; - private bool m_Drying; + } - [Constructible] - public PoolOfAcid() : this(TimeSpan.FromSeconds(10.0), 2, 5) + [Constructible] + public PoolOfAcid(TimeSpan duration, int minDamage, int maxDamage) : base(0x122A) + { + Hue = 0x3F; + Movable = false; + + _minDamage = minDamage; + _maxDamage = maxDamage; + _duration = duration; + + Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick, out _timerToken); + } + + public PoolOfAcid(Serial serial) : base(serial) + { + } + + public override string DefaultName => "a pool of acid"; + + public override void OnDelete() + { + _timerToken.Cancel(); + } + + private void OnTick() + { + var now = Core.Now; + var age = now - Created; + + if (age > _duration) { + Delete(); + return; } - [Constructible] - public PoolOfAcid(TimeSpan duration, int minDamage, int maxDamage) - : base(0x122A) + if (!_drying && age > _duration - age) { - Hue = 0x3F; - Movable = false; - - m_MinDamage = minDamage; - m_MaxDamage = maxDamage; - m_Created = Core.Now; - m_Duration = duration; - - Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick, out _timerToken); + _drying = true; + ItemID = 0x122B; } - public PoolOfAcid(Serial serial) : base(serial) + using var queue = PooledRefQueue.Create(); + + foreach (var m in GetMobilesInRange(0)) { - } - - public override string DefaultName => "a pool of acid"; - - public override void OnDelete() - { - _timerToken.Cancel(); - } - - private void OnTick() - { - var now = Core.Now; - var age = now - m_Created; - - if (age > m_Duration) + if (m.Alive && !m.IsDeadBondedPet && (m is not BaseCreature bc || bc.Controlled || bc.Summoned)) { - Delete(); - } - else - { - if (!m_Drying && age > m_Duration - age) - { - m_Drying = true; - ItemID = 0x122B; - } - - var toDamage = new List(); - - foreach (var m in GetMobilesInRange(0)) - { - if (m.Alive && !m.IsDeadBondedPet && (m is not BaseCreature bc || bc.Controlled || bc.Summoned)) - { - toDamage.Add(m); - } - } - - for (var i = 0; i < toDamage.Count; i++) - { - Damage(toDamage[i]); - } + queue.Enqueue(m); } } - public override bool OnMoveOver(Mobile m) - { - Damage(m); - return true; - } - - public void Damage(Mobile m) - { - m.Damage(Utility.RandomMinMax(m_MinDamage, m_MaxDamage)); - } - - public override void Serialize(IGenericWriter writer) - { - // Don't serialize these - } - - public override void Deserialize(IGenericReader reader) + while (queue.Count > 0) { + Damage(queue.Dequeue()); } } + + public override bool OnMoveOver(Mobile m) + { + Damage(m); + return true; + } + + public void Damage(Mobile m) + { + m.Damage(Utility.RandomMinMax(_minDamage, _maxDamage)); + } + + public override void Serialize(IGenericWriter writer) + { + } + + public override void Deserialize(IGenericReader reader) + { + } } diff --git a/Projects/UOContent/Items/Misc/PromotionalToken.cs b/Projects/UOContent/Items/Misc/PromotionalToken.cs index 264c5e6bd..d38bff9c9 100644 --- a/Projects/UOContent/Items/Misc/PromotionalToken.cs +++ b/Projects/UOContent/Items/Misc/PromotionalToken.cs @@ -23,11 +23,21 @@ namespace Server.Items public override int LabelNumber => 1070997; // A promotional token public abstract Item CreateItemFor(Mobile from); - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1070998, ItemName.ToString()); // Use this to redeem
your ~1_PROMO~ + if (ItemName != null) + { + if (ItemName.Number > 0) + { + list.Add(1070998, ItemName.Number); // Use this to redeem
your ~1_PROMO~ + } + else + { + list.Add(1070998, ItemName.String); // Use this to redeem
your ~1_PROMO~ + } + } } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Misc/Teleporter.cs b/Projects/UOContent/Items/Misc/Teleporter.cs index 88b689899..07dcb6af0 100644 --- a/Projects/UOContent/Items/Misc/Teleporter.cs +++ b/Projects/UOContent/Items/Misc/Teleporter.cs @@ -153,7 +153,7 @@ namespace Server.Items public override int LabelNumber => 1026095; // teleporter - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -168,15 +168,15 @@ namespace Server.Items if (m_MapDest != null) { - list.Add(1060658, "Map\t{0}", m_MapDest); + list.Add(1060658, $"{"Map"}\t{m_MapDest}"); } if (m_PointDest != Point3D.Zero) { - list.Add(1060659, "Coords\t{0}", m_PointDest); + list.Add(1060659, $"{"Coords"}\t{m_PointDest}"); } - list.Add(1060660, "Creatures\t{0}", m_Creatures ? "Yes" : "No"); + list.Add(1060660, $"{"Creatures"}\t{(m_Creatures ? "Yes" : "No")}"); } public override void OnSingleClick(Mobile from) @@ -463,7 +463,7 @@ namespace Server.Items return true; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -479,15 +479,15 @@ namespace Server.Items skillName = "(Invalid)"; } - list.Add(1060661, "{0}\t{1:F1}", skillName, m_Required); + list.Add(1060661, $"{skillName}\t{m_Required:F1}"); if (m_MessageString != null) { - list.Add(1060662, "Message\t{0}", m_MessageString); + list.Add(1060662, $"{"Message"}\t{m_MessageString}"); } else if (m_MessageNumber != 0) { - list.Add(1060662, "Message\t#{0}", m_MessageNumber); + list.Add(1060662, $"{"Message"}\t{m_MessageNumber:#}"); } } @@ -621,20 +621,20 @@ namespace Server.Items public override bool OnMoveOver(Mobile m) => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060661, "Range\t{0}", m_Range); + list.Add(1060661, $"{"Range"}\t{m_Range}"); if (m_Keyword >= 0) { - list.Add(1060662, "Keyword\t{0}", m_Keyword); + list.Add(1060662, $"{"Keyword"}\t{m_Keyword}"); } if (m_Substring != null) { - list.Add(1060663, "Substring\t{0}", m_Substring); + list.Add(1060663, $"{"Substring"}\t{m_Substring}"); } } @@ -1176,7 +1176,7 @@ namespace Server.Items return true; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/WindChimes.cs b/Projects/UOContent/Items/Misc/WindChimes.cs index 06eb62e8d..6e1d5e0ef 100644 --- a/Projects/UOContent/Items/Misc/WindChimes.cs +++ b/Projects/UOContent/Items/Misc/WindChimes.cs @@ -42,7 +42,7 @@ namespace Server.Items base.OnMovement(m, oldLocation); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Quivers/BaseQuiver.cs b/Projects/UOContent/Items/Quivers/BaseQuiver.cs index 6234087bd..7acc0b426 100644 --- a/Projects/UOContent/Items/Quivers/BaseQuiver.cs +++ b/Projects/UOContent/Items/Quivers/BaseQuiver.cs @@ -236,7 +236,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -256,23 +256,23 @@ namespace Server.Items { if (ammo is Arrow) { - list.Add(1075265, "{0}\t{1}", ammo.Amount, Capacity); // Ammo: ~1_QUANTITY~/~2_CAPACITY~ arrows + list.Add(1075265, $"{ammo.Amount}\t{Capacity}"); // Ammo: ~1_QUANTITY~/~2_CAPACITY~ arrows } else if (ammo is Bolt) { - list.Add(1075266, "{0}\t{1}", ammo.Amount, Capacity); // Ammo: ~1_QUANTITY~/~2_CAPACITY~ bolts + list.Add(1075266, $"{ammo.Amount}\t{Capacity}"); // Ammo: ~1_QUANTITY~/~2_CAPACITY~ bolts } } else { - list.Add(1075265, "{0}\t{1}", 0, Capacity); // Ammo: ~1_QUANTITY~/~2_CAPACITY~ arrows + list.Add(1075265, $"0\t{Capacity}"); // Ammo: ~1_QUANTITY~/~2_CAPACITY~ arrows } int prop; if ((prop = m_DamageIncrease) != 0) { - list.Add(1074762, prop.ToString()); // Damage modifier: ~1_PERCENT~% + list.Add(1074762, prop); // Damage modifier: ~1_PERCENT~% } int phys = 0, fire = 0, cold = 0, pois = 0, nrgy = 0, chaos = 0, direct = 0; @@ -281,104 +281,104 @@ namespace Server.Items if (phys != 0) { - list.Add(1060403, phys.ToString()); // physical damage ~1_val~% + list.Add(1060403, phys); // physical damage ~1_val~% } if (fire != 0) { - list.Add(1060405, fire.ToString()); // fire damage ~1_val~% + list.Add(1060405, fire); // fire damage ~1_val~% } if (cold != 0) { - list.Add(1060404, cold.ToString()); // cold damage ~1_val~% + list.Add(1060404, cold); // cold damage ~1_val~% } if (pois != 0) { - list.Add(1060406, pois.ToString()); // poison damage ~1_val~% + list.Add(1060406, pois); // poison damage ~1_val~% } if (nrgy != 0) { - list.Add(1060407, nrgy.ToString()); // energy damage ~1_val + list.Add(1060407, nrgy); // energy damage ~1_val } if (chaos != 0) { - list.Add(1072846, chaos.ToString()); // chaos damage ~1_val~% + list.Add(1072846, chaos); // chaos damage ~1_val~% } if (direct != 0) { - list.Add(1079978, direct.ToString()); // Direct Damage: ~1_PERCENT~% + list.Add(1079978, direct); // Direct Damage: ~1_PERCENT~% } list.Add(1075085); // Requirement: Mondain's Legacy if ((prop = Attributes.DefendChance) != 0) { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% + list.Add(1060408, prop); // defense chance increase ~1_val~% } if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ + list.Add(1060409, prop); // dexterity bonus ~1_val~ } if ((prop = Attributes.EnhancePotions) != 0) { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% + list.Add(1060411, prop); // enhance potions ~1_val~% } if ((prop = Attributes.CastRecovery) != 0) { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ + list.Add(1060412, prop); // faster cast recovery ~1_val~ } if ((prop = Attributes.CastSpeed) != 0) { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ + list.Add(1060413, prop); // faster casting ~1_val~ } if ((prop = Attributes.AttackChance) != 0) { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% + list.Add(1060415, prop); // hit chance increase ~1_val~% } if ((prop = Attributes.BonusHits) != 0) { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ + list.Add(1060431, prop); // hit point increase ~1_val~ } if ((prop = Attributes.BonusInt) != 0) { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ + list.Add(1060432, prop); // intelligence bonus ~1_val~ } if ((prop = Attributes.LowerManaCost) != 0) { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% + list.Add(1060433, prop); // lower mana cost ~1_val~% } if ((prop = Attributes.LowerRegCost) != 0) { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% + list.Add(1060434, prop); // lower reagent cost ~1_val~% } if ((prop = Attributes.Luck) != 0) { - list.Add(1060436, prop.ToString()); // luck ~1_val~ + list.Add(1060436, prop); // luck ~1_val~ } if ((prop = Attributes.BonusMana) != 0) { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ + list.Add(1060439, prop); // mana increase ~1_val~ } if ((prop = Attributes.RegenMana) != 0) { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ + list.Add(1060440, prop); // mana regeneration ~1_val~ } if (Attributes.NightSight != 0) @@ -388,58 +388,54 @@ namespace Server.Items if ((prop = Attributes.ReflectPhysical) != 0) { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% + list.Add(1060442, prop); // reflect physical damage ~1_val~% } if ((prop = Attributes.RegenStam) != 0) { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ + list.Add(1060443, prop); // stamina regeneration ~1_val~ } if ((prop = Attributes.RegenHits) != 0) { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ + list.Add(1060444, prop); // hit point regeneration ~1_val~ } if ((prop = Attributes.SpellDamage) != 0) { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% + list.Add(1060483, prop); // spell damage increase ~1_val~% } if ((prop = Attributes.BonusStam) != 0) { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ + list.Add(1060484, prop); // stamina increase ~1_val~ } if ((prop = Attributes.BonusStr) != 0) { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ + list.Add(1060485, prop); // strength bonus ~1_val~ } if ((prop = Attributes.WeaponSpeed) != 0) { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% + list.Add(1060486, prop); // swing speed increase ~1_val~% } if ((prop = m_LowerAmmoCost) > 0) { - list.Add(1075208, prop.ToString()); // Lower Ammo Cost ~1_Percentage~% + list.Add(1075208, prop); // Lower Ammo Cost ~1_Percentage~% } var weight = ammo != null ? ammo.Weight + ammo.Amount : 0; list.Add( - 1072241, - "{0}\t{1}\t{2}\t{3}", - Items.Count, - DefaultMaxItems, - (int)weight, - DefaultMaxWeight - ); // Contents: ~1_COUNT~/~2_MAXCOUNT items, ~3_WEIGHT~/~4_MAXWEIGHT~ stones + 1072241, // Contents: ~1_COUNT~/~2_MAXCOUNT items, ~3_WEIGHT~/~4_MAXWEIGHT~ stones + $"{Items.Count}\t{DefaultMaxItems}\t{(int)weight}\t{DefaultMaxWeight}" + ); if ((prop = m_WeightReduction) != 0) { - list.Add(1072210, prop.ToString()); // Weight reduction: ~1_PERCENTAGE~% + list.Add(1072210, prop); // Weight reduction: ~1_PERCENTAGE~% } } diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs index 9931c0708..acffef1db 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] public abstract partial class BaseIngot : Item, ICommodity { [InvalidateProperties] @@ -66,11 +68,11 @@ namespace Server.Items } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (Amount > 1) { - list.Add(1050039, "{0}\t#{1}", Amount, 1027154); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1050039, $"{Amount}\t{1027154:#}"); // ~1_NUMBER~ ~2_ITEMNAME~ } else { @@ -78,7 +80,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -98,7 +100,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class IronIngot : BaseIngot { @@ -108,7 +110,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class DullCopperIngot : BaseIngot { @@ -118,7 +120,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class ShadowIronIngot : BaseIngot { @@ -128,7 +130,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class CopperIngot : BaseIngot { @@ -138,7 +140,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class BronzeIngot : BaseIngot { @@ -148,7 +150,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class GoldIngot : BaseIngot { @@ -158,7 +160,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class AgapiteIngot : BaseIngot { @@ -168,7 +170,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class VeriteIngot : BaseIngot { @@ -178,7 +180,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1BF2, 0x1BEF)] public partial class ValoriteIngot : BaseIngot { diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs index 70d8f484e..b1c0b9ce7 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs @@ -1,10 +1,11 @@ +using ModernUO.Serialization; using Server.Engines.Craft; using Server.Mobiles; using Server.Targeting; namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] public abstract partial class BaseOre : Item { public BaseOre(CraftResource resource, int amount = 1) : base(RandomSize()) @@ -82,11 +83,11 @@ namespace Server.Items dropped.Stackable && Stackable && dropped.GetType() == GetType() && dropped.Hue == Hue && dropped.Name == Name && dropped.Amount + Amount <= 60000 && dropped != this; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (Amount > 1) { - list.Add(1050039, "{0}\t#{1}", Amount, 1026583); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1050039, $"{Amount}\t{1026583:#}"); // ~1_NUMBER~ ~2_ITEMNAME~ } else { @@ -94,7 +95,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -363,7 +364,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class IronOre : BaseOre { [Constructible] @@ -382,7 +383,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new IronIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DullCopperOre : BaseOre { [Constructible] @@ -393,7 +394,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new DullCopperIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShadowIronOre : BaseOre { [Constructible] @@ -404,7 +405,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new ShadowIronIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CopperOre : BaseOre { [Constructible] @@ -415,7 +416,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new CopperIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BronzeOre : BaseOre { [Constructible] @@ -426,7 +427,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new BronzeIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GoldOre : BaseOre { [Constructible] @@ -437,7 +438,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new GoldIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AgapiteOre : BaseOre { [Constructible] @@ -448,7 +449,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new AgapiteIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class VeriteOre : BaseOre { [Constructible] @@ -459,7 +460,7 @@ namespace Server.Items public override BaseIngot GetIngot() => new VeriteIngot(); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ValoriteOre : BaseOre { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Fishing/BigFish.cs b/Projects/UOContent/Items/Resources/Fishing/BigFish.cs index 4d0e40ae3..49dc2bc76 100644 --- a/Projects/UOContent/Items/Resources/Fishing/BigFish.cs +++ b/Projects/UOContent/Items/Resources/Fishing/BigFish.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class BigFish : Item, ICarvable { [InvalidateProperties] @@ -25,7 +26,7 @@ namespace Server.Items ScissorHelper(from, new RawFishSteak(), Math.Max(16, (int)Weight) / 4, false); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -36,7 +37,7 @@ namespace Server.Items list.Add(1070857, _fisher.Name); // Caught by ~1_fisherman~ } - list.Add(1070858, ((int)Weight).ToString()); // ~1_weight~ stones + list.Add(1070858, $"{(int)Weight}"); // ~1_weight~ stones } } diff --git a/Projects/UOContent/Items/Resources/Fishing/Fish.cs b/Projects/UOContent/Items/Resources/Fishing/Fish.cs index a84a1f002..0007f92b7 100644 --- a/Projects/UOContent/Items/Resources/Fishing/Fish.cs +++ b/Projects/UOContent/Items/Resources/Fishing/Fish.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Fish : Item, ICarvable { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Fishing/MagicFish.cs b/Projects/UOContent/Items/Resources/Fishing/MagicFish.cs index e64f6ddc0..e89d7b6ac 100644 --- a/Projects/UOContent/Items/Resources/Fishing/MagicFish.cs +++ b/Projects/UOContent/Items/Resources/Fishing/MagicFish.cs @@ -1,10 +1,11 @@ using System; +using ModernUO.Serialization; using Server.Network; using Server.Spells; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseMagicFish : Item { public BaseMagicFish(int hue) : base(0xDD6) => Hue = hue; @@ -42,7 +43,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PrizedFish : BaseMagicFish { [Constructible] @@ -56,7 +57,7 @@ namespace Server.Items public override int LabelNumber => 1041073; // prized fish } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WondrousFish : BaseMagicFish { [Constructible] @@ -70,7 +71,7 @@ namespace Server.Items public override int LabelNumber => 1041074; // wondrous fish } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TrulyRareFish : BaseMagicFish { [Constructible] @@ -84,7 +85,7 @@ namespace Server.Items public override int LabelNumber => 1041075; // truly rare fish } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PeculiarFish : BaseMagicFish { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Masonry/Granite.cs b/Projects/UOContent/Items/Resources/Masonry/Granite.cs index 6b473ac20..c24cb757f 100644 --- a/Projects/UOContent/Items/Resources/Masonry/Granite.cs +++ b/Projects/UOContent/Items/Resources/Masonry/Granite.cs @@ -62,7 +62,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Resources/MiscMLResources.cs b/Projects/UOContent/Items/Resources/MiscMLResources.cs index a0c6d30b4..f72b20447 100644 --- a/Projects/UOContent/Items/Resources/MiscMLResources.cs +++ b/Projects/UOContent/Items/Resources/MiscMLResources.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Blight : Item { [Constructible] @@ -11,7 +13,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LuminescentFungi : Item { [Constructible] @@ -22,7 +24,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CapturedEssence : Item { [Constructible] @@ -33,7 +35,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class EyeOfTheTravesty : Item { [Constructible] @@ -49,7 +51,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Corruption : Item { [Constructible] @@ -65,7 +67,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DreadHornMane : Item { [Constructible] @@ -81,7 +83,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ParasiticPlant : Item { [Constructible] @@ -97,7 +99,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Muculent : Item { [Constructible] @@ -113,7 +115,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DiseasedBark : Item { [Constructible] @@ -129,7 +131,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BarkFragment : Item { [Constructible] @@ -145,7 +147,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GrizzledBones : Item { [Constructible] @@ -161,7 +163,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LardOfParoxysmus : Item { [Constructible] @@ -177,7 +179,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PerfectEmerald : Item { [Constructible] @@ -193,7 +195,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkSapphire : Item { [Constructible] @@ -209,7 +211,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Turquoise : Item { [Constructible] @@ -225,7 +227,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class EcruCitrine : Item { [Constructible] @@ -241,7 +243,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WhitePearl : Item { [Constructible] @@ -257,7 +259,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FireRuby : Item { [Constructible] @@ -273,7 +275,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BlueDiamond : Item { [Constructible] @@ -289,7 +291,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BrilliantAmber : Item { [Constructible] @@ -305,7 +307,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Scourge : Item { [Constructible] @@ -322,7 +324,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Putrefication : Item { [Constructible] @@ -339,7 +341,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Taint : Item { [Constructible] @@ -356,7 +358,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x315A, 0x315B)] public partial class PristineDreadHorn : Item { @@ -366,7 +368,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SwitchItem : Item { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/BaseReagent.cs b/Projects/UOContent/Items/Resources/Reagents/BaseReagent.cs index 66c5fb678..32bfdb4cc 100644 --- a/Projects/UOContent/Items/Resources/Reagents/BaseReagent.cs +++ b/Projects/UOContent/Items/Resources/Reagents/BaseReagent.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseReagent : Item { public BaseReagent(int itemID, int amount = 1) : base(itemID) diff --git a/Projects/UOContent/Items/Resources/Reagents/BatWing.cs b/Projects/UOContent/Items/Resources/Reagents/BatWing.cs index 479c9bffb..474284936 100644 --- a/Projects/UOContent/Items/Resources/Reagents/BatWing.cs +++ b/Projects/UOContent/Items/Resources/Reagents/BatWing.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BatWing : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/BlackPearl.cs b/Projects/UOContent/Items/Resources/Reagents/BlackPearl.cs index 7720c2cee..d2d32f419 100644 --- a/Projects/UOContent/Items/Resources/Reagents/BlackPearl.cs +++ b/Projects/UOContent/Items/Resources/Reagents/BlackPearl.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BlackPearl : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/Bloodmoss.cs b/Projects/UOContent/Items/Resources/Reagents/Bloodmoss.cs index e3405f42b..70559a3b3 100644 --- a/Projects/UOContent/Items/Resources/Reagents/Bloodmoss.cs +++ b/Projects/UOContent/Items/Resources/Reagents/Bloodmoss.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bloodmoss : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/DaemonBlood.cs b/Projects/UOContent/Items/Resources/Reagents/DaemonBlood.cs index 6d49f0b42..10350ebed 100644 --- a/Projects/UOContent/Items/Resources/Reagents/DaemonBlood.cs +++ b/Projects/UOContent/Items/Resources/Reagents/DaemonBlood.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DaemonBlood : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/DaemonBone.cs b/Projects/UOContent/Items/Resources/Reagents/DaemonBone.cs index 8c9cc9ce6..6c8b9a818 100644 --- a/Projects/UOContent/Items/Resources/Reagents/DaemonBone.cs +++ b/Projects/UOContent/Items/Resources/Reagents/DaemonBone.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DaemonBone : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/DeadWood.cs b/Projects/UOContent/Items/Resources/Reagents/DeadWood.cs index 92036861d..39e9e6459 100644 --- a/Projects/UOContent/Items/Resources/Reagents/DeadWood.cs +++ b/Projects/UOContent/Items/Resources/Reagents/DeadWood.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DeadWood : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/DragonsBlood.cs b/Projects/UOContent/Items/Resources/Reagents/DragonsBlood.cs index 4d07593b0..0e695ab43 100644 --- a/Projects/UOContent/Items/Resources/Reagents/DragonsBlood.cs +++ b/Projects/UOContent/Items/Resources/Reagents/DragonsBlood.cs @@ -1,6 +1,8 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DragonsBlood : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/Garlic.cs b/Projects/UOContent/Items/Resources/Reagents/Garlic.cs index 35873ab76..fb616980b 100644 --- a/Projects/UOContent/Items/Resources/Reagents/Garlic.cs +++ b/Projects/UOContent/Items/Resources/Reagents/Garlic.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Garlic : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/Ginseng.cs b/Projects/UOContent/Items/Resources/Reagents/Ginseng.cs index 7fce79ee5..60df8c379 100644 --- a/Projects/UOContent/Items/Resources/Reagents/Ginseng.cs +++ b/Projects/UOContent/Items/Resources/Reagents/Ginseng.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Ginseng : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/GraveDust.cs b/Projects/UOContent/Items/Resources/Reagents/GraveDust.cs index 81a6c4872..f87cf5bf0 100644 --- a/Projects/UOContent/Items/Resources/Reagents/GraveDust.cs +++ b/Projects/UOContent/Items/Resources/Reagents/GraveDust.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GraveDust : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/MandrakeRoot.cs b/Projects/UOContent/Items/Resources/Reagents/MandrakeRoot.cs index 4ad411c42..8493e2991 100644 --- a/Projects/UOContent/Items/Resources/Reagents/MandrakeRoot.cs +++ b/Projects/UOContent/Items/Resources/Reagents/MandrakeRoot.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MandrakeRoot : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/Nightshade.cs b/Projects/UOContent/Items/Resources/Reagents/Nightshade.cs index e5e5c8a28..d39535bb8 100644 --- a/Projects/UOContent/Items/Resources/Reagents/Nightshade.cs +++ b/Projects/UOContent/Items/Resources/Reagents/Nightshade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Nightshade : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/NoxCrystal.cs b/Projects/UOContent/Items/Resources/Reagents/NoxCrystal.cs index 8cd02c9e9..6380167b0 100644 --- a/Projects/UOContent/Items/Resources/Reagents/NoxCrystal.cs +++ b/Projects/UOContent/Items/Resources/Reagents/NoxCrystal.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NoxCrystal : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/PigIron.cs b/Projects/UOContent/Items/Resources/Reagents/PigIron.cs index 46d818737..3b8f81711 100644 --- a/Projects/UOContent/Items/Resources/Reagents/PigIron.cs +++ b/Projects/UOContent/Items/Resources/Reagents/PigIron.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class PigIron : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/SpidersSilk.cs b/Projects/UOContent/Items/Resources/Reagents/SpidersSilk.cs index d4b7754d2..9f97f37ea 100644 --- a/Projects/UOContent/Items/Resources/Reagents/SpidersSilk.cs +++ b/Projects/UOContent/Items/Resources/Reagents/SpidersSilk.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SpidersSilk : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Reagents/SulfurousAsh.cs b/Projects/UOContent/Items/Resources/Reagents/SulfurousAsh.cs index 34b178ebd..40de7e27c 100644 --- a/Projects/UOContent/Items/Resources/Reagents/SulfurousAsh.cs +++ b/Projects/UOContent/Items/Resources/Reagents/SulfurousAsh.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SulfurousAsh : BaseReagent, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs b/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs index 857a8c7a0..e87b0f521 100644 --- a/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs +++ b/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xF95, 0xF96, 0xF97, 0xF98, 0xF99, 0xF9A, 0xF9B, 0xF9C)] public partial class BoltOfCloth : Item, IScissorable, IDyable, ICommodity { diff --git a/Projects/UOContent/Items/Resources/Tailor/Bone.cs b/Projects/UOContent/Items/Resources/Tailor/Bone.cs index 374aa0d66..5b27f8cbe 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Bone.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Bone.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bone : Item, ICommodity { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Tailor/Cloth.cs b/Projects/UOContent/Items/Resources/Tailor/Cloth.cs index d66970c29..b8f49714d 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Cloth.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Cloth.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1766, 0x1768)] public partial class Cloth : Item, IScissorable, IDyable, ICommodity { diff --git a/Projects/UOContent/Items/Resources/Tailor/Cotton.cs b/Projects/UOContent/Items/Resources/Tailor/Cotton.cs index 886f1f3e3..61d6c64fc 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Cotton.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Cotton.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Cotton : Item, IDyable { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Tailor/Flax.cs b/Projects/UOContent/Items/Resources/Tailor/Flax.cs index a0e265caf..ec40e4ee6 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Flax.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Flax.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Flax : Item { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Tailor/Hides.cs b/Projects/UOContent/Items/Resources/Tailor/Hides.cs index e2b32e2f7..e9dbc9cda 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Hides.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Hides.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] public abstract partial class BaseHides : Item, ICommodity { [InvalidateProperties] @@ -53,11 +55,11 @@ namespace Server.Items } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (Amount > 1) { - list.Add(1050039, "{0}\t#{1}", Amount, 1024216); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1050039, $"{Amount}\t{1024216:#}"); // ~1_NUMBER~ ~2_ITEMNAME~ } else { @@ -65,7 +67,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -85,7 +87,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1079, 0x1078)] public partial class Hides : BaseHides, IScissorable { @@ -113,7 +115,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1079, 0x1078)] public partial class SpinedHides : BaseHides, IScissorable { @@ -141,7 +143,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1079, 0x1078)] public partial class HornedHides : BaseHides, IScissorable { @@ -169,7 +171,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1079, 0x1078)] public partial class BarbedHides : BaseHides, IScissorable { diff --git a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs index 2b616b4bb..2431b26a7 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] public abstract partial class BaseLeather : Item, ICommodity { [InvalidateProperties] @@ -53,11 +55,11 @@ namespace Server.Items } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (Amount > 1) { - list.Add(1050039, "{0}\t#{1}", Amount, 1024199); // ~1_NUMBER~ ~2_ITEMNAME~ + list.Add(1050039, $"{Amount}\t{1024199:#}"); // ~1_NUMBER~ ~2_ITEMNAME~ } else { @@ -65,7 +67,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -85,7 +87,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1081, 0x1082)] public partial class Leather : BaseLeather { @@ -95,7 +97,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1081, 0x1082)] public partial class SpinedLeather : BaseLeather { @@ -105,7 +107,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1081, 0x1082)] public partial class HornedLeather : BaseLeather { @@ -115,7 +117,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1081, 0x1082)] public partial class BarbedLeather : BaseLeather { diff --git a/Projects/UOContent/Items/Resources/Tailor/UncutCloth.cs b/Projects/UOContent/Items/Resources/Tailor/UncutCloth.cs index 3daf072b0..05ed011f7 100644 --- a/Projects/UOContent/Items/Resources/Tailor/UncutCloth.cs +++ b/Projects/UOContent/Items/Resources/Tailor/UncutCloth.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Network; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1765, 0x1767)] public partial class UncutCloth : Item, IScissorable, IDyable, ICommodity { diff --git a/Projects/UOContent/Items/Resources/Tailor/Wool.cs b/Projects/UOContent/Items/Resources/Tailor/Wool.cs index 53437fd37..578de9600 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Wool.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Wool.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Wool : Item, IDyable { [Constructible] @@ -91,7 +92,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TaintedWool : Wool { [Constructible] diff --git a/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs b/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs index a644f9f3b..f5fef0825 100644 --- a/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs +++ b/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseClothMaterial : Item, IDyable { public BaseClothMaterial(int itemID, int amount = 1) : base(itemID) @@ -91,7 +92,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DarkYarn : BaseClothMaterial { [Constructible] @@ -100,7 +101,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightYarn : BaseClothMaterial { [Constructible] @@ -109,7 +110,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightYarnUnraveled : BaseClothMaterial { [Constructible] @@ -118,7 +119,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SpoolOfThread : BaseClothMaterial { [Constructible] diff --git a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs index a64473eb5..e332c7cb1 100644 --- a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs +++ b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x4200, 0x4207)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class GargishWoodenShield : BaseShield { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs index 424199d15..54b4f0410 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Server.Mobiles; namespace Server.Items @@ -58,20 +57,26 @@ namespace Server.Items switch (value) { case CampfireStatus.Burning: - ItemID = 0xDE3; - Light = LightType.Circle300; - break; + { + ItemID = 0xDE3; + Light = LightType.Circle300; + break; + } case CampfireStatus.Extinguishing: - ItemID = 0xDE9; - Light = LightType.Circle150; - break; + { + ItemID = 0xDE9; + Light = LightType.Circle150; + break; + } default: - ItemID = 0xDEA; - Light = LightType.ArchedWindowEast; - ClearEntries(); - break; + { + ItemID = 0xDEA; + Light = LightType.ArchedWindowEast; + ClearEntries(); + break; + } } } } @@ -111,8 +116,10 @@ namespace Server.Items return; } - foreach (var entry in m_Entries.ToList()) + for (var i = m_Entries.Count - 1; i >= 0; i--) { + var entry = m_Entries[i]; + if (!entry.Valid || entry.Player.NetState == null) { RemoveEntry(entry); @@ -149,10 +156,13 @@ namespace Server.Items return; } - foreach (var entry in m_Entries.ToList()) + foreach (var entry in m_Entries) { - RemoveEntry(entry); + m_Table.Remove(entry.Player); } + + m_Entries.Clear(); + m_Entries.TrimExcess(); } public override void OnAfterDelete() diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs index 2eac48b20..c5185bb7d 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs @@ -59,7 +59,7 @@ namespace Server.Items bool ICommodity.IsDeedable => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs index 6a6a1dab4..80b7713e4 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs @@ -253,7 +253,7 @@ namespace Server.Items public Item Deed => new TrophyDeed(WestID, NorthID, DeedNumber, m_AddonNumber, m_Hunter, m_AnimalWeight); - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -264,7 +264,7 @@ namespace Server.Items list.Add(1070857, m_Hunter.Name); // Caught by ~1_fisherman~ } - list.Add(1070858, m_AnimalWeight.ToString()); // ~1_weight~ stones + list.Add(1070858, m_AnimalWeight); // ~1_weight~ stones } } @@ -428,7 +428,7 @@ namespace Server.Items public override int LabelNumber => m_DeedNumber; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -439,7 +439,7 @@ namespace Server.Items list.Add(1070857, m_Hunter.Name); // Caught by ~1_fisherman~ } - list.Add(1070858, m_AnimalWeight.ToString()); // ~1_weight~ stones + list.Add(1070858, m_AnimalWeight); // ~1_weight~ stones } } diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs index b848e9070..641b457c5 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs @@ -48,10 +48,10 @@ namespace Server.Items public override void OnSingleClick(Mobile from) { - LabelTo(from, 1050039, $"#{LabelNumber}\t#1041645"); + LabelTo(from, 1050039, $"{LabelNumber:#}\t{1041645:#}"); } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); list.Add(1041645); // recovered from a shipwreck diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs index 8d3f3e515..e49ebba19 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs @@ -63,14 +63,14 @@ namespace Server.Items public virtual bool RequireDeepWater => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); AddNetProperties(list); } - protected virtual void AddNetProperties(ObjectPropertyList list) + protected virtual void AddNetProperties(IPropertyList list) { // as if the name wasn't enough.. list.Add(1017410); // Special Fishing Net @@ -435,7 +435,7 @@ namespace Server.Items public override int LabelNumber => 1063451; // a fabled fishing net - protected override void AddNetProperties(ObjectPropertyList list) + protected override void AddNetProperties(IPropertyList list) { } diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs index 66a98318f..2a948b96d 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs @@ -109,7 +109,7 @@ namespace Server.Items return 100; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -122,7 +122,7 @@ namespace Server.Items list.Add(1060636); // exceptional } - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ } public virtual void DisplayDurabilityTo(Mobile m) diff --git a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs index f3b692cb9..e3a37d000 100644 --- a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs +++ b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(2, false)] + [SerializationGenerator(2, false)] [Flippable(0x1bdd, 0x1be0)] public partial class Log : Item, ICommodity, IAxe { @@ -38,7 +40,7 @@ namespace Server.Items bool ICommodity.IsDeedable => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -86,7 +88,7 @@ namespace Server.Items } } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HeartwoodLog : Log { [Constructible] @@ -97,7 +99,7 @@ namespace Server.Items public override bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 100, new HeartwoodBoard()); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BloodwoodLog : Log { [Constructible] @@ -108,7 +110,7 @@ namespace Server.Items public override bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 100, new BloodwoodBoard()); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FrostwoodLog : Log { [Constructible] @@ -119,7 +121,7 @@ namespace Server.Items public override bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 100, new FrostwoodBoard()); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class OakLog : Log { [Constructible] @@ -130,7 +132,7 @@ namespace Server.Items public override bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 65, new OakBoard()); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AshLog : Log { [Constructible] @@ -141,7 +143,7 @@ namespace Server.Items public override bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 80, new AshBoard()); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class YewLog : Log { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs index b083d3940..9bf834dba 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs @@ -274,10 +274,7 @@ namespace Server.Items public int TitleNumber { get; set; } [CommandProperty(AccessLevel.GameMaster)] - public int MessageNumber { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public string MessageString { get; set; } + public TextDefinition Message { get; set; } public virtual void Warning_Callback(Mobile from, bool okay) { @@ -289,14 +286,14 @@ namespace Server.Items public override void BeginConfirmation(Mobile from) { - if (GumpWidth > 0 && GumpHeight > 0 && TitleNumber > 0 && (MessageNumber > 0 || MessageString != null)) + if (GumpWidth > 0 && GumpHeight > 0 && TitleNumber > 0 && Message?.IsEmpty == false) { from.CloseGump(); from.SendGump( new WarningGump( TitleNumber, TitleColor, - MessageString ?? (object)MessageNumber, + Message, MessageColor, GumpWidth, GumpHeight, @@ -314,7 +311,9 @@ namespace Server.Items { base.Serialize(writer); - writer.Write(0); // version + writer.Write(1); // version + + TextDefinition.Serialize(writer, Message); writer.WriteEncodedInt(GumpWidth); writer.WriteEncodedInt(GumpHeight); @@ -323,9 +322,6 @@ namespace Server.Items writer.WriteEncodedInt(MessageColor); writer.WriteEncodedInt(TitleNumber); - writer.WriteEncodedInt(MessageNumber); - - writer.Write(MessageString); } public override void Deserialize(IGenericReader reader) @@ -336,6 +332,11 @@ namespace Server.Items switch (version) { + case 1: + { + Message = TextDefinition.Deserialize(reader); + goto case 0; + } case 0: { GumpWidth = reader.ReadEncodedInt(); @@ -345,9 +346,13 @@ namespace Server.Items MessageColor = reader.ReadEncodedInt(); TitleNumber = reader.ReadEncodedInt(); - MessageNumber = reader.ReadEncodedInt(); - MessageString = reader.ReadString(); + if (version == 0) + { + var number = reader.ReadEncodedInt(); + var message = reader.ReadString(); + Message = number > 0 ? number : message; + } break; } @@ -388,11 +393,11 @@ namespace Server.Items 40, 400, 200, - 1062050, + 1062050, // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here 32512, false, true - ); // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here + ); } else { @@ -401,11 +406,11 @@ namespace Server.Items 40, 400, 200, - 1062049, + 1062049, // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here 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); @@ -429,7 +434,7 @@ namespace Server.Items 40, 380, 60, - @"Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here" + "Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here" ); AddHtmlLocalized(55, 110, 290, 20, 1011012); // CANCEL diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs index e8a633fab..4b09592df 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -98,7 +98,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs index d7d6d56fb..033f6ca8b 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs @@ -217,7 +217,7 @@ namespace Server.Items InvalidateProperties(); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -232,23 +232,27 @@ namespace Server.Items if (m_TargetMap == Map.Tokuno) { - list.Add(House != null ? 1063260 : 1063259, RuneFormat, desc); // ~1_val~ (Tokuno Islands)[(House)] + list.Add(House != null ? 1063260 : 1063259, $"a recall rune for {desc}"); // ~1_val~ (Tokuno Islands)[(House)] } else if (m_TargetMap == Map.Malas) { - list.Add(House != null ? 1062454 : 1060804, RuneFormat, desc); // ~1_val~ (Malas)[(House)] + list.Add(House != null ? 1062454 : 1060804, $"a recall rune for {desc}"); // ~1_val~ (Malas)[(House)] } else if (m_TargetMap == Map.Felucca) { - list.Add(House != null ? 1062452 : 1060805, RuneFormat, desc); // ~1_val~ (Felucca)[(House)] + list.Add(House != null ? 1062452 : 1060805, $"a recall rune for {desc}"); // ~1_val~ (Felucca)[(House)] } else if (m_TargetMap == Map.Trammel) { - list.Add(House != null ? 1062453 : 1060806, RuneFormat, desc); // ~1_val~ (Trammel)[(House)] + list.Add(House != null ? 1062453 : 1060806, $"a recall rune for {desc}"); // ~1_val~ (Trammel)[(House)] + } + else if (House != null) + { + list.Add($"a recall rune for {desc} ({m_TargetMap})(House)"); } else { - list.Add(House != null ? "{0} ({1})(House)" : "{0} ({1})", string.Format(RuneFormat, desc), m_TargetMap); + list.Add($"a recall rune for {desc} ({m_TargetMap})"); } } } @@ -263,33 +267,33 @@ namespace Server.Items { LabelTo( from, - House != null ? 1063260 : 1063259, + House != null ? 1063260 : 1063259, // ~1_val~ (Tokuno Islands)[(House)] string.Format(RuneFormat, desc) - ); // ~1_val~ (Tokuno Islands)[(House)] + ); } else if (m_TargetMap == Map.Malas) { LabelTo( from, - House != null ? 1062454 : 1060804, + House != null ? 1062454 : 1060804, // ~1_val~ (Malas)[(House)] string.Format(RuneFormat, desc) - ); // ~1_val~ (Malas)[(House)] + ); } else if (m_TargetMap == Map.Felucca) { LabelTo( from, - House != null ? 1062452 : 1060805, + House != null ? 1062452 : 1060805, // ~1_val~ (Felucca)[(House)] string.Format(RuneFormat, desc) - ); // ~1_val~ (Felucca)[(House)] + ); } else if (m_TargetMap == Map.Trammel) { LabelTo( from, - House != null ? 1062453 : 1060806, + House != null ? 1062453 : 1060806, // ~1_val~ (Trammel)[(House)] string.Format(RuneFormat, desc) - ); // ~1_val~ (Trammel)[(House)] + ); } else { diff --git a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs index 2088180cb..3d1ac368c 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs @@ -276,7 +276,7 @@ namespace Server.Items return false; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index 18d0dc042..9de8c67d2 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -564,35 +564,37 @@ namespace Server.Items public override bool OnDragDrop(Mobile from, Item dropped) { - if (dropped is SpellScroll scroll && scroll.Amount == 1) + if (dropped is not SpellScroll { Amount: 1 } scroll) { - var type = GetTypeForSpell(scroll.SpellID); + return false; + } - if (type != SpellbookType) - { - return false; - } + var type = GetTypeForSpell(scroll.SpellID); - if (HasSpell(scroll.SpellID)) - { - from.SendLocalizedMessage(500179); // That spell is already present in that spellbook. - return false; - } + if (type != SpellbookType) + { + return false; + } - var val = scroll.SpellID - BookOffset; + if (HasSpell(scroll.SpellID)) + { + from.SendLocalizedMessage(500179); // That spell is already present in that spellbook. + return false; + } - if (val >= 0 && val < BookCount) - { - m_Content |= (ulong)1 << val; - ++SpellCount; + var val = scroll.SpellID - BookOffset; - InvalidateProperties(); + if (val >= 0 && val < BookCount) + { + m_Content |= (ulong)1 << val; + ++SpellCount; - scroll.Delete(); + InvalidateProperties(); - from.SendSound(0x249, GetWorldLocation()); - return true; - } + scroll.Delete(); + + from.SendSound(0x249, GetWorldLocation()); + return true; } return false; @@ -621,21 +623,21 @@ namespace Server.Items if (strBonus != 0 || dexBonus != 0 || intBonus != 0) { - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - from.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + from.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - from.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + from.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - from.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + from.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } @@ -649,11 +651,11 @@ namespace Server.Items { SkillBonuses.Remove(); - var modName = Serial.ToString(); + var serial = Serial; - from.RemoveStatMod($"{modName}Str"); - from.RemoveStatMod($"{modName}Dex"); - from.RemoveStatMod($"{modName}Int"); + from.RemoveStatMod($"{serial}Str"); + from.RemoveStatMod($"{serial}Dex"); + from.RemoveStatMod($"{serial}Int"); from.CheckStatTimers(); } @@ -671,7 +673,7 @@ namespace Server.Items // The client must know about the spellbook or it will crash! var ns = to.NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -693,7 +695,7 @@ namespace Server.Items to.NetState.SendSpellbookContent(Serial, ItemID, BookOffset + 1, m_Content); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -736,72 +738,72 @@ namespace Server.Items if ((prop = Attributes.WeaponDamage) != 0) { - list.Add(1060401, prop.ToString()); // damage increase ~1_val~% + list.Add(1060401, prop); // damage increase ~1_val~% } if ((prop = Attributes.DefendChance) != 0) { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% + list.Add(1060408, prop); // defense chance increase ~1_val~% } if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ + list.Add(1060409, prop); // dexterity bonus ~1_val~ } if ((prop = Attributes.EnhancePotions) != 0) { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% + list.Add(1060411, prop); // enhance potions ~1_val~% } if ((prop = Attributes.CastRecovery) != 0) { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ + list.Add(1060412, prop); // faster cast recovery ~1_val~ } if ((prop = Attributes.CastSpeed) != 0) { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ + list.Add(1060413, prop); // faster casting ~1_val~ } if ((prop = Attributes.AttackChance) != 0) { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% + list.Add(1060415, prop); // hit chance increase ~1_val~% } if ((prop = Attributes.BonusHits) != 0) { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ + list.Add(1060431, prop); // hit point increase ~1_val~ } if ((prop = Attributes.BonusInt) != 0) { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ + list.Add(1060432, prop); // intelligence bonus ~1_val~ } if ((prop = Attributes.LowerManaCost) != 0) { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% + list.Add(1060433, prop); // lower mana cost ~1_val~% } if ((prop = Attributes.LowerRegCost) != 0) { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% + list.Add(1060434, prop); // lower reagent cost ~1_val~% } if ((prop = Attributes.Luck) != 0) { - list.Add(1060436, prop.ToString()); // luck ~1_val~ + list.Add(1060436, prop); // luck ~1_val~ } if ((prop = Attributes.BonusMana) != 0) { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ + list.Add(1060439, prop); // mana increase ~1_val~ } if ((prop = Attributes.RegenMana) != 0) { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ + list.Add(1060440, prop); // mana regeneration ~1_val~ } if (Attributes.NightSight != 0) @@ -811,17 +813,17 @@ namespace Server.Items if ((prop = Attributes.ReflectPhysical) != 0) { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% + list.Add(1060442, prop); // reflect physical damage ~1_val~% } if ((prop = Attributes.RegenStam) != 0) { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ + list.Add(1060443, prop); // stamina regeneration ~1_val~ } if ((prop = Attributes.RegenHits) != 0) { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ + list.Add(1060444, prop); // hit point regeneration ~1_val~ } if (Attributes.SpellChanneling != 0) @@ -831,30 +833,30 @@ namespace Server.Items if ((prop = Attributes.SpellDamage) != 0) { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% + list.Add(1060483, prop); // spell damage increase ~1_val~% } if ((prop = Attributes.BonusStam) != 0) { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ + list.Add(1060484, prop); // stamina increase ~1_val~ } if ((prop = Attributes.BonusStr) != 0) { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ + list.Add(1060485, prop); // strength bonus ~1_val~ } if ((prop = Attributes.WeaponSpeed) != 0) { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% + list.Add(1060486, prop); // swing speed increase ~1_val~% } if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) { - list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~% + list.Add(1075210, prop); // Increased Karma Loss ~1val~% } - list.Add(1042886, SpellCount.ToString()); // ~1_NUMBERS_OF_SPELLS~ Spells + list.Add(1042886, SpellCount); // ~1_NUMBERS_OF_SPELLS~ Spells } public override void OnSingleClick(Mobile from) @@ -972,21 +974,21 @@ namespace Server.Items { if (strBonus != 0 || dexBonus != 0 || intBonus != 0) { - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - m.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - m.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - m.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } diff --git a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs index 6f35e8b82..edd2eeb4d 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using Server.Collections; using Server.Network; using Server.Spells; using Server.Targeting; @@ -100,27 +100,23 @@ namespace Server.Items true ); - var eable = from.Map.GetMobilesInRange(new Point3D(loc), 2); - var playerVsPlayer = false; - var targets = eable.Where( - m => + var eable = from.Map.GetMobilesInRange(loc, 2); + + using var targets = PooledRefQueue.Create(); + foreach (var m in eable) + { + if (from != m && SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false) && + (!Core.AOS || from.InLOS(m))) + { + targets.Enqueue(m); + + if (m.Player) { - if (from == m || !SpellHelper.ValidIndirectTarget(from, m) || !from.CanBeHarmful(m, false) - || Core.AOS && !from.InLOS(m)) - { - return false; - } - - if (m.Player) - { - playerVsPlayer = true; - } - - return true; + playerVsPlayer = true; } - ) - .ToList(); + } + } eable.Free(); @@ -178,9 +174,9 @@ namespace Server.Items damage /= targets.Count; } - for (var i = 0; i < targets.Count; ++i) + while (targets.Count > 0) { - var m = targets[i]; + var m = targets.Dequeue(); var toDeal = damage; diff --git a/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs b/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs index 31eeddc8c..e9a5125e3 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs @@ -42,7 +42,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -50,7 +50,14 @@ namespace Server.Items if (r != null) { - list.Add(1049644, r.TextDefinition.ToString()); // [~1_stuff~] + if (r.TextDefinition.Number > 0) + { + list.Add(1049644, r.TextDefinition.Number); // [~1_stuff~] + } + else + { + list.Add(1049644, r.TextDefinition.String); // [~1_stuff~] + } } } diff --git a/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs b/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs index cf99c201c..671506b55 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs @@ -92,7 +92,7 @@ namespace Server.Items } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add( 1061133, @@ -100,7 +100,7 @@ namespace Server.Items ); // A repair service contract from ~1_SKILL_TITLE~ ~2_SKILL_NAME~. } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs index d0faa631e..86e97b7f0 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -359,7 +359,7 @@ namespace Server.Items m_Instruments[from] = item; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { var oldUses = m_UsesRemaining; CheckReplenishUses(false); @@ -376,7 +376,7 @@ namespace Server.Items list.Add(1060636); // exceptional } - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ if (m_ReplenishesCharges) { diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs index fbc502329..73a46bebc 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs @@ -87,15 +87,15 @@ namespace Server.Items from.MovingEffect(to, 0x2804, 5, 0, false, false); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ if (m_Poison != null && m_PoisonCharges > 0) { - list.Add(1062412 + m_Poison.Level, m_PoisonCharges.ToString()); + list.Add(1062412 + m_Poison.Level, m_PoisonCharges); } } @@ -123,7 +123,7 @@ namespace Server.Items writer.Write(m_UsesRemaining); - Poison.Serialize(m_Poison, writer); + writer.Write(m_Poison); writer.Write(m_PoisonCharges); } @@ -139,7 +139,7 @@ namespace Server.Items { m_UsesRemaining = reader.ReadInt(); - m_Poison = Poison.Deserialize(reader); + m_Poison = reader.ReadPoison(); m_PoisonCharges = reader.ReadInt(); break; diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs index 77624e823..e01b718f3 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs @@ -73,15 +73,15 @@ namespace Server.Items set { } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ if (m_Poison != null && m_PoisonCharges > 0) { - list.Add(1062412 + m_Poison.Level, m_PoisonCharges.ToString()); + list.Add(1062412 + m_Poison.Level, m_PoisonCharges); } } @@ -93,7 +93,7 @@ namespace Server.Items writer.Write(m_UsesRemaining); - Poison.Serialize(m_Poison, writer); + writer.Write(m_Poison); writer.Write(m_PoisonCharges); } @@ -109,7 +109,7 @@ namespace Server.Items { m_UsesRemaining = reader.ReadInt(); - m_Poison = Poison.Deserialize(reader); + m_Poison = reader.ReadPoison(); m_PoisonCharges = reader.ReadInt(); break; diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs index e93abc6e8..acfebce0c 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs @@ -89,15 +89,15 @@ namespace Server.Items from.MovingEffect(to, 0x27AC, 1, 0, false, false); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ if (m_Poison != null && m_PoisonCharges > 0) { - list.Add(1062412 + m_Poison.Level, m_PoisonCharges.ToString()); + list.Add(1062412 + m_Poison.Level, m_PoisonCharges); } } @@ -136,7 +136,7 @@ namespace Server.Items writer.Write(m_UsesRemaining); - Poison.Serialize(m_Poison, writer); + writer.Write(m_Poison); writer.Write(m_PoisonCharges); } @@ -152,7 +152,7 @@ namespace Server.Items { m_UsesRemaining = reader.ReadInt(); - m_Poison = Poison.Deserialize(reader); + m_Poison = reader.ReadPoison(); m_PoisonCharges = reader.ReadInt(); break; diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs index d87751222..3dfd81b6e 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs @@ -74,15 +74,15 @@ namespace Server.Items set { } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ if (m_Poison != null && m_PoisonCharges > 0) { - list.Add(1062412 + m_Poison.Level, m_PoisonCharges.ToString()); + list.Add(1062412 + m_Poison.Level, m_PoisonCharges); } } @@ -94,7 +94,7 @@ namespace Server.Items writer.Write(m_UsesRemaining); - Poison.Serialize(m_Poison, writer); + writer.Write(m_Poison); writer.Write(m_PoisonCharges); } @@ -110,7 +110,7 @@ namespace Server.Items { m_UsesRemaining = reader.ReadInt(); - m_Poison = Poison.Deserialize(reader); + m_Poison = reader.ReadPoison(); m_PoisonCharges = reader.ReadInt(); break; diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs index da621fd74..d4e1e769e 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BlackDyeTub : DyeTub { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs index e59f8115c..04a733169 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BlazeDyeTub : DyeTub { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs index be13e0150..2cf95b183 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Multis; @@ -11,7 +12,7 @@ namespace Server.Items bool Dye(Mobile from, DyeTub sender); } - [Serializable(2, false)] + [SerializationGenerator(2, false)] public partial class DyeTub : Item, ISecurable { [SerializableField(0)] diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs index 4bab7dab3..8b403c3f7 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class FurnitureDyeTub : DyeTub, IRewardItem { [SerializableField(0)] @@ -28,7 +29,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs index a33ceb428..2e94d43e7 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class LeatherDyeTub : DyeTub, IRewardItem { [SerializableField(0)] @@ -29,7 +30,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicClothDyetub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicClothDyetub.cs index fb1b9d20a..c917fbeb8 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicClothDyetub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicClothDyetub.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MetallicClothDyetub : DyeTub { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicLeatherDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicLeatherDyeTub.cs index 7b11c8fbb..18056ff06 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicLeatherDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/MetallicLeatherDyeTub.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MetallicLeatherDyeTub : LeatherDyeTub { [Constructible] @@ -12,7 +14,7 @@ namespace Server.Items public override bool MetallicHues => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs index d04d01ea9..c8c0ef7c1 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class RewardBlackDyeTub : DyeTub, IRewardItem { [SerializableField(0)] @@ -29,7 +30,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs index 7f04fe35a..6d202d49a 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class RunebookDyeTub : DyeTub, IRewardItem { [SerializableField(0)] @@ -29,7 +30,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs index da2ca3921..34630ac87 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class SpecialDyeTub : DyeTub, IRewardItem { [SerializableField(0)] @@ -25,7 +26,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs index 4d5d53e05..9fa04835a 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.VeteranRewards; namespace Server.Items { - [Serializable(1, false)] + [SerializationGenerator(1, false)] public partial class StatuetteDyeTub : DyeTub, IRewardItem { [SerializableField(0)] @@ -29,7 +30,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteClothDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteClothDyeTub.cs index 6ea785a37..33583c7c5 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteClothDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteClothDyeTub.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items /* High seas, loot from merchant ship's hold, also a "uncommon" loot item */ { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WhiteClothDyeTub : DyeTub { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteLeatherDyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteLeatherDyeTub.cs index 504c680a1..d0f2974db 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteLeatherDyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/WhiteLeatherDyeTub.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WhiteLeatherDyeTub : LeatherDyeTub /* OSI UO 13th anniv gift, from redeemable gift tickets */ { [Constructible] diff --git a/Projects/UOContent/Items/Skill Items/Thief/LockPick.cs b/Projects/UOContent/Items/Skill Items/Thief/LockPick.cs index e94194e44..0e3ae8b03 100644 --- a/Projects/UOContent/Items/Skill Items/Thief/LockPick.cs +++ b/Projects/UOContent/Items/Skill Items/Thief/LockPick.cs @@ -1,163 +1,165 @@ using System; using Server.Targeting; -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; } +namespace Server.Items; - void LockPick(Mobile from); +public interface ILockpickable : IPoint2D +{ + const int CannotPick = 0; + const int MagicLock = -255; + + int LockLevel { get; set; } + bool Locked { get; set; } + Mobile Picker { get; set; } + int MaxLockLevel { get; set; } + int RequiredSkill { get; set; } + + void LockPick(Mobile from); +} + +[Flippable(0x14fc, 0x14fb)] +public class Lockpick : Item +{ + [Constructible] + public Lockpick(int amount = 1) : base(0x14FC) + { + Stackable = true; + Amount = amount; } - [Flippable(0x14fc, 0x14fb)] - public class Lockpick : Item + public Lockpick(Serial serial) : base(serial) { - [Constructible] - public Lockpick(int amount = 1) : base(0x14FC) + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(1); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + if (version == 0 && Weight == 0.1) { - Stackable = true; - Amount = amount; + Weight = -1; } + } - public Lockpick(Serial serial) : base(serial) + 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 readonly Lockpick m_Item; + + public InternalTarget(Lockpick item) : base(1, false, TargetFlags.None) => m_Item = item; + + protected override void OnTarget(Mobile from, object targeted) { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (version == 0 && Weight == 0.1) + if (m_Item.Deleted) { - Weight = -1; + return; + } + + if (targeted is ILockpickable lockpickable) + { + var item = lockpickable as Item; + from.Direction = from.GetDirectionTo(item); + + if (lockpickable.Locked) + { + from.PlaySound(0x241); + + new InternalTimer(from, lockpickable, 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! } } - public override void OnDoubleClick(Mobile from) + private class InternalTimer : Timer { - from.SendLocalizedMessage(502068); // What do you want to pick? - from.Target = new InternalTarget(this); - } + private readonly Mobile m_From; + private readonly ILockpickable m_Item; + private readonly Lockpick m_Lockpick; - private class InternalTarget : Target - { - private readonly Lockpick m_Item; - - public InternalTarget(Lockpick item) : base(1, false, TargetFlags.None) => m_Item = item; - - protected override void OnTarget(Mobile from, object targeted) + public InternalTimer(Mobile from, ILockpickable item, Lockpick lockpick) : base(TimeSpan.FromSeconds(3.0)) { - if (m_Item.Deleted) + m_From = from; + m_Item = item; + m_Lockpick = lockpick; + } + + protected void BrokeLockPickTest() + { + // When failed, a 25% chance to break the lockpick + if (Utility.Random(4) == 0) + { + var item = (Item)m_Item; + + // You broke the lockpick. + item.SendLocalizedMessageTo(m_From, 502074); + + m_From.PlaySound(0x3A4); + m_Lockpick.Consume(); + } + } + + protected override void OnTick() + { + var item = (Item)m_Item; + + if (!m_From.InRange(item.GetWorldLocation(), 1)) { return; } - if (targeted is ILockpickable lockpickable) + if (m_Item.LockLevel is ILockpickable.CannotPick or ILockpickable.MagicLock) { - var item = lockpickable as Item; - from.Direction = from.GetDirectionTo(item); + // 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 (lockpickable.Locked) - { - from.PlaySound(0x241); + if (m_From.Skills.Lockpicking.Value < m_Item.RequiredSkill) + { + /* + // Do some training to gain skills + m_From.CheckSkill( SkillName.Lockpicking, 0, m_Item.LockLevel );*/ - new InternalTimer(from, lockpickable, m_Item).Start(); - } - else - { - // The door is not locked - from.SendLocalizedMessage(502069); // This does not appear to be locked - } + // 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 { - from.SendLocalizedMessage(501666); // You can't unlock that! - } - } - - private class InternalTimer : Timer - { - private readonly Mobile m_From; - private readonly ILockpickable m_Item; - private readonly 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; - } - - protected void BrokeLockPickTest() - { - // When failed, a 25% chance to break the lockpick - if (Utility.Random(4) == 0) - { - var item = (Item)m_Item; - - // You broke the lockpick. - item.SendLocalizedMessageTo(m_From, 502074); - - m_From.PlaySound(0x3A4); - m_Lockpick.Consume(); - } - } - - protected override void OnTick() - { - var item = (Item)m_Item; - - if (!m_From.InRange(item.GetWorldLocation(), 1)) - { - return; - } - - if (m_Item.LockLevel is 0 or -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.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. - } + // The player failed to pick the lock + BrokeLockPickTest(); + item.SendLocalizedMessageTo(m_From, 502075); // You are unable to pick the lock. } } } diff --git a/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs b/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs index e484fc193..5ef23c441 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs @@ -114,7 +114,7 @@ namespace Server.Items return 100; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -127,7 +127,7 @@ namespace Server.Items list.Add(1060636); // exceptional } - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ } public virtual void DisplayDurabilityTo(Mobile m) diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs index f3051982f..5a1604b83 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs @@ -42,7 +42,7 @@ namespace Server.Items } } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs index f980d2391..edf868a4d 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs @@ -24,25 +24,26 @@ namespace Server.Items public override CraftSystem CraftSystem => DefTailoring.CraftSystem; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { - var v = " "; - - if (!CraftResources.IsStandard(Resource)) + if (CraftResources.IsStandard(Resource)) { - var num = CraftResources.GetLocalizationNumber(Resource); - - if (num > 0) - { - v = $"#{num}"; - } - else - { - v = CraftResources.GetName(Resource); - } + list.Add(1061119, " "); // ~1_LEATHER_TYPE~ runic sewing kit + return; } - list.Add(1061119, v); // ~1_LEATHER_TYPE~ runic sewing kit + var num = CraftResources.GetLocalizationNumber(Resource); + + if (num > 0) + { + // ~1_LEATHER_TYPE~ runic sewing kit + list.Add(1061119, $"#{num}"); + } + else + { + // ~1_LEATHER_TYPE~ runic sewing kit + list.Add(1061119, CraftResources.GetName(Resource)); + } } public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs index 872bc099a..cf5503a50 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs @@ -1,9 +1,10 @@ +using ModernUO.Serialization; using Server.Engines.Craft; namespace Server.Items { [Flippable(0x1EB8, 0x1EB9)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TinkerTools : BaseTool { [Constructible] @@ -15,7 +16,7 @@ namespace Server.Items public override CraftSystem CraftSystem => DefTinkering.CraftSystem; } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TinkersTools : BaseTool { [Constructible] diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs index bff115f2b..8f85838fc 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs @@ -141,7 +141,7 @@ namespace Server.Items box.Tracks.AddRange(Tracks); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -169,17 +169,17 @@ namespace Server.Items if (commonSongs > 0) { - list.Add(1075234, commonSongs.ToString()); // ~1_NUMBER~ Common Tracks + list.Add(1075234, commonSongs); // ~1_NUMBER~ Common Tracks } if (uncommonSongs > 0) { - list.Add(1075235, uncommonSongs.ToString()); // ~1_NUMBER~ Uncommon Tracks + list.Add(1075235, uncommonSongs); // ~1_NUMBER~ Uncommon Tracks } if (rareSongs > 0) { - list.Add(1075236, rareSongs.ToString()); // ~1_NUMBER~ Rare Tracks + list.Add(1075236, rareSongs); // ~1_NUMBER~ Rare Tracks } } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs index 502673f23..85105db91 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs @@ -32,7 +32,7 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public MusicName Music { get; set; } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var info = DawnsMusicBox.GetInfo(Music); diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs index a6e2d7e86..887503411 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs @@ -20,7 +20,7 @@ namespace Server.Items public override bool Dye(Mobile from, DyeTub sender) => false; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); @@ -121,11 +121,11 @@ namespace Server.Items return false; } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); - list.Add(1075217, m_Charges.ToString()); // ~1_val~ charges remaining + list.Add(1075217, m_Charges); // ~1_val~ charges remaining } public override void OnDelete() diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs index 8a50d7b88..bb203b645 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs @@ -27,9 +27,9 @@ namespace Server.Items public virtual TalismanForm Form => TalismanForm.Squirrel; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { - list.Add(1075200, $"#{(int)Form}"); + list.Add(1075200, $"{(int)Form:#}"); } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs index 0a053bc94..fb5795e02 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs @@ -73,13 +73,13 @@ namespace Server.Items m_SkillMod = null; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (m_Bonus != 0) { - list.Add(1060451, "#1042354\t{0}", m_Bonus.ToString()); // ~1_skillname~ +~2_val~ + list.Add(1060451, $"{1042354:#}\t{m_Bonus}"); // ~1_skillname~ +~2_val~ } } diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs index e34c1f0ce..8fe83deaa 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs @@ -196,13 +196,13 @@ namespace Server.Items m_SkillMod = null; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (m_Bonus != 0) { - list.Add(1062005, m_Bonus.ToString()); // mining bonus +~1_val~ + list.Add(1062005, m_Bonus); // mining bonus +~1_val~ } } diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs index cb64a2583..209da9950 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs @@ -62,11 +62,11 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ } public virtual void DisplayDurabilityTo(Mobile m) diff --git a/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs b/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs index 33bb6fd86..4d73c97a9 100644 --- a/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs +++ b/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs @@ -65,11 +65,11 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public SecureLevel Level { get; set; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1062925, Petals.ToString()); // Petals: ~1_COUNT~ + list.Add(1062925, Petals); // Petals: ~1_COUNT~ } public override void GetContextMenuEntries(Mobile from, List list) diff --git a/Projects/UOContent/Items/Special/HeritageToken.cs b/Projects/UOContent/Items/Special/HeritageToken.cs index 2f84d86c5..b08a07409 100644 --- a/Projects/UOContent/Items/Special/HeritageToken.cs +++ b/Projects/UOContent/Items/Special/HeritageToken.cs @@ -30,11 +30,11 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1070998, $"#{1076595}"); // Use this to redeem
Your Heritage Items + list.AddLocalized(1070998, 1076595); // Use this to redeem
Your Heritage Items } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Items/Special/Holiday/HolidayBell.cs b/Projects/UOContent/Items/Special/Holiday/HolidayBell.cs index 33b131645..50b578b73 100644 --- a/Projects/UOContent/Items/Special/Holiday/HolidayBell.cs +++ b/Projects/UOContent/Items/Special/Holiday/HolidayBell.cs @@ -73,7 +73,11 @@ public string Giver { get => m_Maker; - set => m_Maker = value; + set + { + m_Maker = value; + InvalidateProperties(); + } } public override string DefaultName => $"A Holiday Bell From {Giver}"; diff --git a/Projects/UOContent/Items/Special/Holiday/HolidayFoods.cs b/Projects/UOContent/Items/Special/Holiday/HolidayFoods.cs index 47d7fa94e..43ad98350 100644 --- a/Projects/UOContent/Items/Special/Holiday/HolidayFoods.cs +++ b/Projects/UOContent/Items/Special/Holiday/HolidayFoods.cs @@ -13,7 +13,7 @@ namespace Server.Items { } - public CandyCane(int itemID) : base(itemID, 1) + public CandyCane(int itemID) : base(itemID) { Stackable = false; LootType = LootType.Blessed; @@ -126,7 +126,7 @@ namespace Server.Items [Constructible] public GingerBreadCookie() - : base(Utility.RandomBool() ? 0x2be1 : 0x2be2, 1) + : base(Utility.RandomBool() ? 0x2be1 : 0x2be2) { Stackable = false; LootType = LootType.Blessed; diff --git a/Projects/UOContent/Items/Special/Holiday/PKHolidayStuff.cs b/Projects/UOContent/Items/Special/Holiday/PKHolidayStuff.cs index c772739cb..be141020c 100644 --- a/Projects/UOContent/Items/Special/Holiday/PKHolidayStuff.cs +++ b/Projects/UOContent/Items/Special/Holiday/PKHolidayStuff.cs @@ -65,7 +65,7 @@ namespace Server.Items public class Spam : Food { [Constructible] - public Spam() : base(0x1044, 1) + public Spam() : base(0x1044) { Stackable = false; LootType = LootType.Blessed; diff --git a/Projects/UOContent/Items/Special/Holiday/Snowman.cs b/Projects/UOContent/Items/Special/Holiday/Snowman.cs index f94f8d02b..5033f162e 100644 --- a/Projects/UOContent/Items/Special/Holiday/Snowman.cs +++ b/Projects/UOContent/Items/Special/Holiday/Snowman.cs @@ -117,7 +117,7 @@ namespace Server.Items public static string GetRandomTitle() => titles.RandomElement(); - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs index 92238b11a..29054bfcc 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs @@ -85,19 +85,18 @@ namespace Server.Items public bool ValidLocation() => m_PlotLocation != Point3D.Zero && m_Facet != null && m_Facet != Map.Internal; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (ValidLocation()) { list.Add( - 1060658, - "location\t{0}", - HouseRaffleStone.FormatLocation(m_PlotLocation, m_Facet, false) - ); // ~1_val~: ~2_val~ - list.Add(1060659, "facet\t{0}", m_Facet); // ~1_val~: ~2_val~ - list.Add(1150486); // [Marked Item] + 1060658, // ~1_val~: ~2_val~ + $"{"location"}\t{HouseRaffleStone.FormatLocation(m_PlotLocation, m_Facet, false)}" + ); + list.Add(1060659, $"{"facet"}\t{m_Facet}"); // ~1_val~: ~2_val~ + list.Add(1150486); // [Marked Item] } if (IsExpired) diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index 248b24751..6ce07cbeb 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs @@ -398,7 +398,7 @@ namespace Server.Items public string FormatPrice() => m_TicketPrice == 0 ? "FREE" : $"{m_TicketPrice} gold"; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -411,13 +411,13 @@ namespace Server.Items { case HouseRaffleState.Active: { - list.Add(1060658, "ticket price\t{0}", FormatPrice()); // ~1_val~: ~2_val~ - list.Add(1060659, "ends\t{0}", m_Started + m_Duration); // ~1_val~: ~2_val~ + list.Add(1060658, $"{"ticket price"}\t{FormatPrice()}"); // ~1_val~: ~2_val~ + list.Add(1060659, $"{"ends"}\t{m_Started + m_Duration}"); // ~1_val~: ~2_val~ break; } case HouseRaffleState.Completed: { - list.Add(1060658, "winner\t{0}", m_Winner == null ? "unknown" : m_Winner.Name); // ~1_val~: ~2_val~ + list.Add(1060658, $"winner\t{m_Winner?.Name ?? "Unknown"}"); // ~1_val~: ~2_val~ break; } } @@ -438,9 +438,9 @@ namespace Server.Items { LabelTo( from, - 1060658, - $"Winner\t{(m_Winner == null ? "Unknown" : m_Winner.Name)}" - ); // ~1_val~: ~2_val~ + 1060658, // ~1_val~: ~2_val~ + $"Winner\t{m_Winner?.Name ?? "Unknown"}" + ); break; } } diff --git a/Projects/UOContent/Items/Special/MiniHouses.cs b/Projects/UOContent/Items/Special/MiniHouses.cs index 651f6d3f3..cf18f61d4 100644 --- a/Projects/UOContent/Items/Special/MiniHouses.cs +++ b/Projects/UOContent/Items/Special/MiniHouses.cs @@ -115,7 +115,7 @@ namespace Server.Items public override BaseAddon Addon => new MiniHouseAddon(m_Type); public override int LabelNumber => 1062096; // a mini house deed - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/MonsterStatuette.cs b/Projects/UOContent/Items/Special/MonsterStatuette.cs index 1767b0df0..ec7c8e887 100644 --- a/Projects/UOContent/Items/Special/MonsterStatuette.cs +++ b/Projects/UOContent/Items/Special/MonsterStatuette.cs @@ -227,7 +227,7 @@ namespace Server.Items base.OnMovement(m, oldLocation); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Rares/Containers/BaseWaterContainer.cs b/Projects/UOContent/Items/Special/Rares/Containers/BaseWaterContainer.cs index 9c353c6b5..6c427bb1c 100644 --- a/Projects/UOContent/Items/Special/Rares/Containers/BaseWaterContainer.cs +++ b/Projects/UOContent/Items/Special/Rares/Containers/BaseWaterContainer.cs @@ -101,7 +101,7 @@ } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { if (IsEmpty) { diff --git a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs index 6deb51f52..faf151f2d 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs @@ -102,11 +102,11 @@ namespace Server.Items }; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); // charges: ~1_val~ + list.Add(1060741, m_Charges); // charges: ~1_val~ } public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs index 9f21565a7..ecc6bf688 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs @@ -85,7 +85,7 @@ namespace Server.Items public string TranslocationItemName => "crystal ball of pet summoning"; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add( 1054131, diff --git a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs index 8cd5832cf..b116ca657 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs @@ -89,21 +89,19 @@ namespace Server.Items public string TranslocationItemName => "bracelet of binding"; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { - list.Add( - 1054000, - $"{m_Charges}\t{m_Inscription.DefaultIfNullOrEmpty(" ")}" - ); // a bracelet of binding : ~1_val~ ~2_val~ + // a bracelet of binding : ~1_val~ ~2_val~ + list.Add(1054000, $"{m_Charges}\t{m_Inscription.DefaultIfNullOrEmpty(" ")}"); } public override void OnSingleClick(Mobile from) { LabelTo( from, - 1054000, + 1054000, // a bracelet of binding : ~1_val~ ~2_val~ $"{m_Charges}\t{m_Inscription.DefaultIfNullOrEmpty(" ")}" - ); // a bracelet of binding : ~1_val~ ~2_val~ + ); } public override void GetContextMenuEntries(Mobile from, List list) diff --git a/Projects/UOContent/Items/Special/SoulStone.cs b/Projects/UOContent/Items/Special/SoulStone.cs index a951f169f..f924e21b2 100644 --- a/Projects/UOContent/Items/Special/SoulStone.cs +++ b/Projects/UOContent/Items/Special/SoulStone.cs @@ -1,5 +1,4 @@ using System; -using Server.Accounting; using Server.Engines.VeteranRewards; using Server.Factions; using Server.Gumps; @@ -122,21 +121,26 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public SecureLevel Level { get; set; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (!IsEmpty) { list.Add( - 1070721, - "#{0}\t{1:F1}", - AosSkillBonuses.GetLabel(Skill), - SkillValue - ); // Skill stored: ~1_skillname~ ~2_skillamount~ + 1070721, // Skill stored: ~1_skillname~ ~2_skillamount~ + $"{AosSkillBonuses.GetLabel(Skill):#}\t{SkillValue:F1}" + ); } - list.Add(1041602, "{0}", LastUserName ?? $"#{1074235}"); // Owner: ~1_val~ + if (LastUserName != null) + { + list.Add(1041602, LastUserName); // Owner: ~1_val~ + } + else + { + list.AddLocalized(1041602, 1074235); // Owner: ~1_val~ + } } private static bool CheckCombat(Mobile m, TimeSpan time) @@ -967,11 +971,11 @@ namespace Server.Items set { } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ } public override void Serialize(IGenericWriter writer) @@ -1095,7 +1099,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs index 1dd35b8f3..e15007091 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs @@ -160,7 +160,7 @@ namespace Server.Items return new PowerScroll(skillName, 100 + Utility.RandomMinMax(min, max) * 5); } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var level = (Value - 105.0) / 5.0; @@ -175,7 +175,7 @@ namespace Server.Items } else { - list.Add("a power scroll of {0} ({1} Skill)", GetName(), Value); + list.Add($"a power scroll of {GetName()} ({Value} Skill)"); } } @@ -189,7 +189,7 @@ namespace Server.Items } else { - LabelTo(from, "a power scroll of {0} ({1} Skill)", GetName(), Value); + LabelTo(from, $"a power scroll of {GetName()} ({Value} Skill)"); } } diff --git a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs index d6b5453c6..39e346c7f 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs @@ -29,11 +29,11 @@ namespace Server.Items public override string DefaultTitle => "Scroll of Alacrity:"; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1071345, "{0} 15 Minutes", GetName()); // Skill: ~1_val~ + list.Add(1071345, $"{GetName()} 15 Minutes"); // Skill: ~1_val~ } public override bool CanUse(Mobile from) diff --git a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs index 80f44cc3e..e013aa399 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs @@ -32,7 +32,7 @@ namespace Server.Items public static ScrollofTranscendence CreateRandom(int min, int max) => new(Utility.RandomSkill(), Utility.RandomMinMax(min, max) * 0.1); - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); list.Add(1076759, $"{GetName()}\t{Value:0.#} Skill Points"); diff --git a/Projects/UOContent/Items/Special/Special Scrolls/StatScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/StatScroll.cs index eabd749db..2b16cf501 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/StatScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/StatScroll.cs @@ -43,7 +43,7 @@ namespace Server.Items public override string DefaultTitle => $"Power Scroll ({((int)Value - 225 >= 0 ? "+" : "")}{(int)Value - 225} Maximum Stats):"; - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var level = ((int)Value - 230) / 5; @@ -59,7 +59,8 @@ namespace Server.Items } else { - list.Add("a scroll of power ({0}{1} Maximum Stats)", Value - 225 >= 0 ? "+" : "", Value - 225); + var diff = Value - 225; + list.Add($"a scroll of power ({(diff >= 0 ? "+" : "")}{diff} Maximum Stats)"); } } @@ -73,7 +74,8 @@ namespace Server.Items } else { - LabelTo(from, "a scroll of power ({0}{1} Maximum Stats)", Value - 225 >= 0 ? "+" : "", Value - 225); + var diff = Value - 225; + LabelTo(from, $"a scroll of power ({(diff >= 0 ? "+" : "")}{diff} Maximum Stats)"); } } diff --git a/Projects/UOContent/Items/Special/Valentines/2007/ValentinesCard.cs b/Projects/UOContent/Items/Special/Valentines/2007/ValentinesCard.cs index 7e851da24..27dff00b5 100644 --- a/Projects/UOContent/Items/Special/Valentines/2007/ValentinesCard.cs +++ b/Projects/UOContent/Items/Special/Valentines/2007/ValentinesCard.cs @@ -52,7 +52,7 @@ namespace Server.Items * */ - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add(m_LabelNumber, $"{m_To ?? Unsigned}\t{m_From ?? Unsigned}"); } diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs b/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs index 1bd7ce1bc..ff7b6527a 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs @@ -338,7 +338,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs index f102d7441..6cf4d1125 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs @@ -74,7 +74,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -155,7 +155,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/BloodyPentagram.cs b/Projects/UOContent/Items/Special/Veteran Rewards/BloodyPentagram.cs index be9f47b1d..5d5e420db 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/BloodyPentagram.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/BloodyPentagram.cs @@ -168,7 +168,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs index ad6232377..31086b084 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs @@ -107,7 +107,7 @@ namespace Server.Items m_Fire?.MoveToWorld(new Point3D(X, Y, Z + ItemData.Height), Map); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -184,7 +184,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs index 4f55b12a1..3fb9a131e 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs @@ -16,7 +16,7 @@ namespace Server.Items public override int LabelNumber => 1076157; // Decorative Cannon - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -27,7 +27,7 @@ namespace Server.Items list.Add(1076223); // 7th Year Veteran Reward } - list.Add(1076207, addon.Charges.ToString()); // Remaining Charges: ~1_val~ + list.Add(1076207, addon.Charges); // Remaining Charges: ~1_val~ } } @@ -462,7 +462,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -471,7 +471,7 @@ namespace Server.Items list.Add(1076223); // 7th Year Veteran Reward } - list.Add(1076207, m_Charges.ToString()); // Remaining Charges: ~1_val~ + list.Add(1076207, m_Charges); // Remaining Charges: ~1_val~ } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs b/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs index a9d99e5a0..8496a9ed4 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs @@ -32,7 +32,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/ContestMiniHouse.cs b/Projects/UOContent/Items/Special/Veteran Rewards/ContestMiniHouse.cs index 753ae19ec..e978d9956 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/ContestMiniHouse.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/ContestMiniHouse.cs @@ -111,7 +111,7 @@ namespace Server.Items base.OnDoubleClick(from); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs b/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs index 71f5da8f8..dfebe6dfa 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs @@ -59,7 +59,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -140,7 +140,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs b/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs index 5e1752e6a..fdc58513b 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs @@ -73,7 +73,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -154,7 +154,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs b/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs index b46114ff2..b1f53b2ac 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs @@ -73,7 +73,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -154,7 +154,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs index 0cb2917e8..116d7f54e 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs @@ -359,7 +359,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs b/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs index 3e8129b20..9cf8086a0 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs @@ -160,7 +160,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs index 80647576c..1c9ac09cb 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs @@ -102,7 +102,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs b/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs index 33aa414b7..bde1d8e2f 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs @@ -15,7 +15,7 @@ namespace Server.Items public override bool ForceShowProperties => ObjectPropertyList.Enabled; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -90,7 +90,7 @@ namespace Server.Items from.SendLocalizedMessage(500489); // You can't use an axe on that. } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -202,7 +202,7 @@ namespace Server.Items base.OnDoubleClick(m); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs index c9c32ede4..01bef3ce5 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs @@ -250,7 +250,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs b/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs index cced8f9cb..132b9ae39 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs @@ -302,7 +302,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs b/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs index a9603d6af..ffd7b759a 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs @@ -99,7 +99,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -110,7 +110,7 @@ namespace Server.Items if (ShowUsesRemaining) { - list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, m_UsesRemaining); // uses remaining: ~1_val~ } } diff --git a/Projects/UOContent/Items/Talismans/BaseTalisman.cs b/Projects/UOContent/Items/Talismans/BaseTalisman.cs index fe338037f..37aca5420 100644 --- a/Projects/UOContent/Items/Talismans/BaseTalisman.cs +++ b/Projects/UOContent/Items/Talismans/BaseTalisman.cs @@ -549,7 +549,7 @@ namespace Server.Items } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (ForceShowName) { @@ -557,14 +557,19 @@ namespace Server.Items } else if (m_Summoner?.IsEmpty == false) { - list.Add( - 1072400, - m_Summoner?.Name ?? "Unknown" - ); // Talisman of ~1_name~ Summoning + var name = m_Summoner?.Name; + if (name?.Number > 0) + { + list.Add(1072400, name.Number); // Talisman of ~1_name~ Summoning + } + else + { + list.Add(1072400, name?.String ?? "Unknown"); // Talisman of ~1_name~ Summoning + } } else if (m_Removal != TalismanRemoval.None) { - list.Add(1072389, $"#{1072000 + (int)m_Removal}"); // Talisman of ~1_name~ + list.AddLocalized(1072389, 1072000 + (int)m_Removal); // Talisman of ~1_name~ } else { @@ -572,7 +577,7 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -595,7 +600,7 @@ namespace Server.Items { if (m_ChargeTime > 0) { - list.Add(1074884, m_ChargeTime.ToString()); // Charge time left: ~1_val~ + list.Add(1074884, m_ChargeTime); // Charge time left: ~1_val~ } else { @@ -608,41 +613,33 @@ namespace Server.Items if (m_Killer?.IsEmpty == false && m_Killer.Amount > 0) { list.Add( - 1072388, - "{0}\t{1}", - m_Killer.Name?.ToString() ?? "Unknown", - m_Killer.Amount - ); // ~1_NAME~ Killer: +~2_val~% + 1072388, // ~1_NAME~ Killer: +~2_val~% + $"{m_Killer.Name ?? "Unknown"}\t{m_Killer.Amount}" + ); } if (m_Protection?.IsEmpty == false && m_Protection.Amount > 0) { list.Add( - 1072387, - "{0}\t{1}", - m_Protection.Name?.ToString() ?? "Unknown", - m_Protection.Amount - ); // ~1_NAME~ Protection: +~2_val~% + 1072387, // ~1_NAME~ Protection: +~2_val~% + $"{m_Protection.Name ?? "Unknown"}\t{m_Protection.Amount}" + ); } if (m_ExceptionalBonus != 0) { list.Add( - 1072395, - "#{0}\t{1}", - AosSkillBonuses.GetLabel(m_Skill), - m_ExceptionalBonus - ); // ~1_NAME~ Exceptional Bonus: ~2_val~% + 1072395, // ~1_NAME~ Exceptional Bonus: ~2_val~% + $"{AosSkillBonuses.GetLabel(m_Skill):#}\t{m_ExceptionalBonus}" + ); } if (m_SuccessBonus != 0) { list.Add( - 1072394, - "#{0}\t{1}", - AosSkillBonuses.GetLabel(m_Skill), - m_SuccessBonus - ); // ~1_NAME~ Bonus: ~2_val~% + 1072394, // ~1_NAME~ Bonus: ~2_val~% + $"{AosSkillBonuses.GetLabel(m_Skill):#}\t{m_SuccessBonus}" + ); } SkillBonuses.GetProperties(list); @@ -651,72 +648,72 @@ namespace Server.Items if ((prop = Attributes.WeaponDamage) != 0) { - list.Add(1060401, prop.ToString()); // damage increase ~1_val~% + list.Add(1060401, prop); // damage increase ~1_val~% } if ((prop = Attributes.DefendChance) != 0) { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% + list.Add(1060408, prop); // defense chance increase ~1_val~% } if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ + list.Add(1060409, prop); // dexterity bonus ~1_val~ } if ((prop = Attributes.EnhancePotions) != 0) { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% + list.Add(1060411, prop); // enhance potions ~1_val~% } if ((prop = Attributes.CastRecovery) != 0) { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ + list.Add(1060412, prop); // faster cast recovery ~1_val~ } if ((prop = Attributes.CastSpeed) != 0) { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ + list.Add(1060413, prop); // faster casting ~1_val~ } if ((prop = Attributes.AttackChance) != 0) { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% + list.Add(1060415, prop); // hit chance increase ~1_val~% } if ((prop = Attributes.BonusHits) != 0) { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ + list.Add(1060431, prop); // hit point increase ~1_val~ } if ((prop = Attributes.BonusInt) != 0) { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ + list.Add(1060432, prop); // intelligence bonus ~1_val~ } if ((prop = Attributes.LowerManaCost) != 0) { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% + list.Add(1060433, prop); // lower mana cost ~1_val~% } if ((prop = Attributes.LowerRegCost) != 0) { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% + list.Add(1060434, prop); // lower reagent cost ~1_val~% } if ((prop = Attributes.Luck) != 0) { - list.Add(1060436, prop.ToString()); // luck ~1_val~ + list.Add(1060436, prop); // luck ~1_val~ } if ((prop = Attributes.BonusMana) != 0) { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ + list.Add(1060439, prop); // mana increase ~1_val~ } if ((prop = Attributes.RegenMana) != 0) { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ + list.Add(1060440, prop); // mana regeneration ~1_val~ } if (Attributes.NightSight != 0) @@ -726,17 +723,17 @@ namespace Server.Items if ((prop = Attributes.ReflectPhysical) != 0) { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% + list.Add(1060442, prop); // reflect physical damage ~1_val~% } if ((prop = Attributes.RegenStam) != 0) { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ + list.Add(1060443, prop); // stamina regeneration ~1_val~ } if ((prop = Attributes.RegenHits) != 0) { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ + list.Add(1060444, prop); // hit point regeneration ~1_val~ } if (Attributes.SpellChanneling != 0) @@ -746,32 +743,32 @@ namespace Server.Items if ((prop = Attributes.SpellDamage) != 0) { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% + list.Add(1060483, prop); // spell damage increase ~1_val~% } if ((prop = Attributes.BonusStam) != 0) { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ + list.Add(1060484, prop); // stamina increase ~1_val~ } if ((prop = Attributes.BonusStr) != 0) { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ + list.Add(1060485, prop); // strength bonus ~1_val~ } if ((prop = Attributes.WeaponSpeed) != 0) { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% + list.Add(1060486, prop); // swing speed increase ~1_val~% } if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) { - list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~% + list.Add(1075210, prop); // Increased Karma Loss ~1val~% } if (m_MaxCharges > 0) { - list.Add(1060741, m_Charges.ToString()); // charges: ~1_val~ + list.Add(1060741, m_Charges); // charges: ~1_val~ } if (m_Slayer != TalismanSlayerName.None) @@ -1241,24 +1238,22 @@ namespace Server.Items 0x100 ); - StatMod mod; - - mod = target.GetStatMod("[Magic] Str Offset"); + var mod = target.GetStatMod("[Magic] Str Curse"); if (mod?.Offset < 0) { - target.RemoveStatMod("[Magic] Str Offset"); + target.RemoveStatMod("[Magic] Str Curse"); } - mod = target.GetStatMod("[Magic] Dex Offset"); + mod = target.GetStatMod("[Magic] Dex Curse"); if (mod?.Offset < 0) { - target.RemoveStatMod("[Magic] Dex Offset"); + target.RemoveStatMod("[Magic] Dex Curse"); } - mod = target.GetStatMod("[Magic] Int Offset"); + mod = target.GetStatMod("[Magic] Int Curse"); if (mod?.Offset < 0) { - target.RemoveStatMod("[Magic] Int Offset"); + target.RemoveStatMod("[Magic] Int Curse"); } target.Paralyzed = false; diff --git a/Projects/UOContent/Items/Talismans/TalismanSummons.cs b/Projects/UOContent/Items/Talismans/TalismanSummons.cs index e74dd8f4f..262334eb7 100644 --- a/Projects/UOContent/Items/Talismans/TalismanSummons.cs +++ b/Projects/UOContent/Items/Talismans/TalismanSummons.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles { // public override bool IsInvulnerable => true; // TODO: Wailing banshees are NOT invulnerable, are any of the others? - public BaseTalismanSummon() : base(AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4) + public BaseTalismanSummon() : base(AIType.AI_Melee, FightMode.None) { // TODO: Stats/skills } diff --git a/Projects/UOContent/Items/Traps/GasTrap.cs b/Projects/UOContent/Items/Traps/GasTrap.cs index 8e07d5646..656a38617 100644 --- a/Projects/UOContent/Items/Traps/GasTrap.cs +++ b/Projects/UOContent/Items/Traps/GasTrap.cs @@ -85,7 +85,7 @@ namespace Server.Items writer.Write(0); // version - Poison.Serialize(Poison, writer); + writer.Write(Poison); } public override void Deserialize(IGenericReader reader) @@ -98,7 +98,7 @@ namespace Server.Items { case 0: { - Poison = Poison.Deserialize(reader); + Poison = reader.ReadPoison(); break; } } diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs index 1f8f4c6fa..61dfd853d 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs @@ -1,124 +1,95 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class TreasureChestLevel1 : LockableContainer { - public class TreasureChestLevel1 : LockableContainer + [Constructible] + public TreasureChestLevel1() : base(0xE41) { - private const int m_Level = 1; + SetChestAppearance(); + Movable = false; - [Constructible] - public TreasureChestLevel1() - : base(0xE41) + TrapType = TrapType.DartTrap; + TrapPower = Utility.Random(1, 25); + Locked = true; + + RequiredSkill = 57; + LockLevel = RequiredSkill - Utility.Random(1, 10); + MaxLockLevel = RequiredSkill + Utility.Random(1, 10); + + // According to OSI, loot in level 1 chest is: + // Gold 25 - 50 + // Bolts 10 + // Gems + // Normal weapon + // Normal armour + // Normal clothing + // Normal jewelry + + // Gold + DropItem(new Gold(Utility.Random(30, 100))); + + // Drop bolts + // DropItem( new Bolt( 10 ) ); + + // Gems + if (Utility.RandomBool()) { - SetChestAppearance(); - Movable = false; - - TrapType = TrapType.DartTrap; - TrapPower = m_Level * Utility.Random(1, 25); - Locked = true; - - RequiredSkill = 57; - LockLevel = RequiredSkill - Utility.Random(1, 10); - MaxLockLevel = RequiredSkill + Utility.Random(1, 10); - - // According to OSI, loot in level 1 chest is: - // Gold 25 - 50 - // Bolts 10 - // Gems - // Normal weapon - // Normal armour - // Normal clothing - // Normal jewelry - - // Gold - DropItem(new Gold(Utility.Random(30, 100))); - - // Drop bolts - // DropItem( new Bolt( 10 ) ); - - // Gems - if (Utility.RandomBool()) - { - var GemLoot = Loot.RandomGem(); - GemLoot.Amount = Utility.Random(1, 3); - DropItem(GemLoot); - } - - // Weapon - if (Utility.RandomBool()) - { - DropItem(Loot.RandomWeapon()); - } - - // Armour - if (Utility.RandomBool()) - { - DropItem(Loot.RandomArmorOrShield()); - } - - // Clothing - if (Utility.RandomBool()) - { - DropItem(Loot.RandomClothing()); - } - - // Jewelry - if (Utility.RandomBool()) - { - DropItem(Loot.RandomJewelry()); - } + var gems = Loot.RandomGem(); + gems.Amount = Utility.Random(1, 3); + DropItem(gems); } - public TreasureChestLevel1(Serial serial) - : base(serial) + // Weapon + if (Utility.RandomBool()) { + DropItem(Loot.RandomWeapon()); } - public override bool Decays => true; - - public override bool IsDecoContainer => false; - - public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override int DefaultGumpID => 0x42; - - public override int DefaultDropSound => 0x42; - - public override Rectangle2D Bounds => new(18, 105, 144, 73); - - private void SetChestAppearance() + // Armour + if (Utility.RandomBool()) { - var UseFirstItemId = Utility.RandomBool(); - - switch (Utility.RandomList(0, 1, 2)) - { - case 0: // Large Crate - ItemID = UseFirstItemId ? 0xe3c : 0xe3d; - GumpID = 0x44; - break; - - case 1: // Medium Crate - ItemID = UseFirstItemId ? 0xe3e : 0xe3f; - GumpID = 0x44; - break; - - case 2: // Small Crate - ItemID = UseFirstItemId ? 0x9a9 : 0xe7e; - GumpID = 0x44; - break; - } + DropItem(Loot.RandomArmorOrShield()); } - public override void Serialize(IGenericWriter writer) + // Clothing + if (Utility.RandomBool()) { - base.Serialize(writer); - writer.Write(1); // version + DropItem(Loot.RandomClothing()); } - public override void Deserialize(IGenericReader reader) + // Jewelry + if (Utility.RandomBool()) { - base.Deserialize(reader); - var version = reader.ReadInt(); + DropItem(Loot.RandomJewelry()); } } + + public override bool Decays => true; + + public override bool IsDecoContainer => false; + + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); + + public override int DefaultGumpID => 0x44; + + public override int DefaultDropSound => 0x42; + + public override Rectangle2D Bounds => new(18, 105, 144, 73); + + private void SetChestAppearance() + { + ItemID = Utility.Random(6) switch + { + 0 => 0xe3c, // Large Crate + 1 => 0xe3d, // Large Crate + 2 => 0xe3e, // Medium Crate + 3 => 0xe3f, // Medium Crate + 4 => 0x9a9, // Small Crate + _ => 0xe7e // Small Crate + }; + } } diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs index 14e4d3946..bc3fe877c 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs @@ -1,147 +1,117 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class TreasureChestLevel2 : LockableContainer { - public class TreasureChestLevel2 : LockableContainer + [Constructible] + public TreasureChestLevel2() : base(0xE41) { - private const int m_Level = 2; + SetChestAppearance(); + Movable = false; - [Constructible] - public TreasureChestLevel2() - : base(0xE41) + TrapType = TrapType.ExplosionTrap; + TrapPower = 2 * Utility.Random(1, 25); + Locked = true; + + RequiredSkill = 72; + LockLevel = RequiredSkill - Utility.Random(1, 10); + MaxLockLevel = RequiredSkill + Utility.Random(1, 10); + + // According to OSI, loot in level 2 chest is: + // Gold 80 - 150 + // Arrows 10 + // Reagents + // Scrolls + // Potions + // Gems + + // Gold + DropItem(new Gold(Utility.Random(70, 100))); + + // Drop bolts + // DropItem( new Arrow( 10 ) ); + + // Reagents + for (var i = Utility.Random(3); i > 0; i--) { - SetChestAppearance(); - Movable = false; - - TrapType = TrapType.ExplosionTrap; - TrapPower = m_Level * Utility.Random(1, 25); - Locked = true; - - RequiredSkill = 72; - LockLevel = RequiredSkill - Utility.Random(1, 10); - MaxLockLevel = RequiredSkill + Utility.Random(1, 10); - - // According to OSI, loot in level 2 chest is: - // Gold 80 - 150 - // Arrows 10 - // Reagents - // Scrolls - // Potions - // Gems - - // Gold - DropItem(new Gold(Utility.Random(70, 100))); - - // Drop bolts - // DropItem( new Arrow( 10 ) ); - - // Reagents - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var ReagentLoot = Loot.RandomReagent(); - ReagentLoot.Amount = Utility.Random(1, m_Level); - DropItem(ReagentLoot); - } - - // Scrolls - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - Item ScrollLoot = Loot.RandomScroll(0, 39, SpellbookType.Regular); - ScrollLoot.Amount = Utility.Random(1, 8); - DropItem(ScrollLoot); - } - - // Potions - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var PotionLoot = Loot.RandomPotion(); - DropItem(PotionLoot); - } - - // Gems - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var GemLoot = Loot.RandomGem(); - GemLoot.Amount = Utility.Random(1, 6); - DropItem(GemLoot); - } + var reagents = Loot.RandomReagent(); + reagents.Amount = Utility.Random(1, 2); + DropItem(reagents); } - public TreasureChestLevel2(Serial serial) - : base(serial) + // Scrolls + if (Utility.RandomBool()) { + var scrolls = Loot.RandomScroll(0, 39, SpellbookType.Regular); + scrolls.Amount = Utility.Random(1, 8); + DropItem(scrolls); } - public override bool Decays => true; - - public override bool IsDecoContainer => false; - - public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override int DefaultGumpID => 0x42; - - public override int DefaultDropSound => 0x42; - - public override Rectangle2D Bounds => new(18, 105, 144, 73); - - private void SetChestAppearance() + // Potions + if (Utility.RandomBool()) { - var UseFirstItemId = Utility.RandomBool(); - - switch (Utility.RandomList(0, 1, 2, 3, 4, 5, 6, 7)) - { - case 0: // Large Crate - ItemID = UseFirstItemId ? 0xe3c : 0xe3d; - GumpID = 0x44; - break; - - case 1: // Medium Crate - ItemID = UseFirstItemId ? 0xe3e : 0xe3f; - GumpID = 0x44; - break; - - case 2: // Small Crate - ItemID = UseFirstItemId ? 0x9a9 : 0xe7e; - GumpID = 0x44; - break; - - case 3: // Wooden Chest - ItemID = UseFirstItemId ? 0xe42 : 0xe43; - GumpID = 0x49; - break; - - case 4: // Metal Chest - ItemID = UseFirstItemId ? 0x9ab : 0xe7c; - GumpID = 0x4A; - break; - - case 5: // Metal Golden Chest - ItemID = UseFirstItemId ? 0xe40 : 0xe41; - GumpID = 0x42; - break; - - case 6: // Keg - ItemID = 0xe7f; - GumpID = 0x3e; - break; - - case 7: // Barrel - ItemID = 0xe77; - GumpID = 0x3e; - break; - } + DropItem(Loot.RandomPotion()); } - public override void Serialize(IGenericWriter writer) + // Gems + if (Utility.RandomBool()) { - base.Serialize(writer); - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadInt(); + var gems = Loot.RandomGem(); + gems.Amount = Utility.Random(1, 6); + DropItem(gems); } } + + public override bool Decays => true; + + public override bool IsDecoContainer => false; + + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); + + public override int DefaultGumpID => 0x42; + + public override int DefaultDropSound => 0x42; + + public override Rectangle2D Bounds => new(18, 105, 144, 73); + + private static readonly (int, int)[] _chestAppearances = + { + // Large Crate + (0xe3c, 0x44), + (0xe3d, 0x44), + + // Medium Crate + (0xe3e, 0x44), + (0xe3f, 0x44), + + // Small Crate + (0x9a9, 0x44), + (0xe7e, 0x44), + + // Wooden Chest + (0xe42, 0x49), + (0xe43, 0x49), + + // Metal Chest + (0x9ab, 0x4A), + (0xe7c, 0x4A), + + // Metal Golden Chest + (0xe40, 0x42), + (0xe41, 0x42), + + // Keg + (0xe7f, 0x3e), + + // Barrel + (0xe77, 0x3e), + }; + + private void SetChestAppearance() + { + (ItemID, GumpID) = _chestAppearances.RandomElement(); + } } diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs index bcd179d9a..9ec26c7bb 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs @@ -1,166 +1,144 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class TreasureChestLevel3 : LockableContainer { - public class TreasureChestLevel3 : LockableContainer + [Constructible] + public TreasureChestLevel3() : base(0xE41) { - private const int m_Level = 3; + SetChestAppearance(); + Movable = false; - [Constructible] - public TreasureChestLevel3() - : base(0xE41) + TrapType = TrapType.PoisonTrap; + TrapPower = 3 * Utility.Random(1, 25); + Locked = true; + + RequiredSkill = 84; + LockLevel = RequiredSkill - Utility.Random(1, 10); + MaxLockLevel = RequiredSkill + Utility.Random(1, 10); + + // According to OSI, loot in level 3 chest is: + // Gold 250 - 350 + // Arrows 10 + // Reagents + // Scrolls + // Potions + // Gems + // Magic Wand + // Magic weapon + // Magic armour + // Magic clothing (not implemented) + // Magic jewelry (not implemented) + + // Gold + DropItem(new Gold(Utility.Random(180, 240))); + + // Drop bolts + // DropItem( new Arrow( 10 ) ); + + // Reagents + for (var i = Utility.Random(2); i >= 0; i--) { - SetChestAppearance(); - Movable = false; - - TrapType = TrapType.PoisonTrap; - TrapPower = m_Level * Utility.Random(1, 25); - Locked = true; - - RequiredSkill = 84; - LockLevel = RequiredSkill - Utility.Random(1, 10); - MaxLockLevel = RequiredSkill + Utility.Random(1, 10); - - // According to OSI, loot in level 3 chest is: - // Gold 250 - 350 - // Arrows 10 - // Reagents - // Scrolls - // Potions - // Gems - // Magic Wand - // Magic weapon - // Magic armour - // Magic clothing (not implemented) - // Magic jewelry (not implemented) - - // Gold - DropItem(new Gold(Utility.Random(180, 240))); - - // Drop bolts - // DropItem( new Arrow( 10 ) ); - - // Reagents - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var ReagentLoot = Loot.RandomReagent(); - ReagentLoot.Amount = Utility.Random(1, 9); - DropItem(ReagentLoot); - } - - // Scrolls - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - Item ScrollLoot = Loot.RandomScroll(0, 47, SpellbookType.Regular); - ScrollLoot.Amount = Utility.Random(1, 12); - DropItem(ScrollLoot); - } - - // Potions - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var PotionLoot = Loot.RandomPotion(); - DropItem(PotionLoot); - } - - // Gems - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var GemLoot = Loot.RandomGem(); - GemLoot.Amount = Utility.Random(1, 9); - DropItem(GemLoot); - } - - // Magic Wand - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - DropItem(Loot.RandomWand()); - } - - // Equipment - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var item = Loot.RandomArmorOrShieldOrWeapon(); - - if (item is BaseWeapon weapon) - { - weapon.DamageLevel = (WeaponDamageLevel)Utility.Random(m_Level); - weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random(m_Level); - weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random(m_Level); - weapon.Quality = WeaponQuality.Regular; - } - else if (item is BaseArmor armor) - { - armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(m_Level); - armor.Durability = (ArmorDurabilityLevel)Utility.Random(m_Level); - armor.Quality = ArmorQuality.Regular; - } - - DropItem(item); - } - - // Clothing - for (var i = Utility.Random(1, 2); i > 1; i--) - { - DropItem(Loot.RandomClothing()); - } - - // Jewelry - for (var i = Utility.Random(1, 2); i > 1; i--) - { - DropItem(Loot.RandomJewelry()); - } + var reagents = Loot.RandomReagent(); + reagents.Amount = Utility.Random(1, 9); + DropItem(reagents); } - public TreasureChestLevel3(Serial serial) - : base(serial) + // Scrolls + for (var i = Utility.Random(3); i > 0; i--) { + var scrolls = Loot.RandomScroll(0, 47, SpellbookType.Regular); + scrolls.Amount = Utility.Random(1, 12); + DropItem(scrolls); } - public override bool Decays => true; - - public override bool IsDecoContainer => false; - - public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override int DefaultGumpID => 0x42; - - public override int DefaultDropSound => 0x42; - - public override Rectangle2D Bounds => new(18, 105, 144, 73); - - private void SetChestAppearance() + // Potions + for (var i = Utility.Random(3); i > 0; i--) { - var UseFirstItemId = Utility.RandomBool(); - switch (Utility.RandomList(0, 1, 2)) + DropItem(Loot.RandomPotion()); + } + + // Gems + for (var i = Utility.Random(3); i > 0; i--) + { + var gems = Loot.RandomGem(); + gems.Amount = Utility.Random(1, 9); + DropItem(gems); + } + + // Magic Wand + for (var i = Utility.Random(3); i > 0; i--) + { + DropItem(Loot.RandomWand()); + } + + // Equipment + for (var i = Utility.Random(3); i > 0; i--) + { + var item = Loot.RandomArmorOrShieldOrWeapon(); + + if (item is BaseWeapon weapon) { - case 0: // Wooden Chest - ItemID = UseFirstItemId ? 0xe42 : 0xe43; - GumpID = 0x49; - break; - - case 1: // Metal Chest - ItemID = UseFirstItemId ? 0x9ab : 0xe7c; - GumpID = 0x4A; - break; - - case 2: // Metal Golden Chest - ItemID = UseFirstItemId ? 0xe40 : 0xe41; - GumpID = 0x42; - break; + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random(3); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random(3); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random(3); + weapon.Quality = WeaponQuality.Regular; } + else if (item is BaseArmor armor) + { + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(3); + armor.Durability = (ArmorDurabilityLevel)Utility.Random(3); + armor.Quality = ArmorQuality.Regular; + } + + DropItem(item); } - public override void Serialize(IGenericWriter writer) + // Clothing + for (var i = Utility.Random(3); i > 0; i--) { - base.Serialize(writer); - writer.Write(1); // version + DropItem(Loot.RandomClothing()); } - public override void Deserialize(IGenericReader reader) + // Jewelry + for (var i = Utility.Random(3); i > 0; i--) { - base.Deserialize(reader); - var version = reader.ReadInt(); + DropItem(Loot.RandomJewelry()); } } + + public override bool Decays => true; + + public override bool IsDecoContainer => false; + + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); + + public override int DefaultGumpID => 0x42; + + public override int DefaultDropSound => 0x42; + + public override Rectangle2D Bounds => new(18, 105, 144, 73); + + private static readonly (int, int)[] _chestAppearances = + { + // Wooden Chest + (0xe42, 0x49), + (0xe43, 0x49), + + // Metal Chest + (0x9ab, 0x4A), + (0xe7c, 0x4A), + + // Metal Golden Chest + (0xe40, 0x42), + (0xe41, 0x42), + }; + + private void SetChestAppearance() + { + (ItemID, GumpID) = _chestAppearances.RandomElement(); + } } diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs index ba550a924..a4bbd17bc 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs @@ -1,175 +1,150 @@ using System; +using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class TreasureChestLevel4 : LockableContainer { - public class TreasureChestLevel4 : LockableContainer + [Constructible] + public TreasureChestLevel4() : base(0xE41) { - private const int m_Level = 4; + SetChestAppearance(); + Movable = false; - [Constructible] - public TreasureChestLevel4() - : base(0xE41) + TrapType = TrapType.ExplosionTrap; + TrapPower = 4 * Utility.Random(10, 25); + Locked = true; + + RequiredSkill = 92; + LockLevel = RequiredSkill - Utility.Random(1, 10); + MaxLockLevel = RequiredSkill + Utility.Random(1, 10); + + // According to OSI, loot in level 4 chest is: + // Gold 500 - 900 + // Reagents + // Scrolls + // Blank scrolls + // Potions + // Gems + // Magic Wand + // Magic weapon + // Magic armour + // Magic clothing (not implemented) + // Magic jewelry (not implemented) + // Crystal ball (not implemented) + + // Gold + DropItem(new Gold(Utility.Random(200, 400))); + + // Reagents + for (var i = Utility.Random(4); i > 0; i--) { - SetChestAppearance(); - Movable = false; - - TrapType = TrapType.ExplosionTrap; - TrapPower = m_Level * Utility.Random(10, 25); - Locked = true; - - RequiredSkill = 92; - LockLevel = RequiredSkill - Utility.Random(1, 10); - MaxLockLevel = RequiredSkill + Utility.Random(1, 10); - - // According to OSI, loot in level 4 chest is: - // Gold 500 - 900 - // Reagents - // Scrolls - // Blank scrolls - // Potions - // Gems - // Magic Wand - // Magic weapon - // Magic armour - // Magic clothing (not implemented) - // Magic jewelry (not implemented) - // Crystal ball (not implemented) - - // Gold - DropItem(new Gold(Utility.Random(200, 400))); - - // Reagents - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var ReagentLoot = Loot.RandomReagent(); - ReagentLoot.Amount = 12; - DropItem(ReagentLoot); - } - - // Scrolls - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - Item ScrollLoot = Loot.RandomScroll(0, 47, SpellbookType.Regular); - ScrollLoot.Amount = 16; - DropItem(ScrollLoot); - } - - // Drop blank scrolls - DropItem(new BlankScroll(Utility.Random(1, m_Level))); - - // Potions - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var PotionLoot = Loot.RandomPotion(); - DropItem(PotionLoot); - } - - // Gems - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var GemLoot = Loot.RandomGem(); - GemLoot.Amount = 12; - DropItem(GemLoot); - } - - // Magic Wand - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - DropItem(Loot.RandomWand()); - } - - // Equipment - for (var i = Utility.Random(1, m_Level); i > 1; i--) - { - var item = Loot.RandomArmorOrShieldOrWeapon(); - - if (item is BaseWeapon weapon) - { - weapon.DamageLevel = (WeaponDamageLevel)Utility.Random(m_Level); - weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random(m_Level); - weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random(m_Level); - weapon.Quality = WeaponQuality.Regular; - } - else if (item is BaseArmor armor) - { - armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(m_Level); - armor.Durability = (ArmorDurabilityLevel)Utility.Random(m_Level); - armor.Quality = ArmorQuality.Regular; - } - - DropItem(item); - } - - // Clothing - for (var i = Utility.Random(1, 2); i > 1; i--) - { - DropItem(Loot.RandomClothing()); - } - - // Jewelry - for (var i = Utility.Random(1, 2); i > 1; i--) - { - DropItem(Loot.RandomJewelry()); - } - - // Crystal ball (not implemented) + var reagents = Loot.RandomReagent(); + reagents.Amount = 12; + DropItem(reagents); } - public TreasureChestLevel4(Serial serial) - : base(serial) + // Scrolls + for (var i = Utility.Random(4); i > 0; i--) { + var scroll = Loot.RandomScroll(0, 47, SpellbookType.Regular); + scroll.Amount = 16; + DropItem(scroll); } - public override bool Decays => true; + // Drop blank scrolls + DropItem(new BlankScroll(Utility.Random(1, 4))); - public override bool IsDecoContainer => false; - - public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override int DefaultGumpID => 0x42; - - public override int DefaultDropSound => 0x42; - - public override Rectangle2D Bounds => new(18, 105, 144, 73); - - private void SetChestAppearance() + // Potions + for (var i = Utility.Random(4); i > 0; i--) { - var UseFirstItemId = Utility.RandomBool(); + DropItem(Loot.RandomPotion()); + } - switch (Utility.Random(4)) + // Gems + for (var i = Utility.Random(4); i > 0; i--) + { + var gems = Loot.RandomGem(); + gems.Amount = 12; + DropItem(gems); + } + + // Magic Wand + for (var i = Utility.Random(4); i > 0; i--) + { + DropItem(Loot.RandomWand()); + } + + // Equipment + for (var i = Utility.Random(4); i > 0; i--) + { + var item = Loot.RandomArmorOrShieldOrWeapon(); + + if (item is BaseWeapon weapon) { - case 0: // Wooden Chest - ItemID = UseFirstItemId ? 0xe42 : 0xe43; - GumpID = 0x49; - break; - - case 1: // Metal Chest - ItemID = UseFirstItemId ? 0x9ab : 0xe7c; - GumpID = 0x4A; - break; - - case 2: // Metal Golden Chest - ItemID = UseFirstItemId ? 0xe40 : 0xe41; - GumpID = 0x42; - break; - - case 3: // Keg - ItemID = 0xe7f; - GumpID = 0x3e; - break; + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random(4); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random(4); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random(4); + weapon.Quality = WeaponQuality.Regular; } + else if (item is BaseArmor armor) + { + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random(4); + armor.Durability = (ArmorDurabilityLevel)Utility.Random(4); + armor.Quality = ArmorQuality.Regular; + } + + DropItem(item); } - public override void Serialize(IGenericWriter writer) + // Clothing + for (var i = Utility.Random(3); i > 0; i--) { - base.Serialize(writer); - writer.Write(1); // version + DropItem(Loot.RandomClothing()); } - public override void Deserialize(IGenericReader reader) + // Jewelry + for (var i = Utility.Random(3); i > 0; i--) { - base.Deserialize(reader); - var version = reader.ReadInt(); + DropItem(Loot.RandomJewelry()); } + + // Crystal ball (not implemented) + } + + public override bool Decays => true; + + public override bool IsDecoContainer => false; + + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); + + public override int DefaultGumpID => 0x42; + + public override int DefaultDropSound => 0x42; + + public override Rectangle2D Bounds => new(18, 105, 144, 73); + + private static readonly (int, int)[] _chestAppearances = + { + // Wooden Chest + (0xe42, 0x49), + (0xe43, 0x49), + + // Metal Chest + (0x9ab, 0x4A), + (0xe7c, 0x4A), + + // Metal Golden Chest + (0xe40, 0x42), + (0xe41, 0x42), + + // Keg + (0xe7f, 0x3e), + }; + + private void SetChestAppearance() + { + (ItemID, GumpID) = _chestAppearances.RandomElement(); } } diff --git a/Projects/UOContent/Items/Wands/BaseWand.cs b/Projects/UOContent/Items/Wands/BaseWand.cs index 310ffe526..0549b7a6c 100644 --- a/Projects/UOContent/Items/Wands/BaseWand.cs +++ b/Projects/UOContent/Items/Wands/BaseWand.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Network; using Server.Spells; using Server.Targeting; @@ -21,7 +22,7 @@ namespace Server.Items ManaDraining } - [Serializable(1, false)] + [SerializationGenerator(1, false)] public abstract partial class BaseWand : BaseBashing { [InvalidateProperties] @@ -122,44 +123,44 @@ namespace Server.Items _charges = reader.ReadInt(); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); switch (_wandEffect) { case WandEffect.Clumsiness: - list.Add(1017326, _charges.ToString()); + list.Add(1017326, $"{_charges}"); break; // clumsiness charges: ~1_val~ case WandEffect.Identification: - list.Add(1017350, _charges.ToString()); + list.Add(1017350, $"{_charges}"); break; // identification charges: ~1_val~ case WandEffect.Healing: - list.Add(1017329, _charges.ToString()); + list.Add(1017329, $"{_charges}"); break; // healing charges: ~1_val~ case WandEffect.Feeblemindedness: - list.Add(1017327, _charges.ToString()); + list.Add(1017327, $"{_charges}"); break; // feeblemind charges: ~1_val~ case WandEffect.Weakness: - list.Add(1017328, _charges.ToString()); + list.Add(1017328, $"{_charges}"); break; // weakness charges: ~1_val~ case WandEffect.MagicArrow: - list.Add(1060492, _charges.ToString()); + list.Add(1060492, $"{_charges}"); break; // magic arrow charges: ~1_val~ case WandEffect.Harming: - list.Add(1017334, _charges.ToString()); + list.Add(1017334, $"{_charges}"); break; // harm charges: ~1_val~ case WandEffect.Fireball: - list.Add(1060487, _charges.ToString()); + list.Add(1060487, $"{_charges}"); break; // fireball charges: ~1_val~ case WandEffect.GreaterHealing: - list.Add(1017330, _charges.ToString()); + list.Add(1017330, $"{_charges}"); break; // greater healing charges: ~1_val~ case WandEffect.Lightning: - list.Add(1060491, _charges.ToString()); + list.Add(1060491, $"{_charges}"); break; // lightning charges: ~1_val~ case WandEffect.ManaDraining: - list.Add(1017339, _charges.ToString()); + list.Add(1017339, $"{_charges}"); break; // mana drain charges: ~1_val~ } } diff --git a/Projects/UOContent/Items/Wands/ClumsyWand.cs b/Projects/UOContent/Items/Wands/ClumsyWand.cs index 2cb94a7af..cbe1653de 100644 --- a/Projects/UOContent/Items/Wands/ClumsyWand.cs +++ b/Projects/UOContent/Items/Wands/ClumsyWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.First; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ClumsyWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/FeebleWand.cs b/Projects/UOContent/Items/Wands/FeebleWand.cs index 1fa71810f..93965c518 100644 --- a/Projects/UOContent/Items/Wands/FeebleWand.cs +++ b/Projects/UOContent/Items/Wands/FeebleWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.First; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FeebleWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/FireballWand.cs b/Projects/UOContent/Items/Wands/FireballWand.cs index 2c80cc160..1024112b8 100644 --- a/Projects/UOContent/Items/Wands/FireballWand.cs +++ b/Projects/UOContent/Items/Wands/FireballWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.Third; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FireballWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/GreaterHealWand.cs b/Projects/UOContent/Items/Wands/GreaterHealWand.cs index 2e9ee8291..9624de117 100644 --- a/Projects/UOContent/Items/Wands/GreaterHealWand.cs +++ b/Projects/UOContent/Items/Wands/GreaterHealWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.Fourth; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GreaterHealWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/HarmWand.cs b/Projects/UOContent/Items/Wands/HarmWand.cs index 853957195..e727fcb31 100644 --- a/Projects/UOContent/Items/Wands/HarmWand.cs +++ b/Projects/UOContent/Items/Wands/HarmWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.Second; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HarmWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/HealWand.cs b/Projects/UOContent/Items/Wands/HealWand.cs index ad40234fa..7319b10fe 100644 --- a/Projects/UOContent/Items/Wands/HealWand.cs +++ b/Projects/UOContent/Items/Wands/HealWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.First; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HealWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/IDWand.cs b/Projects/UOContent/Items/Wands/IDWand.cs index 1c6328ff6..1e916f4a7 100644 --- a/Projects/UOContent/Items/Wands/IDWand.cs +++ b/Projects/UOContent/Items/Wands/IDWand.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class IDWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/LightningWand.cs b/Projects/UOContent/Items/Wands/LightningWand.cs index caf69aee5..9c02f2288 100644 --- a/Projects/UOContent/Items/Wands/LightningWand.cs +++ b/Projects/UOContent/Items/Wands/LightningWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.Fourth; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LightningWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/MagicArrowWand.cs b/Projects/UOContent/Items/Wands/MagicArrowWand.cs index 036602ae2..1e9eb79c0 100644 --- a/Projects/UOContent/Items/Wands/MagicArrowWand.cs +++ b/Projects/UOContent/Items/Wands/MagicArrowWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.First; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MagicArrowWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/ManaDrainWand.cs b/Projects/UOContent/Items/Wands/ManaDrainWand.cs index 61b49bcb9..7e3c78885 100644 --- a/Projects/UOContent/Items/Wands/ManaDrainWand.cs +++ b/Projects/UOContent/Items/Wands/ManaDrainWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.Fourth; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ManaDrainWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Wands/WeaknessWand.cs b/Projects/UOContent/Items/Wands/WeaknessWand.cs index 5361d0bcb..ff0fdeb5f 100644 --- a/Projects/UOContent/Items/Wands/WeaknessWand.cs +++ b/Projects/UOContent/Items/Wands/WeaknessWand.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Spells.First; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WeaknessWand : BaseWand { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs index 069b5b3a0..ddcb9b389 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs @@ -2,114 +2,108 @@ using System; using Server.Mobiles; using Server.Spells.Ninjitsu; -namespace Server.Items +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 { - /// - /// 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 static readonly TimeSpan RemountDelay = TimeSpan.FromSeconds(10.0); + + public override int BaseMana => 20; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) { - public static readonly TimeSpan RemountDelay = TimeSpan.FromSeconds(10.0); - - public override int BaseMana => 20; - - public override bool Validate(Mobile from) + if (!Validate(attacker) || !CheckMana(attacker, true)) { - if (!base.Validate(from)) - { - return false; - } - - if (from.Mounted && from.Weapon is not Lance) - { - from.SendLocalizedMessage(1061283); // You cannot perform that attack while mounted! - return false; - } - - return true; + return; } - public override void OnHit(Mobile attacker, Mobile defender, int damage) + if (defender is ChaosDragoon or ChaosDragoonElite) { - if (!Validate(attacker) || !CheckMana(attacker, true)) + return; + } + + if (attacker.Mounted || attacker.Flying) + { + if (attacker.Weapon is not Lance || !defender.Mounted && !defender.Flying && defender.Weapon is not Lance) { - return; + attacker.SendLocalizedMessage(1061283); // You cannot perform that attack while mounted! + } + } + + ClearCurrentAbility(attacker); + + var mount = defender.Mount; + + if (mount == null && !AnimalForm.UnderTransformation(defender)) + { + attacker.SendLocalizedMessage(1060848); // This attack only works on mounted targets + return; + } + + if (Core.ML && attacker is LesserHiryu && Utility.RandomDouble() <= 0.8) + { + return; // Lesser Hiryu have an 80% chance of missing this attack + } + + DoDismount(attacker, defender, TimeSpan.FromSeconds(10)); + + if (!attacker.Mounted) + { + AOS.Damage(defender, attacker, Utility.RandomMinMax(15, 25), 100, 0, 0, 0, 0); + } + } + + public static void DoDismount(Mobile attacker, Mobile defender, TimeSpan delay, BlockMountType type = BlockMountType.Dazed) + { + 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); + + if (defender is PlayerMobile mobile) + { + if (AnimalForm.UnderTransformation(mobile)) + { + mobile.SendLocalizedMessage(1114066, attacker.Name); // ~1_NAME~ knocked you out of animal form! + } + else if (defender.Flying) + { + defender.SendLocalizedMessage(1113590, attacker.Name); // You have been grounded by ~1_NAME~! + } + else if (mobile.Mounted) + { + mobile.SendLocalizedMessage(1040023); // You have been knocked off of your mount! } - if (defender is ChaosDragoon or ChaosDragoonElite) - { - return; - } + mobile.SetMountBlock(type, delay, true); + } + else + { + defender.Mount.Rider = null; + } - // TODO: Should there be a message here? - if (attacker.Mounted && (attacker.Weapon is not Lance || defender.Weapon is not Lance)) - { - return; - } - - ClearCurrentAbility(attacker); - - var mount = defender.Mount; - - if (mount == null && !AnimalForm.UnderTransformation(defender)) - { - attacker.SendLocalizedMessage(1060848); // This attack only works on mounted targets - return; - } - - if (Core.ML && attacker is LesserHiryu && Utility.RandomDouble() <= 0.8) - { - return; // Lesser Hiryu have an 80% chance of missing this attack - } - - 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); - - if (defender is PlayerMobile mobile) - { - if (AnimalForm.UnderTransformation(mobile)) - { - mobile.SendLocalizedMessage(1114066, attacker.Name); // ~1_NAME~ knocked you out of animal form! - } - else if (mobile.Mounted) - { - mobile.SendLocalizedMessage(1040023); // You have been knocked off of your mount! - } - - mobile.SetMountBlock(BlockMountType.Dazed, TimeSpan.FromSeconds(10), true); - } - else - { - defender.Mount.Rider = null; - } - - if (attacker is PlayerMobile playerMobile) - { - playerMobile.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, true); - } - else if (Core.ML && attacker is BaseCreature { ControlMaster: PlayerMobile pm }) - { - pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false); - } - - if (!attacker.Mounted) - { - AOS.Damage(defender, attacker, Utility.RandomMinMax(15, 25), 100, 0, 0, 0, 0); - } + if (attacker is PlayerMobile playerMobile) + { + playerMobile.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, true); + } + else if (Core.ML && attacker is BaseCreature { ControlMaster: PlayerMobile pm }) + { + pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false); } } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs b/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs deleted file mode 100644 index fc4286c78..000000000 --- a/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; - -namespace Server.Items -{ - /// - /// This attack allows you to disrobe your foe. - /// - public class Disrobe : WeaponAbility - { - public static readonly TimeSpan BlockEquipDuration = TimeSpan.FromSeconds(5.0); - - public override int BaseMana => 20; // Not Sure what amount of mana a creature uses. - - public override void OnHit(Mobile attacker, Mobile defender, int damage) - { - if (!Validate(attacker) || !CheckMana(attacker, true)) - { - return; - } - - ClearCurrentAbility(attacker); - var toDisrobe = defender.FindItemOnLayer(Layer.InnerTorso); - - if (toDisrobe?.Movable == false) - { - toDisrobe = defender.FindItemOnLayer(Layer.OuterTorso); - } - - var pack = defender.Backpack; - - if (pack == null || toDisrobe?.Movable == false) - { - attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. - } - else - { - // attacker.SendLocalizedMessage( 1060092 ); // You disarm their weapon! - defender.SendLocalizedMessage(1062002); // You can no longer wear your ~1_ARMOR~ - - defender.PlaySound(0x3B9); - // defender.FixedParticles( 0x37BE, 232, 25, 9948, EffectLayer.InnerTorso ); - - pack.DropItem(toDisrobe); - - BaseWeapon.BlockEquip(defender, BlockEquipDuration); - } - } - } -} diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs index 17d4ffc8a..c3171dbd8 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs @@ -103,7 +103,7 @@ namespace Server.Items { int damage = Utility.RandomMinMax(15, 35); - AOS.Damage(From, From, damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false); + AOS.Damage(From, From, damage, false, 0, 0, 0, 0, 0, 0, 100); } } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs b/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs new file mode 100644 index 000000000..785aa1b32 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs @@ -0,0 +1,45 @@ +using Server.Mobiles; +using System; + +namespace Server.Items; + +// The projectile will be infused with energy by the attacker causing it to do more damage and stun or dismount the target. +// The player infuses their throwing projectile with mystical power. +// The infused projectile will dismount the target if possible; otherwise it will temporarily stun the target. +// The target will be hit with chaos damage regardless of whether they were dismounted or paralyzed. +public class InfusedThrow : WeaponAbility +{ + public override int BaseMana => 25; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1149563); //The infused projectile strikes a target! + defender.SendLocalizedMessage(1149564); //You are struck by the infused projectile and take damage! + + AOS.Damage(defender, attacker, 15, false, 0, 0, 0, 0, 0, 100); + + IMount mount = defender.Mount; + + if ((defender.Mounted || defender.Flying || Spells.Ninjitsu.AnimalForm.UnderTransformation(defender)) && + !attacker.Mounted && !attacker.Flying && defender is not ChaosDragoon && defender is not ChaosDragoonElite) + { + defender.PlaySound(0x140); + defender.FixedParticles(0x3728, 10, 15, 9955, EffectLayer.Waist); + + Items.Dismount.DoDismount(attacker, defender, TimeSpan.FromSeconds(10)); + } + else + { + defender.FixedEffect(0x376A, 9, 32); + defender.PlaySound(0x204); + defender.Paralyze(TimeSpan.FromSeconds(defender.Player ? 3.0 : 6.0)); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs index 5c98873f1..4c2894a73 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs @@ -41,12 +41,15 @@ namespace Server.Items public static bool IsWounded(Mobile m) => _table.ContainsKey(m); - private static void StopTimer(Mobile m) + private static bool StopTimer(Mobile m) { if (_table.Remove(m, out var timerToken)) { timerToken.Cancel(); + return true; } + + return false; } public static void BeginWound(Mobile m, TimeSpan duration) @@ -58,11 +61,16 @@ namespace Server.Items m.YellowHealthbar = true; } - public static void EndWound(Mobile m) + public static bool EndWound(Mobile m) { - StopTimer(m); - m.YellowHealthbar = false; - m.SendLocalizedMessage(1060208); // You are no longer mortally wounded. + if (StopTimer(m)) + { + m.YellowHealthbar = false; + m.SendLocalizedMessage(1060208); // You are no longer mortally wounded. + return true; + } + + return false; } } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs b/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs new file mode 100644 index 000000000..0510820ba --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs @@ -0,0 +1,91 @@ +using System; +using Server.Collections; + +namespace Server.Items; + +// The thrown projectile will arc to a second target after hitting the primary target. +// Chaos energy will burst from the projectile at each target. This will only hit targets that are in combat with the user. +public class MysticArc : WeaponAbility +{ + private readonly int Damage = 15; + private Mobile _target; + private Mobile _mobile; + + public override int BaseMana => 20; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!CheckMana(attacker, true) && defender != null) + { + return; + } + + BaseThrown weapon = attacker.Weapon as BaseThrown; + + if (weapon == null) + { + return; + } + + using var queue = PooledRefQueue.Create(); + IPooledEnumerable eable = attacker.GetMobilesInRange(weapon.MaxRange); + + foreach (Mobile m in eable) + { + if (m == defender) + { + continue; + } + + if (m.Combatant != attacker) + { + continue; + } + + queue.Enqueue(m); + } + + eable.Free(); + + if (queue.Count > 0) + { + _target = queue.PeekRandom(); + } + + AOS.Damage(defender, attacker, Damage, 0, 0, 0, 0, 0, 100); + + if (_target != null) + { + defender?.MovingEffect(_target, weapon.ItemID, 18, 1, false, false); + + Timer.DelayCall(TimeSpan.FromSeconds(0.3), ThrowAgain); + _mobile = attacker; + } + + ClearCurrentAbility(attacker); + } + + public void ThrowAgain() + { + if (_target == null || _mobile == null) + { + return; + } + + if (_mobile.Weapon is not BaseThrown weapon) + { + return; + } + + if (GetCurrentAbility(_mobile) is MysticArc) + { + ClearCurrentAbility(_mobile); + } + + if (weapon.CheckHit(_mobile, _target)) + { + weapon.OnHit(_mobile, _target, 0.0); + AOS.Damage(_target, _mobile, Damage, 0, 0, 0, 0, 100); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index 9ad7e996f..800e83955 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -44,7 +44,8 @@ namespace Server.Items new PsychicAttack(), new SerpentArrow(), new ForceOfNature(), - new Disrobe() + new InfusedThrow(), + new MysticArc(), }; public static readonly WeaponAbility ArmorIgnore = Abilities[1]; @@ -79,7 +80,8 @@ namespace Server.Items public static readonly WeaponAbility SerpentArrow = Abilities[28]; public static readonly WeaponAbility ForceOfNature = Abilities[29]; - public static readonly WeaponAbility Disrobe = Abilities[30]; + public static readonly WeaponAbility InfusedThrow = Abilities[30]; + public static readonly WeaponAbility MysticArc = Abilities[31]; private static readonly Dictionary m_PlayersTable = new(); diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs index 8165628bf..78da5a8a4 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs @@ -6,12 +6,12 @@ namespace Server.Items { public static class WeaponAbilityPackets { - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.RegisterEncoded(0x19, true, SetAbility); + IncomingPackets.RegisterEncoded(0x19, true, &SetAbility); } - public static void SetAbility(NetState state, IEntity e, EncodedReader reader) + public static unsafe void SetAbility(NetState state, IEntity e, EncodedReader reader) { var m = state.Mobile; var index = reader.ReadInt32(); @@ -32,7 +32,7 @@ namespace Server.Items public static void SendToggleSpecialAbility(this NetState ns, int abilityId, bool active) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs index 32496a2a7..4b0004bac 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using Server.Collections; using Server.Spells; namespace Server.Items @@ -36,23 +36,30 @@ namespace Server.Items attacker.FixedEffect(0x3728, 10, 15); attacker.PlaySound(0x2A1); - var targets = attacker.GetMobilesInRange(1) - .Where( - m => - m?.Deleted == false && m != defender && m != attacker && - SpellHelper.ValidIndirectTarget(attacker, m) && - m.Map == attacker.Map && m.Alive && attacker.CanSee(m) && attacker.CanBeHarmful(m) && - attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m) - ) - .ToList(); + var eable = attacker.GetMobilesInRange(1); + using var queue = PooledRefQueue.Create(); - if (targets.Count <= 0) + foreach (var m in eable) + { + if (m?.Deleted == false && m != defender && m != attacker && + m.Map == attacker.Map && m.Alive && + SpellHelper.ValidIndirectTarget(attacker, m) && + attacker.CanSee(m) && attacker.CanBeHarmful(m) && + attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m)) + { + queue.Enqueue(m); + } + } + + eable.Free(); + + if (queue.Count <= 0) { return; } var bushido = attacker.Skills.Bushido.Value; - var damageBonus = 1.0 + Math.Pow(targets.Count * bushido / 60, 2) / 100; + var damageBonus = 1.0 + Math.Pow(queue.Count * bushido / 60, 2) / 100; if (damageBonus > 2.0) { @@ -61,10 +68,9 @@ namespace Server.Items attacker.RevealingAction(); - for (var i = 0; i < targets.Count; ++i) + while (queue.Count > 0) { - var m = targets[i]; - + var m = queue.Dequeue(); attacker.SendLocalizedMessage(1060161); // The whirling attack strikes a target! m.SendLocalizedMessage(1060162); // You are struck by the whirling attack and take damage! diff --git a/Projects/UOContent/Items/Weapons/Artifacts/AxeOfTheHeavens.cs b/Projects/UOContent/Items/Weapons/Artifacts/AxeOfTheHeavens.cs index a4137f17d..09193ba45 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/AxeOfTheHeavens.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/AxeOfTheHeavens.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class AxeOfTheHeavens : DoubleAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/BladeOfInsanity.cs b/Projects/UOContent/Items/Weapons/Artifacts/BladeOfInsanity.cs index ed6b2b14e..4652aff5a 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/BladeOfInsanity.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/BladeOfInsanity.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BladeOfInsanity : Katana { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/BladeOfTheRighteous.cs b/Projects/UOContent/Items/Weapons/Artifacts/BladeOfTheRighteous.cs index 61f5ad7c7..f89bddd43 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/BladeOfTheRighteous.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/BladeOfTheRighteous.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BladeOfTheRighteous : Longsword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/BoneCrusher.cs b/Projects/UOContent/Items/Weapons/Artifacts/BoneCrusher.cs index b2d5a640d..e682d7428 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/BoneCrusher.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/BoneCrusher.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BoneCrusher : WarMace { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/BreathOfTheDead.cs b/Projects/UOContent/Items/Weapons/Artifacts/BreathOfTheDead.cs index ab9f8d808..b0242f8f7 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/BreathOfTheDead.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/BreathOfTheDead.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BreathOfTheDead : BoneHarvester { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/Frostbringer.cs b/Projects/UOContent/Items/Weapons/Artifacts/Frostbringer.cs index 7ef940a63..f974b7a72 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/Frostbringer.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/Frostbringer.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Frostbringer : Bow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs b/Projects/UOContent/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs index f8512843d..8c6bebbab 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class LegacyOfTheDreadLord : Bardiche { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/SerpentsFang.cs b/Projects/UOContent/Items/Weapons/Artifacts/SerpentsFang.cs index 516cc4bc0..8d7603efc 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/SerpentsFang.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/SerpentsFang.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SerpentsFang : Kryss { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/StaffOfTheMagi.cs b/Projects/UOContent/Items/Weapons/Artifacts/StaffOfTheMagi.cs index f4c993a95..efa426329 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/StaffOfTheMagi.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/StaffOfTheMagi.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] - public partial class StaffOfTheMagi : BlackStaff + [SerializationGenerator(0, false)] + public partial class StaffOfTheMagi : BlackStaff { [Constructible] public StaffOfTheMagi() diff --git a/Projects/UOContent/Items/Weapons/Artifacts/TheBeserkersMaul.cs b/Projects/UOContent/Items/Weapons/Artifacts/TheBeserkersMaul.cs index 76a166e97..10d86f619 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/TheBeserkersMaul.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/TheBeserkersMaul.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheBeserkersMaul : Maul { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/TheDragonSlayer.cs b/Projects/UOContent/Items/Weapons/Artifacts/TheDragonSlayer.cs index cbdb46dc4..54c9c71a7 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/TheDragonSlayer.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/TheDragonSlayer.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheDragonSlayer : Lance { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/TheDryadBow.cs b/Projects/UOContent/Items/Weapons/Artifacts/TheDryadBow.cs index f0cb1edf7..35f4a1282 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/TheDryadBow.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/TheDryadBow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheDryadBow : Bow { private static readonly SkillName[] m_PossibleBonusSkills = diff --git a/Projects/UOContent/Items/Weapons/Artifacts/TheTaskmaster.cs b/Projects/UOContent/Items/Weapons/Artifacts/TheTaskmaster.cs index 7d4444026..36cb33b63 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/TheTaskmaster.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/TheTaskmaster.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheTaskmaster : WarFork { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/TitansHammer.cs b/Projects/UOContent/Items/Weapons/Artifacts/TitansHammer.cs index 90c9ddda1..f2f717e79 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/TitansHammer.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/TitansHammer.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TitansHammer : WarHammer { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Artifacts/ZyronicClaw.cs b/Projects/UOContent/Items/Weapons/Artifacts/ZyronicClaw.cs index ffb46240b..eddc87e3d 100644 --- a/Projects/UOContent/Items/Weapons/Artifacts/ZyronicClaw.cs +++ b/Projects/UOContent/Items/Weapons/Artifacts/ZyronicClaw.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ZyronicClaw : ExecutionersAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/Axe.cs b/Projects/UOContent/Items/Weapons/Axes/Axe.cs index f333739ee..71f47b3b5 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Axe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Axe.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF49, 0xF4a)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Axe : BaseAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs b/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs index 05087567e..1051e22c7 100644 --- a/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Engines.ConPVP; using Server.Engines.Harvest; @@ -12,7 +13,7 @@ namespace Server.Items bool Axe(Mobile from, BaseAxe axe); } - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseAxe : BaseMeleeWeapon { [SerializableField(0)] diff --git a/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs index a27548485..00352dbd3 100644 --- a/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xF47, 0xF48)] public partial class BattleAxe : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs index b16feeb88..fea645c8e 100644 --- a/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xf4b, 0xf4c)] public partial class DoubleAxe : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs b/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs index d3dfb4d18..5bf018f2d 100644 --- a/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs +++ b/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x8FD, 0x4068)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class DualShortAxes : BaseAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs b/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs index ec5e4cd81..6302a26c0 100644 --- a/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xf45, 0xf46)] public partial class ExecutionersAxe : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/Axes/GuardianAxe.cs b/Projects/UOContent/Items/Weapons/Axes/GuardianAxe.cs index 8db584957..99fe86632 100644 --- a/Projects/UOContent/Items/Weapons/Axes/GuardianAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/GuardianAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class GuardianAxe : OrnateAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs b/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs index 45c538f5c..924a54891 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xF43, 0xF44)] public partial class Hatchet : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/Axes/HeavyOrnateAxe.cs b/Projects/UOContent/Items/Weapons/Axes/HeavyOrnateAxe.cs index 77c75599c..5c9a43e78 100644 --- a/Projects/UOContent/Items/Weapons/Axes/HeavyOrnateAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/HeavyOrnateAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class HeavyOrnateAxe : OrnateAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs index c2b41c08d..37f6668f6 100644 --- a/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13FB, 0x13FA)] public partial class LargeBattleAxe : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs b/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs index 0ad19652c..07cac1b08 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.Harvest; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0xE86, 0xE85)] public partial class Pickaxe : BaseAxe, IUsesRemaining { diff --git a/Projects/UOContent/Items/Weapons/Axes/SingingAxe.cs b/Projects/UOContent/Items/Weapons/Axes/SingingAxe.cs index ac816d766..987cc0401 100644 --- a/Projects/UOContent/Items/Weapons/Axes/SingingAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/SingingAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SingingAxe : OrnateAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/ThunderingAxe.cs b/Projects/UOContent/Items/Weapons/Axes/ThunderingAxe.cs index c75735cc5..fee1b8006 100644 --- a/Projects/UOContent/Items/Weapons/Axes/ThunderingAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/ThunderingAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ThunderingAxe : OrnateAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs b/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs index 1a11b2f63..1a87091b2 100644 --- a/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x1443, 0x1442)] public partial class TwoHandedAxe : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs b/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs index ed7895871..b11db81e0 100644 --- a/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.Harvest; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] [Flippable(0x13B0, 0x13AF)] public partial class WarAxe : BaseAxe { diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 2b7b4fd06..7f2a8296e 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -939,21 +939,21 @@ namespace Server.Items { var m = from; - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - m.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - m.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - m.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + m.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } @@ -1004,11 +1004,11 @@ namespace Server.Items return; } - var modName = Serial.ToString(); + var serial = Serial; - m.RemoveStatMod($"{modName}Str"); - m.RemoveStatMod($"{modName}Dex"); - m.RemoveStatMod($"{modName}Int"); + m.RemoveStatMod($"{serial}Str"); + m.RemoveStatMod($"{serial}Dex"); + m.RemoveStatMod($"{serial}Int"); if (!_enableInstaHit && m.Weapon is BaseWeapon weapon) { @@ -2716,8 +2716,6 @@ namespace Server.Items from.Animate(action, 7, 1, true, false, 0); } - private string GetNameString() => Name ?? $"#{LabelNumber}"; - public int GetElementalDamageHue() { GetDamageTypes(null, out _, out var fire, out var cold, out var pois, out var nrgy, out _, out _); @@ -2752,7 +2750,7 @@ namespace Server.Items return hue; } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { var oreType = m_Resource switch { @@ -2776,17 +2774,26 @@ namespace Server.Items _ => 0 }; + var name = Name; + if (oreType != 0) { - list.Add(1053099, "#{0}\t{1}", oreType, GetNameString()); // ~1_oretype~ ~2_armortype~ + if (name != null) + { + list.Add(1053099, $"{oreType:#}\t{name}"); // ~1_oretype~ ~2_armortype~ + } + else + { + list.Add(1053099, $"{oreType:#}\t{LabelNumber:#}"); // ~1_oretype~ ~2_armortype~ + } } - else if (Name == null) + else if (name == null) { list.Add(LabelNumber); } else { - list.Add(Name); + list.Add(name); } /* @@ -2815,7 +2822,7 @@ namespace Server.Items public virtual int GetLuckBonus() => CraftResources.GetInfo(m_Resource)?.AttributeInfo?.WeaponLuck ?? 0; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -2848,17 +2855,17 @@ namespace Server.Items if (ArtifactRarity > 0) { - list.Add(1061078, ArtifactRarity.ToString()); // artifact rarity ~1_val~ + list.Add(1061078, ArtifactRarity); // artifact rarity ~1_val~ } if (this is IUsesRemaining usesRemaining && usesRemaining.ShowUsesRemaining) { - list.Add(1060584, usesRemaining.UsesRemaining.ToString()); // uses remaining: ~1_val~ + list.Add(1060584, usesRemaining.UsesRemaining); // uses remaining: ~1_val~ } if (m_Poison != null && m_PoisonCharges > 0) { - list.Add(1062412 + m_Poison.Level, m_PoisonCharges.ToString()); + list.Add(1062412 + m_Poison.Level, m_PoisonCharges); } if (m_Slayer != SlayerName.None) @@ -2897,107 +2904,107 @@ namespace Server.Items if ((prop = GetDamageBonus() + Attributes.WeaponDamage) != 0) { - list.Add(1060401, prop.ToString()); // damage increase ~1_val~% + list.Add(1060401, prop); // damage increase ~1_val~% } if ((prop = Attributes.DefendChance) != 0) { - list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~% + list.Add(1060408, prop); // defense chance increase ~1_val~% } if ((prop = Attributes.EnhancePotions) != 0) { - list.Add(1060411, prop.ToString()); // enhance potions ~1_val~% + list.Add(1060411, prop); // enhance potions ~1_val~% } if ((prop = Attributes.CastRecovery) != 0) { - list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~ + list.Add(1060412, prop); // faster cast recovery ~1_val~ } if ((prop = Attributes.CastSpeed) != 0) { - list.Add(1060413, prop.ToString()); // faster casting ~1_val~ + list.Add(1060413, prop); // faster casting ~1_val~ } if ((prop = GetHitChanceBonus() + Attributes.AttackChance) != 0) { - list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~% + list.Add(1060415, prop); // hit chance increase ~1_val~% } if ((prop = WeaponAttributes.HitColdArea) != 0) { - list.Add(1060416, prop.ToString()); // hit cold area ~1_val~% + list.Add(1060416, prop); // hit cold area ~1_val~% } if ((prop = WeaponAttributes.HitDispel) != 0) { - list.Add(1060417, prop.ToString()); // hit dispel ~1_val~% + list.Add(1060417, prop); // hit dispel ~1_val~% } if ((prop = WeaponAttributes.HitEnergyArea) != 0) { - list.Add(1060418, prop.ToString()); // hit energy area ~1_val~% + list.Add(1060418, prop); // hit energy area ~1_val~% } if ((prop = WeaponAttributes.HitFireArea) != 0) { - list.Add(1060419, prop.ToString()); // hit fire area ~1_val~% + list.Add(1060419, prop); // hit fire area ~1_val~% } if ((prop = WeaponAttributes.HitFireball) != 0) { - list.Add(1060420, prop.ToString()); // hit fireball ~1_val~% + list.Add(1060420, prop); // hit fireball ~1_val~% } if ((prop = WeaponAttributes.HitHarm) != 0) { - list.Add(1060421, prop.ToString()); // hit harm ~1_val~% + list.Add(1060421, prop); // hit harm ~1_val~% } if ((prop = WeaponAttributes.HitLeechHits) != 0) { - list.Add(1060422, prop.ToString()); // hit life leech ~1_val~% + list.Add(1060422, prop); // hit life leech ~1_val~% } if ((prop = WeaponAttributes.HitLightning) != 0) { - list.Add(1060423, prop.ToString()); // hit lightning ~1_val~% + list.Add(1060423, prop); // hit lightning ~1_val~% } if ((prop = WeaponAttributes.HitLowerAttack) != 0) { - list.Add(1060424, prop.ToString()); // hit lower attack ~1_val~% + list.Add(1060424, prop); // hit lower attack ~1_val~% } if ((prop = WeaponAttributes.HitLowerDefend) != 0) { - list.Add(1060425, prop.ToString()); // hit lower defense ~1_val~% + list.Add(1060425, prop); // hit lower defense ~1_val~% } if ((prop = WeaponAttributes.HitMagicArrow) != 0) { - list.Add(1060426, prop.ToString()); // hit magic arrow ~1_val~% + list.Add(1060426, prop); // hit magic arrow ~1_val~% } if ((prop = WeaponAttributes.HitLeechMana) != 0) { - list.Add(1060427, prop.ToString()); // hit mana leech ~1_val~% + list.Add(1060427, prop); // hit mana leech ~1_val~% } if ((prop = WeaponAttributes.HitPhysicalArea) != 0) { - list.Add(1060428, prop.ToString()); // hit physical area ~1_val~% + list.Add(1060428, prop); // hit physical area ~1_val~% } if ((prop = WeaponAttributes.HitPoisonArea) != 0) { - list.Add(1060429, prop.ToString()); // hit poison area ~1_val~% + list.Add(1060429, prop); // hit poison area ~1_val~% } if ((prop = WeaponAttributes.HitLeechStam) != 0) { - list.Add(1060430, prop.ToString()); // hit stamina leech ~1_val~% + list.Add(1060430, prop); // hit stamina leech ~1_val~% } if (ImmolatingWeaponSpell.IsImmolating(this)) @@ -3007,57 +3014,57 @@ namespace Server.Items if (Core.ML && (ranged?.Velocity ?? 0) != 0) { - list.Add(1072793, prop.ToString()); // Velocity ~1_val~% + list.Add(1072793, prop); // Velocity ~1_val~% } if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~ + list.Add(1060409, prop); // dexterity bonus ~1_val~ } if ((prop = Attributes.BonusHits) != 0) { - list.Add(1060431, prop.ToString()); // hit point increase ~1_val~ + list.Add(1060431, prop); // hit point increase ~1_val~ } if ((prop = Attributes.BonusInt) != 0) { - list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~ + list.Add(1060432, prop); // intelligence bonus ~1_val~ } if ((prop = Attributes.LowerManaCost) != 0) { - list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~% + list.Add(1060433, prop); // lower mana cost ~1_val~% } if ((prop = Attributes.LowerRegCost) != 0) { - list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~% + list.Add(1060434, prop); // lower reagent cost ~1_val~% } if ((prop = GetLowerStatReq()) != 0) { - list.Add(1060435, prop.ToString()); // lower requirements ~1_val~% + list.Add(1060435, prop); // lower requirements ~1_val~% } if ((prop = GetLuckBonus() + Attributes.Luck) != 0) { - list.Add(1060436, prop.ToString()); // luck ~1_val~ + list.Add(1060436, prop); // luck ~1_val~ } if ((prop = WeaponAttributes.MageWeapon) != 0) { - list.Add(1060438, (30 - prop).ToString()); // mage weapon -~1_val~ skill + list.Add(1060438, 30 - prop); // mage weapon -~1_val~ skill } if ((prop = Attributes.BonusMana) != 0) { - list.Add(1060439, prop.ToString()); // mana increase ~1_val~ + list.Add(1060439, prop); // mana increase ~1_val~ } if ((prop = Attributes.RegenMana) != 0) { - list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~ + list.Add(1060440, prop); // mana regeneration ~1_val~ } if (Attributes.NightSight != 0) @@ -3067,22 +3074,22 @@ namespace Server.Items if ((prop = Attributes.ReflectPhysical) != 0) { - list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~% + list.Add(1060442, prop); // reflect physical damage ~1_val~% } if ((prop = Attributes.RegenStam) != 0) { - list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~ + list.Add(1060443, prop); // stamina regeneration ~1_val~ } if ((prop = Attributes.RegenHits) != 0) { - list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~ + list.Add(1060444, prop); // hit point regeneration ~1_val~ } if ((prop = WeaponAttributes.SelfRepair) != 0) { - list.Add(1060450, prop.ToString()); // self repair ~1_val~ + list.Add(1060450, prop); // self repair ~1_val~ } if (Attributes.SpellChanneling != 0) @@ -3092,27 +3099,27 @@ namespace Server.Items if ((prop = Attributes.SpellDamage) != 0) { - list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~% + list.Add(1060483, prop); // spell damage increase ~1_val~% } if ((prop = Attributes.BonusStam) != 0) { - list.Add(1060484, prop.ToString()); // stamina increase ~1_val~ + list.Add(1060484, prop); // stamina increase ~1_val~ } if ((prop = Attributes.BonusStr) != 0) { - list.Add(1060485, prop.ToString()); // strength bonus ~1_val~ + list.Add(1060485, prop); // strength bonus ~1_val~ } if ((prop = Attributes.WeaponSpeed) != 0) { - list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~% + list.Add(1060486, prop); // swing speed increase ~1_val~% } if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) { - list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~% + list.Add(1075210, prop); // Increased Karma Loss ~1val~% } GetDamageTypes( @@ -3128,40 +3135,40 @@ namespace Server.Items if (phys != 0) { - list.Add(1060403, phys.ToString()); // physical damage ~1_val~% + list.Add(1060403, phys); // physical damage ~1_val~% } if (fire != 0) { - list.Add(1060405, fire.ToString()); // fire damage ~1_val~% + list.Add(1060405, fire); // fire damage ~1_val~% } if (cold != 0) { - list.Add(1060404, cold.ToString()); // cold damage ~1_val~% + list.Add(1060404, cold); // cold damage ~1_val~% } if (pois != 0) { - list.Add(1060406, pois.ToString()); // poison damage ~1_val~% + list.Add(1060406, pois); // poison damage ~1_val~% } if (nrgy != 0) { - list.Add(1060407, nrgy.ToString()); // energy damage ~1_val + list.Add(1060407, nrgy); // energy damage ~1_val } if (Core.ML && chaos != 0) { - list.Add(1072846, chaos.ToString()); // chaos damage ~1_val~% + list.Add(1072846, chaos); // chaos damage ~1_val~% } if (Core.ML && direct != 0) { - list.Add(1079978, direct.ToString()); // Direct Damage: ~1_PERCENT~% + list.Add(1079978, direct); // Direct Damage: ~1_PERCENT~% } - list.Add(1061168, "{0}\t{1}", MinDamage.ToString(), MaxDamage.ToString()); // weapon damage ~1_val~ - ~2_val~ + list.Add(1061168, $"{MinDamage}\t{MaxDamage}"); // weapon damage ~1_val~ - ~2_val~ if (Core.ML) { @@ -3169,19 +3176,19 @@ namespace Server.Items } else { - list.Add(1061167, Speed.ToString()); + list.Add(1061167, $"{Speed}"); } if (MaxRange > 1) { - list.Add(1061169, MaxRange.ToString()); // range ~1_val~ + list.Add(1061169, MaxRange); // range ~1_val~ } var strReq = AOS.Scale(StrRequirement, 100 - GetLowerStatReq()); if (strReq > 0) { - list.Add(1061170, strReq.ToString()); // strength requirement ~1_val~ + list.Add(1061170, strReq); // strength requirement ~1_val~ } if (Layer == Layer.TwoHanded) @@ -3222,7 +3229,7 @@ namespace Server.Items if (m_Hits >= 0 && m_MaxHits > 0) { - list.Add(1060639, "{0}\t{1}", m_Hits, m_MaxHits); // durability ~1_val~ / ~2_val~ + list.Add(1060639, $"{m_Hits}\t{m_MaxHits}"); // durability ~1_val~ / ~2_val~ } } @@ -3614,7 +3621,7 @@ namespace Server.Items if (GetSaveFlag(flags, SaveFlag.Poison)) { - Poison.Serialize(m_Poison, writer); + writer.Write(m_Poison); } if (GetSaveFlag(flags, SaveFlag.PoisonCharges)) @@ -3797,7 +3804,7 @@ namespace Server.Items if (GetSaveFlag(flags, SaveFlag.Poison)) { - m_Poison = Poison.Deserialize(reader); + m_Poison = reader.ReadPoison(); } if (GetSaveFlag(flags, SaveFlag.PoisonCharges)) @@ -3971,10 +3978,7 @@ namespace Server.Items parentMobile.AddSkillMod(m_MageMod); } - if (GetSaveFlag(flags, SaveFlag.PlayerConstructed)) - { - PlayerConstructed = true; - } + PlayerConstructed = GetSaveFlag(flags, SaveFlag.PlayerConstructed); SkillBonuses = new AosSkillBonuses(this); @@ -4060,7 +4064,7 @@ namespace Server.Items m_Crafter = reader.ReadEntity(); - m_Poison = Poison.Deserialize(reader); + m_Poison = reader.ReadPoison(); m_PoisonCharges = reader.ReadInt(); if (m_StrReq == OldStrengthReq) @@ -4144,21 +4148,21 @@ namespace Server.Items if (parentMobile != null && (strBonus != 0 || dexBonus != 0 || intBonus != 0)) { - var modName = Serial.ToString(); + var serial = Serial; if (strBonus != 0) { - parentMobile.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + parentMobile.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - parentMobile.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + parentMobile.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - parentMobile.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + parentMobile.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } diff --git a/Projects/UOContent/Items/Weapons/Fists.cs b/Projects/UOContent/Items/Weapons/Fists.cs index e20c66c77..4de4e7d14 100644 --- a/Projects/UOContent/Items/Weapons/Fists.cs +++ b/Projects/UOContent/Items/Weapons/Fists.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; using Server.Engines.ConPVP; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Fists : BaseMeleeWeapon { public Fists() : base(0) @@ -54,105 +55,97 @@ namespace Server.Items private void CheckPreAOSMoves(Mobile attacker, Mobile defender) { + if (!attacker.CanBeginAction()) + { + return; + } + if (attacker.StunReady) { - if (attacker.CanBeginAction()) + if (attacker.Skills.Anatomy.Value < 80.0 || attacker.Skills.Wrestling.Value < 80.0) { - if (attacker.Skills.Anatomy.Value >= 80.0 && - attacker.Skills.Wrestling.Value >= 80.0) - { - if (attacker.Stam >= 15) - { - attacker.Stam -= 15; + attacker.SendLocalizedMessage(1004008); // You are not skilled enough to stun your opponent. + attacker.StunReady = false; + return; + } - if (CheckMove(attacker, SkillName.Anatomy)) - { - StartMoveDelay(attacker); + if (attacker.Stam < 15) + { + attacker.SendLocalizedMessage(1004009); // You are too fatigued to attempt anything. + return; + } - attacker.StunReady = false; + attacker.Stam -= 15; - attacker.SendLocalizedMessage(1004013); // You successfully stun your opponent! - defender.SendLocalizedMessage(1004014); // You have been stunned! + if (CheckMove(attacker, SkillName.Anatomy)) + { + StartMoveDelay(attacker); - 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; - } + 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 if (attacker.DisarmReady) { - if (attacker.CanBeginAction()) + if (!defender.Player && !defender.Body.IsHuman) { - if (defender.Player || defender.Body.IsHuman) - { - if (attacker.Skills.ArmsLore.Value >= 80.0 && - attacker.Skills.Wrestling.Value >= 80.0) - { - if (attacker.Stam >= 15) - { - var toDisarm = defender.FindItemOnLayer(Layer.OneHanded); + attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. + return; + } - if (toDisarm?.Movable == false) - { - toDisarm = defender.FindItemOnLayer(Layer.TwoHanded); - } + if (attacker.Skills.ArmsLore.Value < 80.0 || attacker.Skills.Wrestling.Value < 80.0) + { + attacker.SendLocalizedMessage(1004002); // You are not skilled enough to disarm your opponent. + attacker.DisarmReady = false; + return; + } - var pack = defender.Backpack; + if (attacker.Stam < 15) + { + attacker.SendLocalizedMessage(1004003); // You are too fatigued to attempt anything. + return; + } - if (pack == null || toDisarm?.Movable == false) - { - attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. - } - else if (CheckMove(attacker, SkillName.ArmsLore)) - { - StartMoveDelay(attacker); + var toDisarm = defender.FindItemOnLayer(Layer.OneHanded); - attacker.Stam -= 15; - attacker.DisarmReady = false; + if (toDisarm?.Movable == false) + { + toDisarm = defender.FindItemOnLayer(Layer.TwoHanded); + } - attacker.SendLocalizedMessage(1004006); // You successfully disarm your opponent! - defender.SendLocalizedMessage(1004007); // You have been disarmed! + var pack = defender.Backpack; - pack.DropItem(toDisarm); - } - else - { - attacker.Stam -= 15; + if (pack == null || toDisarm?.Movable == false) + { + attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. + } + else if (CheckMove(attacker, SkillName.ArmsLore)) + { + StartMoveDelay(attacker); - 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. - } + 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. } } } @@ -237,12 +230,7 @@ namespace Server.Items private static void EventSink_StunRequest(Mobile m) { - if (Core.AOS) - { - return; - } - - if (!DuelContext.AllowSpecialAbility(m, "Stun", true)) + if (Core.AOS || !DuelContext.AllowSpecialAbility(m, "Stun", true)) { return; } diff --git a/Projects/UOContent/Items/Weapons/Knives/BaseKnife.cs b/Projects/UOContent/Items/Weapons/Knives/BaseKnife.cs index d9dd99bb2..febf56b00 100644 --- a/Projects/UOContent/Items/Weapons/Knives/BaseKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/BaseKnife.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targets; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseKnife : BaseMeleeWeapon { public BaseKnife(int itemID) : base(itemID) diff --git a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs index 0812fd30e..19743996b 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13F6, 0x13F7)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ButcherKnife : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs b/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs index 830cf58d0..1738cf81c 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xEC3, 0xEC2)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Cleaver : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs index db175fb13..fadcb7daf 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF52, 0xF51)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Dagger : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs index 70041398d..dedd24265 100644 --- a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xEC4, 0xEC5)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class SkinningKnife : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs b/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs index 8a2baf755..92ba5842c 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs @@ -1,10 +1,11 @@ using System; +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { [Flippable(0xF52, 0xF51)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ThrowingDagger : Item { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs index 70fda78d0..de7d6de37 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class BlightGrippedLongbow : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs index 2eb3ed9b0..9cf54f71e 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ColdForgedBlade : ElvenSpellblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs index 77d76c981..597942a57 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class FaerieFire : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs index 230879940..df00c30ec 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class LuminousRuneBlade : RuneBlade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs index cc1c5354e..f7770d0e0 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class MischiefMaker : MagicalShortbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs index 81587f840..1d7dac8e2 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class OverseerSunderedBlade : RadiantScimitar { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs index f7617bfe3..c7ef49486 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class PhantomStaff : WildStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs index e1d850722..4ed88bdff 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class RuneCarvingKnife : AssassinSpike { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs index 6c1c6c73a..e883ff7cd 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ShardThrasher : DiamondMace { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs index 22082ff89..d02d8c09d 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SilvanisFeywoodBow : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs index 2a2258fa7..1945e64d8 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TheNightReaper : RepeatingCrossbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs b/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs index 7d39764d4..f0560945e 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D21, 0x2D2D)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class AssassinSpike : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ButchersWarCleaver.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ButchersWarCleaver.cs index 4afeed5c1..13cb86c65 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ButchersWarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ButchersWarCleaver.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ButchersWarCleaver : WarCleaver { [Constructible] @@ -10,7 +12,7 @@ namespace Server.Items public override int LabelNumber => 1073526; // butcher's war cleaver - public override void AppendChildNameProperties(ObjectPropertyList list) + public override void AppendChildNameProperties(IPropertyList list) { base.AppendChildNameProperties(list); diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs b/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs index 9fbf8d9fa..453e67e19 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D24, 0x2D30)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class DiamondMace : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs index d0b4aa941..199c76d82 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x2D1E, 0x2D2A)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenCompositeLongbow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs index 6c47e6382..459c76a66 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D35, 0x2D29)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenMachete : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs index cb06ecc6c..ebe89b1a5 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D20, 0x2D2C)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class ElvenSpellblade : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs index 9153375fc..79fe3730f 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D22, 0x2D2E)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class Leafblade : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs index e51e3ca59..63f3e0ae0 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x2D2B, 0x2D1F)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class MagicalShortbow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs b/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs index f19f09ca7..1c4f89602 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D28, 0x2D34)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrnateAxe : BaseAxe { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs b/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs index 4f1c037be..6783f36d3 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D33, 0x2D27)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class RadiantScimitar : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs index d5a6bfa79..1adda77d6 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D32, 0x2D26)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class RuneBlade : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs b/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs index 679ab6773..153bf26b7 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D2F, 0x2D23)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class WarCleaver : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs b/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs index a7cc68c0f..4f307bcaa 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x2D25, 0x2D31)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class WildStaff : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs b/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs index 4c2e7dc77..b9870332b 100644 --- a/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs +++ b/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.ConPVP; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseBashing : BaseMeleeWeapon { public BaseBashing(int itemID) : base(itemID) diff --git a/Projects/UOContent/Items/Weapons/Maces/Club.cs b/Projects/UOContent/Items/Weapons/Maces/Club.cs index bf192959c..a4b3333e3 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Club.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Club.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13b4, 0x13b3)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Club : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs b/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs index 7152cc377..de634920d 100644 --- a/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x903, 0x406E)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class DiscMace : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/EmeraldMace.cs b/Projects/UOContent/Items/Weapons/Maces/EmeraldMace.cs index b76750f90..a166ad5ac 100644 --- a/Projects/UOContent/Items/Weapons/Maces/EmeraldMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/EmeraldMace.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class EmeraldMace : DiamondMace { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs b/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs index 8210c2c7a..c28b20f8f 100644 --- a/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs +++ b/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs @@ -1,8 +1,9 @@ using System; +using ModernUO.Serialization; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class FireworksWand : MagicWand { [SerializableField(0)] @@ -19,11 +20,11 @@ namespace Server.Items public override int LabelNumber => 1041424; // a fireworks wand - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); - list.Add(1060741, _charges.ToString()); // charges: ~1_val~ + list.Add(1060741, $"{_charges}"); // charges: ~1_val~ } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs b/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs index 99d0ed0d1..88a74b2aa 100644 --- a/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs +++ b/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x143D, 0x143C)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HammerPick : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/Mace.cs b/Projects/UOContent/Items/Weapons/Maces/Mace.cs index 5d47a060d..b362f04fe 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Mace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Mace.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF5C, 0xF5D)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Mace : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs index a50669b31..31e92e79f 100644 --- a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs +++ b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class MagicWand : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/Maul.cs b/Projects/UOContent/Items/Weapons/Maces/Maul.cs index 6252761a9..ee3054c76 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Maul.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Maul.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x143B, 0x143A)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Maul : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/RubyMace.cs b/Projects/UOContent/Items/Weapons/Maces/RubyMace.cs index 27de75f36..f7d559314 100644 --- a/Projects/UOContent/Items/Weapons/Maces/RubyMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/RubyMace.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class RubyMace : DiamondMace { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/SapphireMace.cs b/Projects/UOContent/Items/Weapons/Maces/SapphireMace.cs index 7a4c8ef66..c85732798 100644 --- a/Projects/UOContent/Items/Weapons/Maces/SapphireMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/SapphireMace.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SapphireMace : DiamondMace { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/Scepter.cs b/Projects/UOContent/Items/Weapons/Maces/Scepter.cs index bf19702cb..d48793e15 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Scepter.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Scepter.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26BC, 0x26C6)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Scepter : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/SilverEtchedMace.cs b/Projects/UOContent/Items/Weapons/Maces/SilverEtchedMace.cs index 804838cae..4e3e48228 100644 --- a/Projects/UOContent/Items/Weapons/Maces/SilverEtchedMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/SilverEtchedMace.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SilverEtchedMace : DiamondMace { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs b/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs index 591a64ae3..dd5ab6944 100644 --- a/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs +++ b/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1439, 0x1438)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WarHammer : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Maces/WarMace.cs b/Projects/UOContent/Items/Weapons/Maces/WarMace.cs index 45527f97e..0ed10402c 100644 --- a/Projects/UOContent/Items/Weapons/Maces/WarMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/WarMace.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1407, 0x1406)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WarMace : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs b/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs index 2f7601fb1..a19bc33c5 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF4D, 0xF4E)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bardiche : BasePoleArm { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/PoleArms/BasePoleArm.cs b/Projects/UOContent/Items/Weapons/PoleArms/BasePoleArm.cs index cb960208d..ea56669ff 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/BasePoleArm.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/BasePoleArm.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Engines.ConPVP; using Server.Engines.Harvest; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BasePoleArm : BaseMeleeWeapon, IUsesRemaining { [SerializableField(0)] diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs b/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs index 0ed0cf584..9356842a5 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x143E, 0x143F)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Halberd : BasePoleArm { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs b/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs index 18f802927..62038c4d4 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs @@ -1,9 +1,10 @@ +using ModernUO.Serialization; using Server.Engines.Harvest; namespace Server.Items { [Flippable(0x26BA, 0x26C4)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Scythe : BasePoleArm { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/AssassinsShortbow.cs b/Projects/UOContent/Items/Weapons/Ranged/AssassinsShortbow.cs index 802b34385..ea2c2411e 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/AssassinsShortbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/AssassinsShortbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class AssassinsShortbow : MagicalShortbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/BarbedLongbow.cs b/Projects/UOContent/Items/Weapons/Ranged/BarbedLongbow.cs index 7fdf8f1bd..c27d6071b 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/BarbedLongbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/BarbedLongbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class BarbedLongbow : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs index 05f3bf3da..f4cdc12bc 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs @@ -1,11 +1,12 @@ using System; +using ModernUO.Serialization; using Server.Mobiles; using Server.Network; using Server.Spells; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseRanged : BaseMeleeWeapon { [SerializableField(0)] diff --git a/Projects/UOContent/Items/Weapons/Ranged/Bow.cs b/Projects/UOContent/Items/Weapons/Ranged/Bow.cs index b5940a5d4..400d2bf8a 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/Bow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/Bow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x13B2, 0x13B1)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs b/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs index 19b1ebf28..58515ae4c 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x26C2, 0x26CC)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CompositeBow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs index 05675d117..cccfd0103 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0xF50, 0xF4F)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Crossbow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/FrozenLongbow.cs b/Projects/UOContent/Items/Weapons/Ranged/FrozenLongbow.cs index e372369df..74829ed1d 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/FrozenLongbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/FrozenLongbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class FrozenLongbow : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs index e41923e75..84679ce07 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x13FD, 0x13FC)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class HeavyCrossbow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/JukaBow.cs b/Projects/UOContent/Items/Weapons/Ranged/JukaBow.cs index 658c9d39c..763d2b373 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/JukaBow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/JukaBow.cs @@ -1,9 +1,10 @@ +using ModernUO.Serialization; using Server.Targeting; namespace Server.Items { [Flippable(0x13B2, 0x13B1)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class JukaBow : Bow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/LightweightShortbow.cs b/Projects/UOContent/Items/Weapons/Ranged/LightweightShortbow.cs index d450d14d6..102ef3677 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/LightweightShortbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/LightweightShortbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class LightweightShortbow : MagicalShortbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/LongbowOfMight.cs b/Projects/UOContent/Items/Weapons/Ranged/LongbowOfMight.cs index c45a14965..20dd19f29 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/LongbowOfMight.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/LongbowOfMight.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class LongbowOfMight : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/MysticalShortbow.cs b/Projects/UOContent/Items/Weapons/Ranged/MysticalShortbow.cs index 83526b00c..4ffea36ae 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/MysticalShortbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/MysticalShortbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class MysticalShortbow : MagicalShortbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/RangersShortbow.cs b/Projects/UOContent/Items/Weapons/Ranged/RangersShortbow.cs index 125a18efb..63cd5f80e 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/RangersShortbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/RangersShortbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class RangersShortbow : MagicalShortbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs index 06978f985..71bac450f 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x26C3, 0x26CD)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class RepeatingCrossbow : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Ranged/SlayerLongbow.cs b/Projects/UOContent/Items/Weapons/Ranged/SlayerLongbow.cs index f0ceb5161..f7e310ca8 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/SlayerLongbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/SlayerLongbow.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SlayerLongbow : ElvenCompositeLongbow { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs index 8abd83332..1f3f9db30 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A8, 0x27F3)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Bokuto : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs index 0eef9f732..136897fdf 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A9, 0x27F4)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Daisho : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs index aad267a06..23d054bb6 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27AD, 0x27F8)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Kama : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs index 7c75dd00a..963b1219f 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A7, 0x27F2)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Lajatang : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs index 3e2f35343..7d61ea419 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A2, 0x27ED)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class NoDachi : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs index 7018a110f..2e5bf689c 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27AE, 0x27F9)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Nunchaku : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs index 94d8357af..df60344a2 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27AF, 0x27FA)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Sai : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs index 2e8cda833..388186818 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27Ab, 0x27F6)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tekagi : BaseKnife { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs index 7ad363f5c..442ae9e55 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A3, 0x27EE)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tessen : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs index 7d2cfa6d3..ab32c41a4 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A6, 0x27F1)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Tetsubo : BaseBashing { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs index ab3b75c33..5bb6b0fd4 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x27A4, 0x27EF)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Wakizashi : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs index d1f69eaf2..56ee5b3a3 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; namespace Server.Items { [Flippable(0x27A5, 0x27F0)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Yumi : BaseRanged { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs index 0a1c911f4..574e2749d 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs @@ -1,9 +1,10 @@ using System; +using ModernUO.Serialization; using Server.Engines.ConPVP; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseSpear : BaseMeleeWeapon { public BaseSpear(int itemID) : base(itemID) diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs index f11dbe9cb..102ceb361 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26BD, 0x26C7)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BladedStaff : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs index cb9d3e603..7d7bfc05a 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26BF, 0x26C9)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class DoubleBladedStaff : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs index 2aee19229..275800cd4 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x904, 0x406D)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class DualPointedSpear : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs index 27754df5b..6c8b724c9 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26BE, 0x26C8)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Pike : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs index b3fdd2d1b..fc3ac6a21 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xE87, 0xE88)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Pitchfork : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs index 86e914010..32b566a0b 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1403, 0x1402)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShortSpear : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs index 98d2ee1c2..26d3e2636 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF62, 0xF63)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Spear : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs index d2164a0d8..3f3f17ecc 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF62, 0xF63)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class TribalSpear : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs index 9857de710..94b672b63 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1405, 0x1404)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class WarFork : BaseSpear { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/BaseStaff.cs b/Projects/UOContent/Items/Weapons/Staves/BaseStaff.cs index 6686b9cc2..57ee0a7a1 100644 --- a/Projects/UOContent/Items/Weapons/Staves/BaseStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/BaseStaff.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseStaff : BaseMeleeWeapon { public BaseStaff(int itemID) : base(itemID) diff --git a/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs b/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs index 6b26eb0f5..a4508680c 100644 --- a/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xDF1, 0xDF0)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BlackStaff : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/GlacialStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GlacialStaff.cs index 02fd7936b..3f66a7c16 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GlacialStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GlacialStaff.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GlacialStaff : BlackStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs index 03d3b0bbc..db2250053 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x905, 0x4070)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class GlassStaff : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs index 8ca5f06a6..3611907b5 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13F8, 0x13F9)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class GnarledStaff : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs b/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs index bb65230ad..e0d5c567d 100644 --- a/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xE89, 0xE8a)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class QuarterStaff : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs b/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs index a75b82ae4..2caebfb21 100644 --- a/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs @@ -1,8 +1,10 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x906, 0x406F)] [TypeAlias("Server.Items.SerpentStoneStaff")] - [Serializable(0)] + [SerializationGenerator(0)] public partial class SerpentstoneStaff : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs index 054c53c07..13b1cd9f5 100644 --- a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs +++ b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs @@ -1,4 +1,5 @@ using System; +using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Mobiles; using Server.Network; @@ -7,7 +8,7 @@ using Server.Targeting; namespace Server.Items { [Flippable(0xE81, 0xE82)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ShepherdsCrook : BaseStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/AdventurersMachete.cs b/Projects/UOContent/Items/Weapons/Swords/AdventurersMachete.cs index 8415307c7..b721a3cb1 100644 --- a/Projects/UOContent/Items/Weapons/Swords/AdventurersMachete.cs +++ b/Projects/UOContent/Items/Weapons/Swords/AdventurersMachete.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class AdventurersMachete : ElvenMachete { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/BaseSword.cs b/Projects/UOContent/Items/Weapons/Swords/BaseSword.cs index 6527c8de8..ffe129d16 100644 --- a/Projects/UOContent/Items/Weapons/Swords/BaseSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/BaseSword.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Targets; namespace Server.Items { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public abstract partial class BaseSword : BaseMeleeWeapon { public BaseSword(int itemID) : base(itemID) diff --git a/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs b/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs index 4bcf21831..21ba7e61f 100644 --- a/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x8FE, 0x4072)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class BloodBlade : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs b/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs index 6eb52ee86..8c3d9fbd2 100644 --- a/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs +++ b/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26BB, 0x26C5)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class BoneHarvester : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/BoneMachete.cs b/Projects/UOContent/Items/Weapons/Swords/BoneMachete.cs index 247dfbb8a..80981472e 100644 --- a/Projects/UOContent/Items/Weapons/Swords/BoneMachete.cs +++ b/Projects/UOContent/Items/Weapons/Swords/BoneMachete.cs @@ -1,8 +1,9 @@ +using ModernUO.Serialization; using Server.Engines.MLQuests.Items; namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class BoneMachete : ElvenMachete, ITicket { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs b/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs index 2ed8317af..e980b11ad 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF5E, 0xF5F)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Broadsword : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/ChargedAssassinSpike.cs b/Projects/UOContent/Items/Weapons/Swords/ChargedAssassinSpike.cs index e71c77e21..1e6aac8fe 100644 --- a/Projects/UOContent/Items/Weapons/Swords/ChargedAssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/Swords/ChargedAssassinSpike.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ChargedAssassinSpike : AssassinSpike { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/CorruptedRuneBlade.cs b/Projects/UOContent/Items/Weapons/Swords/CorruptedRuneBlade.cs index 934dd5304..60a0aec08 100644 --- a/Projects/UOContent/Items/Weapons/Swords/CorruptedRuneBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/CorruptedRuneBlade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class CorruptedRuneBlade : RuneBlade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs index a20e948b1..7bb7f1470 100644 --- a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26C1, 0x26CB)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class CrescentBlade : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs b/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs index 49782febf..922c1fd04 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1441, 0x1440)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Cutlass : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/DarkglowScimitar.cs b/Projects/UOContent/Items/Weapons/Swords/DarkglowScimitar.cs index e57b39401..9fc2684f3 100644 --- a/Projects/UOContent/Items/Weapons/Swords/DarkglowScimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/DarkglowScimitar.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class DarkglowScimitar : RadiantScimitar { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/DiseasedMachete.cs b/Projects/UOContent/Items/Weapons/Swords/DiseasedMachete.cs index e4bacc043..3a21a070f 100644 --- a/Projects/UOContent/Items/Weapons/Swords/DiseasedMachete.cs +++ b/Projects/UOContent/Items/Weapons/Swords/DiseasedMachete.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class DiseasedMachete : ElvenMachete { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs b/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs index 9f2df661f..4b628fe20 100644 --- a/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x90B, 0x4074)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class DreadSword : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/FierySpellblade.cs b/Projects/UOContent/Items/Weapons/Swords/FierySpellblade.cs index ffa064f9e..43428bb9f 100644 --- a/Projects/UOContent/Items/Weapons/Swords/FierySpellblade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/FierySpellblade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class FierySpellblade : ElvenSpellblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs b/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs index 713e684b6..c78c49db2 100644 --- a/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x908, 0x4075)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class GargishTalwar : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs index 1bb290332..e821588ab 100644 --- a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x90C, 0x4073)] - [Serializable(0)] + [SerializationGenerator(0)] public partial class GlassSword : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/IcyScimitar.cs b/Projects/UOContent/Items/Weapons/Swords/IcyScimitar.cs index 20fb37ac4..8e256e1ab 100644 --- a/Projects/UOContent/Items/Weapons/Swords/IcyScimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/IcyScimitar.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class IcyScimitar : RadiantScimitar { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/IcySpellblade.cs b/Projects/UOContent/Items/Weapons/Swords/IcySpellblade.cs index e29c8449d..8b51dcc13 100644 --- a/Projects/UOContent/Items/Weapons/Swords/IcySpellblade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/IcySpellblade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class IcySpellblade : ElvenSpellblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Katana.cs b/Projects/UOContent/Items/Weapons/Swords/Katana.cs index 80c4507c9..34e4110eb 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Katana.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Katana.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13FF, 0x13FE)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Katana : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/KnightsWarCleaver.cs b/Projects/UOContent/Items/Weapons/Swords/KnightsWarCleaver.cs index 384fe615f..4533600b8 100644 --- a/Projects/UOContent/Items/Weapons/Swords/KnightsWarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/Swords/KnightsWarCleaver.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class KnightsWarCleaver : WarCleaver { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Kryss.cs b/Projects/UOContent/Items/Weapons/Swords/Kryss.cs index ff508f726..e6ae7a663 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Kryss.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Kryss.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x1401, 0x1400)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Kryss : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Lance.cs b/Projects/UOContent/Items/Weapons/Swords/Lance.cs index 71ca9ba82..3520855ca 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Lance.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Lance.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x26C0, 0x26CA)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Lance : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/LeafbladeOfEase.cs b/Projects/UOContent/Items/Weapons/Swords/LeafbladeOfEase.cs index c1aaadace..64859eb2f 100644 --- a/Projects/UOContent/Items/Weapons/Swords/LeafbladeOfEase.cs +++ b/Projects/UOContent/Items/Weapons/Swords/LeafbladeOfEase.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class LeafbladeOfEase : Leafblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Longsword.cs b/Projects/UOContent/Items/Weapons/Swords/Longsword.cs index 039f6fbbb..df3310606 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Longsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Longsword.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0xF61, 0xF60)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Longsword : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Luckblade.cs b/Projects/UOContent/Items/Weapons/Swords/Luckblade.cs index 14d9a0f72..65d2ebd58 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Luckblade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Luckblade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class Luckblade : Leafblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/MacheteOfDefense.cs b/Projects/UOContent/Items/Weapons/Swords/MacheteOfDefense.cs index 8a06374c9..b4c985370 100644 --- a/Projects/UOContent/Items/Weapons/Swords/MacheteOfDefense.cs +++ b/Projects/UOContent/Items/Weapons/Swords/MacheteOfDefense.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class MacheteOfDefense : ElvenMachete { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/MagekillerAssassinSpike.cs b/Projects/UOContent/Items/Weapons/Swords/MagekillerAssassinSpike.cs index 081a62789..db87b7890 100644 --- a/Projects/UOContent/Items/Weapons/Swords/MagekillerAssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/Swords/MagekillerAssassinSpike.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class MagekillerAssassinSpike : AssassinSpike { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/MagekillerLeafblade.cs b/Projects/UOContent/Items/Weapons/Swords/MagekillerLeafblade.cs index b65e1c134..66c1fc1af 100644 --- a/Projects/UOContent/Items/Weapons/Swords/MagekillerLeafblade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/MagekillerLeafblade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class MagekillerLeafblade : Leafblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/MagesRuneBlade.cs b/Projects/UOContent/Items/Weapons/Swords/MagesRuneBlade.cs index 2d36fbab8..2b06addb3 100644 --- a/Projects/UOContent/Items/Weapons/Swords/MagesRuneBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/MagesRuneBlade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class MagesRuneBlade : RuneBlade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/OrcishMachete.cs b/Projects/UOContent/Items/Weapons/Swords/OrcishMachete.cs index 3db06037d..e1da83502 100644 --- a/Projects/UOContent/Items/Weapons/Swords/OrcishMachete.cs +++ b/Projects/UOContent/Items/Weapons/Swords/OrcishMachete.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class OrcishMachete : ElvenMachete { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/RuneBladeOfKnowledge.cs b/Projects/UOContent/Items/Weapons/Swords/RuneBladeOfKnowledge.cs index b98243b02..c9b948cfd 100644 --- a/Projects/UOContent/Items/Weapons/Swords/RuneBladeOfKnowledge.cs +++ b/Projects/UOContent/Items/Weapons/Swords/RuneBladeOfKnowledge.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class RuneBladeOfKnowledge : RuneBlade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Runesabre.cs b/Projects/UOContent/Items/Weapons/Swords/Runesabre.cs index ebf11c10e..584355ac3 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Runesabre.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Runesabre.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class Runesabre : RuneBlade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs b/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs index d23458660..d49112d38 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13B6, 0x13B5)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class Scimitar : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/SerratedWarCleaver.cs b/Projects/UOContent/Items/Weapons/Swords/SerratedWarCleaver.cs index 688741bf7..2c94eb4f3 100644 --- a/Projects/UOContent/Items/Weapons/Swords/SerratedWarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/Swords/SerratedWarCleaver.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SerratedWarCleaver : WarCleaver { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/SpellbladeOfDefense.cs b/Projects/UOContent/Items/Weapons/Swords/SpellbladeOfDefense.cs index 3534d1740..bb68850d2 100644 --- a/Projects/UOContent/Items/Weapons/Swords/SpellbladeOfDefense.cs +++ b/Projects/UOContent/Items/Weapons/Swords/SpellbladeOfDefense.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class SpellbladeOfDefense : ElvenSpellblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs index ee8298272..0e17985ca 100644 --- a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13B8, 0x13B7)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class ThinLongsword : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/TrueAssassinSpike.cs b/Projects/UOContent/Items/Weapons/Swords/TrueAssassinSpike.cs index dae410257..c701b007b 100644 --- a/Projects/UOContent/Items/Weapons/Swords/TrueAssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/Swords/TrueAssassinSpike.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TrueAssassinSpike : AssassinSpike { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/TrueLeafblade.cs b/Projects/UOContent/Items/Weapons/Swords/TrueLeafblade.cs index afbbfbe0c..846966093 100644 --- a/Projects/UOContent/Items/Weapons/Swords/TrueLeafblade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/TrueLeafblade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TrueLeafblade : Leafblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/TrueRadiantScimitar.cs b/Projects/UOContent/Items/Weapons/Swords/TrueRadiantScimitar.cs index 8442d7b26..6582519bc 100644 --- a/Projects/UOContent/Items/Weapons/Swords/TrueRadiantScimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/TrueRadiantScimitar.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TrueRadiantScimitar : RadiantScimitar { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/TrueSpellblade.cs b/Projects/UOContent/Items/Weapons/Swords/TrueSpellblade.cs index 5079243c3..06430b24a 100644 --- a/Projects/UOContent/Items/Weapons/Swords/TrueSpellblade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/TrueSpellblade.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TrueSpellblade : ElvenSpellblade { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/TrueWarCleaver.cs b/Projects/UOContent/Items/Weapons/Swords/TrueWarCleaver.cs index 423d995bb..fd5c5c2a7 100644 --- a/Projects/UOContent/Items/Weapons/Swords/TrueWarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/Swords/TrueWarCleaver.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TrueWarCleaver : WarCleaver { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/TwinklingScimitar.cs b/Projects/UOContent/Items/Weapons/Swords/TwinklingScimitar.cs index bdfee5f2b..eac934560 100644 --- a/Projects/UOContent/Items/Weapons/Swords/TwinklingScimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/TwinklingScimitar.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class TwinklingScimitar : RadiantScimitar { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs b/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs index 422be352a..83c6f4de9 100644 --- a/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { [Flippable(0x13B9, 0x13Ba)] - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class VikingSword : BaseSword { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Swords/WoundingAssassinSpike.cs b/Projects/UOContent/Items/Weapons/Swords/WoundingAssassinSpike.cs index fa5615838..96ae8721d 100644 --- a/Projects/UOContent/Items/Weapons/Swords/WoundingAssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/Swords/WoundingAssassinSpike.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class WoundingAssassinSpike : AssassinSpike { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs b/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs new file mode 100644 index 000000000..1c99668ad --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs @@ -0,0 +1,105 @@ +using System; +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public abstract partial class BaseThrown : BaseRanged +{ + public BaseThrown(int itemID) : base(itemID) + { + } + + public abstract int MinThrowRange { get; } + + public virtual int MaxThrowRange => MinThrowRange + 3; + + public override int DefMaxRange + { + get + { + int baseRange = MaxThrowRange; + + return Parent is Mobile attacker + ? baseRange - 3 + (attacker.Str - AosStrengthReq) / ((140 - AosStrengthReq) / 3) + : baseRange; + } + } + + public override int EffectID => ItemID; + + public override Type AmmoType => null; + + public override Item Ammo => null; + + public override int DefHitSound => 0x5D3; + public override int DefMissSound => 0x5D4; + + public override SkillName DefSkill => SkillName.Throwing; + + public override WeaponAnimation DefAnimation => WeaponAnimation.Throwing; + + public override bool OnFired(Mobile attacker, Mobile defender) + { + if (!attacker.InRange(defender, 1)) + { + attacker.MovingEffect(defender, EffectID, 18, 1, false, false, Hue, 0); + } + + return true; + } + + public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1) + { + if (WeaponAbility.GetCurrentAbility(attacker) is not MysticArc) + { + var location = new WorldLocation(defender.Location, attacker.Map); + Timer.StartTimer(TimeSpan.FromSeconds(0.3), () => Return(attacker, defender, location)); + } + + base.OnHit(attacker, defender, damageBonus); + } + + public override void OnMiss(Mobile attacker, Mobile defender) + { + if (WeaponAbility.GetCurrentAbility(attacker) is not MysticArc) + { + var location = new WorldLocation(defender.Location, attacker.Map); + Timer.StartTimer(TimeSpan.FromSeconds(0.3), () => Return(attacker, defender, location)); + } + + base.OnMiss(attacker, defender); + } + + public virtual void Return(Mobile thrower, Mobile target, WorldLocation worldLocation) + { + if (thrower == null) + { + return; + } + + if (target != null) + { + target.MovingEffect(thrower, EffectID, 18, 1, false, false, Hue, 0); + } + else + { + Effects.SendMovingParticles( + new Entity(Serial.Zero, worldLocation.Location, worldLocation.Map), + thrower, + ItemID, + 18, + 0, + false, + false, + Hue, + 0, + 9502, + 1, + 0, + (EffectLayer)255, + 0x100 + ); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs b/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs new file mode 100644 index 000000000..67650361b --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs @@ -0,0 +1,25 @@ +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class Boomerang : BaseThrown +{ + [Constructible] + public Boomerang() : base(0x8FF) + { + Weight = 4.0; + Layer = Layer.OneHanded; + } + + public override int MinThrowRange => 4; + + public override WeaponAbility PrimaryAbility => WeaponAbility.MysticArc; + public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; + public override int AosStrengthReq => 25; + public override int AosMinDamage => 11; + public override int AosMaxDamage => 15; + public override float MlSpeed => 2.75f; + public override int InitMinHits => 31; + public override int InitMaxHits => 60; +} diff --git a/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs b/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs new file mode 100644 index 000000000..889d1e1fb --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs @@ -0,0 +1,27 @@ +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class Cyclone : BaseThrown +{ + [Constructible] + public Cyclone() : base(0x901) + { + Weight = 6.0; + Layer = Layer.OneHanded; + } + + public override int MinThrowRange => 6; + + public override WeaponAbility PrimaryAbility => WeaponAbility.MovingShot; + public override WeaponAbility SecondaryAbility => WeaponAbility.InfusedThrow; + public override int AosStrengthReq => 40; + public override int AosMinDamage => 13; + public override int AosMaxDamage => 17; + public override float MlSpeed => 3.25f; + public override int InitMinHits => 31; + public override int InitMaxHits => 60; + + +} diff --git a/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs b/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs new file mode 100644 index 000000000..724053a39 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs @@ -0,0 +1,26 @@ +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class SoulGlaive : BaseThrown +{ + [Constructible] + public SoulGlaive() : base(0x090A) + { + Weight = 8.0; + Layer = Layer.OneHanded; + } + + public override int MinThrowRange => 8; + + public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; + public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; + public override int AosStrengthReq => 60; + public override int AosMinDamage => 16; + public override int AosMaxDamage => 20; + public override float MlSpeed => 4.00f; + + public override int InitMinHits => 31; + public override int InitMaxHits => 65; +} diff --git a/Projects/UOContent/Items/Weapons/WeaponEnums.cs b/Projects/UOContent/Items/Weapons/WeaponEnums.cs index b09563f05..ab29ac15f 100644 --- a/Projects/UOContent/Items/Weapons/WeaponEnums.cs +++ b/Projects/UOContent/Items/Weapons/WeaponEnums.cs @@ -59,6 +59,7 @@ namespace Server.Items Pierce2H = 14, ShootBow = 18, ShootXBow = 19, - Wrestle = 31 + Wrestle = 31, + Throwing = 32 } } diff --git a/Projects/UOContent/Items/Weapons/Wooden/AncientWildStaff.cs b/Projects/UOContent/Items/Weapons/Wooden/AncientWildStaff.cs index d4ef499b6..277599f81 100644 --- a/Projects/UOContent/Items/Weapons/Wooden/AncientWildStaff.cs +++ b/Projects/UOContent/Items/Weapons/Wooden/AncientWildStaff.cs @@ -1,7 +1,9 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class AncientWildStaff : WildStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Wooden/ArcanistsWildStaff.cs b/Projects/UOContent/Items/Weapons/Wooden/ArcanistsWildStaff.cs index d8e14ccd2..19864932e 100644 --- a/Projects/UOContent/Items/Weapons/Wooden/ArcanistsWildStaff.cs +++ b/Projects/UOContent/Items/Weapons/Wooden/ArcanistsWildStaff.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ArcanistsWildStaff : WildStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Wooden/HardenedWildStaff.cs b/Projects/UOContent/Items/Weapons/Wooden/HardenedWildStaff.cs index 522b719b4..72c251be1 100644 --- a/Projects/UOContent/Items/Weapons/Wooden/HardenedWildStaff.cs +++ b/Projects/UOContent/Items/Weapons/Wooden/HardenedWildStaff.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class HardenedWildStaff : WildStaff { [Constructible] diff --git a/Projects/UOContent/Items/Weapons/Wooden/ThornedWildStaff.cs b/Projects/UOContent/Items/Weapons/Wooden/ThornedWildStaff.cs index 6fc83d763..3f0bdce7f 100644 --- a/Projects/UOContent/Items/Weapons/Wooden/ThornedWildStaff.cs +++ b/Projects/UOContent/Items/Weapons/Wooden/ThornedWildStaff.cs @@ -1,6 +1,8 @@ +using ModernUO.Serialization; + namespace Server.Items { - [Serializable(0)] + [SerializationGenerator(0)] public partial class ThornedWildStaff : WildStaff { [Constructible] diff --git a/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json b/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json index 07d1336c7..887b06c08 100644 --- a/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json +++ b/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json @@ -61,7 +61,7 @@ ] }, { - "name": "Mobiles", + "name": "RawMobiles", "type": "Server.Mobile[]", "rule": "ArrayMigrationRule", "ruleArguments": [ diff --git a/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongDealerIndicator.v1.json b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongDealerIndicator.v1.json new file mode 100644 index 000000000..967bc6e58 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongDealerIndicator.v1.json @@ -0,0 +1,24 @@ +{ + "version": 1, + "type": "Server.Engines.Mahjong.MahjongDealerIndicator", + "properties": [ + { + "name": "Position", + "type": "Server.Point2D", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Point2D" + ] + }, + { + "name": "Direction", + "type": "Server.Engines.Mahjong.MahjongPieceDirection", + "rule": "EnumMigrationRule" + }, + { + "name": "Wind", + "type": "Server.Engines.Mahjong.MahjongWind", + "rule": "EnumMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongDices.v0.json b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongDices.v0.json new file mode 100644 index 000000000..2f5945ff3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongDices.v0.json @@ -0,0 +1,22 @@ +{ + "version": 0, + "type": "Server.Engines.Mahjong.MahjongDices", + "properties": [ + { + "name": "First", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Second", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongGame.v1.json b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongGame.v1.json new file mode 100644 index 000000000..9d4348506 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongGame.v1.json @@ -0,0 +1,69 @@ +{ + "version": 1, + "type": "Server.Engines.Mahjong.MahjongGame", + "properties": [ + { + "name": "Level", + "type": "Server.Multis.SecureLevel", + "rule": "EnumMigrationRule" + }, + { + "name": "Tiles", + "type": "Server.Engines.Mahjong.MahjongTile[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "Server.Engines.Mahjong.MahjongTile", + "RawSerializableMigrationRule", + "DeserializationRequiresParent" + ] + }, + { + "name": "DealerIndicator", + "type": "Server.Engines.Mahjong.MahjongDealerIndicator", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "WallBreakIndicator", + "type": "Server.Engines.Mahjong.MahjongWallBreakIndicator", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Dices", + "type": "Server.Engines.Mahjong.MahjongDices", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Players", + "type": "Server.Engines.Mahjong.MahjongPlayers", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "ShowScores", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "SpectatorVision", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongPlayers.v1.json b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongPlayers.v1.json new file mode 100644 index 000000000..696bd989e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongPlayers.v1.json @@ -0,0 +1,53 @@ +{ + "version": 1, + "type": "Server.Engines.Mahjong.MahjongPlayers", + "properties": [ + { + "name": "Players", + "type": "Server.Mobile[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "InGame", + "type": "bool[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "bool", + "PrimitiveTypeMigrationRule", + "" + ] + }, + { + "name": "PublicHand", + "type": "bool[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "bool", + "PrimitiveTypeMigrationRule", + "" + ] + }, + { + "name": "Scores", + "type": "int[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "int", + "PrimitiveTypeMigrationRule", + "" + ] + }, + { + "name": "DealerPosition", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongTile.v1.json b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongTile.v1.json new file mode 100644 index 000000000..33d13b217 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongTile.v1.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "type": "Server.Engines.Mahjong.MahjongTile", + "properties": [ + { + "name": "Number", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Value", + "type": "Server.Engines.Mahjong.MahjongTileType", + "rule": "EnumMigrationRule" + }, + { + "name": "Position", + "type": "Server.Point2D", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Point2D" + ] + }, + { + "name": "StackLevel", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Direction", + "type": "Server.Engines.Mahjong.MahjongPieceDirection", + "rule": "EnumMigrationRule" + }, + { + "name": "Flipped", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongWallBreakIndicator.v0.json b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongWallBreakIndicator.v0.json new file mode 100644 index 000000000..886f15a4d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Mahjong.MahjongWallBreakIndicator.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.Engines.Mahjong.MahjongWallBreakIndicator", + "properties": [ + { + "name": "Position", + "type": "Server.Point2D", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Point2D" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Amber.v0.json b/Projects/UOContent/Migrations/Server.Items.Amber.v0.json new file mode 100644 index 000000000..9d44ba15c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Amber.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Amber" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Amethyst.v0.json b/Projects/UOContent/Migrations/Server.Items.Amethyst.v0.json new file mode 100644 index 000000000..af9bad81e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Amethyst.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Amethyst" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Apple.v0.json b/Projects/UOContent/Migrations/Server.Items.Apple.v0.json new file mode 100644 index 000000000..f2cc1aebb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Apple.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Apple" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ApplePie.v0.json b/Projects/UOContent/Migrations/Server.Items.ApplePie.v0.json new file mode 100644 index 000000000..bd427f8e9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ApplePie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ApplePie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Aquarium.v4.json b/Projects/UOContent/Migrations/Server.Items.Aquarium.v4.json index 6cd8c06de..142c61178 100644 --- a/Projects/UOContent/Migrations/Server.Items.Aquarium.v4.json +++ b/Projects/UOContent/Migrations/Server.Items.Aquarium.v4.json @@ -31,7 +31,7 @@ "type": "Server.Items.AquariumState", "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -39,7 +39,7 @@ "type": "Server.Items.AquariumState", "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { diff --git a/Projects/UOContent/Migrations/Server.Items.Armoire.v0.json b/Projects/UOContent/Migrations/Server.Items.Armoire.v0.json new file mode 100644 index 000000000..e410f3ea9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Armoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Armoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json new file mode 100644 index 000000000..a66b23b95 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.AwaseMisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Backgammon.v0.json b/Projects/UOContent/Migrations/Server.Items.Backgammon.v0.json new file mode 100644 index 000000000..b5b6599dd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Backgammon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Backgammon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Backpack.v0.json b/Projects/UOContent/Migrations/Server.Items.Backpack.v0.json new file mode 100644 index 000000000..079753f27 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Backpack.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Backpack" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BackpackArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BackpackArtifact.v0.json new file mode 100644 index 000000000..271e12223 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BackpackArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BackpackArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Bacon.v0.json b/Projects/UOContent/Migrations/Server.Items.Bacon.v0.json new file mode 100644 index 000000000..c0dc41202 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Bacon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Bacon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Bag.v0.json b/Projects/UOContent/Migrations/Server.Items.Bag.v0.json new file mode 100644 index 000000000..10c6b3e4d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Bag.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Bag" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Banana.v0.json b/Projects/UOContent/Migrations/Server.Items.Banana.v0.json new file mode 100644 index 000000000..2e0f8209b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Banana.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Banana" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Bananas.v0.json b/Projects/UOContent/Migrations/Server.Items.Bananas.v0.json new file mode 100644 index 000000000..614b49ed1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Bananas.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Bananas" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BarkeepContract.v0.json b/Projects/UOContent/Migrations/Server.Items.BarkeepContract.v0.json new file mode 100644 index 000000000..76a2853cc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BarkeepContract.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BarkeepContract" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Barrel.v0.json b/Projects/UOContent/Migrations/Server.Items.Barrel.v0.json new file mode 100644 index 000000000..b15228a5a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Barrel.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Barrel" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json index c81b88b8f..8ce625193 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json @@ -8,7 +8,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -17,7 +17,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -197,7 +197,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -210,4 +210,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v9.json b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v9.json new file mode 100644 index 000000000..2ea3ab105 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v9.json @@ -0,0 +1,216 @@ +{ + "version": 9, + "type": "Server.Items.BaseArmor", + "properties": [ + { + "name": "Attributes", + "type": "Server.AosAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "ArmorAttributes", + "type": "Server.AosArmorAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "PhysicalBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "FireBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "ColdBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "PoisonBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "EnergyBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "Identified", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxHitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "HitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "Crafter", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Quality", + "type": "Server.Items.ArmorQuality", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Durability", + "type": "Server.Items.ArmorDurabilityLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "ProtectionLevel", + "type": "Server.Items.ArmorProtectionLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "RawResource", + "type": "Server.Items.CraftResource", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "BaseArmorRating", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "StrBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "DexBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "IntBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "StrRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "DexRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "IntRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "MeditationAllowance", + "type": "Server.Items.ArmorMeditationAllowance", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "PlayerConstructed", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseBagBall.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseBagBall.v0.json new file mode 100644 index 000000000..296084218 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseBagBall.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseBagBall" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json new file mode 100644 index 000000000..d47ccdf8d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json @@ -0,0 +1,32 @@ +{ + "version": 2, + "type": "Server.Items.BaseBeverage", + "properties": [ + { + "name": "Poison", + "type": "Server.Poison", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Poison" + ] + }, + { + "name": "Poisoner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "RawContent", + "type": "Server.Items.BeverageType", + "rule": "EnumMigrationRule" + }, + { + "name": "RawQuantity", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseBoard.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseBoard.v2.json new file mode 100644 index 000000000..75c01eb01 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseBoard.v2.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "type": "Server.Items.BaseBoard", + "properties": [ + { + "name": "Level", + "type": "Server.Multis.SecureLevel", + "rule": "EnumMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseBracelet.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseBracelet.v0.json new file mode 100644 index 000000000..d7ac49c34 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseBracelet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseBracelet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json index c59f0615f..ed16ed9f3 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json @@ -14,7 +14,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -23,7 +23,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -32,7 +32,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -41,7 +41,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] }, { @@ -93,4 +93,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v7.json b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v7.json new file mode 100644 index 000000000..0ea558c62 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v7.json @@ -0,0 +1,99 @@ +{ + "version": 7, + "type": "Server.Items.BaseClothing", + "properties": [ + { + "name": "RawResource", + "type": "Server.Items.CraftResource", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Attributes", + "type": "Server.AosAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "ClothingAttributes", + "type": "Server.AosArmorAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Resistances", + "type": "Server.AosElementAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "MaxHitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "HitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "PlayerConstructed", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Crafter", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Quality", + "type": "Server.Items.ClothingQuality", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "StrRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseDecorationArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseDecorationArtifact.v0.json new file mode 100644 index 000000000..3bd499052 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseDecorationArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseDecorationArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseDecorationContainerArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseDecorationContainerArtifact.v0.json new file mode 100644 index 000000000..9718a93a0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseDecorationContainerArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseDecorationContainerArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseEarrings.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseEarrings.v0.json new file mode 100644 index 000000000..0b065bf39 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseEarrings.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseEarrings" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseEquipableLight.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseEquipableLight.v0.json new file mode 100644 index 000000000..83e2e509b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseEquipableLight.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseEquipableLight" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseJewel.v4.json b/Projects/UOContent/Migrations/Server.Items.BaseJewel.v4.json new file mode 100644 index 000000000..6573d187d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseJewel.v4.json @@ -0,0 +1,56 @@ +{ + "version": 4, + "type": "Server.Items.BaseJewel", + "properties": [ + { + "name": "MaxHitPoints", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "HitPoints", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "RawResource", + "type": "Server.Items.CraftResource", + "rule": "EnumMigrationRule" + }, + { + "name": "GemType", + "type": "Server.Items.GemType", + "rule": "EnumMigrationRule" + }, + { + "name": "Attributes", + "type": "Server.AosAttributes", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Resistances", + "type": "Server.AosElementAttributes", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseLight.v1.json b/Projects/UOContent/Migrations/Server.Items.BaseLight.v1.json new file mode 100644 index 000000000..ca43118d1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseLight.v1.json @@ -0,0 +1,43 @@ +{ + "version": 1, + "type": "Server.Items.BaseLight", + "properties": [ + { + "name": "BurntOut", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Burning", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Duration", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "Protected", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "BurnTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@TimerDrift" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseNecklace.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseNecklace.v0.json new file mode 100644 index 000000000..d2fbc8ad8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseNecklace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseNecklace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BasePiece.v0.json b/Projects/UOContent/Migrations/Server.Items.BasePiece.v0.json new file mode 100644 index 000000000..25ebbe0f5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BasePiece.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.BasePiece", + "properties": [ + { + "name": "Board", + "type": "Server.Items.BaseBoard", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseRing.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseRing.v0.json new file mode 100644 index 000000000..e44e47d14 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseRing.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseRing" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseThrown.v0.json b/Projects/UOContent/Migrations/Server.Items.BaseThrown.v0.json new file mode 100644 index 000000000..e705c23b9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseThrown.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BaseThrown" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseTreasureChest.v1.json b/Projects/UOContent/Migrations/Server.Items.BaseTreasureChest.v1.json new file mode 100644 index 000000000..9fb711a6b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseTreasureChest.v1.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "type": "Server.Items.BaseTreasureChest", + "properties": [ + { + "name": "Level", + "type": "Server.Items.BaseTreasureChest.TreasureLevel", + "rule": "EnumMigrationRule" + }, + { + "name": "MinSpawnTime", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "MaxSpawnTime", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket.v0.json new file mode 100644 index 000000000..537b71d86 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket1Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket1Artifact.v0.json new file mode 100644 index 000000000..c614628cb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket1Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket1Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket2Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket2Artifact.v0.json new file mode 100644 index 000000000..1a73e0f16 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket2Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket2Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket3NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket3NorthArtifact.v0.json new file mode 100644 index 000000000..6175f23bf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket3NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket3NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket3WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket3WestArtifact.v0.json new file mode 100644 index 000000000..052ec8b0b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket3WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket3WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket4Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket4Artifact.v0.json new file mode 100644 index 000000000..0d9d1f04f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket4Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket4Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket5NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket5NorthArtifact.v0.json new file mode 100644 index 000000000..5c846cbee --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket5NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket5NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket5WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket5WestArtifact.v0.json new file mode 100644 index 000000000..8471cfd4e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket5WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket5WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Basket6Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Basket6Artifact.v0.json new file mode 100644 index 000000000..7efe70742 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Basket6Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Basket6Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Beads.v0.json b/Projects/UOContent/Migrations/Server.Items.Beads.v0.json new file mode 100644 index 000000000..ecb966df9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Beads.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Beads" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json b/Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json new file mode 100644 index 000000000..9e0e46769 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BentoBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json b/Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json new file mode 100644 index 000000000..7075dfde9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BeverageBottle" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BloodyWaterArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BloodyWaterArtifact.v0.json new file mode 100644 index 000000000..62768f5c6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BloodyWaterArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BloodyWaterArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BooksFaceDownArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BooksFaceDownArtifact.v0.json new file mode 100644 index 000000000..aeef57c93 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BooksFaceDownArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BooksFaceDownArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BooksNorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BooksNorthArtifact.v0.json new file mode 100644 index 000000000..75edbd1c0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BooksNorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BooksNorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BooksWestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BooksWestArtifact.v0.json new file mode 100644 index 000000000..1161373b9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BooksWestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BooksWestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Boomerang.v0.json b/Projects/UOContent/Migrations/Server.Items.Boomerang.v0.json new file mode 100644 index 000000000..564180798 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Boomerang.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Boomerang" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BottleArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BottleArtifact.v0.json new file mode 100644 index 000000000..4bcae0807 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BottleArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BottleArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BowlArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BowlArtifact.v0.json new file mode 100644 index 000000000..b3b120fd2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BowlArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BowlArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BowlFlour.v0.json b/Projects/UOContent/Migrations/Server.Items.BowlFlour.v0.json new file mode 100644 index 000000000..3113a0733 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BowlFlour.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BowlFlour" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BowlsHorizontalArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BowlsHorizontalArtifact.v0.json new file mode 100644 index 000000000..7f9a1bb96 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BowlsHorizontalArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BowlsHorizontalArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BowlsVerticalArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BowlsVerticalArtifact.v0.json new file mode 100644 index 000000000..cb9e76c8b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BowlsVerticalArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BowlsVerticalArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BraceletOfHealth.v0.json b/Projects/UOContent/Migrations/Server.Items.BraceletOfHealth.v0.json new file mode 100644 index 000000000..88c85671f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BraceletOfHealth.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BraceletOfHealth" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Brazier.v0.json b/Projects/UOContent/Migrations/Server.Items.Brazier.v0.json new file mode 100644 index 000000000..e241f90d9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Brazier.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Brazier" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BrazierArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.BrazierArtifact.v0.json new file mode 100644 index 000000000..7601a5c28 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BrazierArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BrazierArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BrazierTall.v0.json b/Projects/UOContent/Migrations/Server.Items.BrazierTall.v0.json new file mode 100644 index 000000000..f89dcfcdf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BrazierTall.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BrazierTall" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BreadLoaf.v0.json b/Projects/UOContent/Migrations/Server.Items.BreadLoaf.v0.json new file mode 100644 index 000000000..486f50047 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BreadLoaf.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BreadLoaf" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BrightlyColoredEggs.v0.json b/Projects/UOContent/Migrations/Server.Items.BrightlyColoredEggs.v0.json new file mode 100644 index 000000000..41968f6bd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BrightlyColoredEggs.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BrightlyColoredEggs" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Cabbage.v0.json b/Projects/UOContent/Migrations/Server.Items.Cabbage.v0.json new file mode 100644 index 000000000..323d3658e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Cabbage.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Cabbage" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Cake.v0.json b/Projects/UOContent/Migrations/Server.Items.Cake.v0.json new file mode 100644 index 000000000..f9a5fc948 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Cake.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Cake" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CakeMix.v0.json b/Projects/UOContent/Migrations/Server.Items.CakeMix.v0.json new file mode 100644 index 000000000..bbf0e5c55 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CakeMix.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CakeMix" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Candelabra.v0.json b/Projects/UOContent/Migrations/Server.Items.Candelabra.v0.json new file mode 100644 index 000000000..8c30f6c7f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Candelabra.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.Items.Candelabra", + "properties": [ + { + "name": "IsShipwreckedItem", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CandelabraStand.v0.json b/Projects/UOContent/Migrations/Server.Items.CandelabraStand.v0.json new file mode 100644 index 000000000..f66380218 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CandelabraStand.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CandelabraStand" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Candle.v0.json b/Projects/UOContent/Migrations/Server.Items.Candle.v0.json new file mode 100644 index 000000000..fa8c3c662 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Candle.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Candle" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CandleLarge.v0.json b/Projects/UOContent/Migrations/Server.Items.CandleLarge.v0.json new file mode 100644 index 000000000..4a2b3da8d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CandleLarge.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CandleLarge" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CandleLong.v0.json b/Projects/UOContent/Migrations/Server.Items.CandleLong.v0.json new file mode 100644 index 000000000..2bf422d2e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CandleLong.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CandleLong" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CandleShort.v0.json b/Projects/UOContent/Migrations/Server.Items.CandleShort.v0.json new file mode 100644 index 000000000..9e7c7864c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CandleShort.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CandleShort" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CandleSkull.v0.json b/Projects/UOContent/Migrations/Server.Items.CandleSkull.v0.json new file mode 100644 index 000000000..28174b43d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CandleSkull.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CandleSkull" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Cantaloupe.v0.json b/Projects/UOContent/Migrations/Server.Items.Cantaloupe.v0.json new file mode 100644 index 000000000..5c61cf30e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Cantaloupe.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Cantaloupe" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Carrot.v0.json b/Projects/UOContent/Migrations/Server.Items.Carrot.v0.json new file mode 100644 index 000000000..d07a22b5b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Carrot.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Carrot" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json b/Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json new file mode 100644 index 000000000..b83ab8bad --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CeramicMug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CheckerBoard.v0.json b/Projects/UOContent/Migrations/Server.Items.CheckerBoard.v0.json new file mode 100644 index 000000000..be1070419 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CheckerBoard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CheckerBoard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CheesePizza.v0.json b/Projects/UOContent/Migrations/Server.Items.CheesePizza.v0.json new file mode 100644 index 000000000..cb9acacf2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CheesePizza.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CheesePizza" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CheeseSlice.v0.json b/Projects/UOContent/Migrations/Server.Items.CheeseSlice.v0.json new file mode 100644 index 000000000..21e32b26c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CheeseSlice.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CheeseSlice" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CheeseWedge.v0.json b/Projects/UOContent/Migrations/Server.Items.CheeseWedge.v0.json new file mode 100644 index 000000000..9cc68c9e1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CheeseWedge.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CheeseWedge" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CheeseWheel.v0.json b/Projects/UOContent/Migrations/Server.Items.CheeseWheel.v0.json new file mode 100644 index 000000000..2e3eb9711 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CheeseWheel.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CheeseWheel" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CherryArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.CherryArmoire.v0.json new file mode 100644 index 000000000..b554f6ad5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CherryArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CherryArmoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Chessboard.v0.json b/Projects/UOContent/Migrations/Server.Items.Chessboard.v0.json new file mode 100644 index 000000000..3956ffa9e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Chessboard.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Chessboard" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ChickenLeg.v0.json b/Projects/UOContent/Migrations/Server.Items.ChickenLeg.v0.json new file mode 100644 index 000000000..439dabe7c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ChickenLeg.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ChickenLeg" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Citrine.v0.json b/Projects/UOContent/Migrations/Server.Items.Citrine.v0.json new file mode 100644 index 000000000..8afa812e6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Citrine.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Citrine" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ClothingBlessDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.ClothingBlessDeed.v0.json new file mode 100644 index 000000000..4e081598a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ClothingBlessDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ClothingBlessDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CocoaButter.v0.json b/Projects/UOContent/Migrations/Server.Items.CocoaButter.v0.json new file mode 100644 index 000000000..b1c97f5cc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CocoaButter.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CocoaButter" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CocoaLiquor.v0.json b/Projects/UOContent/Migrations/Server.Items.CocoaLiquor.v0.json new file mode 100644 index 000000000..3e4c2ef1f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CocoaLiquor.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CocoaLiquor" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CocoaPulp.v0.json b/Projects/UOContent/Migrations/Server.Items.CocoaPulp.v0.json new file mode 100644 index 000000000..546e3c7d8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CocoaPulp.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CocoaPulp" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Coconut.v0.json b/Projects/UOContent/Migrations/Server.Items.Coconut.v0.json new file mode 100644 index 000000000..6cbf6ca7b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Coconut.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Coconut" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CocoonArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.CocoonArtifact.v0.json new file mode 100644 index 000000000..493941395 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CocoonArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CocoonArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CommodityDeed.v1.json b/Projects/UOContent/Migrations/Server.Items.CommodityDeed.v1.json new file mode 100644 index 000000000..57487eb5d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CommodityDeed.v1.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "type": "Server.Items.CommodityDeed", + "properties": [ + { + "name": "Commodity", + "type": "Server.Item", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CookableFood.v0.json b/Projects/UOContent/Migrations/Server.Items.CookableFood.v0.json new file mode 100644 index 000000000..907780a22 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CookableFood.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.Items.CookableFood", + "properties": [ + { + "name": "CookingLevel", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CookedBird.v0.json b/Projects/UOContent/Migrations/Server.Items.CookedBird.v0.json new file mode 100644 index 000000000..2ed0f1a62 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CookedBird.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CookedBird" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CookieMix.v0.json b/Projects/UOContent/Migrations/Server.Items.CookieMix.v0.json new file mode 100644 index 000000000..6c1840ceb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CookieMix.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CookieMix" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Cookies.v0.json b/Projects/UOContent/Migrations/Server.Items.Cookies.v0.json new file mode 100644 index 000000000..f333fa730 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Cookies.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Cookies" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CreatureBackpack.v0.json b/Projects/UOContent/Migrations/Server.Items.CreatureBackpack.v0.json new file mode 100644 index 000000000..6a28e8198 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CreatureBackpack.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CreatureBackpack" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CupsArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.CupsArtifact.v0.json new file mode 100644 index 000000000..c8b9779c4 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CupsArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CupsArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Cyclone.v0.json b/Projects/UOContent/Migrations/Server.Items.Cyclone.v0.json new file mode 100644 index 000000000..00615eced --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Cyclone.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Cyclone" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DamagedBooksArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.DamagedBooksArtifact.v0.json new file mode 100644 index 000000000..23c93bc34 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DamagedBooksArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DamagedBooksArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DarkChocolate.v0.json b/Projects/UOContent/Migrations/Server.Items.DarkChocolate.v0.json new file mode 100644 index 000000000..c183912db --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DarkChocolate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DarkChocolate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DarkSource.v0.json b/Projects/UOContent/Migrations/Server.Items.DarkSource.v0.json new file mode 100644 index 000000000..1f66b7fa7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DarkSource.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DarkSource" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Dates.v0.json b/Projects/UOContent/Migrations/Server.Items.Dates.v0.json new file mode 100644 index 000000000..741bf29d1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Dates.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Dates" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Diamond.v0.json b/Projects/UOContent/Migrations/Server.Items.Diamond.v0.json new file mode 100644 index 000000000..16cfb07fe --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Diamond.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Diamond" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Dices.v0.json b/Projects/UOContent/Migrations/Server.Items.Dices.v0.json new file mode 100644 index 000000000..3097cfff5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Dices.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Dices" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DolphinLeftArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.DolphinLeftArtifact.v0.json new file mode 100644 index 000000000..e9a16e0dd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DolphinLeftArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DolphinLeftArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DolphinRightArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.DolphinRightArtifact.v0.json new file mode 100644 index 000000000..65777313d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DolphinRightArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DolphinRightArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Dough.v0.json b/Projects/UOContent/Migrations/Server.Items.Dough.v0.json new file mode 100644 index 000000000..c8b57c124 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Dough.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Dough" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DragonBardingDeed.v2.json b/Projects/UOContent/Migrations/Server.Items.DragonBardingDeed.v2.json new file mode 100644 index 000000000..a6a26d015 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DragonBardingDeed.v2.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "type": "Server.Items.DragonBardingDeed", + "properties": [ + { + "name": "CraftedBy", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Exceptional", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RawResource", + "type": "Server.Items.CraftResource", + "rule": "EnumMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Drawer.v0.json b/Projects/UOContent/Migrations/Server.Items.Drawer.v0.json new file mode 100644 index 000000000..1fcf3a71f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Drawer.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Drawer" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EarOfCorn.v0.json b/Projects/UOContent/Migrations/Server.Items.EarOfCorn.v0.json new file mode 100644 index 000000000..90b01e2a8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EarOfCorn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EarOfCorn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EasterEggs.v0.json b/Projects/UOContent/Migrations/Server.Items.EasterEggs.v0.json new file mode 100644 index 000000000..72b6c24ce --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EasterEggs.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EasterEggs" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EggCaseArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.EggCaseArtifact.v0.json new file mode 100644 index 000000000..259baf617 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EggCaseArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EggCaseArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Eggs.v0.json b/Projects/UOContent/Migrations/Server.Items.Eggs.v0.json new file mode 100644 index 000000000..f966b68f1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Eggs.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Eggs" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Eggshells.v0.json b/Projects/UOContent/Migrations/Server.Items.Eggshells.v0.json new file mode 100644 index 000000000..511f677ff --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Eggshells.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Eggshells" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElegantArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.ElegantArmoire.v0.json new file mode 100644 index 000000000..4f2625986 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElegantArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElegantArmoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenGlasses.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenGlasses.v0.json index e6524c997..8a640dc28 100644 --- a/Projects/UOContent/Migrations/Server.Items.ElvenGlasses.v0.json +++ b/Projects/UOContent/Migrations/Server.Items.ElvenGlasses.v0.json @@ -8,7 +8,7 @@ "usesSaveFlag": true, "rule": "RawSerializableMigrationRule", "ruleArguments": [ - "" + "DeserializationRequiresParent" ] } ] diff --git a/Projects/UOContent/Migrations/Server.Items.Emerald.v0.json b/Projects/UOContent/Migrations/Server.Items.Emerald.v0.json new file mode 100644 index 000000000..84c2f40e9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Emerald.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Emerald" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json new file mode 100644 index 000000000..334506c92 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyBentoBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyBookcase.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyBookcase.v0.json new file mode 100644 index 000000000..f5e4c51b8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyBookcase.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyBookcase" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyPewterBowl.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyPewterBowl.v0.json new file mode 100644 index 000000000..2d1cb7d46 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyPewterBowl.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyPewterBowl" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyPewterTub.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyPewterTub.v0.json new file mode 100644 index 000000000..9c2461867 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyPewterTub.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyPewterTub" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyWoodenBowl.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyWoodenBowl.v0.json new file mode 100644 index 000000000..9942586dd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyWoodenBowl.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyWoodenBowl" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyWoodenTub.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyWoodenTub.v0.json new file mode 100644 index 000000000..e9147d03c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyWoodenTub.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyWoodenTub" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EssenceOfBattle.v0.json b/Projects/UOContent/Migrations/Server.Items.EssenceOfBattle.v0.json new file mode 100644 index 000000000..a5a69e5be --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EssenceOfBattle.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EssenceOfBattle" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FanNorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.FanNorthArtifact.v0.json new file mode 100644 index 000000000..2b44def64 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FanNorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FanNorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FanWestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.FanWestArtifact.v0.json new file mode 100644 index 000000000..156ece978 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FanWestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FanWestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FancyArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.FancyArmoire.v0.json new file mode 100644 index 000000000..b69db6c19 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FancyArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FancyArmoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FancyDrawer.v0.json b/Projects/UOContent/Migrations/Server.Items.FancyDrawer.v0.json new file mode 100644 index 000000000..40cde68fe --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FancyDrawer.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FancyDrawer" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableCabbage.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableCabbage.v0.json new file mode 100644 index 000000000..8776b3e00 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableCabbage.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableCabbage" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableCarrot.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableCarrot.v0.json new file mode 100644 index 000000000..77b6c7539 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableCarrot.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableCarrot" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableCotton.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableCotton.v0.json new file mode 100644 index 000000000..7f44351fa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableCotton.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableCotton" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableCrop.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableCrop.v0.json new file mode 100644 index 000000000..72bd19707 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableCrop.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.Items.FarmableCrop", + "properties": [ + { + "name": "Picked", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableFlax.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableFlax.v0.json new file mode 100644 index 000000000..738e26776 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableFlax.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableFlax" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableLettuce.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableLettuce.v0.json new file mode 100644 index 000000000..7377cd821 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableLettuce.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableLettuce" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableOnion.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableOnion.v0.json new file mode 100644 index 000000000..9e83694b8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableOnion.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableOnion" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmablePumpkin.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmablePumpkin.v0.json new file mode 100644 index 000000000..5a3efdfb0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmablePumpkin.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmablePumpkin" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableTurnip.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableTurnip.v0.json new file mode 100644 index 000000000..476ee5fec --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableTurnip.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableTurnip" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FarmableWheat.v0.json b/Projects/UOContent/Migrations/Server.Items.FarmableWheat.v0.json new file mode 100644 index 000000000..c91c597d5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FarmableWheat.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FarmableWheat" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableBarrel.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableBarrel.v0.json new file mode 100644 index 000000000..755fca3f0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableBarrel.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableBarrel" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableContainer.v2.json b/Projects/UOContent/Migrations/Server.Items.FillableContainer.v2.json new file mode 100644 index 000000000..bb0c8c156 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableContainer.v2.json @@ -0,0 +1,19 @@ +{ + "version": 2, + "type": "Server.Items.FillableContainer", + "properties": [ + { + "name": "RawContentType", + "type": "Server.Items.FillableContentType", + "rule": "EnumMigrationRule" + }, + { + "name": "RespawnTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@TimerDrift" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableLargeCrate.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableLargeCrate.v0.json new file mode 100644 index 000000000..55a56b0dc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableLargeCrate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableLargeCrate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableMetalBox.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableMetalBox.v0.json new file mode 100644 index 000000000..7a1fc83d4 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableMetalBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableMetalBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableMetalChest.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableMetalChest.v0.json new file mode 100644 index 000000000..43282f2ba --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableMetalChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableMetalChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableMetalGoldenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableMetalGoldenChest.v0.json new file mode 100644 index 000000000..c96422f5f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableMetalGoldenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableMetalGoldenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableSmallCrate.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableSmallCrate.v0.json new file mode 100644 index 000000000..ad9907a95 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableSmallCrate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableSmallCrate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableWoodenBox.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableWoodenBox.v0.json new file mode 100644 index 000000000..c81a4abf8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableWoodenBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableWoodenBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableWoodenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.FillableWoodenChest.v0.json new file mode 100644 index 000000000..ec2d74b8d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableWoodenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FillableWoodenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FinishedWoodenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.FinishedWoodenChest.v0.json new file mode 100644 index 000000000..e2242c3b7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FinishedWoodenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FinishedWoodenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FishSteak.v0.json b/Projects/UOContent/Migrations/Server.Items.FishSteak.v0.json new file mode 100644 index 000000000..a2cedd4a2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FishSteak.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FishSteak" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FlowersArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.FlowersArtifact.v0.json new file mode 100644 index 000000000..c9527ebe3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FlowersArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FlowersArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Food.v0.json b/Projects/UOContent/Migrations/Server.Items.Food.v0.json new file mode 100644 index 000000000..aa86b5b37 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Food.v0.json @@ -0,0 +1,27 @@ +{ + "version": 0, + "type": "Server.Items.Food", + "properties": [ + { + "name": "Poisoner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Poison", + "type": "Server.Poison", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Poison" + ] + }, + { + "name": "FillFactor", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FrenchBread.v0.json b/Projects/UOContent/Migrations/Server.Items.FrenchBread.v0.json new file mode 100644 index 000000000..cb57f7afc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FrenchBread.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FrenchBread" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FriedEggs.v0.json b/Projects/UOContent/Migrations/Server.Items.FriedEggs.v0.json new file mode 100644 index 000000000..aa8d9edbc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FriedEggs.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FriedEggs" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FruitBasket.v0.json b/Projects/UOContent/Migrations/Server.Items.FruitBasket.v0.json new file mode 100644 index 000000000..84b1609dc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FruitBasket.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FruitBasket" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FruitPie.v0.json b/Projects/UOContent/Migrations/Server.Items.FruitPie.v0.json new file mode 100644 index 000000000..75f15878a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FruitPie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FruitPie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FullBookcase.v0.json b/Projects/UOContent/Migrations/Server.Items.FullBookcase.v0.json new file mode 100644 index 000000000..c3d2eb68d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FullBookcase.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FullBookcase" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GildedWoodenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.GildedWoodenChest.v0.json new file mode 100644 index 000000000..fe2ba5fe0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GildedWoodenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GildedWoodenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Glass.v0.json b/Projects/UOContent/Migrations/Server.Items.Glass.v0.json new file mode 100644 index 000000000..a201a73f1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Glass.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Glass" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GlassBottle.v0.json b/Projects/UOContent/Migrations/Server.Items.GlassBottle.v0.json new file mode 100644 index 000000000..f9ae5cd0b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GlassBottle.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GlassBottle" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json b/Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json new file mode 100644 index 000000000..31a38da4a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GlassMug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Goblet.v0.json b/Projects/UOContent/Migrations/Server.Items.Goblet.v0.json new file mode 100644 index 000000000..86789c133 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Goblet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Goblet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GoldBeadNecklace.v0.json b/Projects/UOContent/Migrations/Server.Items.GoldBeadNecklace.v0.json new file mode 100644 index 000000000..9e9ccb739 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GoldBeadNecklace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GoldBeadNecklace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GoldBracelet.v0.json b/Projects/UOContent/Migrations/Server.Items.GoldBracelet.v0.json new file mode 100644 index 000000000..a4039aaeb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GoldBracelet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GoldBracelet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GoldEarrings.v0.json b/Projects/UOContent/Migrations/Server.Items.GoldEarrings.v0.json new file mode 100644 index 000000000..57d9f6e78 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GoldEarrings.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GoldEarrings" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GoldNecklace.v0.json b/Projects/UOContent/Migrations/Server.Items.GoldNecklace.v0.json new file mode 100644 index 000000000..0ae334871 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GoldNecklace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GoldNecklace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GoldRing.v0.json b/Projects/UOContent/Migrations/Server.Items.GoldRing.v0.json new file mode 100644 index 000000000..06714ef02 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GoldRing.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GoldRing" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Grapes.v0.json b/Projects/UOContent/Migrations/Server.Items.Grapes.v0.json new file mode 100644 index 000000000..76923e20b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Grapes.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Grapes" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GreenGourd.v0.json b/Projects/UOContent/Migrations/Server.Items.GreenGourd.v0.json new file mode 100644 index 000000000..cece83700 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GreenGourd.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GreenGourd" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json b/Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json new file mode 100644 index 000000000..ca9179116 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GreenTea" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json b/Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json new file mode 100644 index 000000000..09ae11fea --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GreenTeaBasket" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GruesomeStandardArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.GruesomeStandardArtifact.v0.json new file mode 100644 index 000000000..45dfb8bce --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GruesomeStandardArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GruesomeStandardArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GuildDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.GuildDeed.v0.json new file mode 100644 index 000000000..eac6a29ae --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GuildDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GuildDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GuildTeleporter.v0.json b/Projects/UOContent/Migrations/Server.Items.GuildTeleporter.v0.json new file mode 100644 index 000000000..f30d94e3a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GuildTeleporter.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.GuildTeleporter", + "properties": [ + { + "name": "Stone", + "type": "Server.Item", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Guildstone.v4.json b/Projects/UOContent/Migrations/Server.Items.Guildstone.v4.json new file mode 100644 index 000000000..13c23724f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Guildstone.v4.json @@ -0,0 +1,27 @@ +{ + "version": 4, + "type": "Server.Items.Guildstone", + "properties": [ + { + "name": "GuildName", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "GuildAbbrev", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Guild", + "type": "Server.Guilds.Guild", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GuildstoneDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.GuildstoneDeed.v0.json new file mode 100644 index 000000000..70f822f09 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GuildstoneDeed.v0.json @@ -0,0 +1,27 @@ +{ + "version": 0, + "type": "Server.Items.GuildstoneDeed", + "properties": [ + { + "name": "GuildName", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "GuildAbbrev", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Guild", + "type": "Server.Guilds.Guild", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.HairRestylingDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.HairRestylingDeed.v0.json new file mode 100644 index 000000000..8db04fb3d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.HairRestylingDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.HairRestylingDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Ham.v0.json b/Projects/UOContent/Migrations/Server.Items.Ham.v0.json new file mode 100644 index 000000000..b5f398a4e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Ham.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Ham" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.HangingLantern.v0.json b/Projects/UOContent/Migrations/Server.Items.HangingLantern.v0.json new file mode 100644 index 000000000..e356f97fb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.HangingLantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.HangingLantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.HeatingStand.v0.json b/Projects/UOContent/Migrations/Server.Items.HeatingStand.v0.json new file mode 100644 index 000000000..256c5e950 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.HeatingStand.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.HeatingStand" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.HolidayTreeDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.HolidayTreeDeed.v0.json new file mode 100644 index 000000000..8263aa14e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.HolidayTreeDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.HolidayTreeDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.HoneydewMelon.v0.json b/Projects/UOContent/Migrations/Server.Items.HoneydewMelon.v0.json new file mode 100644 index 000000000..337082e0f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.HoneydewMelon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.HoneydewMelon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.JarHoney.v0.json b/Projects/UOContent/Migrations/Server.Items.JarHoney.v0.json new file mode 100644 index 000000000..9dc72d1ed --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.JarHoney.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.JarHoney" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Jug.v0.json b/Projects/UOContent/Migrations/Server.Items.Jug.v0.json new file mode 100644 index 000000000..11aa8c3f9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Jug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Jug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Keg.v0.json b/Projects/UOContent/Migrations/Server.Items.Keg.v0.json new file mode 100644 index 000000000..7a00e229a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Keg.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Keg" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LambLeg.v0.json b/Projects/UOContent/Migrations/Server.Items.LambLeg.v0.json new file mode 100644 index 000000000..0b14762e0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LambLeg.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LambLeg" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LampPost1.v0.json b/Projects/UOContent/Migrations/Server.Items.LampPost1.v0.json new file mode 100644 index 000000000..a71ae5f05 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LampPost1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LampPost1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LampPost2.v0.json b/Projects/UOContent/Migrations/Server.Items.LampPost2.v0.json new file mode 100644 index 000000000..3c6a06aa3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LampPost2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LampPost2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LampPost3.v0.json b/Projects/UOContent/Migrations/Server.Items.LampPost3.v0.json new file mode 100644 index 000000000..f31c4e975 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LampPost3.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LampPost3" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LampPostArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.LampPostArtifact.v0.json new file mode 100644 index 000000000..2b731c1f5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LampPostArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LampPostArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Lantern.v0.json b/Projects/UOContent/Migrations/Server.Items.Lantern.v0.json new file mode 100644 index 000000000..98dd8da0b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Lantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Lantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LanternOfSouls.v0.json b/Projects/UOContent/Migrations/Server.Items.LanternOfSouls.v0.json new file mode 100644 index 000000000..aeede5b0e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LanternOfSouls.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LanternOfSouls" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LargeBagBall.v0.json b/Projects/UOContent/Migrations/Server.Items.LargeBagBall.v0.json new file mode 100644 index 000000000..24dcc0b7b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LargeBagBall.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LargeBagBall" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LargeCrate.v0.json b/Projects/UOContent/Migrations/Server.Items.LargeCrate.v0.json new file mode 100644 index 000000000..c3d152a8d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LargeCrate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LargeCrate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LeatherTunicArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.LeatherTunicArtifact.v0.json new file mode 100644 index 000000000..fd37349a1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LeatherTunicArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LeatherTunicArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Lemon.v0.json b/Projects/UOContent/Migrations/Server.Items.Lemon.v0.json new file mode 100644 index 000000000..00ec5e31a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Lemon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Lemon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Lemons.v0.json b/Projects/UOContent/Migrations/Server.Items.Lemons.v0.json new file mode 100644 index 000000000..8feb21f2e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Lemons.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Lemons" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Lettuce.v0.json b/Projects/UOContent/Migrations/Server.Items.Lettuce.v0.json new file mode 100644 index 000000000..6bccdabeb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Lettuce.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Lettuce" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LibraryBookcase.v0.json b/Projects/UOContent/Migrations/Server.Items.LibraryBookcase.v0.json new file mode 100644 index 000000000..3c1812b6f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LibraryBookcase.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LibraryBookcase" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LightSource.v0.json b/Projects/UOContent/Migrations/Server.Items.LightSource.v0.json new file mode 100644 index 000000000..dc3adc368 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LightSource.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LightSource" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Lime.v0.json b/Projects/UOContent/Migrations/Server.Items.Lime.v0.json new file mode 100644 index 000000000..a4f943752 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Lime.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Lime" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Limes.v0.json b/Projects/UOContent/Migrations/Server.Items.Limes.v0.json new file mode 100644 index 000000000..a9d505b98 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Limes.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Limes" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LockableContainer.v0.json b/Projects/UOContent/Migrations/Server.Items.LockableContainer.v0.json new file mode 100644 index 000000000..e9d8da768 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LockableContainer.v0.json @@ -0,0 +1,62 @@ +{ + "version": 0, + "type": "Server.Items.LockableContainer", + "properties": [ + { + "name": "IsShipwreckedItem", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TrapOnLockpick", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RequiredSkill", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxLockLevel", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "KeyValue", + "type": "uint", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LockLevel", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RawLocked", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ManStatuetteEastArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.ManStatuetteEastArtifact.v0.json new file mode 100644 index 000000000..62b7bc6ec --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ManStatuetteEastArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ManStatuetteEastArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ManStatuetteSouthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.ManStatuetteSouthArtifact.v0.json new file mode 100644 index 000000000..8d58d643a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ManStatuetteSouthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ManStatuetteSouthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MapleArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.MapleArmoire.v0.json new file mode 100644 index 000000000..2f0d3f4fa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MapleArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MapleArmoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MarkContainer.v0.json b/Projects/UOContent/Migrations/Server.Items.MarkContainer.v0.json new file mode 100644 index 000000000..489250da4 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MarkContainer.v0.json @@ -0,0 +1,46 @@ +{ + "version": 0, + "type": "Server.Items.MarkContainer", + "properties": [ + { + "name": "RawAutoLock", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RelockTimer", + "type": "Server.Items.MarkContainer.InternalTimer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@TimerDrift" + ] + }, + { + "name": "TargetMap", + "type": "Server.Map", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Map" + ] + }, + { + "name": "Target", + "type": "Server.Point3D", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Point3D" + ] + }, + { + "name": "Description", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MeatPie.v0.json b/Projects/UOContent/Migrations/Server.Items.MeatPie.v0.json new file mode 100644 index 000000000..9e1994934 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MeatPie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MeatPie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MediumCrate.v0.json b/Projects/UOContent/Migrations/Server.Items.MediumCrate.v0.json new file mode 100644 index 000000000..1d1cbab84 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MediumCrate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MediumCrate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MetalBox.v0.json b/Projects/UOContent/Migrations/Server.Items.MetalBox.v0.json new file mode 100644 index 000000000..5ccce1a91 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MetalBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MetalBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MetalChest.v0.json b/Projects/UOContent/Migrations/Server.Items.MetalChest.v0.json new file mode 100644 index 000000000..b0f41c3c9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MetalChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MetalChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MetalGoldenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.MetalGoldenChest.v0.json new file mode 100644 index 000000000..3f1d63f0d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MetalGoldenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MetalGoldenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MetalGoldenTreasureChest.v0.json b/Projects/UOContent/Migrations/Server.Items.MetalGoldenTreasureChest.v0.json new file mode 100644 index 000000000..c4e11b5e3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MetalGoldenTreasureChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MetalGoldenTreasureChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MetalTreasureChest.v0.json b/Projects/UOContent/Migrations/Server.Items.MetalTreasureChest.v0.json new file mode 100644 index 000000000..75aa355b8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MetalTreasureChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MetalTreasureChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MilkChocolate.v0.json b/Projects/UOContent/Migrations/Server.Items.MilkChocolate.v0.json new file mode 100644 index 000000000..3358354cf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MilkChocolate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MilkChocolate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json new file mode 100644 index 000000000..4aaa4304a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Muffins.v0.json b/Projects/UOContent/Migrations/Server.Items.Muffins.v0.json new file mode 100644 index 000000000..9c7d8a9ff --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Muffins.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Muffins" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.NameChangeDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.NameChangeDeed.v0.json new file mode 100644 index 000000000..265569945 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.NameChangeDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.NameChangeDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Necklace.v0.json b/Projects/UOContent/Migrations/Server.Items.Necklace.v0.json new file mode 100644 index 000000000..3404812bf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Necklace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Necklace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.NewPlayerTicket.v0.json b/Projects/UOContent/Migrations/Server.Items.NewPlayerTicket.v0.json new file mode 100644 index 000000000..b73d06bb7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.NewPlayerTicket.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.NewPlayerTicket", + "properties": [ + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Onion.v0.json b/Projects/UOContent/Migrations/Server.Items.Onion.v0.json new file mode 100644 index 000000000..b5130b863 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Onion.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Onion" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OpenCoconut.v0.json b/Projects/UOContent/Migrations/Server.Items.OpenCoconut.v0.json new file mode 100644 index 000000000..94cd7d532 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OpenCoconut.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OpenCoconut" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnamentOfTheMagician.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnamentOfTheMagician.v0.json new file mode 100644 index 000000000..e76794585 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnamentOfTheMagician.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnamentOfTheMagician" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnateWoodenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnateWoodenChest.v0.json new file mode 100644 index 000000000..16c2b29d5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnateWoodenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnateWoodenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting1NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting1NorthArtifact.v0.json new file mode 100644 index 000000000..40b3e66da --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting1NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting1NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting1WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting1WestArtifact.v0.json new file mode 100644 index 000000000..b2f21b5f5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting1WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting1WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting2NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting2NorthArtifact.v0.json new file mode 100644 index 000000000..2031ec7bb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting2NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting2NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting2WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting2WestArtifact.v0.json new file mode 100644 index 000000000..e0db930f6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting2WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting2WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting3Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting3Artifact.v0.json new file mode 100644 index 000000000..ff244ec11 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting3Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting3Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting4NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting4NorthArtifact.v0.json new file mode 100644 index 000000000..13d2251dd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting4NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting4NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting4WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting4WestArtifact.v0.json new file mode 100644 index 000000000..22a8a0fe5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting4WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting4WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting5NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting5NorthArtifact.v0.json new file mode 100644 index 000000000..a2261668d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting5NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting5NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting5WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting5WestArtifact.v0.json new file mode 100644 index 000000000..0870e57b3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting5WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting5WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting6NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting6NorthArtifact.v0.json new file mode 100644 index 000000000..78b84c295 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting6NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting6NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Painting6WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Painting6WestArtifact.v0.json new file mode 100644 index 000000000..3dff6badd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Painting6WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Painting6WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PaperLantern.v0.json b/Projects/UOContent/Migrations/Server.Items.PaperLantern.v0.json new file mode 100644 index 000000000..f06c1c07a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PaperLantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PaperLantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ParagonChest.v0.json b/Projects/UOContent/Migrations/Server.Items.ParagonChest.v0.json new file mode 100644 index 000000000..285d0ce98 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ParagonChest.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.Items.ParagonChest", + "properties": [ + { + "name": "Name", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "InternString" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Peach.v0.json b/Projects/UOContent/Migrations/Server.Items.Peach.v0.json new file mode 100644 index 000000000..5d6e4a7ba --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Peach.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Peach" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PeachCobbler.v0.json b/Projects/UOContent/Migrations/Server.Items.PeachCobbler.v0.json new file mode 100644 index 000000000..1263c061f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PeachCobbler.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PeachCobbler" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Pear.v0.json b/Projects/UOContent/Migrations/Server.Items.Pear.v0.json new file mode 100644 index 000000000..38e7e73aa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Pear.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Pear" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PendantOfTheMagi.v0.json b/Projects/UOContent/Migrations/Server.Items.PendantOfTheMagi.v0.json new file mode 100644 index 000000000..cc7a26f04 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PendantOfTheMagi.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PendantOfTheMagi" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterBowlOfCarrots.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfCarrots.v0.json new file mode 100644 index 000000000..f04bcf6c0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfCarrots.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterBowlOfCarrots" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterBowlOfCorn.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfCorn.v0.json new file mode 100644 index 000000000..7d7af90d0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfCorn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterBowlOfCorn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterBowlOfLettuce.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfLettuce.v0.json new file mode 100644 index 000000000..20b28ba7a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfLettuce.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterBowlOfLettuce" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterBowlOfPeas.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfPeas.v0.json new file mode 100644 index 000000000..14a8225ba --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfPeas.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterBowlOfPeas" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterBowlOfPotatos.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfPotatos.v0.json new file mode 100644 index 000000000..fa7f89157 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterBowlOfPotatos.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterBowlOfPotatos" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json new file mode 100644 index 000000000..12010a8c9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterMug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PicnicBasket.v0.json b/Projects/UOContent/Migrations/Server.Items.PicnicBasket.v0.json new file mode 100644 index 000000000..ded401af8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PicnicBasket.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PicnicBasket" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackBishop.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackBishop.v0.json new file mode 100644 index 000000000..5f8df22fe --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackBishop.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackBishop" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackChecker.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackChecker.v0.json new file mode 100644 index 000000000..5179c0e65 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackChecker.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackChecker" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackKing.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackKing.v0.json new file mode 100644 index 000000000..1c595a4ce --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackKing.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackKing" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackKnight.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackKnight.v0.json new file mode 100644 index 000000000..12c0c2b30 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackKnight.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackKnight" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackPawn.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackPawn.v0.json new file mode 100644 index 000000000..e875fddd4 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackPawn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackPawn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackQueen.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackQueen.v0.json new file mode 100644 index 000000000..bbde0e9e7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackQueen.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackQueen" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceBlackRook.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceBlackRook.v0.json new file mode 100644 index 000000000..8f34dceb3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceBlackRook.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceBlackRook" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhiteBishop.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhiteBishop.v0.json new file mode 100644 index 000000000..28de1dca9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhiteBishop.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhiteBishop" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhiteChecker.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhiteChecker.v0.json new file mode 100644 index 000000000..0d0049e99 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhiteChecker.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhiteChecker" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhiteKing.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhiteKing.v0.json new file mode 100644 index 000000000..e26819c8c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhiteKing.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhiteKing" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhiteKnight.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhiteKnight.v0.json new file mode 100644 index 000000000..d550344f1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhiteKnight.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhiteKnight" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhitePawn.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhitePawn.v0.json new file mode 100644 index 000000000..f3921fda1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhitePawn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhitePawn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhiteQueen.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhiteQueen.v0.json new file mode 100644 index 000000000..2e8b7aa57 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhiteQueen.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhiteQueen" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PieceWhiteRook.v0.json b/Projects/UOContent/Migrations/Server.Items.PieceWhiteRook.v0.json new file mode 100644 index 000000000..807f09712 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PieceWhiteRook.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PieceWhiteRook" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json b/Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json new file mode 100644 index 000000000..1b34123cc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Pitcher" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PlainWoodenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.PlainWoodenChest.v0.json new file mode 100644 index 000000000..bdf8b28d1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PlainWoodenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PlainWoodenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Pouch.v0.json b/Projects/UOContent/Migrations/Server.Items.Pouch.v0.json new file mode 100644 index 000000000..7a2cc5ea7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Pouch.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Pouch" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Pumpkin.v0.json b/Projects/UOContent/Migrations/Server.Items.Pumpkin.v0.json new file mode 100644 index 000000000..83ab02724 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Pumpkin.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Pumpkin" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PumpkinPie.v0.json b/Projects/UOContent/Migrations/Server.Items.PumpkinPie.v0.json new file mode 100644 index 000000000..72f3a3155 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PumpkinPie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PumpkinPie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Quiche.v0.json b/Projects/UOContent/Migrations/Server.Items.Quiche.v0.json new file mode 100644 index 000000000..c130ac70b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Quiche.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Quiche" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RawBird.v0.json b/Projects/UOContent/Migrations/Server.Items.RawBird.v0.json new file mode 100644 index 000000000..0475acb12 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RawBird.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RawBird" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RawChickenLeg.v0.json b/Projects/UOContent/Migrations/Server.Items.RawChickenLeg.v0.json new file mode 100644 index 000000000..280168865 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RawChickenLeg.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RawChickenLeg" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RawFishSteak.v0.json b/Projects/UOContent/Migrations/Server.Items.RawFishSteak.v0.json new file mode 100644 index 000000000..375f1cd89 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RawFishSteak.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RawFishSteak" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RawLambLeg.v0.json b/Projects/UOContent/Migrations/Server.Items.RawLambLeg.v0.json new file mode 100644 index 000000000..5b126e759 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RawLambLeg.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RawLambLeg" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RawRibs.v0.json b/Projects/UOContent/Migrations/Server.Items.RawRibs.v0.json new file mode 100644 index 000000000..d622b8be9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RawRibs.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RawRibs" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RedArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.RedArmoire.v0.json new file mode 100644 index 000000000..92d088926 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RedArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RedArmoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RedHangingLantern.v0.json b/Projects/UOContent/Migrations/Server.Items.RedHangingLantern.v0.json new file mode 100644 index 000000000..7e48a84f6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RedHangingLantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RedHangingLantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json new file mode 100644 index 000000000..b1cdf2e03 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RedMisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ResilientBracer.v0.json b/Projects/UOContent/Migrations/Server.Items.ResilientBracer.v0.json new file mode 100644 index 000000000..0e8deb175 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ResilientBracer.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ResilientBracer" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Ribs.v0.json b/Projects/UOContent/Migrations/Server.Items.Ribs.v0.json new file mode 100644 index 000000000..de0182bcf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Ribs.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Ribs" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RingOfTheElements.v0.json b/Projects/UOContent/Migrations/Server.Items.RingOfTheElements.v0.json new file mode 100644 index 000000000..bcc0992cc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RingOfTheElements.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RingOfTheElements" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RingOfTheVile.v0.json b/Projects/UOContent/Migrations/Server.Items.RingOfTheVile.v0.json new file mode 100644 index 000000000..0cc53f9a3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RingOfTheVile.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RingOfTheVile" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RoastPig.v0.json b/Projects/UOContent/Migrations/Server.Items.RoastPig.v0.json new file mode 100644 index 000000000..b7b4d04c2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RoastPig.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RoastPig" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RockArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.RockArtifact.v0.json new file mode 100644 index 000000000..8f58e3f1b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RockArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RockArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RoundPaperLantern.v0.json b/Projects/UOContent/Migrations/Server.Items.RoundPaperLantern.v0.json new file mode 100644 index 000000000..70715fb8f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RoundPaperLantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RoundPaperLantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Ruby.v0.json b/Projects/UOContent/Migrations/Server.Items.Ruby.v0.json new file mode 100644 index 000000000..d98101a7c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Ruby.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Ruby" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RuinedPaintingArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.RuinedPaintingArtifact.v0.json new file mode 100644 index 000000000..0fed9ca14 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RuinedPaintingArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RuinedPaintingArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SackFlour.v0.json b/Projects/UOContent/Migrations/Server.Items.SackFlour.v0.json new file mode 100644 index 000000000..8087f39fc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SackFlour.v0.json @@ -0,0 +1,14 @@ +{ + "version": 0, + "type": "Server.Items.SackFlour", + "properties": [ + { + "name": "Quantity", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SackOfSugar.v0.json b/Projects/UOContent/Migrations/Server.Items.SackOfSugar.v0.json new file mode 100644 index 000000000..8e0a647eb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SackOfSugar.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SackOfSugar" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SaddleArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SaddleArtifact.v0.json new file mode 100644 index 000000000..503b174d1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SaddleArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SaddleArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SakeArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SakeArtifact.v0.json new file mode 100644 index 000000000..9401028a8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SakeArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SakeArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SalvageBag.v0.json b/Projects/UOContent/Migrations/Server.Items.SalvageBag.v0.json new file mode 100644 index 000000000..05f0387ef --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SalvageBag.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SalvageBag" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Sapphire.v0.json b/Projects/UOContent/Migrations/Server.Items.Sapphire.v0.json new file mode 100644 index 000000000..a12b238df --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Sapphire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Sapphire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Sausage.v0.json b/Projects/UOContent/Migrations/Server.Items.Sausage.v0.json new file mode 100644 index 000000000..d803d2258 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Sausage.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Sausage" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SausagePizza.v0.json b/Projects/UOContent/Migrations/Server.Items.SausagePizza.v0.json new file mode 100644 index 000000000..ec0ee27cf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SausagePizza.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SausagePizza" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Sculpture1Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Sculpture1Artifact.v0.json new file mode 100644 index 000000000..b3ba13f74 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Sculpture1Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Sculpture1Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Sculpture2Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Sculpture2Artifact.v0.json new file mode 100644 index 000000000..110a50784 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Sculpture2Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Sculpture2Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SheafOfHay.v0.json b/Projects/UOContent/Migrations/Server.Items.SheafOfHay.v0.json new file mode 100644 index 000000000..d3d763d6c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SheafOfHay.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SheafOfHay" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ShojiLantern.v0.json b/Projects/UOContent/Migrations/Server.Items.ShojiLantern.v0.json new file mode 100644 index 000000000..68d7d19a0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ShojiLantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ShojiLantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ShortCabinet.v0.json b/Projects/UOContent/Migrations/Server.Items.ShortCabinet.v0.json new file mode 100644 index 000000000..04937d754 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ShortCabinet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ShortCabinet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SilverBeadNecklace.v0.json b/Projects/UOContent/Migrations/Server.Items.SilverBeadNecklace.v0.json new file mode 100644 index 000000000..e97248aa8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SilverBeadNecklace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SilverBeadNecklace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SilverBracelet.v0.json b/Projects/UOContent/Migrations/Server.Items.SilverBracelet.v0.json new file mode 100644 index 000000000..a329f03f6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SilverBracelet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SilverBracelet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SilverEarrings.v0.json b/Projects/UOContent/Migrations/Server.Items.SilverEarrings.v0.json new file mode 100644 index 000000000..ee60c9a3b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SilverEarrings.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SilverEarrings" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SilverNecklace.v0.json b/Projects/UOContent/Migrations/Server.Items.SilverNecklace.v0.json new file mode 100644 index 000000000..1be9db682 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SilverNecklace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SilverNecklace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SilverRing.v0.json b/Projects/UOContent/Migrations/Server.Items.SilverRing.v0.json new file mode 100644 index 000000000..2e0338653 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SilverRing.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SilverRing" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SkinnedDeerArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SkinnedDeerArtifact.v0.json new file mode 100644 index 000000000..8cf6586c2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SkinnedDeerArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SkinnedDeerArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SkinnedGoatArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SkinnedGoatArtifact.v0.json new file mode 100644 index 000000000..b110bcab3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SkinnedGoatArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SkinnedGoatArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SkullCandleArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SkullCandleArtifact.v0.json new file mode 100644 index 000000000..440decb75 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SkullCandleArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SkullCandleArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SlabOfBacon.v0.json b/Projects/UOContent/Migrations/Server.Items.SlabOfBacon.v0.json new file mode 100644 index 000000000..347f27ab9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SlabOfBacon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SlabOfBacon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SmallBagBall.v0.json b/Projects/UOContent/Migrations/Server.Items.SmallBagBall.v0.json new file mode 100644 index 000000000..02f023431 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SmallBagBall.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SmallBagBall" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SmallCrate.v0.json b/Projects/UOContent/Migrations/Server.Items.SmallCrate.v0.json new file mode 100644 index 000000000..c911e5111 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SmallCrate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SmallCrate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SmallPumpkin.v0.json b/Projects/UOContent/Migrations/Server.Items.SmallPumpkin.v0.json new file mode 100644 index 000000000..3af9f9d9f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SmallPumpkin.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SmallPumpkin" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SmallWatermelon.v0.json b/Projects/UOContent/Migrations/Server.Items.SmallWatermelon.v0.json new file mode 100644 index 000000000..fb49115bc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SmallWatermelon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SmallWatermelon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SoulGlaive.v0.json b/Projects/UOContent/Migrations/Server.Items.SoulGlaive.v0.json new file mode 100644 index 000000000..325bcded6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SoulGlaive.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SoulGlaive" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningWheelEastAddon.v0.json similarity index 100% rename from Projects/UOContent/Migrations/Server.Items.SpinningwheelEastAddon.v0.json rename to Projects/UOContent/Migrations/Server.Items.SpinningWheelEastAddon.v0.json diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningWheelEastDeed.v0.json similarity index 100% rename from Projects/UOContent/Migrations/Server.Items.SpinningwheelEastDeed.v0.json rename to Projects/UOContent/Migrations/Server.Items.SpinningWheelEastDeed.v0.json diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningWheelSouthAddon.v0.json similarity index 100% rename from Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthAddon.v0.json rename to Projects/UOContent/Migrations/Server.Items.SpinningWheelSouthAddon.v0.json diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningWheelSouthDeed.v0.json similarity index 100% rename from Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthDeed.v0.json rename to Projects/UOContent/Migrations/Server.Items.SpinningWheelSouthDeed.v0.json diff --git a/Projects/UOContent/Migrations/Server.Items.SplitCoconut.v0.json b/Projects/UOContent/Migrations/Server.Items.SplitCoconut.v0.json new file mode 100644 index 000000000..b2997c010 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SplitCoconut.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SplitCoconut" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Squash.v0.json b/Projects/UOContent/Migrations/Server.Items.Squash.v0.json new file mode 100644 index 000000000..0d6182f02 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Squash.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Squash" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StarSapphire.v0.json b/Projects/UOContent/Migrations/Server.Items.StarSapphire.v0.json new file mode 100644 index 000000000..a72f1d8d8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StarSapphire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.StarSapphire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StretchedHideArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.StretchedHideArtifact.v0.json new file mode 100644 index 000000000..5864f95d2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StretchedHideArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.StretchedHideArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StrongBackpack.v0.json b/Projects/UOContent/Migrations/Server.Items.StrongBackpack.v0.json new file mode 100644 index 000000000..2429aa2cc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StrongBackpack.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.StrongBackpack" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StrongBox.v0.json b/Projects/UOContent/Migrations/Server.Items.StrongBox.v0.json new file mode 100644 index 000000000..5949fdd81 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StrongBox.v0.json @@ -0,0 +1,16 @@ +{ + "version": 0, + "type": "Server.Items.StrongBox", + "properties": [ + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "House", + "type": "Server.Multis.BaseHouse", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StuddedLeggingsArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.StuddedLeggingsArtifact.v0.json new file mode 100644 index 000000000..eba0ff0e0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StuddedLeggingsArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.StuddedLeggingsArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StuddedTunicArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.StuddedTunicArtifact.v0.json new file mode 100644 index 000000000..4ed9d4287 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StuddedTunicArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.StuddedTunicArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json b/Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json new file mode 100644 index 000000000..483d32d8e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SushiPlatter" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json b/Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json new file mode 100644 index 000000000..1028b71aa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SushiRolls" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SweetDough.v0.json b/Projects/UOContent/Migrations/Server.Items.SweetDough.v0.json new file mode 100644 index 000000000..cca6c99ca --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SweetDough.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SweetDough" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay1NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay1NorthArtifact.v0.json new file mode 100644 index 000000000..810db7f36 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay1NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay1NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay1WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay1WestArtifact.v0.json new file mode 100644 index 000000000..50cfbc144 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay1WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay1WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay2NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay2NorthArtifact.v0.json new file mode 100644 index 000000000..33aaaad72 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay2NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay2NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay2WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay2WestArtifact.v0.json new file mode 100644 index 000000000..33df5a093 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay2WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay2WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay3EastArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay3EastArtifact.v0.json new file mode 100644 index 000000000..6adc5ea9a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay3EastArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay3EastArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay3SouthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay3SouthArtifact.v0.json new file mode 100644 index 000000000..0be1d44eb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay3SouthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay3SouthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay4NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay4NorthArtifact.v0.json new file mode 100644 index 000000000..6f18cc9f2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay4NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay4NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay4WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay4WestArtifact.v0.json new file mode 100644 index 000000000..d87c9ae60 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay4WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay4WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay5NorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay5NorthArtifact.v0.json new file mode 100644 index 000000000..f7549c1c9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay5NorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay5NorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SwordDisplay5WestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.SwordDisplay5WestArtifact.v0.json new file mode 100644 index 000000000..953b64866 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SwordDisplay5WestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SwordDisplay5WestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TallCabinet.v0.json b/Projects/UOContent/Migrations/Server.Items.TallCabinet.v0.json new file mode 100644 index 000000000..3503ffa6d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TallCabinet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TallCabinet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Talwar.v0.json b/Projects/UOContent/Migrations/Server.Items.Talwar.v0.json deleted file mode 100644 index d69b5f76b..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Talwar.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.Talwar" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TarotCardsArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.TarotCardsArtifact.v0.json new file mode 100644 index 000000000..c35d04652 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TarotCardsArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TarotCardsArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TeapotNorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.TeapotNorthArtifact.v0.json new file mode 100644 index 000000000..0cc360e43 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TeapotNorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TeapotNorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TeapotWestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.TeapotWestArtifact.v0.json new file mode 100644 index 000000000..78e012076 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TeapotWestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TeapotWestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Torch.v0.json b/Projects/UOContent/Migrations/Server.Items.Torch.v0.json new file mode 100644 index 000000000..d1ed7780a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Torch.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Torch" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tourmaline.v0.json b/Projects/UOContent/Migrations/Server.Items.Tourmaline.v0.json new file mode 100644 index 000000000..56572c492 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tourmaline.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Tourmaline" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TowerLanternArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.TowerLanternArtifact.v0.json new file mode 100644 index 000000000..24b052b7f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TowerLanternArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TowerLanternArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TrappableContainer.v3.json b/Projects/UOContent/Migrations/Server.Items.TrappableContainer.v3.json new file mode 100644 index 000000000..83ddf011a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TrappableContainer.v3.json @@ -0,0 +1,27 @@ +{ + "version": 3, + "type": "Server.Items.TrappableContainer", + "properties": [ + { + "name": "TrapLevel", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TrapPower", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TrapType", + "type": "Server.Items.TrapType", + "rule": "EnumMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel1.v0.json b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel1.v0.json new file mode 100644 index 000000000..a02072708 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TreasureChestLevel1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel2.v0.json b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel2.v0.json new file mode 100644 index 000000000..321df3f4d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TreasureChestLevel2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel3.v0.json b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel3.v0.json new file mode 100644 index 000000000..5e279bc26 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel3.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TreasureChestLevel3" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel4.v0.json b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel4.v0.json new file mode 100644 index 000000000..eeb91f0cd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureChestLevel4.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TreasureChestLevel4" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json new file mode 100644 index 000000000..9bf36d71a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json @@ -0,0 +1,55 @@ +{ + "version": 2, + "type": "Server.Items.TreasureMapChest", + "properties": [ + { + "name": "Guardians", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Temporary", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Level", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ExpireTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@TimerDrift" + ] + }, + { + "name": "Lifted", + "type": "System.Collections.Generic.HashSet\u003CServer.Item\u003E", + "rule": "HashSetMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TripleFanNorthArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.TripleFanNorthArtifact.v0.json new file mode 100644 index 000000000..09d293e31 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TripleFanNorthArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TripleFanNorthArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TripleFanWestArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.TripleFanWestArtifact.v0.json new file mode 100644 index 000000000..4bdca34be --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TripleFanWestArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TripleFanWestArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Turnip.v0.json b/Projects/UOContent/Migrations/Server.Items.Turnip.v0.json new file mode 100644 index 000000000..f8f0c302e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Turnip.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Turnip" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UnbakedApplePie.v0.json b/Projects/UOContent/Migrations/Server.Items.UnbakedApplePie.v0.json new file mode 100644 index 000000000..99f050908 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UnbakedApplePie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UnbakedApplePie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UnbakedFruitPie.v0.json b/Projects/UOContent/Migrations/Server.Items.UnbakedFruitPie.v0.json new file mode 100644 index 000000000..54548d26b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UnbakedFruitPie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UnbakedFruitPie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UnbakedMeatPie.v0.json b/Projects/UOContent/Migrations/Server.Items.UnbakedMeatPie.v0.json new file mode 100644 index 000000000..142fd4044 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UnbakedMeatPie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UnbakedMeatPie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UnbakedPeachCobbler.v0.json b/Projects/UOContent/Migrations/Server.Items.UnbakedPeachCobbler.v0.json new file mode 100644 index 000000000..6d2b12e77 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UnbakedPeachCobbler.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UnbakedPeachCobbler" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UnbakedPumpkinPie.v0.json b/Projects/UOContent/Migrations/Server.Items.UnbakedPumpkinPie.v0.json new file mode 100644 index 000000000..2d5ac5cc6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UnbakedPumpkinPie.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UnbakedPumpkinPie" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UnbakedQuiche.v0.json b/Projects/UOContent/Migrations/Server.Items.UnbakedQuiche.v0.json new file mode 100644 index 000000000..71633b3ad --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UnbakedQuiche.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UnbakedQuiche" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UncookedCheesePizza.v0.json b/Projects/UOContent/Migrations/Server.Items.UncookedCheesePizza.v0.json new file mode 100644 index 000000000..37913c49f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UncookedCheesePizza.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UncookedCheesePizza" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.UncookedSausagePizza.v0.json b/Projects/UOContent/Migrations/Server.Items.UncookedSausagePizza.v0.json new file mode 100644 index 000000000..c65c92a89 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.UncookedSausagePizza.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.UncookedSausagePizza" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Urn1Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Urn1Artifact.v0.json new file mode 100644 index 000000000..261d2866a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Urn1Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Urn1Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Urn2Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.Urn2Artifact.v0.json new file mode 100644 index 000000000..723f06743 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Urn2Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Urn2Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Vanilla.v0.json b/Projects/UOContent/Migrations/Server.Items.Vanilla.v0.json new file mode 100644 index 000000000..b8b2b8a7b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Vanilla.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Vanilla" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json b/Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json new file mode 100644 index 000000000..691547581 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.VendorRentalContract.v0.json @@ -0,0 +1,30 @@ +{ + "version": 0, + "type": "Server.Items.VendorRentalContract", + "properties": [ + { + "name": "RentalDurationId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Price", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LandlordRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WallSconce.v0.json b/Projects/UOContent/Migrations/Server.Items.WallSconce.v0.json new file mode 100644 index 000000000..d0283988d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WallSconce.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WallSconce" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WallTorch.v0.json b/Projects/UOContent/Migrations/Server.Items.WallTorch.v0.json new file mode 100644 index 000000000..f552e5e58 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WallTorch.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WallTorch" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json b/Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json new file mode 100644 index 000000000..0f8c5b12a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Wasabi" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json b/Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json new file mode 100644 index 000000000..084999ea5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WasabiClumps" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Watermelon.v0.json b/Projects/UOContent/Migrations/Server.Items.Watermelon.v0.json new file mode 100644 index 000000000..5fed967d8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Watermelon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Watermelon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WheatSheaf.v0.json b/Projects/UOContent/Migrations/Server.Items.WheatSheaf.v0.json new file mode 100644 index 000000000..f989c6934 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WheatSheaf.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WheatSheaf" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WhiteChocolate.v0.json b/Projects/UOContent/Migrations/Server.Items.WhiteChocolate.v0.json new file mode 100644 index 000000000..a59371f31 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WhiteChocolate.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WhiteChocolate" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WhiteHangingLantern.v0.json b/Projects/UOContent/Migrations/Server.Items.WhiteHangingLantern.v0.json new file mode 100644 index 000000000..06213191a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WhiteHangingLantern.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WhiteHangingLantern" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json new file mode 100644 index 000000000..efe1f9e4b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WhiteMisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowl.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowl.v0.json new file mode 100644 index 000000000..09282000b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowl.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowl" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfCarrots.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfCarrots.v0.json new file mode 100644 index 000000000..262bf8b3a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfCarrots.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowlOfCarrots" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfCorn.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfCorn.v0.json new file mode 100644 index 000000000..d28cf0b4e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfCorn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowlOfCorn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfLettuce.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfLettuce.v0.json new file mode 100644 index 000000000..655bc2d84 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfLettuce.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowlOfLettuce" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfPeas.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfPeas.v0.json new file mode 100644 index 000000000..5704fe63c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfPeas.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowlOfPeas" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfStew.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfStew.v0.json new file mode 100644 index 000000000..45b7da55a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfStew.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowlOfStew" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfTomatoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfTomatoSoup.v0.json new file mode 100644 index 000000000..97ce4c1da --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBowlOfTomatoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBowlOfTomatoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBox.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBox.v0.json new file mode 100644 index 000000000..e6441683e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenChest.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenChest.v0.json new file mode 100644 index 000000000..7b527cf6b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenFootLocker.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenFootLocker.v0.json new file mode 100644 index 000000000..ec607aa35 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenFootLocker.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenFootLocker" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenTreasureChest.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenTreasureChest.v0.json new file mode 100644 index 000000000..73b84ec68 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenTreasureChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenTreasureChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.YellowGourd.v0.json b/Projects/UOContent/Migrations/Server.Items.YellowGourd.v0.json new file mode 100644 index 000000000..c3123d6fa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.YellowGourd.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.YellowGourd" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ZenRock1Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.ZenRock1Artifact.v0.json new file mode 100644 index 000000000..5c499202e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ZenRock1Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ZenRock1Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ZenRock2Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.ZenRock2Artifact.v0.json new file mode 100644 index 000000000..ff6522468 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ZenRock2Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ZenRock2Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ZenRock3Artifact.v0.json b/Projects/UOContent/Migrations/Server.Items.ZenRock3Artifact.v0.json new file mode 100644 index 000000000..3f006c6a8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ZenRock3Artifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ZenRock3Artifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json new file mode 100644 index 000000000..54f9c360a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v0.json @@ -0,0 +1,62 @@ +{ + "version": 0, + "type": "Server.Mobiles.RentedVendor", + "properties": [ + { + "name": "RentalDurationId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalPrice", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LandlordRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RenterRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RenewalPrice", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalExpireTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "DeltaTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index 025631871..ed395c055 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Items; using Server.Mobiles; using Server.Spells; @@ -565,21 +566,21 @@ namespace Server if (strBonus != 0 || dexBonus != 0 || intBonus != 0) { - var modName = Owner.Serial.ToString(); + var serial = Owner.Serial; if (strBonus != 0) { - to.AddStatMod(new StatMod(StatType.Str, $"{modName}Str", strBonus, TimeSpan.Zero)); + to.AddStatMod(new StatMod(StatType.Str, $"{serial}Str", strBonus, TimeSpan.Zero)); } if (dexBonus != 0) { - to.AddStatMod(new StatMod(StatType.Dex, $"{modName}Dex", dexBonus, TimeSpan.Zero)); + to.AddStatMod(new StatMod(StatType.Dex, $"{serial}Dex", dexBonus, TimeSpan.Zero)); } if (intBonus != 0) { - to.AddStatMod(new StatMod(StatType.Int, $"{modName}Int", intBonus, TimeSpan.Zero)); + to.AddStatMod(new StatMod(StatType.Int, $"{serial}Int", intBonus, TimeSpan.Zero)); } } @@ -588,11 +589,11 @@ namespace Server public void RemoveStatBonuses(Mobile from) { - var modName = Owner.Serial.ToString(); + var serial = Owner.Serial; - from.RemoveStatMod($"{modName}Str"); - from.RemoveStatMod($"{modName}Dex"); - from.RemoveStatMod($"{modName}Int"); + from.RemoveStatMod($"{serial}Str"); + from.RemoveStatMod($"{serial}Dex"); + from.RemoveStatMod($"{serial}Int"); from.CheckStatTimers(); } @@ -954,7 +955,7 @@ namespace Server public sealed class AosSkillBonuses : BaseAttributes { - private List m_Mods; + private HashSet m_Mods; public AosSkillBonuses(Item owner) : base(owner) { @@ -1034,13 +1035,13 @@ namespace Server set => SetSkill(4, value); } - public void GetProperties(ObjectPropertyList list) + public void GetProperties(IPropertyList list) { for (var i = 0; i < 5; ++i) { if (GetValues(i, out var skill, out var bonus)) { - list.Add(1060451 + i, "#{0}\t{1}", GetLabel(skill), bonus); + list.Add(1060451 + i, $"{GetLabel(skill):#}\t{bonus}"); } } } @@ -1067,7 +1068,7 @@ namespace Server continue; } - m_Mods ??= new List(); + m_Mods ??= new HashSet(); SkillMod sk = new DefaultSkillMod(skill, true, bonus); sk.ObeyCap = true; @@ -1083,10 +1084,10 @@ namespace Server return; } - for (var i = 0; i < m_Mods.Count; ++i) + foreach (var mod in m_Mods) { - var m = m_Mods[i].Owner; - m_Mods[i].Remove(); + var m = mod.Owner; + mod.Remove(); if (Core.ML) { @@ -1295,7 +1296,7 @@ namespace Server } [PropertyObject] - [EmbeddedSerializable(0)] + [SerializationGenerator(0)] public abstract partial class BaseAttributes { [SerializableField(0, setter: "private")] @@ -1321,9 +1322,9 @@ namespace Server public bool IsEmpty => _names == 0; - [SerializableParent] - private readonly Item _owner; + private Item _owner; + [DirtyTrackingEntity] public Item Owner => _owner; public int GetValue(int bitmask) @@ -1458,7 +1459,7 @@ namespace Server } } - if (Owner.Parent is Mobile m) + if (Owner?.Parent is Mobile m) { m.CheckStatTimers(); m.UpdateResistances(); @@ -1474,7 +1475,7 @@ namespace Server } } - Owner.InvalidateProperties(); + Owner?.InvalidateProperties(); } private int GetIndex(uint mask) diff --git a/Projects/UOContent/Misc/AccountPrompt.cs b/Projects/UOContent/Misc/AccountPrompt.cs index 0e537c0d5..28f6d08d0 100644 --- a/Projects/UOContent/Misc/AccountPrompt.cs +++ b/Projects/UOContent/Misc/AccountPrompt.cs @@ -1,38 +1,42 @@ using System; using Server.Accounting; +using Server.Logging; -namespace Server.Misc +namespace Server.Misc; + +public static class AccountPrompt { - public static class AccountPrompt + private static readonly ILogger logger = LogFactory.GetLogger(typeof(AccountPrompt)); + + public static void Initialize() { - public static void Initialize() + if (Accounts.Count == 0) { - if (Accounts.Count == 0) + Console.WriteLine("This server has no accounts."); + Console.Write("Do you want to create the owner account now? (y/n): "); + + var answer = Console.ReadLine(); + if (answer is "y" or "Y") { - Console.WriteLine("This server has no accounts."); - Console.Write("Do you want to create the owner account now? (y/n): "); + Console.WriteLine(); - var answer = Console.ReadLine(); - if (answer is "y" or "Y") + Console.Write("Username: "); + var username = Console.ReadLine(); + + Console.Write("Password: "); + var password = Console.ReadLine(); + + var a = new Account(username, password) { - Console.WriteLine(); + AccessLevel = AccessLevel.Owner + }; - Console.Write("Username: "); - var username = Console.ReadLine(); - - Console.Write("Password: "); - var password = Console.ReadLine(); - - var a = new Account(username, password); - a.AccessLevel = AccessLevel.Owner; - - Console.WriteLine("Account created."); - } - else - { - Console.WriteLine(); - Console.WriteLine("Account not created."); - } + logger.Information("Owner account created: {Username}", username); + ServerAccess.AddProtectedAccount(a, true); + } + else + { + logger.Warning("No owner account created."); } } } diff --git a/Projects/UOContent/Misc/BuffIcons.cs b/Projects/UOContent/Misc/BuffIcons.cs index 0c9e48340..7e7621568 100644 --- a/Projects/UOContent/Misc/BuffIcons.cs +++ b/Projects/UOContent/Misc/BuffIcons.cs @@ -162,7 +162,7 @@ namespace Server long ticks ) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -207,7 +207,7 @@ namespace Server public static void SendRemoveBuffPacket(NetState ns, Serial mob, BuffIcon iconID) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Misc/CharacterCreation.cs b/Projects/UOContent/Misc/CharacterCreation.cs index da82bb8ec..c8c3e2875 100644 --- a/Projects/UOContent/Misc/CharacterCreation.cs +++ b/Projects/UOContent/Misc/CharacterCreation.cs @@ -7,1272 +7,1273 @@ using Server.Logging; using Server.Mobiles; using Server.Network; -namespace Server.Misc +namespace Server.Misc; + +public static class CharacterCreation { - public static class CharacterCreation + private static readonly ILogger logger = LogFactory.GetLogger(typeof(CharacterCreation)); + + // Allowed skills that are not race or era specific + private static readonly HashSet _allowedStartingSkills = new() { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(CharacterCreation)); + SkillName.Alchemy, + SkillName.Anatomy, + SkillName.AnimalLore, + SkillName.AnimalTaming, + SkillName.Archery, + SkillName.ArmsLore, + SkillName.Begging, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Bushido, + SkillName.Camping, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Chivalry, + SkillName.Cooking, + SkillName.DetectHidden, + SkillName.Discordance, + SkillName.EvalInt, + SkillName.Fencing, + SkillName.Fishing, + SkillName.Focus, + SkillName.Forensics, + SkillName.Healing, + SkillName.Herding, + SkillName.Hiding, + SkillName.Imbuing, + SkillName.Inscribe, + SkillName.ItemID, + SkillName.Lockpicking, + SkillName.Lumberjacking, + SkillName.Macing, + SkillName.Magery, + SkillName.Meditation, + SkillName.Mining, + SkillName.Musicianship, + SkillName.Mysticism, + SkillName.Necromancy, + SkillName.Ninjitsu, + SkillName.Parry, + SkillName.Peacemaking, + SkillName.Poisoning, + SkillName.Provocation, + SkillName.MagicResist, + SkillName.Snooping, + SkillName.SpiritSpeak, + SkillName.Stealing, + SkillName.Swords, + SkillName.Tactics, + SkillName.Tailoring, + SkillName.TasteID, + SkillName.Throwing, + SkillName.Tinkering, + SkillName.Tracking, + SkillName.Veterinary, + SkillName.Wrestling + }; - // Allowed skills that are not race or era specific - private static readonly HashSet _allowedStartingSkills = new() + private static readonly TimeSpan BadStartMessageDelay = TimeSpan.FromSeconds(3.5); + + private static readonly CityInfo _newHavenInfo = + new("New Haven", "The Bountiful Harvest Inn", 3503, 2574, 14, Map.Trammel); + + public static void Initialize() + { + // Register our event handler + EventSink.CharacterCreated += EventSink_CharacterCreated; + } + + private static void AddBackpack(this Mobile m) + { + var pack = m.Backpack; + + if (pack == null) { - SkillName.Alchemy, - SkillName.Anatomy, - SkillName.AnimalLore, - SkillName.AnimalTaming, - SkillName.Archery, - SkillName.ArmsLore, - SkillName.Begging, - SkillName.Blacksmith, - SkillName.Fletching, - SkillName.Bushido, - SkillName.Camping, - SkillName.Carpentry, - SkillName.Cartography, - SkillName.Chivalry, - SkillName.Cooking, - SkillName.DetectHidden, - SkillName.Discordance, - SkillName.EvalInt, - SkillName.Fencing, - SkillName.Fishing, - SkillName.Focus, - SkillName.Forensics, - SkillName.Healing, - SkillName.Herding, - SkillName.Hiding, - SkillName.Imbuing, - SkillName.Inscribe, - SkillName.ItemID, - SkillName.Lockpicking, - SkillName.Lumberjacking, - SkillName.Macing, - SkillName.Magery, - SkillName.Meditation, - SkillName.Mining, - SkillName.Musicianship, - SkillName.Mysticism, - SkillName.Necromancy, - SkillName.Ninjitsu, - SkillName.Parry, - SkillName.Peacemaking, - SkillName.Poisoning, - SkillName.Provocation, - SkillName.MagicResist, - SkillName.Snooping, - SkillName.SpiritSpeak, - SkillName.Stealing, - SkillName.Swords, - SkillName.Tactics, - SkillName.Tailoring, - SkillName.TasteID, - SkillName.Throwing, - SkillName.Tinkering, - SkillName.Tracking, - SkillName.Veterinary, - SkillName.Wrestling - }; + pack = new Backpack(); + pack.Movable = false; - private static readonly TimeSpan BadStartMessageDelay = TimeSpan.FromSeconds(3.5); - - private static readonly CityInfo _newHavenInfo = - new("New Haven", "The Bountiful Harvest Inn", 3503, 2574, 14, Map.Trammel); - - public static void Initialize() - { - // Register our event handler - EventSink.CharacterCreated += EventSink_CharacterCreated; + m.AddItem(pack); } - private static void AddBackpack(this Mobile m) + m.PackItem(new RedBook("a book", m.Name, 20, true)); + m.PackItem(new Gold(1000)); // Starting gold can be customized here + m.PackItem(new Dagger()); + m.PackItem(new Candle()); + } + + private static Mobile CreateMobile(Account a) + { + if (a.Count >= a.Limit) { - var pack = m.Backpack; - - if (pack == null) - { - pack = new Backpack(); - pack.Movable = false; - - m.AddItem(pack); - } - - m.PackItem(new RedBook("a book", m.Name, 20, true)); - m.PackItem(new Gold(1000)); // Starting gold can be customized here - m.PackItem(new Dagger()); - m.PackItem(new Candle()); - } - - private static Mobile CreateMobile(Account a) - { - if (a.Count >= a.Limit) - { - return null; - } - - for (var i = 0; i < a.Length; ++i) - { - if (a[i] == null) - { - return a[i] = new PlayerMobile(); - } - } - return null; } - private static void EventSink_CharacterCreated(CharacterCreatedEventArgs args) + for (var i = 0; i < a.Length; ++i) { - if (!VerifyProfession(args.Profession)) + if (a[i] == null) { - args.Profession = 0; + return a[i] = new PlayerMobile(); } - - var state = args.State; - - if (state == null) - { - return; - } - - var newChar = CreateMobile(args.Account as Account); - - if (newChar == null) - { - logger.Information("Login: {0}: Character creation failed, account full", state); - return; - } - - args.Mobile = newChar; - - newChar.Player = true; - newChar.AccessLevel = args.Account.AccessLevel; - newChar.Female = args.Female; - newChar.Race = Core.Expansion >= args.Race.RequiredExpansion ? args.Race : Race.DefaultRace; - newChar.Hue = newChar.Race.ClipSkinHue(args.Hue & 0x3FFF) | 0x8000; - newChar.Hunger = 20; - - var young = false; - - if (newChar is PlayerMobile pm) - { - pm.Profession = args.Profession; - - if (pm.AccessLevel == AccessLevel.Player && ((Account)pm.Account).Young) - { - young = pm.Young = true; - } - } - - SetName(newChar, args.Name); - - newChar.AddBackpack(); - - SetStats(newChar, state, args.Stats, args.Profession); - SetSkills(newChar, args.Skills, args.Profession, args.ShirtHue, args.PantsHue); - - var 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 (TestCenter.Enabled) - { - TestCenter.FillBankbox(newChar); - } - - if (young) - { - var ticket = new NewPlayerTicket(); - ticket.Owner = newChar; - newChar.BankBox.DropItem(ticket); - } - - var city = GetStartLocation(args, young); - - newChar.MoveToWorld(city.Location, city.Map); - - logger.Information( - "Login: {0}: New character being created (account={1}, character={2}, serial={3}, started.city={4}, started.location={5}, started.map={6})", - state, - args.Account.Username, - newChar.Name, - newChar.Serial, - city.City, - city.Location, - city.Map); - - new WelcomeTimer(newChar).Start(); } - public static bool VerifyProfession(int profession) => - profession >= 0 && profession < ProfessionInfo.Professions.Length; + return null; + } - private static CityInfo GetStartLocation(CharacterCreatedEventArgs args, bool isYoung) + private static void EventSink_CharacterCreated(CharacterCreatedEventArgs args) + { + if (!VerifyProfession(args.Profession)) { - if (Core.ML) - { - return _newHavenInfo; // We don't get the client Version until AFTER Character creation - } - - var useHaven = isYoung; - - var flags = args.State?.Flags ?? ClientFlags.None; - var m = args.Mobile; - - var profession = ProfessionInfo.Professions[args.Profession]; - - switch (profession?.Name.ToLowerInvariant()) - { - case "necromancer": - { - if ((flags & ClientFlags.Malas) != 0) - { - return new CityInfo("Umbra", "Mardoth's Tower", 2114, 1301, -50, Map.Malas); - } - - useHaven = true; - - /* - * 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. - */ - Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1062205)); - - break; - } - case "paladin": - { - return _newHavenInfo; - } - case "samurai": - { - if ((flags & ClientFlags.Tokuno) != 0) - { - return new CityInfo("Samurai DE", "Haoti's Grounds", 368, 780, -1, Map.Malas); - } - - useHaven = true; - - /* - * 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. - */ - Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1063487)); - - break; - } - case "ninja": - { - if ((flags & ClientFlags.Tokuno) != 0) - { - return new CityInfo("Ninja DE", "Enimo's Residence", 414, 823, -1, Map.Malas); - } - - useHaven = true; - - /* - * 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. - */ - Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1063487)); - - break; - } - } - - return useHaven ? _newHavenInfo : args.City; + args.Profession = 0; } - private static void SetStats(Mobile m, NetState state, StatNameValue[] stats, int prof) + var state = args.State; + + if (state == null) { - var maxStats = state.NewCharacterCreation ? 90 : 80; - - var str = 0; - var dex = 0; - var intel = 0; - - if (prof > 0) - { - stats = ProfessionInfo.Professions[prof]?.Stats ?? stats; - } - - for (var i = 0; i < stats.Length; i++) - { - var (statType, value) = stats[i]; - switch (statType) - { - case StatType.Str: str = value; break; - case StatType.Dex: dex = value; break; - case StatType.Int: intel = value; break; - } - } - - if (str is < 10 or > 60 || dex is < 10 or > 60 || intel is < 10 or > 60 || str + dex + intel != maxStats) - { - str = 10; - dex = 10; - intel = 10; - } - - m.InitStats(str, dex, intel); + return; } - private static void SetName(Mobile m, string name) + var newChar = CreateMobile(args.Account as Account); + + if (newChar == null) { - name = name.Trim(); - - if (!NameVerification.Validate(name, 2, 16, true, false, true, 1, NameVerification.SpaceDashPeriodQuote)) - { - name = "Generic Player"; - } - - m.Name = name; + logger.Information("Login: {NetState}: Character creation failed, account full", state); + return; } - private static bool ValidateSkills(int raceFlag, SkillNameValue[] skills) + args.Mobile = newChar; + + newChar.Player = true; + newChar.AccessLevel = args.Account.AccessLevel; + newChar.Female = args.Female; + newChar.Race = Core.Expansion >= args.Race.RequiredExpansion ? args.Race : Race.DefaultRace; + newChar.Hue = newChar.Race.ClipSkinHue(args.Hue & 0x3FFF) | 0x8000; + newChar.Hunger = 20; + + var young = false; + + if (newChar is PlayerMobile pm) { - var total = 0; + pm.Profession = args.Profession; - for (var i = 0; i < skills.Length; ++i) + if (pm.AccessLevel == AccessLevel.Player && ((Account)pm.Account).Young) { - var (name, value) = skills[i]; - var notValid = value is < 0 or > 50 || !_allowedStartingSkills.Contains(name) || - !Core.AOS && name is SkillName.Necromancy or SkillName.Chivalry || - !Core.SE && name is SkillName.Ninjitsu or SkillName.Bushido || - Core.SA && (raceFlag == Race.AllowGargoylesOnly && name == SkillName.Archery || - raceFlag != Race.AllowGargoylesOnly && name == SkillName.Throwing) || - !Core.SA && name is SkillName.Throwing or SkillName.Imbuing; - - if (notValid) - { - skills[i] = default; - continue; - } - - total += value; - - for (var j = i + 1; j < skills.Length; ++j) - { - var (nameCheck, valueCheck) = skills[j]; - if (valueCheck > 0 && nameCheck == name) - { - return false; - } - } + young = pm.Young = true; } - - return total is 100 or 120; } - private static void SetSkills(Mobile m, SkillNameValue[] skills, int prof, int shirtHue, int pantsHue) + SetName(newChar, args.Name); + + newChar.AddBackpack(); + + SetStats(newChar, state, args.Stats, args.Profession); + SetSkills(newChar, args.Skills, args.Profession, args.ShirtHue, args.PantsHue); + + var race = newChar.Race; + + if (race.ValidateHair(newChar, args.HairID)) { - ProfessionInfo profession = null; - if (prof > 0) - { - profession = ProfessionInfo.Professions[prof]; - skills = ProfessionInfo.Professions[prof]?.Skills ?? skills; - } - else if (!ValidateSkills(m.Race.RaceFlag, skills)) // This does not check for skills that are not allowed by expansion - { - return; - } + newChar.HairItemID = args.HairID; + newChar.HairHue = race.ClipHairHue(args.HairHue & 0x3FFF); + } - var addSkillItems = true; - var elf = m.Race == Race.Elf; - var gargoyle = m.Race == Race.Gargoyle; + if (race.ValidateFacialHair(newChar, args.BeardID)) + { + newChar.FacialHairItemID = args.BeardID; + newChar.FacialHairHue = race.ClipHairHue(args.BeardHue & 0x3FFF); + } - switch (profession?.Name.ToLowerInvariant()) - { - case "necromancer": - { - Container regs = new BagOfNecroReagents { LootType = LootType.Regular }; + if (TestCenter.Enabled) + { + TestCenter.FillBankbox(newChar); + } - if (!Core.AOS) - { - foreach (var item in regs.Items) - { - item.LootType = LootType.Newbied; - } - } + if (young) + { + var ticket = new NewPlayerTicket(); + ticket.Owner = newChar; + newChar.BankBox.DropItem(ticket); + } - m.PackItem(regs); + var city = GetStartLocation(args, young); - EquipItem(m, new BoneHelm()); + newChar.MoveToWorld(city.Location, city.Map); - if (elf) - { - EquipItem(m, new ElvenMachete()); - EquipItem(m, NecroHue(new LeafChest())); - EquipItem(m, NecroHue(new LeafArms())); - EquipItem(m, NecroHue(new LeafGloves())); - EquipItem(m, NecroHue(new LeafGorget())); - EquipItem(m, NecroHue(new LeafLegs())); - EquipItem(m, new ElvenBoots()); - } - else if (gargoyle) - { - EquipItem(m, new GlassSword()); - EquipItem(m, NecroHue(m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1())); - EquipItem(m, NecroHue(m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1())); - EquipItem(m, NecroHue(m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1())); - EquipItem(m, NecroHue(m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1())); - } - else - { - EquipItem(m, new BoneHarvester()); - EquipItem(m, NecroHue(new LeatherChest())); - EquipItem(m, NecroHue(new LeatherArms())); - EquipItem(m, NecroHue(new LeatherGloves())); - EquipItem(m, NecroHue(new LeatherGorget())); - EquipItem(m, NecroHue(new LeatherLegs())); - EquipItem(m, NecroHue(new Skirt())); - EquipItem(m, new Sandals(0x8FD)); - } + logger.Information( + "Login: {0}: New character being created (account={1}, character={2}, serial={3}, started.city={4}, started.location={5}, started.map={6})", + state, + args.Account.Username, + newChar.Name, + newChar.Serial, + city.City, + city.Location, + city.Map); - // animate dead, evil omen, pain spike, summon familiar, wraith form - m.PackItem(new NecromancerSpellbook(0x8981ul) { LootType = LootType.Blessed }); + new WelcomeTimer(newChar).Start(); + } - addSkillItems = false; + public static bool VerifyProfession(int profession) => + profession >= 0 && profession < ProfessionInfo.Professions.Length; - break; - } - case "paladin": - { - if (elf) - { - EquipItem(m, new ElvenMachete()); - EquipItem(m, new WingedHelm()); - EquipItem(m, new LeafGorget()); - EquipItem(m, new LeafArms()); - EquipItem(m, new LeafChest()); - EquipItem(m, new LeafLegs()); - EquipItem(m, new LeafGloves()); - EquipItem(m, new ElvenBoots()); // Verify hue - } - else if (gargoyle) - { - EquipItem(m, new GlassSword()); - EquipItem(m, m.Female ? new GargishStoneChestType2() : new GargishStoneChestType1()); - EquipItem(m, m.Female ? new GargishStoneArmsType2() : new GargishStoneArmsType1()); - EquipItem(m, m.Female ? new GargishStoneKiltType2() : new GargishStoneKiltType1()); - EquipItem(m, m.Female ? new GargishStoneLegsType2() : new GargishStoneLegsType1()); - } - else - { - EquipItem(m, new Broadsword()); - EquipItem(m, new Helmet()); - EquipItem(m, new PlateGorget()); - EquipItem(m, new RingmailArms()); - EquipItem(m, new RingmailChest()); - EquipItem(m, new RingmailLegs()); - EquipItem(m, new RingmailGloves()); - EquipItem(m, new ThighBoots(0x748)); - EquipItem(m, new Cloak(0xCF)); - EquipItem(m, new BodySash(0xCF)); - } + private static CityInfo GetStartLocation(CharacterCreatedEventArgs args, bool isYoung) + { + var post6000Supported = !TileMatrix.Pre6000ClientSupport; - m.PackItem(new BookOfChivalry { LootType = LootType.Blessed }); + if (Core.ML && post6000Supported) + { + return _newHavenInfo; // We don't get the client Version until AFTER Character creation + } - addSkillItems = false; + var useHaven = isYoung; - break; - } - case "samurai": - { - addSkillItems = false; + var flags = args.State?.Flags ?? ClientFlags.None; + var m = args.Mobile; - if (elf) - { - EquipItem(m, new RavenHelm()); - EquipItem(m, new HakamaShita(0x2C3)); - EquipItem(m, new Hakama(0x2C3)); - EquipItem(m, new SamuraiTabi(0x2C3)); - EquipItem(m, new TattsukeHakama(0x22D)); - EquipItem(m, new Bokuto()); - } - else if (gargoyle) - { - EquipItem(m, new GargishTalwar()); - EquipItem(m, m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1()); - EquipItem(m, m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1()); - EquipItem(m, m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1()); - EquipItem(m, m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1()); - } - else - { - EquipItem(m, new LeatherJingasa()); - EquipItem(m, new HakamaShita(0x2C3)); - EquipItem(m, new Hakama(0x2C3)); - EquipItem(m, new SamuraiTabi(0x2C3)); - EquipItem(m, new TattsukeHakama(0x22D)); - EquipItem(m, new Bokuto()); - } + var profession = ProfessionInfo.Professions[args.Profession]; - m.PackItem(new Scissors()); - m.PackItem(new Bandage(50)); - m.PackItem(new BookOfBushido()); - - break; - } - case "ninja": - { - addSkillItems = false; - - int[] hues = { 0x1A8, 0xEC, 0x99, 0x90, 0xB5, 0x336, 0x89 }; - // TODO: Verify that's ALL the hues for that above. - - if (elf) - { - EquipItem(m, new AssassinSpike()); - EquipItem(m, new TattsukeHakama(hues.RandomElement())); - EquipItem(m, new HakamaShita(0x2C3)); - EquipItem(m, new NinjaTabi(0x2C3)); - EquipItem(m, new Kasa()); - } - else if (gargoyle) - { - EquipItem(m, new DualPointedSpear()); - EquipItem(m, m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1()); - EquipItem(m, m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1()); - EquipItem(m, m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1()); - EquipItem(m, m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1()); - } - else - { - EquipItem(m, new Tekagi()); - EquipItem(m, new TattsukeHakama(hues.RandomElement())); - EquipItem(m, new HakamaShita(0x2C3)); - EquipItem(m, new NinjaTabi(0x2C3)); - EquipItem(m, new Kasa()); - } - - m.PackItem(new SmokeBomb()); - m.PackItem(new SmokeBomb()); - m.PackItem(new SmokeBomb()); - m.PackItem(new SmokeBomb()); - m.PackItem(new SmokeBomb()); - m.PackItem(new BookOfNinjitsu()); - break; - } - case "swordsman": - case "fencer": - case "warrior": - case "mace fighter": - { - if (elf) - { - EquipItem(m, new Circlet()); - EquipItem(m, new HideGorget()); - EquipItem(m, new HideChest()); - EquipItem(m, new HidePauldrons()); - EquipItem(m, new HideGloves()); - EquipItem(m, new HidePants()); - EquipItem(m, new ElvenBoots()); - } - else if (gargoyle) - { - EquipItem(m, m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1()); - EquipItem(m, m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1()); - EquipItem(m, m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1()); - EquipItem(m, m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1()); - } - else - { - EquipItem(m, new Bascinet()); - EquipItem(m, new StuddedGorget()); - EquipItem(m, new StuddedChest()); - EquipItem(m, new StuddedArms()); - EquipItem(m, new StuddedGloves()); - EquipItem(m, new StuddedLegs()); - EquipItem(m, new ThighBoots()); - } - break; - } - } - - if (addSkillItems) - { - m.AddShirt(shirtHue); - m.AddPants(pantsHue); - m.AddShoes(); - - // All elves get a wild staff - if (elf) + switch (profession?.Name.ToLowerInvariant()) + { + case "necromancer": { - EquipItem(m, new WildStaff()); + if ((flags & ClientFlags.Malas) != 0) + { + return new CityInfo("Umbra", "Mardoth's Tower", 2114, 1301, -50, Map.Malas); + } + + useHaven = true; + + /* + * 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. + */ + Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1062205)); + + break; } + case "paladin": + { + return _newHavenInfo; + } + case "samurai": + { + if ((flags & ClientFlags.Tokuno) != 0) + { + return new CityInfo("Samurai DE", "Haoti's Grounds", 368, 780, -1, Map.Malas); + } + + useHaven = true; + + /* + * 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. + */ + Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1063487)); + + break; + } + case "ninja": + { + if ((flags & ClientFlags.Tokuno) != 0) + { + return new CityInfo("Ninja DE", "Enimo's Residence", 414, 823, -1, Map.Malas); + } + + useHaven = true; + + /* + * 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. + */ + Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1063487)); + + break; + } + } + + return post6000Supported && useHaven ? _newHavenInfo : args.City; + } + + private static void SetStats(Mobile m, NetState state, StatNameValue[] stats, int prof) + { + var maxStats = state.NewCharacterCreation ? 90 : 80; + + var str = 0; + var dex = 0; + var intel = 0; + + if (prof > 0) + { + stats = ProfessionInfo.Professions[prof]?.Stats ?? stats; + } + + for (var i = 0; i < stats.Length; i++) + { + var (statType, value) = stats[i]; + switch (statType) + { + case StatType.Str: str = value; break; + case StatType.Dex: dex = value; break; + case StatType.Int: intel = value; break; + } + } + + if (str is < 10 or > 60 || dex is < 10 or > 60 || intel is < 10 or > 60 || str + dex + intel != maxStats) + { + 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, false, true, 1, NameVerification.SpaceDashPeriodQuote)) + { + name = "Generic Player"; + } + + m.Name = name; + } + + private static bool ValidateSkills(int raceFlag, SkillNameValue[] skills) + { + var total = 0; + + for (var i = 0; i < skills.Length; ++i) + { + var (name, value) = skills[i]; + var notValid = value is < 0 or > 50 || !_allowedStartingSkills.Contains(name) || + !Core.AOS && name is SkillName.Necromancy or SkillName.Chivalry || + !Core.SE && name is SkillName.Ninjitsu or SkillName.Bushido || + Core.SA && (raceFlag == Race.AllowGargoylesOnly && name == SkillName.Archery || + raceFlag != Race.AllowGargoylesOnly && name == SkillName.Throwing) || + !Core.SA && name is SkillName.Throwing or SkillName.Imbuing; + + if (notValid) + { + skills[i] = default; + continue; } - for (var i = 0; i < skills.Length; ++i) + total += value; + + for (var j = i + 1; j < skills.Length; ++j) { - var (name, value) = skills[i]; - if (value <= 0) + var (nameCheck, valueCheck) = skills[j]; + if (valueCheck > 0 && nameCheck == name) { - continue; - } - - var skill = m.Skills[name]; - - if (skill != null) - { - skill.BaseFixedPoint = value * 10; - m.AddSkillItems(name); + return false; } } } - private static void EquipItem(Mobile m, Item item, bool mustEquip = false) + return total is 100 or 120; + } + + private static void SetSkills(Mobile m, SkillNameValue[] skills, int prof, int shirtHue, int pantsHue) + { + ProfessionInfo profession = null; + if (prof > 0) { - if (item == null) - { - return; - } - - if (!Core.AOS) - { - item.LootType = LootType.Newbied; - } - - if (m?.EquipItem(item) == true) - { - return; - } - - var pack = m?.Backpack; - - if (!mustEquip && pack != null) - { - pack.DropItem(item); - } - else - { - item.Delete(); - } + profession = ProfessionInfo.Professions[prof]; + skills = ProfessionInfo.Professions[prof]?.Skills ?? skills; + } + else if (!ValidateSkills(m.Race.RaceFlag, skills)) // This does not check for skills that are not allowed by expansion + { + return; } - private static void PackItem(this Mobile m, Item item) + var addSkillItems = true; + var elf = m.Race == Race.Elf; + var gargoyle = m.Race == Race.Gargoyle; + + switch (profession?.Name.ToLowerInvariant()) { - if (!Core.AOS) - { - item.LootType = LootType.Newbied; - } - - var pack = m.Backpack; - - if (pack != null) - { - pack.DropItem(item); - } - else - { - item.Delete(); - } - } - - private static void AddShirt(this Mobile m, int shirtHue) - { - var hue = Utility.ClipDyedHue(shirtHue & 0x3FFF); - var raceFlag = m.Race.RaceFlag; - - var shirt = raceFlag switch - { - Race.AllowElvesOnly => new ElvenShirt(hue), - Race.AllowGargoylesOnly when m.Female => new GargishClothChestType2 { Hue = hue }, - Race.AllowGargoylesOnly => new GargishClothChestType1 { Hue = hue }, - // Humans - _ => (Item)(Utility.Random(3) switch + case "necromancer": { - 0 => new Shirt(hue), - 1 => new FancyShirt(hue), - _ => new Doublet(hue) - }) - }; + Container regs = new BagOfNecroReagents { LootType = LootType.Regular }; - EquipItem(m, shirt); - } + if (!Core.AOS) + { + foreach (var item in regs.Items) + { + item.LootType = LootType.Newbied; + } + } - private static void AddPants(this Mobile m, int pantsHue) - { - var hue = Utility.ClipDyedHue(pantsHue & 0x3FFF); - var raceFlag = m.Race.RaceFlag; - var female = m.Female; + m.PackItem(regs); - var pants = raceFlag switch - { - Race.AllowElvesOnly => new ElvenPants(hue), - Race.AllowGargoylesOnly when female => new GargishClothLegsType2 { Hue = hue }, - Race.AllowGargoylesOnly => new GargishClothLegsType1 { Hue = hue }, - // Humans - _ => (Item)(Utility.RandomBool() switch + EquipItem(m, new BoneHelm()); + + if (elf) + { + EquipItem(m, new ElvenMachete()); + EquipItem(m, NecroHue(new LeafChest())); + EquipItem(m, NecroHue(new LeafArms())); + EquipItem(m, NecroHue(new LeafGloves())); + EquipItem(m, NecroHue(new LeafGorget())); + EquipItem(m, NecroHue(new LeafLegs())); + EquipItem(m, new ElvenBoots()); + } + else if (gargoyle) + { + EquipItem(m, new GlassSword()); + EquipItem(m, NecroHue(m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1())); + EquipItem(m, NecroHue(m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1())); + EquipItem(m, NecroHue(m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1())); + EquipItem(m, NecroHue(m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1())); + } + else + { + EquipItem(m, new BoneHarvester()); + EquipItem(m, NecroHue(new LeatherChest())); + EquipItem(m, NecroHue(new LeatherArms())); + EquipItem(m, NecroHue(new LeatherGloves())); + EquipItem(m, NecroHue(new LeatherGorget())); + EquipItem(m, NecroHue(new LeatherLegs())); + EquipItem(m, NecroHue(new Skirt())); + EquipItem(m, new Sandals(0x8FD)); + } + + // animate dead, evil omen, pain spike, summon familiar, wraith form + m.PackItem(new NecromancerSpellbook(0x8981ul) { LootType = LootType.Blessed }); + + addSkillItems = false; + + break; + } + case "paladin": { - true when female => new Skirt(hue), - true => new LongPants(hue), - false when female => new Kilt(hue), - false => new ShortPants(hue) - }) - }; + if (elf) + { + EquipItem(m, new ElvenMachete()); + EquipItem(m, new WingedHelm()); + EquipItem(m, new LeafGorget()); + EquipItem(m, new LeafArms()); + EquipItem(m, new LeafChest()); + EquipItem(m, new LeafLegs()); + EquipItem(m, new LeafGloves()); + EquipItem(m, new ElvenBoots()); // Verify hue + } + else if (gargoyle) + { + EquipItem(m, new GlassSword()); + EquipItem(m, m.Female ? new GargishStoneChestType2() : new GargishStoneChestType1()); + EquipItem(m, m.Female ? new GargishStoneArmsType2() : new GargishStoneArmsType1()); + EquipItem(m, m.Female ? new GargishStoneKiltType2() : new GargishStoneKiltType1()); + EquipItem(m, m.Female ? new GargishStoneLegsType2() : new GargishStoneLegsType1()); + } + else + { + EquipItem(m, new Broadsword()); + EquipItem(m, new Helmet()); + EquipItem(m, new PlateGorget()); + EquipItem(m, new RingmailArms()); + EquipItem(m, new RingmailChest()); + EquipItem(m, new RingmailLegs()); + EquipItem(m, new RingmailGloves()); + EquipItem(m, new ThighBoots(0x748)); + EquipItem(m, new Cloak(0xCF)); + EquipItem(m, new BodySash(0xCF)); + } - EquipItem(m, pants); + m.PackItem(new BookOfChivalry { LootType = LootType.Blessed }); + + addSkillItems = false; + + break; + } + case "samurai": + { + addSkillItems = false; + + if (elf) + { + EquipItem(m, new RavenHelm()); + EquipItem(m, new HakamaShita(0x2C3)); + EquipItem(m, new Hakama(0x2C3)); + EquipItem(m, new SamuraiTabi(0x2C3)); + EquipItem(m, new TattsukeHakama(0x22D)); + EquipItem(m, new Bokuto()); + } + else if (gargoyle) + { + EquipItem(m, new GargishTalwar()); + EquipItem(m, m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1()); + EquipItem(m, m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1()); + EquipItem(m, m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1()); + EquipItem(m, m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1()); + } + else + { + EquipItem(m, new LeatherJingasa()); + EquipItem(m, new HakamaShita(0x2C3)); + EquipItem(m, new Hakama(0x2C3)); + EquipItem(m, new SamuraiTabi(0x2C3)); + EquipItem(m, new TattsukeHakama(0x22D)); + EquipItem(m, new Bokuto()); + } + + m.PackItem(new Scissors()); + m.PackItem(new Bandage(50)); + m.PackItem(new BookOfBushido()); + + break; + } + case "ninja": + { + addSkillItems = false; + + int[] hues = { 0x1A8, 0xEC, 0x99, 0x90, 0xB5, 0x336, 0x89 }; + // TODO: Verify that's ALL the hues for that above. + + if (elf) + { + EquipItem(m, new AssassinSpike()); + EquipItem(m, new TattsukeHakama(hues.RandomElement())); + EquipItem(m, new HakamaShita(0x2C3)); + EquipItem(m, new NinjaTabi(0x2C3)); + EquipItem(m, new Kasa()); + } + else if (gargoyle) + { + EquipItem(m, new DualPointedSpear()); + EquipItem(m, m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1()); + EquipItem(m, m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1()); + EquipItem(m, m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1()); + EquipItem(m, m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1()); + } + else + { + EquipItem(m, new Tekagi()); + EquipItem(m, new TattsukeHakama(hues.RandomElement())); + EquipItem(m, new HakamaShita(0x2C3)); + EquipItem(m, new NinjaTabi(0x2C3)); + EquipItem(m, new Kasa()); + } + + m.PackItem(new SmokeBomb()); + m.PackItem(new SmokeBomb()); + m.PackItem(new SmokeBomb()); + m.PackItem(new SmokeBomb()); + m.PackItem(new SmokeBomb()); + m.PackItem(new BookOfNinjitsu()); + break; + } + case "swordsman": + case "fencer": + case "warrior": + case "mace fighter": + { + if (elf) + { + EquipItem(m, new Circlet()); + EquipItem(m, new HideGorget()); + EquipItem(m, new HideChest()); + EquipItem(m, new HidePauldrons()); + EquipItem(m, new HideGloves()); + EquipItem(m, new HidePants()); + EquipItem(m, new ElvenBoots()); + } + else if (gargoyle) + { + EquipItem(m, m.Female ? new GargishLeatherChestType2() : new GargishLeatherChestType1()); + EquipItem(m, m.Female ? new GargishLeatherArmsType2() : new GargishLeatherArmsType1()); + EquipItem(m, m.Female ? new GargishLeatherKiltType2() : new GargishLeatherKiltType1()); + EquipItem(m, m.Female ? new GargishLeatherLegsType2() : new GargishLeatherLegsType1()); + } + else + { + EquipItem(m, new Bascinet()); + EquipItem(m, new StuddedGorget()); + EquipItem(m, new StuddedChest()); + EquipItem(m, new StuddedArms()); + EquipItem(m, new StuddedGloves()); + EquipItem(m, new StuddedLegs()); + EquipItem(m, new ThighBoots()); + } + break; + } } - private static void AddShoes(this Mobile m) + if (addSkillItems) { - if (m.Race == Race.Elf) + m.AddShirt(shirtHue); + m.AddPants(pantsHue); + m.AddShoes(); + + // All elves get a wild staff + if (elf) { - EquipItem(m, new ElvenBoots()); - } - else if (m.Race == Race.Human) - { - EquipItem(m, new Shoes(Utility.RandomYellowHue())); + EquipItem(m, new WildStaff()); } } - private static void PackInstrument(this Mobile m) + for (var i = 0; i < skills.Length; ++i) { - Item instrument = Utility.Random(6) switch + var (name, value) = skills[i]; + if (value <= 0) { - 0 => new Drums(), - 1 => new Harp(), - 2 => new LapHarp(), - 3 => new Lute(), - 4 => new Tambourine(), - _ => new TambourineTassel() - }; + continue; + } - m.PackItem(instrument); - } + var skill = m.Skills[name]; - private static void PackScroll(this Mobile m, int circle) - { - Item item = (Utility.Random(8) * (circle + 1)) switch + if (skill != null) { - 0 => new ClumsyScroll(), - 1 => new CreateFoodScroll(), - 2 => new FeeblemindScroll(), - 3 => new HealScroll(), - 4 => new MagicArrowScroll(), - 5 => new NightSightScroll(), - 6 => new ReactiveArmorScroll(), - 7 => new WeakenScroll(), - 8 => new AgilityScroll(), - 9 => new CunningScroll(), - 10 => new CureScroll(), - 11 => new HarmScroll(), - 12 => new MagicTrapScroll(), - 13 => new MagicUnTrapScroll(), - 14 => new ProtectionScroll(), - 15 => new StrengthScroll(), - 16 => new BlessScroll(), - 17 => new FireballScroll(), - 18 => new MagicLockScroll(), - 19 => new PoisonScroll(), - 20 => new TelekinesisScroll(), - 21 => new TeleportScroll(), - 22 => new UnlockScroll(), - _ => new WallOfStoneScroll() - }; - - m.PackItem(item); - } - - private static void PackTinkerPart(this Mobile m) - { - Item item = Utility.Random(4) switch - { - 0 => new Axle(), - 1 => new Gears(), - 2 => new Hinge(), - 3 => new Springs() - }; - - m.PackItem(item); - } - - private static Item NecroHue(Item item) - { - item.Hue = 0x2C3; - - return item; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Item Robe(int raceFlag, bool female, int hue) => - raceFlag switch - { - Race.AllowElvesOnly when female => new FemaleElvenRobe(hue), - Race.AllowElvesOnly => new MaleElvenRobe(hue), - _ => new Robe(hue) - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Item SwordsWeapon(int raceFlag) => - raceFlag switch - { - Race.AllowElvesOnly => new RuneBlade(), - Race.AllowGargoylesOnly => new DreadSword(), - _ => new Katana() - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Item MacingWeapon(int raceFlag) => - raceFlag switch - { - Race.AllowElvesOnly => new DiamondMace(), - Race.AllowGargoylesOnly => new DiscMace(), - _ => new Club() - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Item FencingWeapon(int raceFlag) => - raceFlag switch - { - Race.AllowElvesOnly => new Leafblade(), - Race.AllowGargoylesOnly => new BloodBlade(), - _ => new Kryss() - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Item RangedWeapon(int raceFlag) => - raceFlag switch - { - Race.AllowElvesOnly => new ElvenCompositeLongbow(), - Race.AllowGargoylesOnly => new SerpentstoneStaff(), - _ => new Bow() - }; - - private static void AddSkillItems(this Mobile m, SkillName skill) - { - var raceFlag = m.Race.RaceFlag; - var elf = m.Race == Race.Elf; - var human = m.Race == Race.Human; - var gargoyle = m.Race == Race.Gargoyle; - var elfOrHuman = Race.IsAllowedRace(m.Race, Race.AllowHumanOrElves); - var female = m.Female; - - switch (skill) - { - case SkillName.Alchemy: - { - m.PackItem(new Bottle(4)); - m.PackItem(new MortarPestle()); - EquipItem(m, Robe(raceFlag, female, Utility.RandomPinkHue())); - - break; - } - case SkillName.Anatomy: - { - m.PackItem(new Bandage(3)); - EquipItem(m, Robe(raceFlag, female, Utility.RandomYellowHue())); - - break; - } - case SkillName.AnimalLore: - { - if (elf) - { - EquipItem(m, new WildStaff()); - } - else - { - EquipItem(m, new ShepherdsCrook()); - } - - EquipItem(m, Robe(raceFlag, female, Utility.RandomGreenHue())); - - break; - } - case SkillName.AnimalTaming: - { - if (human) - { - EquipItem(m, new ShepherdsCrook()); - } - - break; - } - case SkillName.Archery: - { - m.PackItem(new Arrow(25)); - - EquipItem(m, RangedWeapon(raceFlag)); - - break; - } - case SkillName.ArmsLore: - { - Item item = Utility.Random(3) switch - { - 0 => FencingWeapon(raceFlag), - 1 => MacingWeapon(raceFlag), - _ => SwordsWeapon(raceFlag) - }; - EquipItem(m, item); - - break; - } - case SkillName.Begging: - { - Item staff = raceFlag switch - { - Race.AllowElvesOnly => new WildStaff(), - Race.AllowGargoylesOnly => new GlassStaff(), - _ => new GnarledStaff() - }; - - EquipItem(m, staff); - break; - } - case SkillName.Blacksmith: - { - m.PackItem(new Tongs()); - m.PackItem(new Pickaxe()); - m.PackItem(new Pickaxe()); - m.PackItem(new IronIngot(50)); - EquipItem(m, new HalfApron(Utility.RandomYellowHue())); - break; - } - case SkillName.Bushido: - { - if (elfOrHuman) - { - // Delete pants - m?.FindItemOnLayer(Layer.OuterLegs)?.Delete(); - - EquipItem(m, new Hakama()); - EquipItem(m, new Kasa()); - } - - EquipItem(m, new BookOfBushido()); - break; - } - case SkillName.Fletching: - { - m.PackItem(new Board(14)); - m.PackItem(new Feather(5)); - m.PackItem(new Shaft(5)); - break; - } - case SkillName.Camping: - { - m.PackItem(new Bedroll()); - m.PackItem(new Kindling(5)); - break; - } - case SkillName.Carpentry: - { - m.PackItem(new Board(10)); - m.PackItem(new Saw()); - - if (elfOrHuman) - { - EquipItem(m, new HalfApron(Utility.RandomYellowHue())); - } - - break; - } - case SkillName.Cartography: - { - m.PackItem(new BlankMap()); - m.PackItem(new BlankMap()); - m.PackItem(new BlankMap()); - m.PackItem(new BlankMap()); - m.PackItem(new Sextant()); - break; - } - case SkillName.Cooking: - { - m.PackItem(new Kindling(2)); - m.PackItem(new RawLambLeg()); - m.PackItem(new RawChickenLeg()); - m.PackItem(new RawFishSteak()); - m.PackItem(new SackFlour()); - m.PackItem(new Pitcher(BeverageType.Water)); - break; - } - case SkillName.Chivalry: - { - m.PackItem(new BookOfChivalry()); - break; - } - case SkillName.DetectHidden: - { - if (elfOrHuman) - { - EquipItem(m, new Cloak(0x455)); - } - - break; - } - case SkillName.Discordance: - { - m.PackInstrument(); - break; - } - case SkillName.Fencing: - { - EquipItem(m, FencingWeapon(raceFlag)); - break; - } - case SkillName.Fishing: - { - EquipItem(m, new FishingPole()); - - var hue = Utility.RandomYellowHue(); - if (elf) - { - EquipItem(m, new Circlet { Hue = hue }); - } - else if (human) - { - EquipItem(m, new FloppyHat(hue)); - } - - break; - } - case SkillName.Healing: - { - m.PackItem(new Bandage(50)); - m.PackItem(new Scissors()); - break; - } - case SkillName.Herding: - { - EquipItem(m, new ShepherdsCrook()); - break; - } - case SkillName.Hiding: - { - if (elfOrHuman) - { - EquipItem(m, new Cloak(0x455)); - } - - break; - } - case SkillName.Inscribe: - { - m.PackItem(new BlankScroll(2)); - m.PackItem(new BlueBook()); - break; - } - case SkillName.ItemID: - { - Item staff = raceFlag switch - { - Race.AllowElvesOnly => new WildStaff(), - Race.AllowGargoylesOnly => new SerpentstoneStaff(), - _ => new GnarledStaff() - }; - - EquipItem(m, staff); - break; - } - case SkillName.Lockpicking: - { - m.PackItem(new Lockpick(20)); - break; - } - case SkillName.Lumberjacking: - { - EquipItem(m, elfOrHuman ? new Hatchet() : new DualShortAxes()); - break; - } - case SkillName.Macing: - { - EquipItem(m, MacingWeapon(raceFlag)); - break; - } - case SkillName.Magery: - { - var regs = new BagOfReagents(30) { LootType = LootType.Regular }; - - if (!Core.AOS) - { - foreach (var item in regs.Items) - { - item.LootType = LootType.Newbied; - } - } - - m.PackItem(regs); - m.PackScroll(0); - m.PackScroll(1); - m.PackScroll(2); - - EquipItem(m, new Spellbook(0x382A8C38ul) { LootType = LootType.Blessed }); - EquipItem(m, Robe(raceFlag, female, Utility.RandomBlueHue())); - - if (elf) - { - EquipItem(m, new Circlet()); - } - else if (human) - { - EquipItem(m, new WizardsHat()); - } - - break; - } - case SkillName.Mining: - { - m.PackItem(new Pickaxe()); - break; - } - case SkillName.Musicianship: - { - m.PackInstrument(); - break; - } - case SkillName.Necromancy: - { - if (Core.ML) - { - m.PackItem(new BagOfNecroReagents { LootType = LootType.Regular }); - } - - break; - } - case SkillName.Ninjitsu: - { - if (elfOrHuman) - { - // Delete pants - m?.FindItemOnLayer(Layer.OuterLegs)?.Delete(); - - EquipItem(m, new Hakama(0x2C3)); // Only ninjas get the hued one. - EquipItem(m, new Kasa()); - } - - EquipItem(m, new BookOfNinjitsu()); - break; - } - case SkillName.Parry: - { - Item shield = raceFlag switch - { - Race.AllowGargoylesOnly => new GargishWoodenShield(), - _ => new WoodenShield() - }; - EquipItem(m, shield); - - break; - } - case SkillName.Peacemaking: - { - m.PackInstrument(); - break; - } - case SkillName.Poisoning: - { - m.PackItem(new LesserPoisonPotion()); - m.PackItem(new LesserPoisonPotion()); - break; - } - case SkillName.Provocation: - { - m.PackInstrument(); - break; - } - case SkillName.Stealing: - case SkillName.Snooping: - { - m.PackItem(new Lockpick(20)); - break; - } - case SkillName.SpiritSpeak: - { - EquipItem(m, new Cloak(0x455)); - break; - } - case SkillName.Tactics: - case SkillName.Swords: - { - EquipItem(m, SwordsWeapon(raceFlag)); - - break; - } - case SkillName.Tailoring: - { - m.PackItem(new BoltOfCloth()); - m.PackItem(new SewingKit()); - break; - } - case SkillName.Tinkering: - { - if (!Core.AOS) - { - m.PackTinkerPart(); - m.PackTinkerPart(); - m.PackTinkerPart(); - } - m.PackItem(new TinkerTools()); - break; - } - case SkillName.Tracking: - { - if (elfOrHuman) - { - // Delete shoes - m?.FindItemOnLayer(Layer.Shoes)?.Delete(); - - var hue = Utility.RandomYellowHue(); - EquipItem(m, elf ? new ElvenBoots(hue) : new Boots(hue)); - } - - EquipItem(m, new SkinningKnife()); - break; - } - case SkillName.Veterinary: - { - m.PackItem(new Bandage(5)); - m.PackItem(new Scissors()); - break; - } - case SkillName.Wrestling: - { - Item item = raceFlag switch - { - Race.AllowElvesOnly => new LeafGloves(), - Race.AllowGargoylesOnly when m.Female => new GargishLeatherArmsType2(), - Race.AllowGargoylesOnly => new GargishLeatherArmsType1(), - _ => new LeatherGloves() - }; - EquipItem(m, item); - break; - } - case SkillName.Throwing: - { - if (gargoyle) - { - // EquipItem(m, new Boomerang()); - } - - break; - } - case SkillName.Mysticism: - { - // PackItem(new MysticBook(0xAB)); - break; - } + skill.BaseFixedPoint = value * 10; + m.AddSkillItems(name); } } } + + private static void EquipItem(Mobile m, Item item, bool mustEquip = false) + { + if (item == null) + { + return; + } + + if (!Core.AOS) + { + item.LootType = LootType.Newbied; + } + + if (m?.EquipItem(item) == true) + { + return; + } + + var pack = m?.Backpack; + + if (!mustEquip && pack != null) + { + pack.DropItem(item); + } + else + { + item.Delete(); + } + } + + private static void PackItem(this Mobile m, Item item) + { + if (!Core.AOS) + { + item.LootType = LootType.Newbied; + } + + var pack = m.Backpack; + + if (pack != null) + { + pack.DropItem(item); + } + else + { + item.Delete(); + } + } + + private static void AddShirt(this Mobile m, int shirtHue) + { + var hue = Utility.ClipDyedHue(shirtHue & 0x3FFF); + var raceFlag = m.Race.RaceFlag; + + var shirt = raceFlag switch + { + Race.AllowElvesOnly => new ElvenShirt(hue), + Race.AllowGargoylesOnly when m.Female => new GargishClothChestType2 { Hue = hue }, + Race.AllowGargoylesOnly => new GargishClothChestType1 { Hue = hue }, + // Humans + _ => (Item)(Utility.Random(3) switch + { + 0 => new Shirt(hue), + 1 => new FancyShirt(hue), + _ => new Doublet(hue) + }) + }; + + EquipItem(m, shirt); + } + + private static void AddPants(this Mobile m, int pantsHue) + { + var hue = Utility.ClipDyedHue(pantsHue & 0x3FFF); + var raceFlag = m.Race.RaceFlag; + var female = m.Female; + + var pants = raceFlag switch + { + Race.AllowElvesOnly => new ElvenPants(hue), + Race.AllowGargoylesOnly when female => new GargishClothLegsType2 { Hue = hue }, + Race.AllowGargoylesOnly => new GargishClothLegsType1 { Hue = hue }, + // Humans + _ => (Item)(Utility.RandomBool() switch + { + true when female => new Skirt(hue), + true => new LongPants(hue), + false when female => new Kilt(hue), + false => new ShortPants(hue) + }) + }; + + EquipItem(m, pants); + } + + private static void AddShoes(this Mobile m) + { + if (m.Race == Race.Elf) + { + EquipItem(m, new ElvenBoots()); + } + else if (m.Race == Race.Human) + { + EquipItem(m, new Shoes(Utility.RandomYellowHue())); + } + } + + private static void PackInstrument(this Mobile m) + { + Item instrument = Utility.Random(6) switch + { + 0 => new Drums(), + 1 => new Harp(), + 2 => new LapHarp(), + 3 => new Lute(), + 4 => new Tambourine(), + _ => new TambourineTassel() + }; + + m.PackItem(instrument); + } + + private static void PackScroll(this Mobile m, int circle) + { + Item item = (Utility.Random(8) * (circle + 1)) switch + { + 0 => new ClumsyScroll(), + 1 => new CreateFoodScroll(), + 2 => new FeeblemindScroll(), + 3 => new HealScroll(), + 4 => new MagicArrowScroll(), + 5 => new NightSightScroll(), + 6 => new ReactiveArmorScroll(), + 7 => new WeakenScroll(), + 8 => new AgilityScroll(), + 9 => new CunningScroll(), + 10 => new CureScroll(), + 11 => new HarmScroll(), + 12 => new MagicTrapScroll(), + 13 => new MagicUnTrapScroll(), + 14 => new ProtectionScroll(), + 15 => new StrengthScroll(), + 16 => new BlessScroll(), + 17 => new FireballScroll(), + 18 => new MagicLockScroll(), + 19 => new PoisonScroll(), + 20 => new TelekinesisScroll(), + 21 => new TeleportScroll(), + 22 => new UnlockScroll(), + _ => new WallOfStoneScroll() + }; + + m.PackItem(item); + } + + private static void PackTinkerPart(this Mobile m) + { + Item item = Utility.Random(4) switch + { + 0 => new Axle(), + 1 => new Gears(), + 2 => new Hinge(), + 3 => new Springs() + }; + + m.PackItem(item); + } + + private static Item NecroHue(Item item) + { + item.Hue = 0x2C3; + + return item; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Item Robe(int raceFlag, bool female, int hue) => + raceFlag switch + { + Race.AllowElvesOnly when female => new FemaleElvenRobe(hue), + Race.AllowElvesOnly => new MaleElvenRobe(hue), + _ => new Robe(hue) + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Item SwordsWeapon(int raceFlag) => + raceFlag switch + { + Race.AllowElvesOnly => new RuneBlade(), + Race.AllowGargoylesOnly => new DreadSword(), + _ => new Katana() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Item MacingWeapon(int raceFlag) => + raceFlag switch + { + Race.AllowElvesOnly => new DiamondMace(), + Race.AllowGargoylesOnly => new DiscMace(), + _ => new Club() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Item FencingWeapon(int raceFlag) => + raceFlag switch + { + Race.AllowElvesOnly => new Leafblade(), + Race.AllowGargoylesOnly => new BloodBlade(), + _ => new Kryss() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Item RangedWeapon(int raceFlag) => + raceFlag switch + { + Race.AllowElvesOnly => new ElvenCompositeLongbow(), + Race.AllowGargoylesOnly => new SerpentstoneStaff(), + _ => new Bow() + }; + + private static void AddSkillItems(this Mobile m, SkillName skill) + { + var raceFlag = m.Race.RaceFlag; + var elf = m.Race == Race.Elf; + var human = m.Race == Race.Human; + var gargoyle = m.Race == Race.Gargoyle; + var elfOrHuman = Race.IsAllowedRace(m.Race, Race.AllowHumanOrElves); + var female = m.Female; + + switch (skill) + { + case SkillName.Alchemy: + { + m.PackItem(new Bottle(4)); + m.PackItem(new MortarPestle()); + EquipItem(m, Robe(raceFlag, female, Utility.RandomPinkHue())); + + break; + } + case SkillName.Anatomy: + { + m.PackItem(new Bandage(3)); + EquipItem(m, Robe(raceFlag, female, Utility.RandomYellowHue())); + + break; + } + case SkillName.AnimalLore: + { + if (elf) + { + EquipItem(m, new WildStaff()); + } + else + { + EquipItem(m, new ShepherdsCrook()); + } + + EquipItem(m, Robe(raceFlag, female, Utility.RandomGreenHue())); + + break; + } + case SkillName.AnimalTaming: + { + if (human) + { + EquipItem(m, new ShepherdsCrook()); + } + + break; + } + case SkillName.Archery: + { + m.PackItem(new Arrow(25)); + + EquipItem(m, RangedWeapon(raceFlag)); + + break; + } + case SkillName.ArmsLore: + { + Item item = Utility.Random(3) switch + { + 0 => FencingWeapon(raceFlag), + 1 => MacingWeapon(raceFlag), + _ => SwordsWeapon(raceFlag) + }; + EquipItem(m, item); + + break; + } + case SkillName.Begging: + { + Item staff = raceFlag switch + { + Race.AllowElvesOnly => new WildStaff(), + Race.AllowGargoylesOnly => new GlassStaff(), + _ => new GnarledStaff() + }; + + EquipItem(m, staff); + break; + } + case SkillName.Blacksmith: + { + m.PackItem(new Tongs()); + m.PackItem(new Pickaxe()); + m.PackItem(new Pickaxe()); + m.PackItem(new IronIngot(50)); + EquipItem(m, new HalfApron(Utility.RandomYellowHue())); + break; + } + case SkillName.Bushido: + { + if (elfOrHuman) + { + // Delete pants + m.FindItemOnLayer(Layer.OuterLegs)?.Delete(); + + EquipItem(m, new Hakama()); + EquipItem(m, new Kasa()); + } + + EquipItem(m, new BookOfBushido()); + break; + } + case SkillName.Fletching: + { + m.PackItem(new Board(14)); + m.PackItem(new Feather(5)); + m.PackItem(new Shaft(5)); + break; + } + case SkillName.Camping: + { + m.PackItem(new Bedroll()); + m.PackItem(new Kindling(5)); + break; + } + case SkillName.Carpentry: + { + m.PackItem(new Board(10)); + m.PackItem(new Saw()); + + if (elfOrHuman) + { + EquipItem(m, new HalfApron(Utility.RandomYellowHue())); + } + + break; + } + case SkillName.Cartography: + { + m.PackItem(new BlankMap()); + m.PackItem(new BlankMap()); + m.PackItem(new BlankMap()); + m.PackItem(new BlankMap()); + m.PackItem(new Sextant()); + break; + } + case SkillName.Cooking: + { + m.PackItem(new Kindling(2)); + m.PackItem(new RawLambLeg()); + m.PackItem(new RawChickenLeg()); + m.PackItem(new RawFishSteak()); + m.PackItem(new SackFlour()); + m.PackItem(new Pitcher(BeverageType.Water)); + break; + } + case SkillName.Chivalry: + { + m.PackItem(new BookOfChivalry()); + break; + } + case SkillName.DetectHidden: + { + if (elfOrHuman) + { + EquipItem(m, new Cloak(0x455)); + } + + break; + } + case SkillName.Discordance: + { + m.PackInstrument(); + break; + } + case SkillName.Fencing: + { + EquipItem(m, FencingWeapon(raceFlag)); + break; + } + case SkillName.Fishing: + { + EquipItem(m, new FishingPole()); + + var hue = Utility.RandomYellowHue(); + if (elf) + { + EquipItem(m, new Circlet { Hue = hue }); + } + else if (human) + { + EquipItem(m, new FloppyHat(hue)); + } + + break; + } + case SkillName.Healing: + { + m.PackItem(new Bandage(50)); + m.PackItem(new Scissors()); + break; + } + case SkillName.Herding: + { + EquipItem(m, new ShepherdsCrook()); + break; + } + case SkillName.Hiding: + { + if (elfOrHuman) + { + EquipItem(m, new Cloak(0x455)); + } + + break; + } + case SkillName.Inscribe: + { + m.PackItem(new BlankScroll(2)); + m.PackItem(new BlueBook()); + break; + } + case SkillName.ItemID: + { + Item staff = raceFlag switch + { + Race.AllowElvesOnly => new WildStaff(), + Race.AllowGargoylesOnly => new SerpentstoneStaff(), + _ => new GnarledStaff() + }; + + EquipItem(m, staff); + break; + } + case SkillName.Lockpicking: + { + m.PackItem(new Lockpick(20)); + break; + } + case SkillName.Lumberjacking: + { + EquipItem(m, elfOrHuman ? new Hatchet() : new DualShortAxes()); + break; + } + case SkillName.Macing: + { + EquipItem(m, MacingWeapon(raceFlag)); + break; + } + case SkillName.Magery: + { + var regs = new BagOfReagents(30) { LootType = LootType.Regular }; + + if (!Core.AOS) + { + foreach (var item in regs.Items) + { + item.LootType = LootType.Newbied; + } + } + + m.PackItem(regs); + m.PackScroll(0); + m.PackScroll(1); + m.PackScroll(2); + + EquipItem(m, new Spellbook(0x382A8C38ul) { LootType = LootType.Blessed }); + EquipItem(m, Robe(raceFlag, female, Utility.RandomBlueHue())); + + if (elf) + { + EquipItem(m, new Circlet()); + } + else if (human) + { + EquipItem(m, new WizardsHat()); + } + + break; + } + case SkillName.Mining: + { + m.PackItem(new Pickaxe()); + break; + } + case SkillName.Musicianship: + { + m.PackInstrument(); + break; + } + case SkillName.Necromancy: + { + if (Core.ML) + { + m.PackItem(new BagOfNecroReagents { LootType = LootType.Regular }); + } + + break; + } + case SkillName.Ninjitsu: + { + if (elfOrHuman) + { + // Delete pants + m.FindItemOnLayer(Layer.OuterLegs)?.Delete(); + + EquipItem(m, new Hakama(0x2C3)); // Only ninjas get the hued one. + EquipItem(m, new Kasa()); + } + + EquipItem(m, new BookOfNinjitsu()); + break; + } + case SkillName.Parry: + { + Item shield = raceFlag switch + { + Race.AllowGargoylesOnly => new GargishWoodenShield(), + _ => new WoodenShield() + }; + EquipItem(m, shield); + + break; + } + case SkillName.Peacemaking: + { + m.PackInstrument(); + break; + } + case SkillName.Poisoning: + { + m.PackItem(new LesserPoisonPotion()); + m.PackItem(new LesserPoisonPotion()); + break; + } + case SkillName.Provocation: + { + m.PackInstrument(); + break; + } + case SkillName.Stealing: + case SkillName.Snooping: + { + m.PackItem(new Lockpick(20)); + break; + } + case SkillName.SpiritSpeak: + { + EquipItem(m, new Cloak(0x455)); + break; + } + case SkillName.Tactics: + case SkillName.Swords: + { + EquipItem(m, SwordsWeapon(raceFlag)); + + break; + } + case SkillName.Tailoring: + { + m.PackItem(new BoltOfCloth()); + m.PackItem(new SewingKit()); + break; + } + case SkillName.Tinkering: + { + if (!Core.AOS) + { + m.PackTinkerPart(); + m.PackTinkerPart(); + m.PackTinkerPart(); + } + m.PackItem(new TinkerTools()); + break; + } + case SkillName.Tracking: + { + if (elfOrHuman) + { + // Delete shoes + m.FindItemOnLayer(Layer.Shoes)?.Delete(); + + var hue = Utility.RandomYellowHue(); + EquipItem(m, elf ? new ElvenBoots(hue) : new Boots(hue)); + } + + EquipItem(m, new SkinningKnife()); + break; + } + case SkillName.Veterinary: + { + m.PackItem(new Bandage(5)); + m.PackItem(new Scissors()); + break; + } + case SkillName.Wrestling: + { + Item item = raceFlag switch + { + Race.AllowElvesOnly => new LeafGloves(), + Race.AllowGargoylesOnly when m.Female => new GargishLeatherArmsType2(), + Race.AllowGargoylesOnly => new GargishLeatherArmsType1(), + _ => new LeatherGloves() + }; + EquipItem(m, item); + break; + } + case SkillName.Throwing: + { + if (gargoyle) + { + EquipItem(m, new Boomerang()); + } + + break; + } + case SkillName.Mysticism: + { + // PackItem(new MysticBook(0xAB)); + break; + } + } + } } diff --git a/Projects/UOContent/Misc/Cleanup.cs b/Projects/UOContent/Misc/Cleanup.cs index 5678ab851..176d92188 100644 --- a/Projects/UOContent/Misc/Cleanup.cs +++ b/Projects/UOContent/Misc/Cleanup.cs @@ -122,14 +122,14 @@ namespace Server.Misc if (boxes > 0) { logger.Information( - "Cleanup: Detected {0} inaccessible items, including {1} bank boxes, removing..", + "Cleanup: Detected {Count} inaccessible items, including {BankBoxes} bank boxes, removing..", items.Count, boxes ); } else { - logger.Information("Cleanup: Detected {0} inaccessible items, removing..", items.Count); + logger.Information("Cleanup: Detected {Count} inaccessible items, removing..", items.Count); } for (var i = 0; i < items.Count; ++i) diff --git a/Projects/UOContent/Misc/ClientVerification.cs b/Projects/UOContent/Misc/ClientVerification.cs index 7b264eec8..08ff2f7dc 100644 --- a/Projects/UOContent/Misc/ClientVerification.cs +++ b/Projects/UOContent/Misc/ClientVerification.cs @@ -1,6 +1,4 @@ using System; -using System.Buffers.Binary; -using System.IO; using Server.Buffers; using Server.Gumps; using Server.Logging; @@ -14,7 +12,6 @@ namespace Server.Misc private static readonly ILogger logger = LogFactory.GetLogger(typeof(ClientVerification)); private static bool _enable; - private static bool _detectClientRequirement; private static InvalidClientResponse _invalidClientResponse; private static string _versionExpression; @@ -33,11 +30,6 @@ namespace Server.Misc MinRequired = ServerConfiguration.GetSetting("clientVerification.minRequired", (ClientVersion)null); MaxRequired = ServerConfiguration.GetSetting("clientVerification.maxRequired", (ClientVersion)null); - if (MinRequired == null && MaxRequired == null) - { - _detectClientRequirement = ServerConfiguration.GetOrUpdateSetting("clientVerification.detectFromClientExe", true); - } - _enable = ServerConfiguration.GetOrUpdateSetting("clientVerification.enable", true); _invalidClientResponse = ServerConfiguration.GetOrUpdateSetting("clientVerification.invalidClientResponse", InvalidClientResponse.Kick); @@ -53,40 +45,9 @@ namespace Server.Misc { EventSink.ClientVersionReceived += EventSink_ClientVersionReceived; - if (_detectClientRequirement) + if (MinRequired == null && MaxRequired == null) { - var path = Core.FindDataFile("client.exe", false); - - if (File.Exists(path)) - { - using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - var buffer = GC.AllocateUninitializedArray((int)fs.Length, true); - fs.Read(buffer); - // VS_VERSION_INFO (unicode) - Span vsVersionInfo = stackalloc byte[] - { - 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, - 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, - 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00, - 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00 - }; - - for (var i = 0; i < buffer.Length; i++) - { - if (vsVersionInfo.SequenceEqual(buffer.AsSpan(i, 30))) - { - var offset = i + 42; // 30 + 12 - - var minorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset)); - var majorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 2)); - var privatePart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 4)); - var buildPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 6)); - - MinRequired = new ClientVersion(majorPart, minorPart, buildPart, privatePart); - break; - } - } - } + MinRequired = UOClient.ServerClientVersion; } if (MinRequired != null || MaxRequired != null) @@ -120,7 +81,7 @@ namespace Server.Misc private static void EventSink_ClientVersionReceived(NetState state, ClientVersion version) { - using var message = new ValueStringBuilder(); + using var message = ValueStringBuilder.Create(); if (!_enable || state.Mobile?.AccessLevel != AccessLevel.Player) { diff --git a/Projects/UOContent/Misc/CrashGuard.cs b/Projects/UOContent/Misc/CrashGuard.cs index c41b213fa..a6a695c89 100644 --- a/Projects/UOContent/Misc/CrashGuard.cs +++ b/Projects/UOContent/Misc/CrashGuard.cs @@ -195,7 +195,7 @@ namespace Server.Misc if (m != null) { - op.Write($" (mobile = 0x{m.Serial.Value:X} '{m.Name}')"); + op.Write($" (mobile = {m.Serial} '{m.Name}')"); } op.WriteLine(); diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/DecorativeTopiary.cs b/Projects/UOContent/Misc/Gifts/Winter2004/DecorativeTopiary.cs index 3e7f3c02b..22862e652 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/DecorativeTopiary.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/DecorativeTopiary.cs @@ -20,7 +20,7 @@ namespace Server.Items LabelTo(from, 1070880); // Winter 2004 } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/FestiveCactus.cs b/Projects/UOContent/Misc/Gifts/Winter2004/FestiveCactus.cs index e347b2ada..ff92515af 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/FestiveCactus.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/FestiveCactus.cs @@ -20,7 +20,7 @@ namespace Server.Items LabelTo(from, 1070880); // Winter 2004 } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs b/Projects/UOContent/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs index 8103c7c00..c65cf69d4 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs @@ -49,7 +49,7 @@ namespace Server.Items LabelTo(from, 1070880); // Winter 2004 } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs b/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs index 004f2fb43..67645e105 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs @@ -202,7 +202,7 @@ namespace Server.Items LabelTo(from, 1070880); // Winter 2004 } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs b/Projects/UOContent/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs index 69e09d85a..47335b9d5 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs @@ -47,7 +47,7 @@ namespace Server.Items LabelTo(from, 1070880); // Winter 2004 } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/SnowyTree.cs b/Projects/UOContent/Misc/Gifts/Winter2004/SnowyTree.cs index c9c038acc..329240a50 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/SnowyTree.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/SnowyTree.cs @@ -20,7 +20,7 @@ namespace Server.Items LabelTo(from, 1070880); // Winter 2004 } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Misc/HardwareInfo.cs b/Projects/UOContent/Misc/HardwareInfo.cs index e3a651da7..eeaff8304 100644 --- a/Projects/UOContent/Misc/HardwareInfo.cs +++ b/Projects/UOContent/Misc/HardwareInfo.cs @@ -87,9 +87,9 @@ namespace Server [CommandProperty(AccessLevel.GameMaster)] public DateTime TimeReceived { get; private set; } - public static void Configure() + public static unsafe void Configure() { - IncomingPackets.Register(0xD9, 0x10C, false, OnReceive); + IncomingPackets.Register(0xD9, 0x10C, false, &OnReceive); CommandSystem.Register("HWInfo", AccessLevel.GameMaster, HWInfo_OnCommand); } @@ -141,7 +141,7 @@ namespace Server } } - public static void OnReceive(NetState state, CircularBufferReader reader, ref int packetLength) + public static void OnReceive(NetState state, CircularBufferReader reader, int packetLength) { reader.ReadByte(); // 1: <4.0.1a, 2>=4.0.1a diff --git a/Projects/UOContent/Misc/LootPack.cs b/Projects/UOContent/Misc/LootPack.cs index 4e71aa7d9..6939c4d4c 100644 --- a/Projects/UOContent/Misc/LootPack.cs +++ b/Projects/UOContent/Misc/LootPack.cs @@ -1092,7 +1092,7 @@ namespace Server return; } - Count = Utility.ToInt32(str.Substring(start, index)); + Count = Utility.ToInt32(str.AsSpan(start, index)); start = index + 1; index = str.IndexOf('+', start); @@ -1109,7 +1109,7 @@ namespace Server index = str.Length; } - Sides = Utility.ToInt32(str.Substring(start, index - start)); + Sides = Utility.ToInt32(str.AsSpan(start, index - start)); if (index == str.Length) { @@ -1119,7 +1119,7 @@ namespace Server start = index + 1; index = str.Length; - Bonus = Utility.ToInt32(str.Substring(start, index - start)); + Bonus = Utility.ToInt32(str.AsSpan(start, index - start)); if (negative) { diff --git a/Projects/UOContent/Misc/PacketThrottles.cs b/Projects/UOContent/Misc/PacketThrottles.cs index d2b666ed9..33ef421c7 100644 --- a/Projects/UOContent/Misc/PacketThrottles.cs +++ b/Projects/UOContent/Misc/PacketThrottles.cs @@ -10,9 +10,9 @@ namespace Server.Network { // Delay in milliseconds private static readonly int[] Delays = new int[0x100]; - private static string ThrottlesConfiguration = "Configuration/throttles.json"; + private const string ThrottlesConfiguration = "Configuration/throttles.json"; - public static void Initialize() + public static unsafe void Initialize() { CommandSystem.Register("GetThrottle", AccessLevel.Administrator, GetThrottle); CommandSystem.Register("SetThrottle", AccessLevel.Administrator, SetThrottle); @@ -38,8 +38,8 @@ namespace Server.Network } else { - Delays[0x03] = 5; // Speech - Delays[0xAD] = 5; // Speech + Delays[0x03] = 25; // Speech + Delays[0xAD] = 25; // Speech Delays[0x75] = 500; // Rename request } @@ -47,7 +47,7 @@ namespace Server.Network { if (Delays[i] > 0) { - IncomingPackets.RegisterThrottler(i, Throttle); + IncomingPackets.RegisterThrottler(i, &Throttle); } } @@ -77,7 +77,7 @@ namespace Server.Network [Usage("SetThrottle ")] [Description("Sets a throttle for the given packet.")] - public static void SetThrottle(CommandEventArgs e) + public static unsafe void SetThrottle(CommandEventArgs e) { if (e.Length != 2) { @@ -104,7 +104,7 @@ namespace Server.Network if (oldDelay == 0 && delay > 0) { - IncomingPackets.RegisterThrottler(packetID, Throttle); + IncomingPackets.RegisterThrottler(packetID, &Throttle); } else if (oldDelay > 0 && delay == 0) { @@ -141,7 +141,7 @@ namespace Server.Network return true; } - if (Core.TickCount < ns.GetPacketDelay(packetID) + Delays[packetID]) + if (Core.TickCount < ns.GetPacketTime(packetID) + Delays[packetID]) { drop = true; return false; diff --git a/Projects/UOContent/Misc/Poison.cs b/Projects/UOContent/Misc/Poison.cs index 68d61d977..e0ce1542b 100644 --- a/Projects/UOContent/Misc/Poison.cs +++ b/Projects/UOContent/Misc/Poison.cs @@ -158,8 +158,8 @@ namespace Server AOS.Damage(m_Mobile, From, damage, 0, 0, 0, 100, 0); - if (Utility.RandomDouble() >= 0.60 - ) // OSI: randomly revealed between first and third damage tick, guessing 60% chance + // OSI: randomly revealed between first and third damage tick, guessing 60% chance + if (Utility.RandomDouble() >= 0.60) { m_Mobile.RevealingAction(); } diff --git a/Projects/UOContent/Misc/ProfessionInfo.cs b/Projects/UOContent/Misc/ProfessionInfo.cs index 6a2902d20..0c945556b 100644 --- a/Projects/UOContent/Misc/ProfessionInfo.cs +++ b/Projects/UOContent/Misc/ProfessionInfo.cs @@ -15,19 +15,21 @@ namespace Server return true; } - var lowerName = name.ToLowerInvariant().Replace(" ", ""); + var lowerName = name?.ToLowerInvariant().Replace(" ", ""); - foreach (var so in SkillInfo.Table) + if (!string.IsNullOrEmpty(lowerName)) { - if (lowerName == so.ProfessionSkillName.ToLowerInvariant()) + foreach (var so in SkillInfo.Table) { - skillName = (SkillName)so.SkillID; - return true; + if (lowerName == so.ProfessionSkillName.ToLowerInvariant()) + { + skillName = (SkillName)so.SkillID; + return true; + } } } return false; - } static ProfessionInfo() diff --git a/Projects/UOContent/Misc/ServerAccess.cs b/Projects/UOContent/Misc/ServerAccess.cs new file mode 100644 index 000000000..b5f86b9b7 --- /dev/null +++ b/Projects/UOContent/Misc/ServerAccess.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using Server.Accounting; +using Server.Json; +using Server.Logging; +using Server.Network; + +namespace Server.Misc; + +public static class ServerAccess +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerAccess)); + private const string _serverAccessConfigurationPath = "Configuration/server-access.json"; + public static ServerAccessConfiguration ServerAccessConfiguration { get; private set; } + + public static void SaveConfiguration() + { + var path = Path.Join(Core.BaseDirectory, _serverAccessConfigurationPath); + + JsonConfig.Serialize(path, ServerAccessConfiguration ??= new ServerAccessConfiguration()); + } + + public static void AddProtectedAccount(Account acct, bool save = false) + { + var username = acct.Username.ToLower(); + ServerAccessConfiguration.ProtectedAccounts.Add(username); + logger.Information("Protected account added: {Username}", username); + + if (save) + { + SaveConfiguration(); + } + } + + public static void RemoveProtectedAccount(Account acct, bool save = false) + { + var username = acct.Username.ToLower(); + ServerAccessConfiguration.ProtectedAccounts.Remove(username); + logger.Information("Protected account removed: {Username}", username); + + if (save) + { + SaveConfiguration(); + } + } + + public static void Configure() + { + var path = Path.Join(Core.BaseDirectory, _serverAccessConfigurationPath); + + if (!File.Exists(path)) + { + SaveConfiguration(); + return; + } + + ServerAccessConfiguration = JsonConfig.Deserialize(path); + if (ServerAccessConfiguration.ProtectedAccounts.Count > 0) + { + var protectedAccounts = string.Join(", ", ServerAccessConfiguration.ProtectedAccounts); + logger.Information("Protected accounts registered: {Count}", protectedAccounts); + } + } + + public static void Initialize() + { + EventSink.AccountLogin += EventSink_ResetProtectedAccount; + } + + public static void EventSink_ResetProtectedAccount(AccountLoginEventArgs e) + { + var username = e.Username.ToLower(); + if (!ServerAccessConfiguration.ProtectedAccounts.Contains(username)) + { + return; + } + + var acct = Accounts.GetAccount(username); + if (acct == null || !acct.Banned && acct.AccessLevel >= AccessLevel.Owner || !acct.CheckPassword(e.Password)) + { + return; + } + + acct.Banned = false; + acct.AccessLevel = AccessLevel.Owner; + + logger.Warning("Protected account \"{Username}\" has been reset.", username); + + if (e.RejectReason is ALRReason.Blocked or ALRReason.BadPass or ALRReason.BadComm) + { + e.Accepted = true; + } + } +} + +public record ServerAccessConfiguration +{ + [JsonPropertyName("protectedAccounts")] + public HashSet ProtectedAccounts { get; set; } = new(); +} diff --git a/Projects/UOContent/Misc/ServerList.cs b/Projects/UOContent/Misc/ServerList.cs index d457a102a..7b20de027 100644 --- a/Projects/UOContent/Misc/ServerList.cs +++ b/Projects/UOContent/Misc/ServerList.cs @@ -104,7 +104,7 @@ namespace Server.Misc if (_publicAddress != null) { - logger.Information("Auto-detected public IP address ({0})", _publicAddress); + logger.Information("Auto-detected public IP address ({IPAddress})", _publicAddress); } else { diff --git a/Projects/UOContent/Misc/SkillCheck.cs b/Projects/UOContent/Misc/SkillCheck.cs index e17527c3a..56f94bd01 100644 --- a/Projects/UOContent/Misc/SkillCheck.cs +++ b/Projects/UOContent/Misc/SkillCheck.cs @@ -3,488 +3,491 @@ using Server.Factions; using Server.Mobiles; using Server.Regions; -namespace Server.Misc +namespace Server.Misc; + +// TODO: Make this entirely configurable +public static class SkillCheck { - public static class SkillCheck + public enum Stat { - public enum Stat + Str, + Dex, + Int + } + + // Publish 16 changed max stats from 100 to 125 + private static int StatMax = Core.LBR ? 125 : 100; + + 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 readonly bool AntiMacroCode = !Core.ML; // Change this to false to disable anti-macro code + + public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes(5.0); // How long do we remember targets/locations? + + private static readonly bool[] UseAntiMacro = + { + // 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 + true, // Mysticism = 55 + true, // Imbuing = 56 + false, // Throwing = 57 + }; + + private static readonly TimeSpan m_StatGainDelay = TimeSpan.FromMinutes(Core.ML ? 0.05 : 15); + private static readonly TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes(5.0); + + public static void Initialize() + { + Mobile.SkillCheckLocationHandler = Mobile_SkillCheckLocation; + Mobile.SkillCheckDirectLocationHandler = Mobile_SkillCheckDirectLocation; + + Mobile.SkillCheckTargetHandler = Mobile_SkillCheckTarget; + Mobile.SkillCheckDirectTargetHandler = Mobile_SkillCheckDirectTarget; + } + + public static bool Mobile_SkillCheckLocation(Mobile from, SkillName skillName, double minSkill, double maxSkill) + { + var skill = from.Skills[skillName]; + + if (skill == null) { - Str, - Dex, - Int + return false; } - public const int Allowance = 3; // How many times may we use the same location/target for gain + var value = skill.Value; - private const int - LocationSize = 5; // The size of eeach location, make this smaller so players dont have to move as far - - private static readonly bool AntiMacroCode = !Core.ML; // Change this to false to disable anti-macro code - - public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes(5.0); // How long do we remember targets/locations? - - private static readonly bool[] UseAntiMacro = + if (value < minSkill) { - // 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 - true, // Mysticism = 55 - true, // Imbuing = 56 - false, // Throwing = 57 - }; - - private static readonly TimeSpan m_StatGainDelay = TimeSpan.FromMinutes(Core.ML ? 0.05 : 15); - private static readonly TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes(5.0); - - public static void Initialize() - { - Mobile.SkillCheckLocationHandler = Mobile_SkillCheckLocation; - Mobile.SkillCheckDirectLocationHandler = Mobile_SkillCheckDirectLocation; - - Mobile.SkillCheckTargetHandler = Mobile_SkillCheckTarget; - Mobile.SkillCheckDirectTargetHandler = Mobile_SkillCheckDirectTarget; + return false; // Too difficult } - public static bool Mobile_SkillCheckLocation(Mobile from, SkillName skillName, double minSkill, double maxSkill) + if (value >= maxSkill) { - var skill = from.Skills[skillName]; - - if (skill == null) - { - return false; - } - - var value = skill.Value; - - if (value < minSkill) - { - return false; // Too difficult - } - - if (value >= maxSkill) - { - return true; // No challenge - } - - var chance = (value - minSkill) / (maxSkill - minSkill); - - var loc = new Point2D(from.Location.X / LocationSize, from.Location.Y / LocationSize); - return CheckSkill(from, skill, loc, chance); + return true; // No challenge } - public static bool Mobile_SkillCheckDirectLocation(Mobile from, SkillName skillName, double chance) + var chance = (value - minSkill) / (maxSkill - minSkill); + + var 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) + { + var skill = from.Skills[skillName]; + + if (skill == null) { - var skill = from.Skills[skillName]; - - if (skill == null) - { - return false; - } - - if (chance < 0.0) - { - return false; // Too difficult - } - - if (chance >= 1.0) - { - return true; // No challenge - } - - var loc = new Point2D(from.Location.X / LocationSize, from.Location.Y / LocationSize); - return CheckSkill(from, skill, loc, chance); + return false; } - public static bool CheckSkill(Mobile from, Skill skill, object amObj, double chance) + if (chance < 0.0) { - if (from.Skills.Cap == 0) - { - return false; - } - - var success = chance >= Utility.RandomDouble(); - var 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 creature && creature.Controlled) - { - gc *= 2; - } - - if (from.Alive && (gc >= Utility.RandomDouble() && AllowGain(from, skill, amObj) || skill.Base < 10.0)) - { - Gain(from, skill); - } - - return success; + return false; // Too difficult } - public static bool Mobile_SkillCheckTarget( - Mobile from, SkillName skillName, object target, double minSkill, - double maxSkill - ) + if (chance >= 1.0) { - var skill = from.Skills[skillName]; - - if (skill == null) - { - return false; - } - - var value = skill.Value; - - if (value < minSkill) - { - return false; // Too difficult - } - - if (value >= maxSkill) - { - return true; // No challenge - } - - var chance = (value - minSkill) / (maxSkill - minSkill); - - return CheckSkill(from, skill, target, chance); + return true; // No challenge } - public static bool Mobile_SkillCheckDirectTarget(Mobile from, SkillName skillName, object target, double chance) + var 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) { - var skill = from.Skills[skillName]; - - if (skill == null) - { - return false; - } - - if (chance < 0.0) - { - return false; // Too difficult - } - - if (chance >= 1.0) - { - return true; // No challenge - } - - return CheckSkill(from, skill, target, chance); + return false; } - private static bool AllowGain(Mobile from, Skill skill, object obj) + var success = chance >= Utility.RandomDouble(); + var 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) { - if (Core.AOS && Faction.InSkillLoss(from)) // Changed some time between the introduction of AoS and SE. - { - return false; - } - - if (AntiMacroCode && from is PlayerMobile mobile && UseAntiMacro[skill.Info.SkillID]) - { - return mobile.AntiMacroCheck(skill, obj); - } - - return true; + gc = 0.01; } - public static void Gain(Mobile from, Skill skill) + if (from is BaseCreature creature && creature.Controlled) { - if (from.Region.IsPartOf()) + 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 + ) + { + var skill = from.Skills[skillName]; + + if (skill == null) + { + return false; + } + + var value = skill.Value; + + if (value < minSkill) + { + return false; // Too difficult + } + + if (value >= maxSkill) + { + return true; // No challenge + } + + var chance = (value - minSkill) / (maxSkill - minSkill); + + return CheckSkill(from, skill, target, chance); + } + + public static bool Mobile_SkillCheckDirectTarget(Mobile from, SkillName skillName, object target, double chance) + { + var skill = from.Skills[skillName]; + + if (skill == null) + { + return false; + } + + if (chance < 0.0) + { + return false; // Too difficult + } + + 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 (Core.AOS && Faction.InSkillLoss(from)) // Changed some time between the introduction of AoS and SE. + { + return false; + } + + if (AntiMacroCode && from is PlayerMobile mobile && UseAntiMacro[skill.Info.SkillID]) + { + return mobile.AntiMacroCheck(skill, obj); + } + + return true; + } + + public static void Gain(Mobile from, Skill skill) + { + if (from.Region.IsPartOf()) + { + return; + } + + if (from is BaseCreature creature && creature.IsDeadPet) + { + return; + } + + if (skill.SkillName == SkillName.Focus && from is BaseCreature) + { + return; + } + + if (skill.Base < skill.Cap && skill.Lock == SkillLock.Up) + { + var toGain = 1; + + if (skill.Base <= 10.0) { - return; + toGain = Utility.Random(4) + 1; } - if (from is BaseCreature creature && creature.IsDeadPet) - { - return; - } + var skills = from.Skills; - if (skill.SkillName == SkillName.Focus && from is BaseCreature) + if (from.Player && skills.Total / skills.Cap >= Utility.RandomDouble()) { - return; - } - - if (skill.Base < skill.Cap && skill.Lock == SkillLock.Up) - { - var toGain = 1; - - if (skill.Base <= 10.0) + for (var i = 0; i < skills.Length; ++i) { - toGain = Utility.Random(4) + 1; - } + var toLower = skills[i]; - var skills = from.Skills; - - if (from.Player && skills.Total / skills.Cap >= Utility.RandomDouble()) - { - for (var i = 0; i < skills.Length; ++i) + if (toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain) { - var toLower = skills[i]; - - if (toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain) - { - toLower.BaseFixedPoint -= toGain; - break; - } - } - } - - if (from is PlayerMobile pm && skill.SkillName == pm.AcceleratedSkill && - pm.AcceleratedStart > Core.Now) - { - toGain *= Utility.RandomMinMax(2, 5); - } - - if (!from.Player || skills.Total + toGain <= skills.Cap) - { - skill.BaseFixedPoint += toGain; - } - } - - if (skill.Lock == SkillLock.Up) - { - var 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) - { - return stat switch - { - Stat.Str => from.StrLock == StatLockType.Down && from.RawStr > 10, - Stat.Dex => from.DexLock == StatLockType.Down && from.RawDex > 10, - Stat.Int => from.IntLock == StatLockType.Down && from.RawInt > 10, - _ => false - }; - } - - public static bool CanRaise(Mobile from, Stat stat) - { - if (!(from is BaseCreature creature && creature.Controlled)) - { - if (from.RawStatTotal >= from.StatCap) - { - return false; - } - } - - return stat switch - { - Stat.Str => from.StrLock == StatLockType.Up && from.RawStr < 125, - Stat.Dex => from.DexLock == StatLockType.Up && from.RawDex < 125, - Stat.Int => from.IntLock == StatLockType.Up && from.RawInt < 125, - _ => 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; - } - + toLower.BaseFixedPoint -= toGain; 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; - } + if (from is PlayerMobile pm && skill.SkillName == pm.AcceleratedSkill && + pm.AcceleratedStart > Core.Now) + { + toGain *= Utility.RandomMinMax(2, 5); + } - 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; - } + if (!from.Player || skills.Total + toGain <= skills.Cap) + { + skill.BaseFixedPoint += toGain; } } - public static void GainStat(Mobile from, Stat stat) + if (skill.Lock == SkillLock.Up) { - switch (stat) + var info = skill.Info; + + if (from.StrLock == StatLockType.Up && info.StrGain / 33.3 > Utility.RandomDouble()) { - case Stat.Str: - { - if (from is BaseCreature creature && creature.Controlled) - { - if (creature.LastStrGain + m_PetStatGainDelay >= Core.Now) - { - return; - } - } - else if (from.LastStrGain + m_StatGainDelay >= Core.Now) - { - return; - } - - from.LastStrGain = Core.Now; - break; - } - case Stat.Dex: - { - if (from is BaseCreature creature && creature.Controlled) - { - if (creature.LastDexGain + m_PetStatGainDelay >= Core.Now) - { - return; - } - } - else if (from.LastDexGain + m_StatGainDelay >= Core.Now) - { - return; - } - - from.LastDexGain = Core.Now; - break; - } - case Stat.Int: - { - if (from is BaseCreature creature && creature.Controlled) - { - if (creature.LastIntGain + m_PetStatGainDelay >= Core.Now) - { - return; - } - } - else if (from.LastIntGain + m_StatGainDelay >= Core.Now) - { - return; - } - - from.LastIntGain = Core.Now; - break; - } + 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); } - - var atrophy = from.RawStatTotal / (double)from.StatCap >= Utility.RandomDouble(); - - IncreaseStat(from, stat, atrophy); } } + + public static bool CanLower(Mobile from, Stat stat) + { + return stat switch + { + Stat.Str => from.StrLock == StatLockType.Down && from.RawStr > 10, + Stat.Dex => from.DexLock == StatLockType.Down && from.RawDex > 10, + Stat.Int => from.IntLock == StatLockType.Down && from.RawInt > 10, + _ => false + }; + } + + public static bool CanRaise(Mobile from, Stat stat) + { + if (!(from is BaseCreature creature && creature.Controlled)) + { + if (from.RawStatTotal >= from.StatCap) + { + return false; + } + } + + return stat switch + { + Stat.Str => from.StrLock == StatLockType.Up && from.RawStr < StatMax, + Stat.Dex => from.DexLock == StatLockType.Up && from.RawDex < StatMax, + Stat.Int => from.IntLock == StatLockType.Up && from.RawInt < StatMax, + _ => 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; + } + } + } + + public static void GainStat(Mobile from, Stat stat) + { + switch (stat) + { + case Stat.Str: + { + if (from is BaseCreature creature && creature.Controlled) + { + if (creature.LastStrGain + m_PetStatGainDelay >= Core.Now) + { + return; + } + } + else if (from.LastStrGain + m_StatGainDelay >= Core.Now) + { + return; + } + + from.LastStrGain = Core.Now; + break; + } + case Stat.Dex: + { + if (from is BaseCreature creature && creature.Controlled) + { + if (creature.LastDexGain + m_PetStatGainDelay >= Core.Now) + { + return; + } + } + else if (from.LastDexGain + m_StatGainDelay >= Core.Now) + { + return; + } + + from.LastDexGain = Core.Now; + break; + } + case Stat.Int: + { + if (from is BaseCreature creature && creature.Controlled) + { + if (creature.LastIntGain + m_PetStatGainDelay >= Core.Now) + { + return; + } + } + else if (from.LastIntGain + m_StatGainDelay >= Core.Now) + { + return; + } + + from.LastIntGain = Core.Now; + break; + } + } + + var atrophy = from.RawStatTotal / (double)from.StatCap >= Utility.RandomDouble(); + + IncreaseStat(from, stat, atrophy); + } } diff --git a/Projects/UOContent/Misc/TextDefinition.cs b/Projects/UOContent/Misc/TextDefinition.cs index 8fbb4407c..97d71507b 100644 --- a/Projects/UOContent/Misc/TextDefinition.cs +++ b/Projects/UOContent/Misc/TextDefinition.cs @@ -66,7 +66,7 @@ namespace Server }; } - public static void AddTo(ObjectPropertyList list, TextDefinition def) + public static void AddTo(IPropertyList list, TextDefinition def) { if (def == null) { diff --git a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs index 2ceb4276c..063fc5221 100644 --- a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs +++ b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs @@ -2,43 +2,42 @@ using System.Collections.Generic; using Server.Mobiles; using Server.Targeting; -namespace Server.Targets +namespace Server.Targets; + +public class AIControlMobileTarget : Target { - public class AIControlMobileTarget : Target + private readonly List m_List; + + public AIControlMobileTarget(BaseAI ai, OrderType order) : base( + -1, + false, + order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None + ) { - private readonly List m_List; + m_List = new List(); + Order = order; - public AIControlMobileTarget(BaseAI ai, OrderType order) : base( - -1, - false, - order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None - ) + AddAI(ai); + } + + public OrderType Order { get; } + + public void AddAI(BaseAI ai) + { + if (!m_List.Contains(ai)) { - m_List = new List(); - Order = order; - - AddAI(ai); + m_List.Add(ai); } + } - public OrderType Order { get; } - - public void AddAI(BaseAI ai) + protected override void OnTarget(Mobile from, object o) + { + if (o is Mobile m) { - if (!m_List.Contains(ai)) + for (var i = 0; i < m_List.Count; ++i) { - m_List.Add(ai); - } - } - - protected override void OnTarget(Mobile from, object o) - { - if (o is Mobile m) - { - for (var i = 0; i < m_List.Count; ++i) - { - m_List[i].EndPickTarget(from, m, Order); - } + m_List[i].EndPickTarget(from, m, Order); } } } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index a339bf04a..73fd6c900 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -7,125 +7,140 @@ * */ -namespace Server.Mobiles +namespace Server.Mobiles; + +public class AnimalAI : BaseAI { - public class AnimalAI : BaseAI + public AnimalAI(BaseCreature m) : base(m) { - public AnimalAI(BaseCreature m) : base(m) + } + + public override bool DoActionWander() + { + // New, only flee @ 10% + + var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health { - } - - public override bool DoActionWander() - { - // New, only flee @ 10% - - var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; - - if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health + if (m_Mobile.Debug) { 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 + Action = ActionType.Flee; + } + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) { - base.DoActionWander(); + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); } + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + var combatant = m_Mobile.Combatant; + + if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone.."); + } + + Action = ActionType.Wander; + return true; } - public override bool DoActionCombat() + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - var combatant = m_Mobile.Combatant; - - if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map) + if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) { - m_Mobile.DebugSay("My combatant is gone.."); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}"); + } Action = ActionType.Wander; return true; } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (m_Mobile.Debug) { - 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; - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I should be closer to {0}", combatant.Name); - } + m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); } - - if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) - { - var 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() + if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) { var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; - if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health + if (hitPercent <= 0.1) { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am low on health!"); + } + Action = ActionType.Flee; } - else + } + + return true; + } + + public override bool DoActionBackoff() + { + var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // 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)) { - if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) + if (m_Mobile.Debug) { - 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; + m_Mobile.DebugSay("Well, here I am safe"); } + + Action = ActionType.Wander; + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my focus, lets relax"); } - return true; + Action = ActionType.Wander; } - public override bool DoActionFlee() - { - AcquireFocusMob(m_Mobile.RangePerception * 2, m_Mobile.FightMode, true, false, true); + return true; + } - m_Mobile.FocusMob ??= m_Mobile.Combatant; + public override bool DoActionFlee() + { + AcquireFocusMob(m_Mobile.RangePerception * 2, m_Mobile.FightMode, true, false, true); - return base.DoActionFlee(); - } + m_Mobile.FocusMob ??= m_Mobile.Combatant; + + return base.DoActionFlee(); } } diff --git a/Projects/UOContent/Mobiles/AI/ArcherAI.cs b/Projects/UOContent/Mobiles/AI/ArcherAI.cs index 75b9a7058..01faf9f5c 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -1,135 +1,126 @@ +using System; using Server.Items; -namespace Server.Mobiles -{ - public class ArcherAI : BaseAI - { - public ArcherAI(BaseCreature m) : base(m) - { - } +namespace Server.Mobiles; - public override bool DoActionWander() +public class ArcherAI : BaseAI +{ + public ArcherAI(BaseCreature m) : base(m) + { + } + + public override bool DoActionWander() + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I have no combatant"); + } - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) { - 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(); + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); } + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + return base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + if (m_Mobile.Combatant?.Deleted != false || !m_Mobile.Combatant.Alive || + m_Mobile.Combatant.IsDeadBondedPet) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is deleted"); + } + + Action = ActionType.Guard; return true; } - public override bool DoActionCombat() + if (Core.TickCount - m_Mobile.LastMoveTime > 1000) { - if (m_Mobile.Combatant?.Deleted != false || !m_Mobile.Combatant.Alive || - m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.DebugSay("My combatant is deleted"); - Action = ActionType.Guard; - return true; - } - - if (Core.TickCount - m_Mobile.LastMoveTime > 1000) - { - if ( - m_Mobile.Combatant != null && - !WalkMobileRange( - m_Mobile.Combatant, - 1, - true, - m_Mobile.RangeFight, - m_Mobile.Weapon.MaxRange - ) + if ( + m_Mobile.Combatant != null && + !WalkMobileRange( + m_Mobile.Combatant, + 1, + true, + m_Mobile.RangeFight, + m_Mobile.Weapon.MaxRange ) + ) + { + 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 am still not in range of {0}", m_Mobile.Combatant.Name); + m_Mobile.DebugSay($"I have lost {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; - } + m_Mobile.Combatant = null; + Action = ActionType.Guard; + return true; } } + } - // When we have no ammo, we flee - var pack = m_Mobile.Backpack; + // When we have no ammo, we flee + var pack = m_Mobile.Backpack; - if (pack?.FindItemByType() == null) + if (pack?.FindItemByType() == null) + { + Action = ActionType.Flee; + return true; + } + + // At 20% we should check if we must leave + if (m_Mobile.Combatant != null && m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee) + { + // 10% to flee + the diff of hits + var fleeChance = 10 + Math.Max(0, m_Mobile.Combatant.Hits - m_Mobile.Hits); + + if (Utility.Random(0, 100) > fleeChance) { Action = ActionType.Flee; - return true; } - - // At 20% we should check if we must leave - if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee) - { - var 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) - { - var 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; - } + 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 {m_Mobile.FocusMob.Name}, attacking"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); + } + + return true; + } +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI.cs index af010e7c4..77312eb8c 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using Server.ContextMenus; using Server.Engines.Quests; using Server.Engines.Quests.Necro; @@ -14,848 +13,876 @@ using Server.Spells.Spellweaving; using Server.Targets; using MoveImpl = Server.Movement.MovementImpl; -namespace Server.Mobiles +namespace Server.Mobiles; + +public enum AIType { - 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 +{ + private static readonly SkillName[] m_KeywordTable = { - AI_Use_Default, - AI_Melee, - AI_Animal, - AI_Archer, - AI_Healer, - AI_Vendor, - AI_Mage, - AI_Berserk, - AI_Predator, - AI_Thief + 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 + }; + + private static readonly Queue m_Obstacles = new(); + protected ActionType m_Action; + + public BaseCreature m_Mobile; + + private long m_NextDetectHidden; + private long m_NextStopGuard; + + protected PathFollower m_Path; + public Timer m_Timer; + + 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.Location).Active) + { + activate = false; + } + else + { + activate = true; + } + + if (activate) + { + m_Timer.Start(); + } + + Action = ActionType.Wander; } - public enum ActionType + public ActionType Action { - Wander, - Combat, - Guard, - Flee, - Backoff, - Interact + get => m_Action; + set + { + m_Action = value; + OnActionChanged(); + } } - public abstract class BaseAI + public long NextMove { get; set; } + + public virtual bool CanDetectHidden => m_Mobile.Skills.DetectHidden.Value > 0; + + public virtual bool WasNamed(string speech) { - private static readonly SkillName[] m_KeywordTable = + var name = m_Mobile.Name; + + return name != null && speech.InsensitiveStartsWith(name); + } + + public virtual void GetContextMenuEntries(Mobile from, List list) + { + if (from.Alive && m_Mobile.Controlled && from.InRange(m_Mobile, 14)) { - 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 - }; - - private static readonly Queue m_Obstacles = new(); - protected ActionType m_Action; - - public BaseCreature m_Mobile; - - private long m_NextDetectHidden; - private long m_NextStopGuard; - - protected PathFollower m_Path; - public Timer m_Timer; - - public BaseAI(BaseCreature m) - { - m_Mobile = m; - - m_Timer = new AITimer(this); - - bool activate; - - if (!m.PlayerRangeSensitive) + if (from == m_Mobile.ControlMaster) { - activate = true; - } - else if (World.Loading) - { - activate = false; - } - else if (m.Map == null || m.Map == Map.Internal || !m.Map.GetSector(m.Location).Active) - { - activate = false; - } - else - { - activate = true; - } + 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 (activate) - { - m_Timer.Start(); - } - - Action = ActionType.Wander; - } - - public ActionType Action - { - get => m_Action; - set - { - m_Action = value; - OnActionChanged(); - } - } - - public long NextMove { get; set; } - - public virtual bool CanDetectHidden => m_Mobile.Skills.DetectHidden.Value > 0; - - public virtual bool WasNamed(string speech) - { - var name = m_Mobile.Name; - - return name != null && speech.InsensitiveStartsWith(name); - } - - public virtual void GetContextMenuEntries(Mobile from, List list) - { - if (from.Alive && m_Mobile.Controlled && from.InRange(m_Mobile, 14)) - { - if (from == m_Mobile.ControlMaster) + if (m_Mobile.CanDrop) { - 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 && m_Mobile is not GrizzledMare) - { - 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 + list.Add(new InternalEntry(from, 6109, 14, m_Mobile, this, OrderType.Drop)); // Command: Drop } - else if (m_Mobile.IsPetFriend(from)) + + 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 && m_Mobile is not GrizzledMare) { - 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 + 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) + 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) { - if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map) + return; + } + + var isOwner = from == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(from); + + if (!isOwner && !isFriend) + { + return; + } + + 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 t) + { + if (t.Order == order) + { + t.AddAI(this); + } + } + } + + public virtual void OnAggressiveAction(Mobile aggressor) + { + var 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; + } + + var isOwner = from == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(from); + + if (!isOwner && !isFriend) + { + return; + } + + if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) + { + return; + } + + if (order == OrderType.Attack) + { + if (target is BaseCreature creature && creature.IsScaryToPets && m_Mobile.IsScaredOfScaryThings) + { + m_Mobile.SayTo(from, "Your pet refuses to attack this creature!"); return; } - var isOwner = from == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(from); - - if (!isOwner && !isFriend) + if (SolenHelper.CheckRedFriendship(from) && + target is RedSolenInfiltratorQueen or RedSolenInfiltratorWarrior or RedSolenQueen or RedSolenWarrior or RedSolenWorker + || SolenHelper.CheckBlackFriendship(from) && + target is BlackSolenInfiltratorQueen or BlackSolenInfiltratorWarrior or BlackSolenQueen or BlackSolenWarrior or BlackSolenWorker) { + from.SendAsciiMessage("You can not force your pet to attack a creature you are protected from."); return; } - if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) + if (target is BaseFactionGuard) { + m_Mobile.SayTo(from, "Your pet refuses to attack the guard."); 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 t) - { - if (t.Order == order) - { - t.AddAI(this); - } - } } - public virtual void OnAggressiveAction(Mobile aggressor) + if (m_Mobile.CheckControlChance(from)) { - var currentCombat = m_Mobile.Combatant; + m_Mobile.ControlTarget = target; + m_Mobile.ControlOrder = order; + } + } - if (currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && - m_Mobile.GetDistanceToSqrt(currentCombat) > m_Mobile.GetDistanceToSqrt(aggressor)) - { - m_Mobile.Combatant = aggressor; - } + public virtual bool HandlesOnSpeech(Mobile from) + { + if (from.AccessLevel >= AccessLevel.GameMaster) + { + return true; } - public virtual void EndPickTarget(Mobile from, Mobile target, OrderType order) + if (from.Alive && m_Mobile.Controlled && m_Mobile.Commandable && + (from == m_Mobile.ControlMaster || m_Mobile.IsPetFriend(from))) { - if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map || - !from.CheckAlive()) - { - return; - } - - var isOwner = from == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(from); - - if (!isOwner && !isFriend) - { - return; - } - - if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) - { - return; - } - - if (order == OrderType.Attack) - { - if (target is BaseCreature creature && creature.IsScaryToPets && m_Mobile.IsScaredOfScaryThings) - { - m_Mobile.SayTo(from, "Your pet refuses to attack this creature!"); - return; - } - - if (SolenHelper.CheckRedFriendship(from) && - target is RedSolenInfiltratorQueen or RedSolenInfiltratorWarrior or RedSolenQueen or RedSolenWarrior or RedSolenWorker - || SolenHelper.CheckBlackFriendship(from) && - target is BlackSolenInfiltratorQueen or BlackSolenInfiltratorWarrior or BlackSolenQueen or BlackSolenWarrior or BlackSolenWorker) - { - from.SendAsciiMessage("You can not force your pet to attack a creature you are protected from."); - return; - } - - if (target is 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; - } + return true; } - public virtual bool HandlesOnSpeech(Mobile from) + return from.Alive && from.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown(); + } + + public virtual void OnSpeech(SpeechEventArgs e) + { + if (e.Mobile.Alive && e.Mobile.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown()) { - if (from.AccessLevel >= AccessLevel.GameMaster) + if (e.HasKeyword(0x9D) && WasNamed(e.Speech)) // *move* { - 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(); - } - - 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) { - 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 - { - Clock.GetTime(m_Mobile, out var generalNumber, out _); - - 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 - { - var foundSomething = false; - - var ourSkills = m_Mobile.Skills; - var theirSkills = e.Mobile.Skills; - - for (var i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i) - { - var skill = ourSkills[i]; - var theirSkill = theirSkills[i]; - - if (skill?.Base >= 60.0 && m_Mobile.CheckTeach(skill.SkillName, e.Mobile)) - { - var toTeach = skill.Base / 3.0; - - if (toTeach > 42.0) - { - toTeach = 42.0; - } - - if (toTeach > theirSkill.Base) - { - var 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. - } - } + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { - var toTrain = (SkillName)(-1); - - for (var i = 0; toTrain == (SkillName)(-1) && i < e.Keywords.Length; ++i) - { - var keyword = e.Keywords[i]; - - if (keyword == 0x154) - { - toTrain = SkillName.Anatomy; - } - else if (keyword >= 0x6D && keyword <= 0x9C) - { - var 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 - { - var skills = m_Mobile.Skills; - var 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); - } - } - } + // Excuse me? + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); + WalkRandomInHome(2, 2, 1); } } - - if (m_Mobile.Controlled && m_Mobile.Commandable) + else if (e.HasKeyword(0x9E) && WasNamed(e.Speech)) // *time* { - m_Mobile.DebugSay("Listening..."); - - var isOwner = e.Mobile == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(e.Mobile); - - if (e.Mobile.Alive && (isOwner || isFriend)) + if (m_Mobile.Combatant != null) { - m_Mobile.DebugSay("It's from my master"); + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + } + else + { + Clock.GetTime(m_Mobile, out var generalNumber, out _); - var keywords = e.Keywords; - var speech = e.Speech; + 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 + { + var foundSomething = false; - // First, check the all* - for (var i = 0; i < keywords.Length; ++i) + var ourSkills = m_Mobile.Skills; + var theirSkills = e.Mobile.Skills; + + for (var i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i) { - var keyword = keywords[i]; + var skill = ourSkills[i]; + var theirSkill = theirSkills[i]; - switch (keyword) + if (skill?.Base >= 60.0 && m_Mobile.CheckTeach(skill.SkillName, e.Mobile)) { - case 0x164: // all come + var toTeach = skill.Base / 3.0; + + if (toTeach > 42.0) + { + toTeach = 42.0; + } + + if (toTeach > theirSkill.Base) + { + var number = 1043059 + i; + + if (number > 1043107) { - if (!isOwner) - { - break; - } - - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Come; - } - - return; + continue; } - case 0x165: // all follow + + if (!foundSomething) { - BeginPickTarget(e.Mobile, OrderType.Follow); - return; + m_Mobile.Say(1043058); // I can train the following: } - case 0x166: // all guard - case 0x16B: // all guard me - { - if (!isOwner) - { - break; - } - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Guard; - } + m_Mobile.Say(number); - 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 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; - } + foundSomething = true; + } } } - // No all*, so check *command - for (var i = 0; i < keywords.Length; ++i) + if (!foundSomething) { - var 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 || m_Mobile is GrizzledMare) - { - 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 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 || m_Mobile is GrizzledMare) - { - 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; - } - } + m_Mobile.Say(501505); // Alas, I cannot teach thee anything. } } } else { - if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) + var toTrain = (SkillName)(-1); + + for (var i = 0; toTrain == (SkillName)(-1) && i < e.Keywords.Length; ++i) { - m_Mobile.DebugSay("It's from a GM"); + var keyword = e.Keywords[i]; - if (m_Mobile.FindMyName(e.Speech, true)) + if (keyword == 0x154) { - var str = e.Speech.Split(' '); - int i; + toTrain = SkillName.Anatomy; + } + else if (keyword >= 0x6D && keyword <= 0x9C) + { + var index = keyword - 0x6D; - for (i = 0; i < str.Length; i++) + if (index >= 0 && index < m_KeywordTable.Length) { - var word = str[i]; + toTrain = m_KeywordTable[index]; + } + } + } - if (word.InsensitiveEquals("obey")) + 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 + { + var skills = m_Mobile.Skills; + var 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) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Listening..."); + } + + var isOwner = e.Mobile == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(e.Mobile); + + if (e.Mobile.Alive && (isOwner || isFriend)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("It's from my master"); + } + + var keywords = e.Keywords; + var speech = e.Speech; + + // First, check the all* + for (var i = 0; i < keywords.Length; ++i) + { + var keyword = keywords[i]; + + switch (keyword) + { + case 0x164: // all come { - m_Mobile.SetControlMaster(e.Mobile); - - if (m_Mobile.Summoned) + if (!isOwner) { - m_Mobile.SummonMaster = e.Mobile; + 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 + case 0x16B: // all guard me + { + 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 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 (var i = 0; i < keywords.Length; ++i) + { + var 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 || m_Mobile is GrizzledMare) + { + 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 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 || m_Mobile is GrizzledMare) + { + 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) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("It's from a GM"); + } + + if (m_Mobile.FindMyName(e.Speech, true)) + { + var str = e.Speech.Split(' '); + int i; + + for (i = 0; i < str.Length; i++) + { + var word = str[i]; + + if (word.InsensitiveEquals("obey")) + { + m_Mobile.SetControlMaster(e.Mobile); + + if (m_Mobile.Summoned) + { + m_Mobile.SummonMaster = e.Mobile; + } + + return; } } } } } + } - public virtual bool Think() + public virtual bool Think() + { + if (m_Mobile.Deleted) { - 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; - } + return false; } - public virtual void OnActionChanged() + if (CheckFlee()) { - switch (Action) - { - case ActionType.Wander: + 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: + case ActionType.Combat: + { m_Mobile.Warmode = true; m_Mobile.FocusMob = null; m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; + } - case ActionType.Guard: + case ActionType.Guard: + { m_Mobile.Warmode = true; m_Mobile.FocusMob = null; m_Mobile.Combatant = null; @@ -863,333 +890,535 @@ namespace Server.Mobiles m_NextStopGuard = Core.TickCount + (int)TimeSpan.FromSeconds(10).TotalMilliseconds; m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; + } - case ActionType.Flee: + case ActionType.Flee: + { m_Mobile.Warmode = true; m_Mobile.FocusMob = null; m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; break; + } - case ActionType.Interact: + case ActionType.Interact: + { m_Mobile.Warmode = false; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; break; + } - case ActionType.Backoff: + case ActionType.Backoff: + { m_Mobile.Warmode = false; m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; break; - } + } } + } - public virtual bool OnAtWayPoint() => true; + public virtual bool OnAtWayPoint() => true; - public virtual bool DoActionWander() + public virtual bool DoActionWander() + { + if (CheckHerding()) { - if (CheckHerding()) + if (m_Mobile.Debug) { m_Mobile.DebugSay("Praise the shepherd!"); } - else if (m_Mobile.CurrentWayPoint != null) + } + else if (m_Mobile.CurrentWayPoint != null) + { + var 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) { - var 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) + if (m_Mobile.Debug) { m_Mobile.DebugSay("I will move towards my waypoint."); - DoMove(m_Mobile.GetDirectionTo(m_Mobile.CurrentWayPoint)); } - else if (OnAtWayPoint()) + + DoMove(m_Mobile.GetDirectionTo(m_Mobile.CurrentWayPoint)); + } + else if (OnAtWayPoint()) + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I will go to the next waypoint"); - m_Mobile.CurrentWayPoint = point.NextPoint; - if (point.NextPoint?.Deleted == true) + } + + m_Mobile.CurrentWayPoint = point.NextPoint; + if (point.NextPoint?.Deleted == true) + { + m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; + } + } + } + else if (m_Mobile.IsAnimatedDead) + { + // animated dead follow their master + var 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?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + } + + return true; + } + + public virtual bool DoActionCombat() + { + if (Core.AOS && CheckHerding()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + } + else + { + var c = m_Mobile.Combatant; + + if (c?.Deleted != false || 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 (Core.AOS && CheckHerding()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + } + else if (Core.TickCount - m_NextStopGuard < 0) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am on guard"); + } + // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I stopped being on guard"); + } + + Action = ActionType.Wander; + } + + return true; + } + + public virtual bool DoActionFlee() + { + var from = m_Mobile.FocusMob; + + if (from?.Deleted != false || from.Map != m_Mobile.Map) + { + if (m_Mobile.Debug) + { + 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)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have fled"); + } + + Action = ActionType.Guard; + return true; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am fleeing!"); + } + + return true; + } + + public virtual bool DoActionInteract() => true; + + public virtual bool DoActionBackoff() => true; + + public virtual bool Obey() + { + return !m_Mobile.Deleted && m_Mobile.ControlOrder switch + { + OrderType.None => DoOrderNone(), + OrderType.Come => DoOrderCome(), + OrderType.Drop => DoOrderDrop(), + OrderType.Friend => DoOrderFriend(), + OrderType.Unfriend => DoOrderUnfriend(), + OrderType.Guard => DoOrderGuard(), + OrderType.Attack => DoOrderAttack(), + OrderType.Patrol => DoOrderPatrol(), + OrderType.Release => DoOrderRelease(), + OrderType.Stay => DoOrderStay(), + OrderType.Stop => DoOrderStop(), + OrderType.Follow => DoOrderFollow(), + OrderType.Transfer => DoOrderTransfer(), + _ => false + }; + } + + public virtual void OnCurrentOrderChanged() + { + if (m_Mobile.Deleted || m_Mobile.ControlMaster?.Deleted != false) + { + 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 = false; + 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; + m_Mobile.ControlMaster.SendLocalizedMessage(1049671, m_Mobile.Name); // ~1_PETNAME~ is now guarding you. + 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.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.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() + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have no order"); + } + + WalkRandomInHome(3, 2, 1); + + if (m_Mobile.Combatant?.Deleted == false && 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?.Deleted != false) + { + return true; + } + + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlMaster); + + if (iCurrDist > m_Mobile.RangePerception) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my master. I stay here"); + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My master told me come"); + } + + // Not exactly OSI style, but better than nothing. + var bRun = iCurrDist > 5; + + if (WalkMobileRange(m_Mobile.ControlMaster, 1, bRun, 0, 1)) + { + if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Warmode = true; + // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); + } + else + { + m_Mobile.Warmode = false; + } + } + } + + return true; + } + + public virtual bool DoOrderDrop() + { + if (m_Mobile.IsDeadPet || !m_Mobile.CanDrop) + { + return true; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I drop my stuff for my master"); + } + + var pack = m_Mobile.Backpack; + + if (pack != null) + { + var list = pack.Items; + + for (var 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() + { + var target = m_Mobile.TargetLocation; + + if (target == null) + { + return false; // Creature is not being herded + } + + var distance = m_Mobile.GetDistanceToSqrt(target); + + if (!(distance is < 1 or > 15)) + { + DoMove(m_Mobile.GetDirectionTo(target)); + return true; + } + + if (distance < 1 && target.X == 1076 && target.Y == 450 && m_Mobile is HordeMinionFamiliar) + { + if (m_Mobile.ControlMaster is PlayerMobile pm) + { + var qs = pm.Quest; + + if (qs is DarkTidesQuest) + { + QuestObjective obj = qs.FindObjective(); + + if (obj?.Completed == false) { - m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; + m_Mobile.AddToBackpack(new ScrollOfAbraxus()); + obj.Complete(); } } } - else if (m_Mobile.IsAnimatedDead) - { - // animated dead follow their master - var 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?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); - } - - return true; } - public virtual bool DoActionCombat() + m_Mobile.TargetLocation = null; + return false; // At the target or too far away + } + + public virtual bool DoOrderFollow() + { + if (CheckHerding()) { - if (Core.AOS && CheckHerding()) + if (m_Mobile.Debug) { m_Mobile.DebugSay("Praise the shepherd!"); } - else - { - var c = m_Mobile.Combatant; - - if (c?.Deleted != false || 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() + else if (m_Mobile.ControlTarget?.Deleted == false && m_Mobile.ControlTarget != m_Mobile) { - if (Core.AOS && CheckHerding()) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - else if (Core.TickCount - m_NextStopGuard < 0) - { - m_Mobile.DebugSay("I am on guard"); - // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); - } - else - { - m_Mobile.DebugSay("I stopped being on guard"); - Action = ActionType.Wander; - } - - return true; - } - - public virtual bool DoActionFlee() - { - var from = m_Mobile.FocusMob; - - if (from?.Deleted != false || 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; - } - - m_Mobile.DebugSay("I am fleeing!"); - - return true; - } - - public virtual bool DoActionInteract() => true; - - public virtual bool DoActionBackoff() => true; - - public virtual bool Obey() - { - if (m_Mobile.Deleted) - { - return false; - } - - return m_Mobile.ControlOrder switch - { - OrderType.None => DoOrderNone(), - OrderType.Come => DoOrderCome(), - OrderType.Drop => DoOrderDrop(), - OrderType.Friend => DoOrderFriend(), - OrderType.Unfriend => DoOrderUnfriend(), - OrderType.Guard => DoOrderGuard(), - OrderType.Attack => DoOrderAttack(), - OrderType.Patrol => DoOrderPatrol(), - OrderType.Release => DoOrderRelease(), - OrderType.Stay => DoOrderStay(), - OrderType.Stop => DoOrderStop(), - OrderType.Follow => DoOrderFollow(), - OrderType.Transfer => DoOrderTransfer(), - _ => false - }; - } - - public virtual void OnCurrentOrderChanged() - { - if (m_Mobile.Deleted || m_Mobile.ControlMaster?.Deleted != false) - { - 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; - var petname = $"{m_Mobile.Name}"; - m_Mobile.ControlMaster.SendLocalizedMessage(1049671, petname); // ~1_PETNAME~ is now guarding you. - 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.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.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?.Deleted == false && 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?.Deleted != false) - { - return true; - } - - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlMaster); + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlTarget); 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; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost the one to follow. I stay here"); + } + + if (m_Mobile.Combatant?.Deleted == false && 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 come"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"My master told me to follow: {m_Mobile.ControlTarget.Name}"); + } // Not exactly OSI style, but better than nothing. var bRun = iCurrDist > 5; - if (WalkMobileRange(m_Mobile.ControlMaster, 1, bRun, 0, 1)) + if (WalkMobileRange(m_Mobile.ControlTarget, 1, bRun, 0, 1)) { if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && !m_Mobile.Combatant.IsDeadBondedPet) @@ -1200,432 +1429,342 @@ namespace Server.Mobiles else { m_Mobile.Warmode = false; + if (Core.AOS) + { + m_Mobile.CurrentSpeed = 0.1; + } } } } - - return true; } - - public virtual bool DoOrderDrop() + else { - if (m_Mobile.IsDeadPet || !m_Mobile.CanDrop) + if (m_Mobile.Debug) { - return true; - } - - m_Mobile.DebugSay("I drop my stuff for my master"); - - var pack = m_Mobile.Backpack; - - if (pack != null) - { - var list = pack.Items; - - for (var i = list.Count - 1; i >= 0; --i) - { - if (i < list.Count) - { - list[i].MoveToWorld(m_Mobile.Location, m_Mobile.Map); - } - } + m_Mobile.DebugSay("I have nobody to follow"); } m_Mobile.ControlTarget = null; m_Mobile.ControlOrder = OrderType.None; - - return true; } - public virtual bool CheckHerding() + return true; + } + + public virtual bool DoOrderFriend() + { + var from = m_Mobile.ControlMaster; + var to = m_Mobile.ControlTarget; + + if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) { - var target = m_Mobile.TargetLocation; + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* + } + else + { + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - if (target == null) + if (youngFrom && !youngTo) { - return false; // Creature is not being herded + from.SendLocalizedMessage(502040); // As a young player, you may not friend pets to older players. } - - var distance = m_Mobile.GetDistanceToSqrt(target); - - if (!(distance is < 1 or > 15)) + else if (!youngFrom && youngTo) { - DoMove(m_Mobile.GetDirectionTo(target)); - return true; + from.SendLocalizedMessage(502041); // As an older player, you may not friend pets to young players. } - - if (distance < 1 && target.X == 1076 && target.Y == 450 && m_Mobile is HordeMinionFamiliar) + else if (from.CanBeBeneficial(to, true)) { - if (m_Mobile.ControlMaster is PlayerMobile pm) + NetState fromState = from.NetState, toState = to.NetState; + + if (fromState != null && toState != null) { - var qs = pm.Quest; - - if (qs is DarkTidesQuest) + if (from.HasTrade) { - QuestObjective obj = qs.FindObjective(); - - if (obj?.Completed == false) - { - m_Mobile.AddToBackpack(new ScrollOfAbraxus()); - obj.Complete(); - } + from.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending } - } - } - - m_Mobile.TargetLocation = null; - return false; // At the target or too far away - } - - public virtual bool DoOrderFollow() - { - if (CheckHerding()) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - else if (m_Mobile.ControlTarget?.Deleted == false && m_Mobile.ControlTarget != m_Mobile) - { - var 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?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) + else if (to.HasTrade) { - m_Mobile.Warmode = true; - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + 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 { - m_Mobile.Warmode = false; - } - } - else - { - m_Mobile.DebugSay("My master told me to follow: {0}", m_Mobile.ControlTarget.Name); + // ~1_NAME~ will now accept movement commands from ~2_NAME~. + from.SendLocalizedMessage(1049676, $"{m_Mobile.Name}\t{to.Name}"); - // Not exactly OSI style, but better than nothing. - var bRun = iCurrDist > 5; + /* ~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, $"{from.Name}\t{m_Mobile.Name}"); - if (WalkMobileRange(m_Mobile.ControlTarget, 1, bRun, 0, 1)) - { - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Warmode = true; - // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); - } - else - { - m_Mobile.Warmode = false; - if (Core.AOS) - { - m_Mobile.CurrentSpeed = 0.1; - } - } + m_Mobile.AddPetFriend(to); + + m_Mobile.ControlTarget = to; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; } } } - else - { - m_Mobile.DebugSay("I have nobody to follow"); - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; - } + } + m_Mobile.ControlTarget = from; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + + public virtual bool DoOrderUnfriend() + { + var from = m_Mobile.ControlMaster; + var to = m_Mobile.ControlTarget; + + if (from?.Deleted != false || to?.Deleted != false || from == to || !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, $"{m_Mobile.Name}\t{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, $"{from.Name}\t{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; } - public virtual bool DoOrderFriend() + var controlMaster = m_Mobile.ControlMaster; + + if (controlMaster?.Deleted != false) { - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; + return true; + } - if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) + var combatant = m_Mobile.Combatant; + + var aggressors = controlMaster.Aggressors; + + if (aggressors.Count > 0) + { + for (var i = 0; i < aggressors.Count; ++i) { - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* - } - else - { - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + var info = aggressors[i]; + var attacker = info.Attacker; - if (youngFrom && !youngTo) + if (attacker?.Deleted == false && + attacker.GetDistanceToSqrt(m_Mobile) <= m_Mobile.RangePerception) { - 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 (combatant == null || attacker.GetDistanceToSqrt(controlMaster) < + combatant.GetDistanceToSqrt(controlMaster)) { - 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, $"{m_Mobile.Name}\t{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, $"{from.Name}\t{m_Mobile.Name}"); - - m_Mobile.AddPetFriend(to); - - m_Mobile.ControlTarget = to; - m_Mobile.ControlOrder = OrderType.Follow; - - return true; - } + combatant = attacker; } } } - m_Mobile.ControlTarget = from; - m_Mobile.ControlOrder = OrderType.Follow; - - return true; + if (combatant != null && m_Mobile.Debug) + { + m_Mobile.DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); + } } - public virtual bool DoOrderUnfriend() + if (combatant?.Deleted == false && combatant != m_Mobile && combatant != m_Mobile.ControlMaster && + combatant.Alive && !combatant.IsDeadBondedPet && m_Mobile.CanSee(combatant) && + m_Mobile.CanBeHarmful(combatant, false) && combatant.Map == m_Mobile.Map) { - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; - - if (from?.Deleted != false || to?.Deleted != false || from == to || !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, $"{m_Mobile.Name}\t{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, $"{from.Name}\t{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; - } - - var controlMaster = m_Mobile.ControlMaster; - - if (controlMaster?.Deleted != false) - { - return true; - } - - var combatant = m_Mobile.Combatant; - - var aggressors = controlMaster.Aggressors; - - if (aggressors.Count > 0) - { - for (var i = 0; i < aggressors.Count; ++i) - { - var info = aggressors[i]; - var attacker = info.Attacker; - - if (attacker?.Deleted == false && - 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 attack one of those bastards!"); - } - } - - if (combatant?.Deleted == false && combatant != m_Mobile && combatant != m_Mobile.ControlMaster && - combatant.Alive && !combatant.IsDeadBondedPet && m_Mobile.CanSee(combatant) && - m_Mobile.CanBeHarmful(combatant, false) && combatant.Map == m_Mobile.Map) + if (m_Mobile.Debug) { 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.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 + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("Nothing to guard from"); - - m_Mobile.Warmode = false; - if (Core.AOS) - { - m_Mobile.CurrentSpeed = 0.1; - } - - WalkMobileRange(controlMaster, 1, false, 0, 1); } + m_Mobile.Warmode = false; + if (Core.AOS) + { + m_Mobile.CurrentSpeed = 0.1; + } + + WalkMobileRange(controlMaster, 1, false, 0, 1); + } + + return true; + } + + public virtual bool DoOrderAttack() + { + if (m_Mobile.IsDeadPet) + { return true; } - public virtual bool DoOrderAttack() + if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Map != m_Mobile.Map || + !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet) { - if (m_Mobile.IsDeadPet) - { - return true; - } - - if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Map != m_Mobile.Map || - !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet) + if (m_Mobile.Debug) { 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." ); + } - if (Core.AOS) - { - m_Mobile.ControlTarget = m_Mobile.ControlMaster; - m_Mobile.ControlOrder = OrderType.Follow; - } - else - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; - } - - if (m_Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) - { - Mobile newCombatant = null; - var newScore = 0.0; - - foreach (var aggr in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) - { - if (!m_Mobile.CanSee(aggr) || aggr.Combatant != m_Mobile) - { - continue; - } - - if (aggr.IsDeadBondedPet || !aggr.Alive) - { - continue; - } - - var aggrScore = m_Mobile.GetFightModeRanking(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(); - } - } + if (Core.AOS) + { + m_Mobile.ControlTarget = m_Mobile.ControlMaster; + m_Mobile.ControlOrder = OrderType.Follow; } else { - m_Mobile.DebugSay("Attacking target..."); - Think(); + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; } - return true; + if (m_Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) + { + Mobile newCombatant = null; + var newScore = 0.0; + + foreach (var aggr in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + { + if (!m_Mobile.CanSee(aggr) || aggr.Combatant != m_Mobile) + { + continue; + } + + if (aggr.IsDeadBondedPet || !aggr.Alive) + { + continue; + } + + var aggrScore = m_Mobile.GetFightModeRanking(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; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("But -that- is not dead. Here we go again..."); + } + + Think(); + } + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attacking target..."); + } + + Think(); } - public virtual bool DoOrderPatrol() + return true; + } + + public virtual bool DoOrderPatrol() + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("This order is not yet coded"); - return true; } - public virtual bool DoOrderRelease() + return true; + } + + public virtual bool DoOrderRelease() + { + if (m_Mobile.Debug) { 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; - - var spawner = m_Mobile.Spawner; - - if (spawner != null && spawner.HomeLocation != Point3D.Zero) - { - m_Mobile.Home = spawner.HomeLocation; - m_Mobile.RangeHome = spawner.HomeRange; - } - - if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) - { - m_Mobile.Delete(); - } - - m_Mobile.BeginDeleteTimer(); - m_Mobile.DropBackpack(); - - return true; } - public virtual bool DoOrderStay() + 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; + + var spawner = m_Mobile.Spawner; + + if (spawner != null && spawner.HomeLocation != Point3D.Zero) + { + m_Mobile.Home = spawner.HomeLocation; + m_Mobile.RangeHome = spawner.HomeRange; + } + + if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) + { + m_Mobile.Delete(); + } + + m_Mobile.BeginDeleteTimer(); + m_Mobile.DropBackpack(); + + return true; + } + + public virtual bool DoOrderStay() + { + if (m_Mobile.Debug) { if (CheckHerding()) { @@ -1635,146 +1774,182 @@ namespace Server.Mobiles { m_Mobile.DebugSay("My master told me to stay"); } + } + return true; + } + + public virtual bool DoOrderStop() + { + if (m_Mobile.ControlMaster?.Deleted != false) + { return true; } - public virtual bool DoOrderStop() + if (m_Mobile.Debug) { - if (m_Mobile.ControlMaster?.Deleted != false) + 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; + + if (Core.ML) + { + WalkRandomInHome(3, 2, 1); + } + else + { + m_Mobile.ControlOrder = OrderType.None; + } + + return true; + } + + public virtual bool DoOrderTransfer() + { + if (m_Mobile.IsDeadPet) + { + return true; + } + + var from = m_Mobile.ControlMaster; + var to = m_Mobile.ControlTarget; + + if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) + { + if (m_Mobile.Debug) { - return true; + m_Mobile.DebugSay($"Begin transfer with {to.Name}"); } - m_Mobile.DebugSay("My master told me to stop."); + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.ControlMaster); - m_Mobile.Home = m_Mobile.Location; - - m_Mobile.ControlTarget = null; - - if (Core.ML) + if (youngFrom && !youngTo) { - WalkRandomInHome(3, 2, 1); + 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)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + 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)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + 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 transferred to you because it has recently been in combat."); } else { - m_Mobile.ControlOrder = OrderType.None; - } + NetState fromState = from.NetState, toState = to.NetState; - return true; - } - - public virtual bool DoOrderTransfer() - { - if (m_Mobile.IsDeadPet) - { - return true; - } - - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; - - if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) - { - m_Mobile.DebugSay("Begin transfer with {0}", to.Name); - - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (youngFrom && !youngTo) + if (fromState != null && toState != null) { - 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)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - 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)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - 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 transferred 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) { - 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)); - } + 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() + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + + return true; + } + + public virtual bool DoBardPacified() + { + if (Core.Now < m_Mobile.BardEndTime) { - if (Core.Now < m_Mobile.BardEndTime) + if (m_Mobile.Debug) { m_Mobile.DebugSay("I am pacified, I wait"); - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; } - else + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + } + else + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I'm not pacified any longer"); - m_Mobile.BardPacified = false; } - return true; + m_Mobile.BardPacified = false; } - public virtual bool DoBardProvoked() + return true; + } + + public virtual bool DoBardProvoked() + { + if (Core.Now >= m_Mobile.BardEndTime && + (m_Mobile.BardMaster?.Deleted != false || + m_Mobile.BardMaster.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt(m_Mobile.BardMaster) > + m_Mobile.RangePerception)) { - if (Core.Now >= m_Mobile.BardEndTime && - (m_Mobile.BardMaster?.Deleted != false || - m_Mobile.BardMaster.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt(m_Mobile.BardMaster) > - m_Mobile.RangePerception)) + if (m_Mobile.Debug) { 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?.Deleted != false || m_Mobile.BardTarget.Map != m_Mobile.Map || + m_Mobile.GetDistanceToSqrt(m_Mobile.BardTarget) > m_Mobile.RangePerception) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my provoke target"); + } + m_Mobile.BardProvoked = false; m_Mobile.BardMaster = null; m_Mobile.BardTarget = null; @@ -1784,545 +1959,519 @@ namespace Server.Mobiles } else { - if (m_Mobile.BardTarget?.Deleted != false || 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 = m_Mobile.BardTarget; + m_Action = ActionType.Combat; - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; + 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 (var i = 0; i < iSteps; i++) + { + if (Utility.Random(8 * iChanceToNotMove) <= 8) + { + var iRndMove = Utility.Random(0, 8 + 9 * iChanceToDir); + + switch (iRndMove) + { + case 0: + { + DoMove(Direction.Up); + break; + } + case 1: + { + DoMove(Direction.North); + break; + } + case 2: + { + DoMove(Direction.Left); + break; + } + case 3: + { + DoMove(Direction.West); + break; + } + case 5: + { + DoMove(Direction.Down); + break; + } + case 6: + { + DoMove(Direction.South); + break; + } + case 7: + { + DoMove(Direction.Right); + break; + } + case 8: + { + DoMove(Direction.East); + break; + } + default: + { + DoMove(m_Mobile.Direction); + break; + } + } + } + } + } + + public double TransformMoveDelay(double delay) + { + // Monster is passive + if (m_Mobile is { Controlled: false, Summoned: false } && Math.Abs(delay - m_Mobile.PassiveSpeed) < 0.0001) + { + delay *= 3; + } + + if (!m_Mobile.IsDeadPet && (m_Mobile.ReduceSpeedWithDamage || m_Mobile.IsSubdued)) + { + int stats, statsMax; + if (Core.HS) + { + stats = m_Mobile.Stam; + statsMax = m_Mobile.StamMax; + } + else + { + stats = m_Mobile.Hits; + statsMax = m_Mobile.HitsMax; + } + + var offset = statsMax <= 0 ? 1.0 : Math.Max(0, stats) / (double)statsMax; + + if (offset < 1.0) + { + delay += m_Mobile.PassiveSpeed * (1.0 - offset); + } + } + + return delay; + } + + public virtual bool CheckMove() => Core.TickCount - NextMove >= 0; + + public virtual bool DoMove(Direction d, bool badStateOk = false) + { + var res = DoMoveImpl(d); + + return res is MoveResult.Success or MoveResult.SuccessAutoTurn || badStateOk && res == MoveResult.BadState; + } + + public virtual MoveResult DoMoveImpl(Direction d) + { + if (m_Mobile.Deleted || m_Mobile.Frozen || m_Mobile.Paralyzed || + m_Mobile.Spell?.IsCasting == true || m_Mobile.DisallowAllMoves) + { + return MoveResult.BadState; + } + + if (!CheckMove()) + { + return MoveResult.BadState; + } + + // This makes them always move one step, never any direction changes + // TODO: This is firing off deltas which aren't needed. Look into replacing/removing this + m_Mobile.Direction = d; + + var delay = (int)(TransformMoveDelay(m_Mobile.CurrentSpeed) * 1000); + NextMove += delay; + + if (Core.TickCount - NextMove > 0) + { + NextMove = Core.TickCount; + } + + m_Mobile.Pushing = false; + + MoveImpl.IgnoreMovableImpassables = m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles; + + if ((m_Mobile.Direction & Direction.Mask) != (d & Direction.Mask)) + { + var v = m_Mobile.Move(d); + + MoveImpl.IgnoreMovableImpassables = false; + return v ? MoveResult.Success : MoveResult.Blocked; + } + + if (m_Mobile.Move(d)) + { + MoveImpl.IgnoreMovableImpassables = false; + return MoveResult.Success; + } + + var wasPushing = m_Mobile.Pushing; + + var blocked = true; + + var canOpenDoors = m_Mobile.CanOpenDoors; + var canDestroyObstacles = m_Mobile.CanDestroyObstacles; + + if (canOpenDoors || canDestroyObstacles) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); + } + + var map = m_Mobile.Map; + + if (map != null) + { + int x = m_Mobile.X, y = m_Mobile.Y; + Movement.Movement.Offset(d, ref x, ref y); + + var destroyables = 0; + + var eable = map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1); + + foreach (var item in eable) + { + if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z && + m_Mobile.Z + 16 > door.Z) + { + if (door.X != x || door.Y != y) + { + continue; + } + + 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) + { + var item = m_Obstacles.Dequeue(); + + if (item is BaseDoor door) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay( + "Little do they expect, I've learned how to open doors. Didn't they read the script??" + ); + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("*twist*"); + } + + door.Use(m_Mobile); + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay( + $"Ugabooga. I'm so big and tough I can destroy it: {item.GetType().Name}" + ); + } + + if (item is Container cont) + { + for (var i = 0; i < cont.Items.Count; ++i) + { + var check = cont.Items[i]; + + if (check.Movable && check.ItemData.Impassable && + cont.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) + { + var offset = Utility.RandomDouble() >= 0.6 ? 1 : -1; + + for (var 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; + } + + 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 RegionSpawner rs) + { + Region region = rs.SpawnRegion; + + if (m_Mobile.Region.AcceptsSpawnsFrom(region)) + { + m_Mobile.WalkRegion = region; + WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); + m_Mobile.WalkRegion = null; } else { - m_Mobile.Combatant = m_Mobile.BardTarget; - m_Action = ActionType.Combat; - - m_Mobile.OnThink(); - Think(); + 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 (var i = 0; i < iSteps; i++) + { + if (m_Mobile.RangeHome != 0) + { + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.Home); + + if (iCurrDist < m_Mobile.RangeHome * 2 / 3) + { + WalkRandom(iChanceToNotMove, iChanceToDir, 1); + } + else if (iCurrDist > 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()) + { + var combatant = m_Mobile.Combatant; + + if (combatant == null) + { + WalkRandom(1, 2, 1); + } + else + { + var d = combatant.GetDirectionTo(m_Mobile); + + d = (Direction)((int)d + Utility.RandomMinMax(-1, +1)); + + m_Mobile.Direction = d; + m_Mobile.Move(d); + } return true; } - public virtual void WalkRandom(int iChanceToNotMove, int iChanceToDir, int iSteps) + return false; + } + + public virtual void OnTeleported() + { + if (m_Path != null) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) - { - return; - } - - for (var i = 0; i < iSteps; i++) - { - if (Utility.Random(8 * iChanceToNotMove) <= 8) - { - var iRndMove = Utility.Random(0, 8 + 9 * iChanceToDir); - - switch (iRndMove) - { - case 0: - DoMove(Direction.Up); - break; - case 1: - DoMove(Direction.North); - break; - case 2: - DoMove(Direction.Left); - break; - case 3: - DoMove(Direction.West); - break; - case 5: - DoMove(Direction.Down); - break; - case 6: - DoMove(Direction.South); - break; - case 7: - DoMove(Direction.Right); - break; - case 8: - DoMove(Direction.East); - break; - default: - DoMove(m_Mobile.Direction); - break; - } - } - } - } - - public double TransformMoveDelay(double delay) - { - var isPassive = delay == m_Mobile.PassiveSpeed; - var isControlled = m_Mobile.Controlled || m_Mobile.Summoned; - - if (delay == 0.2) - { - delay = 0.3; - } - else if (delay == 0.25) - { - delay = 0.45; - } - else if (delay == 0.3) - { - delay = 0.6; - } - else if (delay == 0.4) - { - delay = 0.9; - } - else if (delay == 0.5) - { - delay = 1.05; - } - else if (delay == 0.6) - { - delay = 1.2; - } - else if (delay == 0.8) - { - delay = 1.5; - } - - if (isPassive) - { - delay += 0.2; - } - - if (!isControlled) - { - delay += 0.1; - } - else if (m_Mobile.Controlled) - { - if (m_Mobile.ControlOrder == OrderType.Follow && m_Mobile.ControlTarget == m_Mobile.ControlMaster) - { - delay *= 0.5; - } - - delay -= 0.075; - } - - if (m_Mobile.ReduceSpeedWithDamage || m_Mobile.IsSubdued) - { - var offset = (double)m_Mobile.Hits / m_Mobile.HitsMax; - - if (offset < 0.0) - { - offset = 0.0; - } - else if (offset > 1.0) - { - offset = 1.0; - } - - offset = 1.0 - offset; - - delay += offset * 0.8; - } - - if (delay < 0.0) - { - delay = 0.0; - } - - if (double.IsNaN(delay)) - { - using (var op = new StreamWriter("nan_transform.txt", true)) - { - op.WriteLine( - $"NaN in TransformMoveDelay: {Core.Now}, {GetType()}, {m_Mobile?.GetType()}, {m_Mobile.HitsMax}" - ); - } - - return 1.0; - } - - return delay; - } - - public virtual bool CheckMove() => Core.TickCount - NextMove >= 0; - - public virtual bool DoMove(Direction d) => DoMove(d, false); - - public virtual bool DoMove(Direction d, bool badStateOk) - { - var res = DoMoveImpl(d); - - return res is MoveResult.Success or MoveResult.SuccessAutoTurn || badStateOk && res == MoveResult.BadState; - } - - public virtual MoveResult DoMoveImpl(Direction d) - { - if (m_Mobile.Deleted || m_Mobile.Frozen || m_Mobile.Paralyzed || - m_Mobile.Spell?.IsCasting == true || m_Mobile.DisallowAllMoves) - { - return MoveResult.BadState; - } - - if (!CheckMove()) - { - return MoveResult.BadState; - } - - // This makes them always move one step, never any direction changes - // TODO: This is firing off deltas which aren't needed. Look into replacing/removing this - m_Mobile.Direction = d; - - var delay = (int)(TransformMoveDelay(m_Mobile.CurrentSpeed) * 1000); - - NextMove += delay; - - if (Core.TickCount - NextMove > 0) - { - NextMove = Core.TickCount; - } - - m_Mobile.Pushing = false; - - MoveImpl.IgnoreMovableImpassables = m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles; - - if ((m_Mobile.Direction & Direction.Mask) != (d & Direction.Mask)) - { - var v = m_Mobile.Move(d); - - MoveImpl.IgnoreMovableImpassables = false; - return v ? MoveResult.Success : MoveResult.Blocked; - } - - if (m_Mobile.Move(d)) - { - MoveImpl.IgnoreMovableImpassables = false; - return MoveResult.Success; - } - - var wasPushing = m_Mobile.Pushing; - - var blocked = true; - - var canOpenDoors = m_Mobile.CanOpenDoors; - var canDestroyObstacles = m_Mobile.CanDestroyObstacles; - - if (canOpenDoors || canDestroyObstacles) - { - m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); - - var map = m_Mobile.Map; - - if (map != null) - { - int x = m_Mobile.X, y = m_Mobile.Y; - Movement.Movement.Offset(d, ref x, ref y); - - var destroyables = 0; - - var eable = map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1); - - foreach (var item in eable) - { - if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z && - m_Mobile.Z + 16 > door.Z) - { - if (door.X != x || door.Y != y) - { - continue; - } - - 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) - { - var item = m_Obstacles.Dequeue(); - - if (item is BaseDoor door) - { - m_Mobile.DebugSay( - "Little do they expect, I've learned how to open doors. Didn't they read the script??" - ); - m_Mobile.DebugSay("*twist*"); - - door.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 cont) - { - for (var i = 0; i < cont.Items.Count; ++i) - { - var check = cont.Items[i]; - - if (check.Movable && check.ItemData.Impassable && - cont.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) - { - var offset = Utility.RandomDouble() >= 0.6 ? 1 : -1; - - for (var 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; - } - - 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 RegionSpawner rs) - { - Region region = rs.SpawnRegion; - - 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 (var i = 0; i < iSteps; i++) - { - if (m_Mobile.RangeHome != 0) - { - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.Home); - - if (iCurrDist < m_Mobile.RangeHome * 2 / 3) - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - else if (iCurrDist > 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()) - { - var combatant = m_Mobile.Combatant; - - if (combatant == null) - { - WalkRandom(1, 2, 1); - } - else - { - var 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; - } - - public virtual void OnTeleported() - { - if (m_Path != null) + if (m_Mobile.Debug) { m_Mobile.DebugSay("Teleported; repathing"); - m_Path.ForceRepath(); } + + m_Path.ForceRepath(); } + } - public virtual bool MoveTo(Mobile m, bool run, int range) + public virtual bool MoveTo(Mobile m, bool run, int range) + { + if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves || m?.Deleted != false) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves || m?.Deleted != false) - { - return false; - } - - if (m_Mobile.InRange(m, range)) - { - m_Path = null; - return true; - } - - if (m_Path?.Goal == m) - { - if (m_Path.Follow(run, 1)) - { - m_Path = null; - return true; - } - } - else if (!DoMove(m_Mobile.GetDirectionTo(m, run), true)) - { - m_Path = new PathFollower(m_Mobile, m) { Mover = 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.InRange(m, range)) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + m_Path = null; + return true; + } + + if (m_Path?.Goal == m) + { + if (m_Path.Follow(run, 1)) { - return false; + m_Path = null; + return true; } + } + else if (!DoMove(m_Mobile.GetDirectionTo(m, run), true)) + { + m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; - if (m == null) + if (m_Path.Follow(run, 1)) { - return false; + m_Path = null; + return true; } + } + else + { + m_Path = null; + return true; + } - for (var i = 0; i < iSteps; i++) + 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) + { + return false; + } + + for (var i = 0; i < iSteps; i++) + { + // Get the current distance + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m); + + if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) { - // Get the current distance - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m); + var needCloser = iCurrDist > iWantDistMax; + var needFurther = !needCloser; - if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) + if (needCloser && m_Path != null && m_Path.Goal == m) { - var needCloser = iCurrDist > iWantDistMax; - var needFurther = !needCloser; - - if (needCloser && m_Path != null && m_Path.Goal == m) + if (m_Path.Follow(bRun, 1)) { + m_Path = null; + } + } + else + { + var dirTo = iCurrDist > iWantDistMax ? + m_Mobile.GetDirectionTo(m, bRun) : m.GetDirectionTo(m_Mobile, bRun); + + if (!DoMove(dirTo, true) && needCloser) + { + m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; + if (m_Path.Follow(bRun, 1)) { m_Path = null; @@ -2330,769 +2479,747 @@ namespace Server.Mobiles } else { - var dirTo = iCurrDist > iWantDistMax ? - m_Mobile.GetDirectionTo(m, bRun) : m.GetDirectionTo(m_Mobile, bRun); - - if (!DoMove(dirTo, true) && needCloser) - { - m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; - - if (m_Path.Follow(bRun, 1)) - { - m_Path = null; - } - } - else - { - m_Path = null; - } + m_Path = null; } } - else - { - return true; - } } - - // Get the current distance - var iNewDist = (int)m_Mobile.GetDistanceToSqrt(m); - - return iNewDist >= iWantDistMin && iNewDist <= iWantDistMax; - } - - /* - * Here we check to acquire a target from our surrounding - * - * 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?.Deleted != false) - { - m_Mobile.FocusMob = null; - return false; - } - - m_Mobile.FocusMob = m_Mobile.BardTarget; - return m_Mobile.FocusMob != null; - } - - if (m_Mobile.Controlled) - { - if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Hidden || - !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet || - !m_Mobile.InRange(m_Mobile.ControlTarget, m_Mobile.RangePerception * 2)) - { - if (m_Mobile.ControlTarget != null && m_Mobile.ControlTarget != m_Mobile.ControlMaster) - { - m_Mobile.ControlTarget = null; - } - - m_Mobile.FocusMob = null; - return false; - } - - 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 (Core.TickCount - m_Mobile.NextReacquireTime < 0) - { - m_Mobile.FocusMob = null; - return false; - } - - m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds; - - m_Mobile.DebugSay("Acquiring..."); - - var map = m_Mobile.Map; - - if (map != null) - { - Mobile newFocusMob = null; - var val = double.MinValue; - - var eable = map.GetMobilesInRange(m_Mobile.Location, iRange); - - foreach (var m in eable) - { - if (m.Deleted || m.Blessed) - { - continue; - } - - // Let's not target ourselves... - if (m == m_Mobile || m is BaseFamiliar) - { - 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; - } - - var bc = m as BaseCreature; - var pm = m as PlayerMobile; - - if (Core.AOS && bc?.Summoned == true && bc?.Controlled != true) - { - 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 (!SpellHelper.ValidIndirectTarget(m_Mobile.SummonMaster, m)) - { - continue; - } - - // Animated creatures cannot attack players directly. - if (pm != null && m_Mobile.IsAnimatedDead) - { - continue; - } - } - - // If we only want faction friends, make sure it's one. - if (bFacFriend && !m_Mobile.IsFriend(m)) - { - continue; - } - - // Ignore anyone under EtherealVoyage - if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell))) - { - continue; - } - - // Ignore players with activated honor - if (pm?.HonorActive == true && m_Mobile.Combatant != m) - { - continue; - } - - if (acqType is FightMode.Aggressor or FightMode.Evil) - { - var bValid = IsHostile(m); - - if (!bValid) - { - bValid = m_Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || - m_Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; - } - - if (acqType == FightMode.Evil && !bValid) - { - if (bc?.Controlled == true && bc?.ControlMaster != null) - { - bValid = bc.ControlMaster.Karma < 0; - } - else - { - bValid = m.Karma < 0; - } - } - - if (!bValid) - { - continue; - } - } - else - { - // Same goes for faction enemies. - if (bFacFoe && !m_Mobile.IsEnemy(m)) - { - 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; - } - } - - var theirVal = m_Mobile.GetFightModeRanking(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; - } - - private bool IsHostile(Mobile from) - { - var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count); - - if (m_Mobile.Combatant == from || from.Combatant == m_Mobile) + else { return true; } + } - if (count > 0) - { - for (var a = 0; a < count; ++a) - { - if (a < m_Mobile.Aggressed.Count && m_Mobile.Aggressed[a].Attacker == from) - { - return true; - } + // Get the current distance + var iNewDist = (int)m_Mobile.GetDistanceToSqrt(m); - if (a < m_Mobile.Aggressors.Count && m_Mobile.Aggressors[a].Defender == from) - { - return true; - } - } - } + return iNewDist >= iWantDistMin && iNewDist <= iWantDistMax; + } + /* + * Here we check to acquire a target from our surrounding + * + * 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; } - public virtual void DetectHidden() + if (m_Mobile.BardProvoked) { - if (m_Mobile.Deleted || m_Mobile.Map == null) + if (m_Mobile.BardTarget?.Deleted != false) { - return; + m_Mobile.FocusMob = null; + return false; } - m_Mobile.DebugSay("Checking for hidden players"); - - var srcSkill = m_Mobile.Skills.DetectHidden.Value; - - if (srcSkill <= 0) - { - return; - } - - var eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception); - - foreach (var trg in eable) - { - 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); - - var trgHiding = trg.Skills.Hiding.Value / 2.9; - var trgStealth = trg.Skills.Stealth.Value / 1.8; - - var 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! - } - } - } - - eable.Free(); + m_Mobile.FocusMob = m_Mobile.BardTarget; + return m_Mobile.FocusMob != null; } - public virtual void Deactivate() + if (m_Mobile.Controlled) { - if (m_Mobile.PlayerRangeSensitive) + if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Hidden || + !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet || + !m_Mobile.InRange(m_Mobile.ControlTarget, m_Mobile.RangePerception * 2)) { - m_Timer.Stop(); - - var spawner = m_Mobile.Spawner; - - if (spawner?.ReturnOnDeactivate == true && !m_Mobile.Controlled && ( - spawner.HomeLocation == Point3D.Zero && !m_Mobile.Region.AcceptsSpawnsFrom(spawner.Region) || - !m_Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) - )) + if (m_Mobile.ControlTarget != null && m_Mobile.ControlTarget != m_Mobile.ControlMaster) { - Timer.StartTimer(ReturnToHome); + m_Mobile.ControlTarget = null; } + + m_Mobile.FocusMob = null; + return false; } + + m_Mobile.FocusMob = m_Mobile.ControlTarget; + return m_Mobile.FocusMob != null; } - private void ReturnToHome() + if (m_Mobile.ConstantFocus != null) { - if (m_Mobile.Spawner != null) + if (m_Mobile.Debug) { - var loc = m_Mobile.Spawner.GetSpawnPosition(m_Mobile, m_Mobile.Spawner.Map); - - if (loc != Point3D.Zero) - { - m_Mobile.MoveToWorld(loc, m_Mobile.Spawner.Map); - } + m_Mobile.DebugSay("Acquired my constant focus"); } + + m_Mobile.FocusMob = m_Mobile.ConstantFocus; + return true; } - public virtual void Activate() + if (acqType == FightMode.None) { - if (!m_Timer.Running) - { - m_Timer.Delay = TimeSpan.Zero; - m_Timer.Start(); - } + m_Mobile.FocusMob = null; + return false; } - /* - * The mobile changed it speed, we must adjust the timer - */ - public virtual void OnCurrentSpeedChanged() + if (acqType == FightMode.Aggressor && m_Mobile.Aggressors.Count == 0 && m_Mobile.Aggressed.Count == 0 && + m_Mobile.FactionAllegiance == null && m_Mobile.EthicAllegiance == null) { - 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(); + m_Mobile.FocusMob = null; + return false; } - private class InternalEntry : ContextMenuEntry + if (Core.TickCount - m_Mobile.NextReacquireTime < 0) { - private readonly BaseAI m_AI; - private readonly Mobile m_From; - private readonly BaseCreature m_Mobile; - private readonly 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 is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop) - { - Enabled = false; - } - } - - public override void OnClick() - { - if (!m_Mobile.Deleted && m_Mobile.Controlled && m_From.CheckAlive()) - { - if (m_Mobile.IsDeadPet && m_Order is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop) - { - return; - } - - var isOwner = m_From == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(m_From); - - if (!isOwner && !isFriend) - { - return; - } - - 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; - } - - m_From.SendGump(new ConfirmReleaseGump(m_From, m_Mobile)); - - break; - } - default: - { - if (m_Mobile.CheckControlChance(m_From)) - { - m_Mobile.ControlOrder = m_Order; - } - - break; - } - } - } - } + m_Mobile.FocusMob = null; + return false; } - private class TransferItem : Item + m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds; + + if (m_Mobile.Debug) { - private readonly BaseCreature m_Creature; - - public TransferItem(BaseCreature creature) - : base(ShrinkTable.Lookup(creature)) - { - m_Creature = creature; - - Movable = false; - - if (!Core.AOS) - { - Name = creature.Name; - } - else if (ItemID == ShrinkTable.DefaultItemID || - creature.GetType().IsDefined(typeof(FriendlyNameAttribute), false) || creature is Reptalon) - { - 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 - - Hue = creature.Hue & 0x0FFF; - } - - public TransferItem(Serial serial) - : base(serial) - { - } - - public static bool IsInCombat(BaseCreature creature) => - creature != null && (creature.Aggressors.Count > 0 || creature.Aggressed.Count > 0); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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?.Deleted != false || m_Creature.ControlMaster != from || - !from.CheckAlive() || !to.CheckAlive()) - { - return false; - } - - if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) - { - return false; - } - - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - 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)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - 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)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - 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 transferred 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?.Deleted != false || 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()); - - var args = $"{from.Name}\t{m_Creature.Name}\t{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. - } - } - } + m_Mobile.DebugSay("Acquiring..."); } - /* - * The Timer object - */ - private class AITimer : Timer + var map = m_Mobile.Map; + + if (map == null) { - private readonly BaseAI m_Owner; + // TODO: Is this correct? Maybe it should return false? + return m_Mobile.FocusMob != null; + } - public AITimer(BaseAI owner) - : base( - TimeSpan.FromSeconds(Utility.RandomDouble()), - TimeSpan.FromSeconds(Math.Max(0.0, owner.m_Mobile.CurrentSpeed)) - ) + Mobile newFocusMob = null; + var val = double.MinValue; + + var eable = map.GetMobilesInRange(m_Mobile.Location, iRange); + + foreach (var m in eable) + { + if (m.Deleted || m.Blessed) { - m_Owner = owner; - - m_Owner.m_NextDetectHidden = Core.TickCount; + continue; } - protected override void OnTick() + // Let's not target ourselves... + if (m == m_Mobile || m is BaseFamiliar) { - if (m_Owner.m_Mobile.Deleted) + 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; + } + + var bc = m as BaseCreature; + var pm = m as PlayerMobile; + + if (Core.AOS && bc?.Summoned == true && bc?.Controlled != true) + { + 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) { - Stop(); - return; + continue; } - if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + // It also must abide by harmful spell rules. + if (!SpellHelper.ValidIndirectTarget(m_Mobile.SummonMaster, m)) { - m_Owner.Deactivate(); - return; + continue; } - if (m_Owner.m_Mobile.PlayerRangeSensitive) // have to check this in the timer.... + // Animated creatures cannot attack players directly. + if (pm != null && m_Mobile.IsAnimatedDead) { - var sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile.Location); - if (!sect.Active) + continue; + } + } + + // If we only want faction friends, make sure it's one. + if (bFacFriend && !m_Mobile.IsFriend(m)) + { + continue; + } + + // Ignore anyone under EtherealVoyage + if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell))) + { + continue; + } + + // Ignore players with activated honor + if (pm?.HonorActive == true && m_Mobile.Combatant != m) + { + continue; + } + + if (acqType is FightMode.Aggressor or FightMode.Evil) + { + var bValid = IsHostile(m); + + if (!bValid) + { + bValid = m_Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || + m_Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; + } + + if (acqType == FightMode.Evil && !bValid) + { + if (bc?.Controlled == true && bc?.ControlMaster != null) { - m_Owner.Deactivate(); - return; - } - } - - m_Owner.m_Mobile.OnThink(); - - if (m_Owner.m_Mobile.Deleted) - { - Stop(); - return; - } - - if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) - { - m_Owner.Deactivate(); - 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; - } + bValid = bc.ControlMaster.Karma < 0; } else { - if (!m_Owner.Obey()) - { - Stop(); - return; - } + bValid = m.Karma < 0; } } - if (m_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0) + if (!bValid) { - m_Owner.DetectHidden(); + continue; + } + } + else + { + // Same goes for faction enemies. + if (bFacFoe && !m_Mobile.IsEnemy(m)) + { + continue; + } - // Not exactly OSI style, approximation. - var delay = Math.Min(15000 / m_Owner.m_Mobile.Int, 60); + // If it's an enemy factioned mobile, make sure we can be harmful to it. + if (bFacFoe && !bFacFriend && !m_Mobile.CanBeHarmful(m, false)) + { + continue; + } + } - var min = delay * (9 / 10); // 13s at 1000 int, 33s at 400 int, 54s at <250 int - var max = delay * (10 / 9); // 16s at 1000 int, 41s at 400 int, 66s at <250 int + var theirVal = m_Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); - m_Owner.m_NextDetectHidden = Core.TickCount + - (int)TimeSpan.FromSeconds(Utility.RandomMinMax(min, max)).TotalMilliseconds; + if (theirVal > val && m_Mobile.InLOS(m)) + { + newFocusMob = m; + val = theirVal; + } + } + + eable.Free(); + + m_Mobile.FocusMob = newFocusMob; + return m_Mobile.FocusMob != null; + } + + private bool IsHostile(Mobile from) + { + var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count); + + if (count <= 0 || m_Mobile.Combatant == from || from.Combatant == m_Mobile) + { + return true; + } + + for (var a = 0; a < count; ++a) + { + if (a < m_Mobile.Aggressed.Count && m_Mobile.Aggressed[a].Attacker == from) + { + return true; + } + + if (a < m_Mobile.Aggressors.Count && m_Mobile.Aggressors[a].Defender == from) + { + return true; + } + } + + return false; + } + + public virtual void DetectHidden() + { + if (m_Mobile.Deleted || m_Mobile.Map == null) + { + return; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Checking for hidden players"); + } + + var srcSkill = m_Mobile.Skills.DetectHidden.Value; + + if (srcSkill <= 0) + { + return; + } + + var eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception); + + foreach (var trg in eable) + { + if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && + m_Mobile.InLOS(trg)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"Trying to detect {trg.Name}"); + } + + var trgHiding = trg.Skills.Hiding.Value / 2.9; + var trgStealth = trg.Skills.Stealth.Value / 1.8; + + var 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! } } } + + eable.Free(); } -} + + public virtual void Deactivate() + { + if (m_Mobile.PlayerRangeSensitive) + { + m_Timer.Stop(); + + var spawner = m_Mobile.Spawner; + + if (spawner?.ReturnOnDeactivate == true && !m_Mobile.Controlled && ( + spawner.HomeLocation == Point3D.Zero && !m_Mobile.Region.AcceptsSpawnsFrom(spawner.Region) || + !m_Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) + )) + { + Timer.StartTimer(ReturnToHome); + } + } + } + + private void ReturnToHome() + { + if (m_Mobile.Spawner != null) + { + var loc = m_Mobile.Spawner.GetSpawnPosition(m_Mobile, m_Mobile.Spawner.Map); + + if (loc != Point3D.Zero) + { + m_Mobile.MoveToWorld(loc, m_Mobile.Spawner.Map); + } + } + } + + public virtual void Activate() + { + if (!m_Timer.Running) + { + // We want to randomize the time at which the AI activates. + // This triggers when a mob is first created since it moves from the internal map to it's added location + // If we spawn lots of mobs, we don't want their AI synchronized exactly. + m_Timer.Delay = TimeSpan.FromMilliseconds(Utility.Random(48) * 8); + m_Timer.Start(); + } + } + + /* + * The mobile changed speeds, we must adjust the timer + */ + public virtual void OnCurrentSpeedChanged() + { + m_Timer.Interval = TimeSpan.FromSeconds(Math.Max(0.008, m_Mobile.CurrentSpeed)); + } + + private class InternalEntry : ContextMenuEntry + { + private readonly BaseAI m_AI; + private readonly Mobile m_From; + private readonly BaseCreature m_Mobile; + private readonly 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 is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop) + { + Enabled = false; + } + } + + public override void OnClick() + { + if (m_Mobile.Deleted || !m_Mobile.Controlled || !m_From.CheckAlive()) + { + return; + } + + if (m_Mobile.IsDeadPet && m_Order is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop) + { + return; + } + + var isOwner = m_From == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(m_From); + + if (!isOwner && !isFriend) + { + return; + } + + 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; + } + + m_From.SendGump(new ConfirmReleaseGump(m_From, m_Mobile)); + + break; + } + default: + { + if (m_Mobile.CheckControlChance(m_From)) + { + m_Mobile.ControlOrder = m_Order; + } + + break; + } + } + } + } + + private class TransferItem : Item + { + private readonly BaseCreature m_Creature; + + public TransferItem(BaseCreature creature) + : base(ShrinkTable.Lookup(creature)) + { + m_Creature = creature; + + Movable = false; + + if (!Core.AOS) + { + Name = creature.Name; + } + else if (ItemID == ShrinkTable.DefaultItemID || + creature.GetType().IsDefined(typeof(FriendlyNameAttribute), false) || creature is Reptalon) + { + 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 + + Hue = creature.Hue & 0x0FFF; + } + + public TransferItem(Serial serial) + : base(serial) + { + } + + public static bool IsInCombat(BaseCreature creature) => + creature != null && (creature.Aggressors.Count > 0 || creature.Aggressed.Count > 0); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Delete(); + } + + public override void GetProperties(IPropertyList 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?.Deleted != false || m_Creature.ControlMaster != from || + !from.CheckAlive() || !to.CheckAlive()) + { + return false; + } + + if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) + { + return false; + } + + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + 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)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + from.SendLocalizedMessage(1043248, args); + // The pet will not accept you as a master because it does not trust you.~3_BLANK~ + to.SendLocalizedMessage(1043249, args); + + return false; + } + else if (accepted && !m_Creature.CanBeControlledBy(from)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + from.SendLocalizedMessage(1043250, args); + // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + to.SendLocalizedMessage(1043251, args); + } + 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 transferred 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?.Deleted != false || m_Creature.ControlMaster != from || !from.CheckAlive() || + !to.CheckAlive()) + { + return; + } + + if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) + { + return; + } + + if (accepted && 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()); + + var args = $"{from.Name}\t{m_Creature.Name}\t{to.Name}"; + + from.SendLocalizedMessage(1043253, args); // You have transferred your pet to ~3_GETTER~. + // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. + to.SendLocalizedMessage(1043252, args); + } + } + } + + /* + * The Timer object + */ + private class AITimer : Timer + { + private readonly BaseAI m_Owner; + + public AITimer(BaseAI owner) : base( + TimeSpan.FromMilliseconds(Utility.Random(96) * 8), + TimeSpan.FromSeconds(Math.Max(0.0, owner.m_Mobile.CurrentSpeed)) + ) + { + m_Owner = owner; + m_Owner.m_NextDetectHidden = Core.TickCount; + } + + protected override void OnTick() + { + if (m_Owner.m_Mobile.Deleted) + { + Stop(); + return; + } + + if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + { + m_Owner.Deactivate(); + return; + } + + if (m_Owner.m_Mobile.PlayerRangeSensitive) // have to check this in the timer.... + { + var sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile.Location); + if (!sect.Active) + { + m_Owner.Deactivate(); + return; + } + } + + m_Owner.m_Mobile.OnThink(); + + if (m_Owner.m_Mobile.Deleted) + { + Stop(); + return; + } + + if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + { + m_Owner.Deactivate(); + 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_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0) + { + m_Owner.DetectHidden(); + + // Not exactly OSI style, approximation. + var delay = Math.Min(15000 / m_Owner.m_Mobile.Int, 60); + + var min = delay * (9 / 10); // 13s at 1000 int, 33s at 400 int, 54s at <250 int + var max = delay * (10 / 9); // 16s at 1000 int, 41s at 400 int, 66s at <250 int + + m_Owner.m_NextDetectHidden = Core.TickCount + + (int)TimeSpan.FromSeconds(Utility.RandomMinMax(min, max)).TotalMilliseconds; + } + } + } +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/BerserkAI.cs b/Projects/UOContent/Mobiles/AI/BerserkAI.cs index f1f64f23e..d43449938 100644 --- a/Projects/UOContent/Mobiles/AI/BerserkAI.cs +++ b/Projects/UOContent/Mobiles/AI/BerserkAI.cs @@ -1,91 +1,97 @@ -namespace Server.Mobiles +namespace Server.Mobiles; + +public class BerserkAI : BaseAI { - public class BerserkAI : BaseAI + public BerserkAI(BaseCreature m) : base(m) { - public BerserkAI(BaseCreature m) : base(m) + } + + public override bool DoActionWander() + { + if (m_Mobile.Debug) { + m_Mobile.DebugSay("I have no combatant"); } - public override bool DoActionWander() + if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Closest, false, true, true)) { - m_Mobile.DebugSay("I have No Combatant"); - - if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Closest, false, true, true)) + if (m_Mobile.Debug) { - 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(); + 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?.Deleted != false) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is deleted"); + } + + Action = ActionType.Guard; return true; } - public override bool DoActionCombat() + if ( + m_Mobile.Combatant != null && + !WalkMobileRange( + m_Mobile.Combatant, + 1, + true, + m_Mobile.RangeFight, + m_Mobile.RangeFight + ) + ) { - if (m_Mobile.Combatant?.Deleted != false) + if (m_Mobile.Debug) { - m_Mobile.DebugSay("My combatant is deleted"); + 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; } - - if ( - m_Mobile.Combatant != null && - !WalkMobileRange( - m_Mobile.Combatant, - 1, - true, - m_Mobile.RangeFight, - m_Mobile.RangeFight - ) - ) - { - 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; - } + 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 {m_Mobile.FocusMob.Name}, attacking"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); + } + + return true; + } +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/HealerAI.cs b/Projects/UOContent/Mobiles/AI/HealerAI.cs index cf5d19824..ad2b7bc3d 100644 --- a/Projects/UOContent/Mobiles/AI/HealerAI.cs +++ b/Projects/UOContent/Mobiles/AI/HealerAI.cs @@ -4,179 +4,168 @@ using Server.Spells.Fourth; using Server.Spells.Second; using Server.Targeting; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class HealerAI : BaseAI { - public class HealerAI : BaseAI + private static readonly NeedDelegate m_Cure = NeedCure; + private static readonly NeedDelegate m_GHeal = NeedGHeal; + private static readonly NeedDelegate m_LHeal = NeedLHeal; + private static readonly NeedDelegate[] m_ACure = { m_Cure }; + private static readonly NeedDelegate[] m_AGHeal = { m_GHeal }; + private static readonly NeedDelegate[] m_ALHeal = { m_LHeal }; + private static readonly NeedDelegate[] m_All = { m_Cure, m_GHeal, m_LHeal }; + + public HealerAI(BaseCreature m) : base(m) { - private static readonly NeedDelegate m_Cure = NeedCure; - private static readonly NeedDelegate m_GHeal = NeedGHeal; - private static readonly NeedDelegate m_LHeal = NeedLHeal; - private static readonly NeedDelegate[] m_ACure = { m_Cure }; - private static readonly NeedDelegate[] m_AGHeal = { m_GHeal }; - private static readonly NeedDelegate[] m_ALHeal = { m_LHeal }; - private static readonly NeedDelegate[] m_All = { m_Cure, m_GHeal, m_LHeal }; + } - public HealerAI(BaseCreature m) : base(m) + public override bool Think() + { + if (m_Mobile.Deleted) { + return false; } - public override bool Think() + var targ = m_Mobile.Target; + + if (targ != null) { - if (m_Mobile.Deleted) + var spellTarg = targ as ISpellTarget; + + if (spellTarg?.Spell is CureSpell) { - return false; + ProcessTarget(targ, m_ACure); } - - var targ = m_Mobile.Target; - - if (targ != null) + else if (spellTarg?.Spell is GreaterHealSpell) { - var spellTarg = targ as ISpellTarget; - - if (spellTarg?.Spell is CureSpell) - { - ProcessTarget(targ, m_ACure); - } - else if (spellTarg?.Spell is GreaterHealSpell) - { - ProcessTarget(targ, m_AGHeal); - } - else if (spellTarg?.Spell is HealSpell) - { - ProcessTarget(targ, m_ALHeal); - } - else - { - targ.Cancel(m_Mobile, TargetCancelType.Canceled); - } + ProcessTarget(targ, m_AGHeal); } - else + else if (spellTarg?.Spell is HealSpell) { - var 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).Cast()) - { - new CureSpell(m_Mobile).Cast(); - } - } - else if (NeedGHeal(toHelp)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("{0} needs a greater heal", toHelp.Name); - } - - if (!new GreaterHealSpell(m_Mobile).Cast()) - { - new HealSpell(m_Mobile).Cast(); - } - } - else if (NeedLHeal(toHelp)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("{0} needs a lesser heal", toHelp.Name); - } - - new HealSpell(m_Mobile).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 void ProcessTarget(Target targ, NeedDelegate[] func) - { - var 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); - } + ProcessTarget(targ, m_ALHeal); } else { targ.Cancel(m_Mobile, TargetCancelType.Canceled); } } - - private Mobile Find(params NeedDelegate[] funcs) + else { - if (m_Mobile.Deleted) + var toHelp = Find(m_All); + + if (toHelp != null) { - return null; - } - - var map = m_Mobile.Map; - - if (map != null) - { - var prio = 0.0; - Mobile found = null; - - foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + if (NeedCure(toHelp)) { - if (!m_Mobile.CanSee(m) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team) + if (m_Mobile.Debug) { - continue; + m_Mobile.DebugSay($"{toHelp.Name} needs a cure"); } - for (var i = 0; i < funcs.Length; ++i) + if (!new CureSpell(m_Mobile).Cast()) { - if (funcs[i](m)) - { - var val = -m_Mobile.GetDistanceToSqrt(m); - - if (found == null || val > prio) - { - prio = val; - found = m; - } - - break; - } + new CureSpell(m_Mobile).Cast(); } } + else if (NeedGHeal(toHelp)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("{toHelp.Name} needs a greater heal"); + } - return found; + if (!new GreaterHealSpell(m_Mobile).Cast()) + { + new HealSpell(m_Mobile).Cast(); + } + } + else if (NeedLHeal(toHelp)) + { + m_Mobile.DebugSay("{toHelp.Name} needs a lesser heal"); + new HealSpell(m_Mobile).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 void ProcessTarget(Target targ, NeedDelegate[] func) + { + var toHelp = Find(func); + + if (toHelp == null) + { + targ.Cancel(m_Mobile, TargetCancelType.Canceled); + } + else if (targ.Range != -1 && !m_Mobile.InRange(toHelp, targ.Range)) + { + DoMove(m_Mobile.GetDirectionTo(toHelp) | Direction.Running); + } + else + { + targ.Invoke(m_Mobile, toHelp); + } + } + + private Mobile Find(params NeedDelegate[] funcs) + { + if (m_Mobile.Deleted) + { return null; } - private static bool NeedCure(Mobile m) => m.Poisoned; + var map = m_Mobile.Map; - private static bool NeedGHeal(Mobile m) => m.Hits < m.HitsMax - 40; + if (map == null) + { + return null; + } - private static bool NeedLHeal(Mobile m) => m.Hits < m.HitsMax - 10; + var prio = 0.0; + Mobile found = null; - private delegate bool NeedDelegate(Mobile m); + foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + { + if (!m_Mobile.CanSee(m) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team) + { + continue; + } + + for (var i = 0; i < funcs.Length; ++i) + { + if (funcs[i](m)) + { + var val = -m_Mobile.GetDistanceToSqrt(m); + + if (found == null || val > prio) + { + prio = val; + found = m; + } + + break; + } + } + } + + return found; } -} + + private static bool NeedCure(Mobile m) => m.Poisoned; + + private static bool NeedGHeal(Mobile m) => m.Hits < m.HitsMax - 40; + + private static bool NeedLHeal(Mobile m) => m.Hits < m.HitsMax - 10; + + private delegate bool NeedDelegate(Mobile m); +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index 9088e27f3..5984c0a2f 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -10,511 +10,386 @@ using Server.Spells.Sixth; using Server.Spells.Third; using Server.Targeting; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class MageAI : BaseAI { - public class MageAI : BaseAI + 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 + + private static readonly int[] m_Offsets = { - 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 + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1, - private static readonly int[] m_Offsets = - { - -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 - }; - - protected int m_Combo = -1; - - private Mobile m_LastTarget; - private Point3D m_LastTargetLoc; - private long m_NextCastTime; - private long m_NextHealTime; - - private LandTarget m_RevealTarget; - - public MageAI(BaseCreature m) - : base(m) + -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 + }; + + protected int m_Combo = -1; + + private Mobile m_LastTarget; + private Point3D m_LastTargetLoc; + private long m_NextCastTime; + private long m_NextHealTime; + + private LandTarget m_RevealTarget; + + public MageAI(BaseCreature m) + : base(m) + { + } + + public virtual bool SmartAI => m_Mobile is BaseVendor or BaseEscortable or Changeling; + + public virtual bool IsNecromancer => Core.AOS && m_Mobile.Skills.Necromancy.Value > 50; + + public override bool Think() + { + if (m_Mobile.Deleted) { + return false; } - public virtual bool SmartAI => m_Mobile is BaseVendor or BaseEscortable or Changeling; + return ProcessTarget() || base.Think(); + } - public virtual bool IsNecromancer => Core.AOS && m_Mobile.Skills.Necromancy.Value > 50; + public virtual double ScaleBySkill(double v, SkillName skill) => v * m_Mobile.Skills[skill].Value / 100; - public override bool Think() + public override bool DoActionWander() + { + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - if (m_Mobile.Deleted) + if (m_Mobile.Debug) { - return false; + m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); } - if (ProcessTarget()) - { - return true; - } - - return base.Think(); + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + m_NextCastTime = Core.TickCount; } - - public virtual double ScaleBySkill(double v, SkillName skill) => v * m_Mobile.Skills[skill].Value / 100; - - public override bool DoActionWander() + else if (SmartAI && m_Mobile.Mana < m_Mobile.ManaMax && !m_Mobile.Meditating) { - 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; - m_NextCastTime = Core.TickCount; - } - else if (SmartAI && m_Mobile.Mana < m_Mobile.ManaMax && !m_Mobile.Meditating) + if (m_Mobile.Debug) { m_Mobile.DebugSay("I am going to meditate"); - - m_Mobile.UseSkill(SkillName.Meditation); } - else + + m_Mobile.UseSkill(SkillName.Meditation); + } + else + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I am wandering"); - - m_Mobile.Warmode = false; - - base.DoActionWander(); - - if (Utility.RandomDouble() < 0.05) - { - var spell = CheckCastHealingSpell(); - - spell?.Cast(); - } } - return true; + m_Mobile.Warmode = false; + + base.DoActionWander(); + + if (Utility.RandomDouble() < 0.05) + { + var spell = CheckCastHealingSpell(); + + spell?.Cast(); + } } - private Spell CheckCastHealingSpell() - { - // If I'm poisoned, always attempt to cure. - if (m_Mobile.Poisoned) - { - return new CureSpell(m_Mobile); - } + return true; + } - // Summoned creatures never heal themselves. - if (m_Mobile.Summoned) + private Spell CheckCastHealingSpell() + { + // If I'm poisoned, always attempt to cure. + if (m_Mobile.Poisoned) + { + return new CureSpell(m_Mobile); + } + + // Summoned creatures never heal themselves. + if (m_Mobile.Summoned) + { + return null; + } + + if (m_Mobile.Controlled) + { + if (Core.TickCount - m_NextHealTime < 0) { return null; } + } - if (m_Mobile.Controlled) + if (!SmartAI) + { + if (ScaleBySkill(HealChance, SkillName.Magery) < Utility.RandomDouble()) { - if (Core.TickCount - m_NextHealTime < 0) - { - return null; - } + return null; } + } + else if (Utility.Random(0, 4 + (m_Mobile.Hits == 0 ? m_Mobile.HitsMax : m_Mobile.HitsMax / m_Mobile.Hits)) < 3) + { + return null; + } - if (!SmartAI) + Spell spell = null; + + if (m_Mobile.Hits < m_Mobile.HitsMax - 50) + { + if (UseNecromancy()) { - if (ScaleBySkill(HealChance, SkillName.Magery) < Utility.RandomDouble()) - { - return null; - } + m_Mobile.UseSkill(SkillName.SpiritSpeak); } 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) - { - if (UseNecromancy()) - { - m_Mobile.UseSkill(SkillName.SpiritSpeak); - } - else - { - spell = new GreaterHealSpell(m_Mobile); - } - } - else if (m_Mobile.Hits < m_Mobile.HitsMax - 10) - { - spell = new HealSpell(m_Mobile); - } - - double delay; - - if (m_Mobile.Int >= 500) - { - delay = Utility.RandomMinMax(7, 10); - } - else - { - delay = Math.Sqrt(600 - m_Mobile.Int); - } - - m_NextHealTime = Core.TickCount + (int)TimeSpan.FromSeconds(delay).TotalMilliseconds; - - 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); - } + spell = new GreaterHealSpell(m_Mobile); } } - - public void RunFrom(Mobile m) + else if (m_Mobile.Hits < m_Mobile.HitsMax - 10) { - Run((m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask); + spell = new HealSpell(m_Mobile); } - public void OnFailedMove() + var delay = m_Mobile.Int >= 500 ? Utility.RandomMinMax(7, 10) : Math.Sqrt(600 - m_Mobile.Int); + + m_NextHealTime = Core.TickCount + (int)TimeSpan.FromSeconds(delay).TotalMilliseconds; + + return spell; + } + + public void RunTo(Mobile m) + { + if (!SmartAI) { - if (!m_Mobile.DisallowAllMoves && (SmartAI - ? Utility.Random(4) == 0 - : ScaleBySkill(TeleportChance, SkillName.Magery) > Utility.RandomDouble())) + if (!MoveTo(m, true, m_Mobile.RangeFight)) { - m_Mobile.Target?.Cancel(m_Mobile, TargetCancelType.Canceled); - - new TeleportSpell(m_Mobile).Cast(); - - m_Mobile.DebugSay("I am stuck, I'm going to try teleporting away"); + OnFailedMove(); } - else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) - { - 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"); - } + return; } - public void Run(Direction d) + if (m.Paralyzed || m.Frozen) { - if (m_Mobile.Spell?.IsCasting == true || m_Mobile.Paralyzed || m_Mobile.Frozen || - m_Mobile.DisallowAllMoves) + if (m_Mobile.InRange(m, 1)) { - return; + RunFrom(m); } - - m_Mobile.Direction = d | Direction.Running; - - if (!DoMove(m_Mobile.Direction, true)) + else if (!m_Mobile.InRange(m, m_Mobile.RangeFight > 2 ? m_Mobile.RangeFight : 2) && !MoveTo(m, true, 1)) { OnFailedMove(); } } - - public virtual bool UseNecromancy() + else if (!m_Mobile.InRange(m, m_Mobile.RangeFight)) { - if (IsNecromancer) + if (!MoveTo(m, true, 1)) { - return Utility.Random( - m_Mobile.Skills.Magery.BaseFixedPoint + - m_Mobile.Skills.Necromancy.BaseFixedPoint - ) >= - m_Mobile.Skills.Magery.BaseFixedPoint; + 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 + : ScaleBySkill(TeleportChance, SkillName.Magery) > Utility.RandomDouble())) + { + m_Mobile.Target?.Cancel(m_Mobile, TargetCancelType.Canceled); + + new TeleportSpell(m_Mobile).Cast(); + + if (m_Mobile.Debug) + { + 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 {m_Mobile.FocusMob.Name}"); } - return false; + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am stuck"); + } + } + + public void Run(Direction d) + { + if (m_Mobile.Spell?.IsCasting == true || m_Mobile.Paralyzed || m_Mobile.Frozen || + m_Mobile.DisallowAllMoves) + { + return; } - public virtual Spell GetRandomDamageSpell() => - UseNecromancy() ? GetRandomDamageSpellNecro() : GetRandomDamageSpellMage(); + m_Mobile.Direction = d | Direction.Running; - public virtual Spell GetRandomDamageSpellNecro() + if (!DoMove(m_Mobile.Direction, true)) { - var bound = m_Mobile.Skills.Necromancy.Value >= 100 ? 5 : 3; + OnFailedMove(); + } + } - switch (Utility.Random(bound)) + public virtual bool UseNecromancy() + { + var magery = m_Mobile.Skills.Magery.BaseFixedPoint; + var necro = m_Mobile.Skills.Necromancy.BaseFixedPoint; + return IsNecromancer && Utility.Random(magery + necro) >= magery; + } + + public virtual Spell GetRandomDamageSpell() => + UseNecromancy() ? GetRandomDamageSpellNecro() : GetRandomDamageSpellMage(); + + public virtual Spell GetRandomDamageSpellNecro() + { + var bound = m_Mobile.Skills.Necromancy.Value >= 100 ? 5 : 3; + + return Utility.Random(bound) switch + { + 0 => new PainSpikeSpell(m_Mobile), + 1 => new PoisonStrikeSpell(m_Mobile), + 2 => new StrangleSpell(m_Mobile), + 3 => new WitherSpell(m_Mobile), + _ => new VengefulSpiritSpell(m_Mobile) + }; + } + + public virtual Spell GetRandomDamageSpellMage() + { + var maxCircle = Math.Clamp((int)((m_Mobile.Skills.Magery.Value + 20.0) / (100.0 / 7.0)), 1, 8); + + return Utility.Random(maxCircle * 2) switch + { + 0 => new MagicArrowSpell(m_Mobile), + 1 => new MagicArrowSpell(m_Mobile), + 2 => new HarmSpell(m_Mobile), + 3 => new HarmSpell(m_Mobile), + 4 => new FireballSpell(m_Mobile), + 5 => new FireballSpell(m_Mobile), + 6 => new LightningSpell(m_Mobile), + 7 => new LightningSpell(m_Mobile), + 8 => new MindBlastSpell(m_Mobile), + 9 => new MindBlastSpell(m_Mobile), + 10 => new EnergyBoltSpell(m_Mobile), + 11 => new ExplosionSpell(m_Mobile), + _ => new FlameStrikeSpell(m_Mobile) + }; + } + + public virtual Spell GetRandomCurseSpell() => + UseNecromancy() ? GetRandomCurseSpellNecro() : GetRandomCurseSpellMage(); + + public virtual Spell GetRandomCurseSpellNecro() + { + return Utility.Random(4) switch + { + 0 => new BloodOathSpell(m_Mobile), + 1 => new CorpseSkinSpell(m_Mobile), + 2 => new EvilOmenSpell(m_Mobile), + _ => new MindRotSpell(m_Mobile) + }; + } + + public virtual Spell GetRandomCurseSpellMage() + { + if (m_Mobile.Skills.Magery.Value >= 40.0 && Utility.Random(4) == 0) + { + return new CurseSpell(m_Mobile); + } + + return Utility.Random(3) switch + { + 0 => new WeakenSpell(m_Mobile), + 1 => new ClumsySpell(m_Mobile), + _ => new FeeblemindSpell(m_Mobile) + }; + } + + public virtual Spell GetRandomManaDrainSpell() + { + if (m_Mobile.Skills.Magery.Value >= 80.0 && Utility.RandomBool()) + { + return new ManaVampireSpell(m_Mobile); + } + + return new ManaDrainSpell(m_Mobile); + } + + public virtual Spell DoDispel(Mobile toDispel) + { + if (!SmartAI) + { + return ScaleBySkill(DispelChance, SkillName.Magery) > Utility.RandomDouble() + ? new DispelSpell(m_Mobile) + : ChooseSpell(toDispel); + } + + var spell = CheckCastHealingSpell(); + + if (spell == null) + { + var distance = (int)m_Mobile.GetDistanceToSqrt(toDispel); + if (!m_Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) { - case 0: - m_Mobile.DebugSay("Pain Spike"); - return new PainSpikeSpell(m_Mobile); - case 1: - m_Mobile.DebugSay("Poison Strike"); - return new PoisonStrikeSpell(m_Mobile); - case 2: - m_Mobile.DebugSay("Strangle"); - return new StrangleSpell(m_Mobile); - case 3: - m_Mobile.DebugSay("Wither"); - return new WitherSpell(m_Mobile); - default: - m_Mobile.DebugSay("Vengeful Spirit"); - return new VengefulSpiritSpell(m_Mobile); + spell = new TeleportSpell(m_Mobile); + } + else if (Utility.Random(3) == 0 && !m_Mobile.InRange(toDispel, 3) && !toDispel.Paralyzed && !toDispel.Frozen) + { + spell = new ParalyzeSpell(m_Mobile); + } + else + { + spell = new DispelSpell(m_Mobile); } } - public virtual Spell GetRandomDamageSpellMage() + return spell; + } + + public virtual Spell ChooseSpell(Mobile c) + { + Spell spell; + + if (!SmartAI) { - var maxCircle = Math.Clamp((int)((m_Mobile.Skills.Magery.Value + 20.0) / (100.0 / 7.0)), 1, 8); - - return Utility.Random(maxCircle * 2) switch - { - 0 => new MagicArrowSpell(m_Mobile), - 1 => new MagicArrowSpell(m_Mobile), - 2 => new HarmSpell(m_Mobile), - 3 => new HarmSpell(m_Mobile), - 4 => new FireballSpell(m_Mobile), - 5 => new FireballSpell(m_Mobile), - 6 => new LightningSpell(m_Mobile), - 7 => new LightningSpell(m_Mobile), - 8 => new MindBlastSpell(m_Mobile), - 9 => new MindBlastSpell(m_Mobile), - 10 => new EnergyBoltSpell(m_Mobile), - 11 => new ExplosionSpell(m_Mobile), - _ => new FlameStrikeSpell(m_Mobile) - }; - } - - public virtual Spell GetRandomCurseSpell() => - UseNecromancy() ? GetRandomCurseSpellNecro() : GetRandomCurseSpellMage(); - - public virtual Spell GetRandomCurseSpellNecro() - { - switch (Utility.Random(4)) - { - case 0: - m_Mobile.DebugSay("Blood Oath"); - return new BloodOathSpell(m_Mobile); - case 1: - m_Mobile.DebugSay("Corpse Skin"); - return new CorpseSkinSpell(m_Mobile); - case 2: - m_Mobile.DebugSay("Evil Omen"); - return new EvilOmenSpell(m_Mobile); - default: - m_Mobile.DebugSay("Mind Rot"); - return new MindRotSpell(m_Mobile); - } - } - - public virtual Spell GetRandomCurseSpellMage() - { - if (m_Mobile.Skills.Magery.Value >= 40.0 && Utility.Random(4) == 0) - { - return new CurseSpell(m_Mobile); - } - - return Utility.Random(3) switch - { - 0 => new WeakenSpell(m_Mobile), - 1 => new ClumsySpell(m_Mobile), - _ => new FeeblemindSpell(m_Mobile) - }; - } - - public virtual Spell GetRandomManaDrainSpell() - { - if (m_Mobile.Skills.Magery.Value >= 80.0 && Utility.RandomBool()) - { - return new ManaVampireSpell(m_Mobile); - } - - return new ManaDrainSpell(m_Mobile); - } - - public virtual Spell DoDispel(Mobile toDispel) - { - if (!SmartAI) - { - if (ScaleBySkill(DispelChance, SkillName.Magery) > Utility.RandomDouble()) - { - return new DispelSpell(m_Mobile); - } - - return ChooseSpell(toDispel); - } - - var spell = CheckCastHealingSpell(); - - if (spell == null) - { - var distance = (int)m_Mobile.GetDistanceToSqrt(toDispel); - if (!m_Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) - { - spell = new TeleportSpell(m_Mobile); - } - else if (Utility.Random(3) == 0 && !m_Mobile.InRange(toDispel, 3) && !toDispel.Paralyzed && !toDispel.Frozen) - { - spell = new ParalyzeSpell(m_Mobile); - } - else - { - spell = new DispelSpell(m_Mobile); - } - } - - return spell; - } - - public virtual Spell ChooseSpell(Mobile c) - { - Spell spell; - - if (!SmartAI) - { - spell = CheckCastHealingSpell(); - - if (spell != null) - { - return spell; - } - - if (IsNecromancer) - { - var psDamage = - (m_Mobile.Skills.SpiritSpeak.Value - c.Skills.MagicResist.Value) / 10 + - (c.Player ? 18 : 30); - - if (psDamage > c.Hits) - { - return new PainSpikeSpell(m_Mobile); - } - } - - switch (Utility.Random(16)) - { - case 0: - case 1: // Poison them - { - if (c.Poisoned) - { - goto default; - } - - m_Mobile.DebugSay("Attempting to poison"); - - spell = new PoisonSpell(m_Mobile); - break; - } - case 2: // Bless ourselves - { - m_Mobile.DebugSay("Blessing myself"); - - spell = new BlessSpell(m_Mobile); - break; - } - case 3: - case 4: // Curse them - { - m_Mobile.DebugSay("Attempting to curse"); - - spell = GetRandomCurseSpell(); - break; - } - case 5: // Paralyze them - { - if (c.Paralyzed || m_Mobile.Skills.Magery.Value <= 50.0) - { - goto default; - } - - m_Mobile.DebugSay("Attempting to paralyze"); - - spell = new ParalyzeSpell(m_Mobile); - break; - } - case 6: // Drain mana - { - m_Mobile.DebugSay("Attempting to drain mana"); - - spell = GetRandomManaDrainSpell(); - break; - } - case 7: // Invis ourselves - { - if (Utility.RandomBool()) - { - goto default; - } - - m_Mobile.DebugSay("Attempting to invis myself"); - - spell = new InvisibilitySpell(m_Mobile); - break; - } - default: // Damage them - { - m_Mobile.DebugSay("Just doing damage"); - - spell = GetRandomDamageSpell(); - break; - } - } - - return spell; - } - spell = CheckCastHealingSpell(); if (spell != null) @@ -522,584 +397,693 @@ namespace Server.Mobiles return spell; } - switch (Utility.Random(3)) + if (IsNecromancer) { - case 0: // Poison them + var psDamage = + (m_Mobile.Skills.SpiritSpeak.Value - c.Skills.MagicResist.Value) / 10 + + (c.Player ? 18 : 30); + + if (psDamage > c.Hits) + { + return new PainSpikeSpell(m_Mobile); + } + } + + switch (Utility.Random(16)) + { + case 0: + case 1: // Poison them { if (c.Poisoned) { - goto case 1; + goto default; } + m_Mobile.DebugSay("Attempting to poison"); + spell = new PoisonSpell(m_Mobile); break; } - case 1: // Deal some damage + case 2: // Bless ourselves { + m_Mobile.DebugSay("Blessing myself"); + + spell = new BlessSpell(m_Mobile); + break; + } + case 3: + case 4: // Curse them + { + m_Mobile.DebugSay("Attempting to curse"); + + spell = GetRandomCurseSpell(); + break; + } + case 5: // Paralyze them + { + if (c.Paralyzed || m_Mobile.Skills.Magery.Value <= 50.0) + { + goto default; + } + + m_Mobile.DebugSay("Attempting to paralyze"); + + spell = new ParalyzeSpell(m_Mobile); + break; + } + case 6: // Drain mana + { + m_Mobile.DebugSay("Attempting to drain mana"); + + spell = GetRandomManaDrainSpell(); + break; + } + case 7: // Invis ourselves + { + if (Utility.RandomBool()) + { + goto default; + } + + m_Mobile.DebugSay("Attempting to invis myself"); + + spell = new InvisibilitySpell(m_Mobile); + break; + } + default: // Damage them + { + m_Mobile.DebugSay("Just doing damage"); + spell = GetRandomDamageSpell(); - break; } - default: // Set up a combo + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"Casting {spell.Name}"); + } + + return spell; + } + + spell = CheckCastHealingSpell(); + + if (spell != null) + { + return spell; + } + + switch (Utility.Random(3)) + { + case 0: // Poison them + { + if (c.Poisoned) { - if (m_Mobile.Mana > 15 && m_Mobile.Mana < 40) - { - if (c.Paralyzed && !c.Poisoned && !m_Mobile.Meditating) - { - m_Mobile.DebugSay("I am going to meditate"); + goto case 1; + } - m_Mobile.UseSkill(SkillName.Meditation); - } - else if (!c.Poisoned) - { - spell = new ParalyzeSpell(m_Mobile); - } - } - else if (m_Mobile.Mana > 60) + spell = new PoisonSpell(m_Mobile); + break; + } + case 1: // Deal some damage + { + spell = GetRandomDamageSpell(); + break; + } + default: // Set up a combo + { + if (m_Mobile.Mana is > 15 and < 40) + { + if (c.Paralyzed && !c.Poisoned && !m_Mobile.Meditating) { - if (Utility.RandomBool() && !c.Paralyzed && !c.Frozen && !c.Poisoned) - { - m_Combo = 0; - spell = new ParalyzeSpell(m_Mobile); - } - else - { - m_Combo = 1; - spell = new ExplosionSpell(m_Mobile); - } + m_Mobile.DebugSay("I am going to meditate"); + + m_Mobile.UseSkill(SkillName.Meditation); } + else if (!c.Poisoned) + { + spell = new ParalyzeSpell(m_Mobile); + } + } + else if (m_Mobile.Mana > 60) + { + if (Utility.RandomBool() && !c.Paralyzed && !c.Frozen && !c.Poisoned) + { + m_Combo = 0; + spell = new ParalyzeSpell(m_Mobile); + } + else + { + m_Combo = 1; + spell = new ExplosionSpell(m_Mobile); + } + } + + break; + } + } + + if (m_Mobile.Debug) + { + if (spell != null) + { + m_Mobile.DebugSay($"Casting {spell.Name}"); + } + else + { + m_Mobile.DebugSay("I don't have a spell to use!"); + } + } + return spell; + } + + public virtual Spell DoCombo(Mobile c) + { + Spell spell = null; + + if (m_Combo == 0) + { + spell = new ExplosionSpell(m_Mobile); + ++m_Combo; // Move to next spell + } + else if (m_Combo == 1) + { + spell = new WeakenSpell(m_Mobile); + ++m_Combo; // Move to next spell + } + else if (m_Combo == 2) + { + if (!c.Poisoned) + { + spell = new PoisonSpell(m_Mobile); + } + else if (IsNecromancer) + { + spell = new StrangleSpell(m_Mobile); + } + + ++m_Combo; // Move to next spell + } + + if (m_Combo == 3 && spell == null) + { + switch (Utility.Random(IsNecromancer ? 4 : 3)) + { + case 0: + { + if (c.Int < c.Dex) + { + spell = new FeeblemindSpell(m_Mobile); + } + else + { + spell = new ClumsySpell(m_Mobile); + } + + ++m_Combo; // Move to next spell + break; + } + case 1: + { + spell = new EnergyBoltSpell(m_Mobile); + m_Combo = -1; // Reset combo state + break; + } + case 2: + { + spell = new FlameStrikeSpell(m_Mobile); + m_Combo = -1; // Reset combo state + break; + } + default: + { + spell = new PainSpikeSpell(m_Mobile); + m_Combo = -1; // Reset combo state break; } } - - return spell; + } + else if (m_Combo == 4 && spell == null) + { + spell = new MindBlastSpell(m_Mobile); + m_Combo = -1; } - public virtual Spell DoCombo(Mobile c) + return spell; + } + + private TimeSpan GetDelay(Spell spell) + { + if (SmartAI || spell is DispelSpell) { - Spell spell = null; - - if (m_Combo == 0) - { - spell = new ExplosionSpell(m_Mobile); - ++m_Combo; // Move to next spell - } - else if (m_Combo == 1) - { - spell = new WeakenSpell(m_Mobile); - ++m_Combo; // Move to next spell - } - else if (m_Combo == 2) - { - if (!c.Poisoned) - { - spell = new PoisonSpell(m_Mobile); - } - else if (IsNecromancer) - { - spell = new StrangleSpell(m_Mobile); - } - - ++m_Combo; // Move to next spell - } - - if (m_Combo == 3 && spell == null) - { - switch (Utility.Random(IsNecromancer ? 4 : 3)) - { - case 0: - { - if (c.Int < c.Dex) - { - spell = new FeeblemindSpell(m_Mobile); - } - else - { - spell = new ClumsySpell(m_Mobile); - } - - ++m_Combo; // Move to next spell - - break; - } - case 1: - { - spell = new EnergyBoltSpell(m_Mobile); - m_Combo = -1; // Reset combo state - break; - } - case 2: - { - spell = new FlameStrikeSpell(m_Mobile); - m_Combo = -1; // Reset combo state - break; - } - default: - { - spell = new PainSpikeSpell(m_Mobile); - m_Combo = -1; // Reset combo state - break; - } - } - } - else if (m_Combo == 4 && spell == null) - { - spell = new MindBlastSpell(m_Mobile); - m_Combo = -1; - } - - return spell; + return TimeSpan.FromSeconds(m_Mobile.ActiveSpeed); } - private TimeSpan GetDelay(Spell spell) + var del = ScaleBySkill(3.0, SkillName.Magery); + var min = 6.0 - del * 0.75; + var max = 6.0 - del * 1.25; + + return TimeSpan.FromSeconds(min + (max - min) * Utility.RandomDouble()); + } + + public override bool DoActionCombat() + { + var c = m_Mobile.Combatant; + m_Mobile.Warmode = true; + + if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !m_Mobile.CanSee(c) || + !m_Mobile.CanBeHarmful(c, false) || c.Map != m_Mobile.Map) { - if (SmartAI || spell is DispelSpell) + // 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)) { - return TimeSpan.FromSeconds(m_Mobile.ActiveSpeed); - } - - var del = ScaleBySkill(3.0, SkillName.Magery); - var min = 6.0 - del * 0.75; - var max = 6.0 - del * 1.25; - - return TimeSpan.FromSeconds(min + (max - min) * Utility.RandomDouble()); - } - - public override bool DoActionCombat() - { - var c = m_Mobile.Combatant; - m_Mobile.Warmode = true; - - if (c?.Deleted != false || !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 + $"Something happened to my combatant, so I am going to fight {m_Mobile.FocusMob.Name}" ); - - m_Mobile.Combatant = c = m_Mobile.FocusMob; - m_Mobile.FocusMob = null; } - else + + m_Mobile.Combatant = c = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); - Action = ActionType.Guard; - return true; } + + 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("I will switch to {0}", m_Mobile.FocusMob.Name); - m_Mobile.Combatant = c = m_Mobile.FocusMob; - m_Mobile.FocusMob = null; - } - } - - if (!Core.AOS && SmartAI && !m_Mobile.StunReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && - m_Mobile.Skills.Anatomy.Value >= 80.0) - { - EventSink.InvokeStunRequest(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.CanFlee) - { - if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) - { - // We are low on health, should we flee? - - bool flee; - - if (m_Mobile.Hits < c.Hits) - { - // We are more hurt than them - - var 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) - { - m_Mobile.DebugSay("I am going to flee from {0}", c.Name); - - Action = ActionType.Flee; - return true; - } - } - } - - if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0 && m_Mobile.InRange(c, Core.ML ? 10 : 12)) - { - // We are ready to cast a spell - - Spell spell; - var 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); - } - 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 or GreaterHealSpell && !c.Poisoned - ) // They have a heal spell out - { - spell = new PoisonSpell(m_Mobile); - } - 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, Core.ML ? 10 : 12)) - { - RunTo(toDispel); - } - } - else - { - RunTo(c); - } - - spell?.Cast(); - - m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; - } - else if (m_Mobile.Spell?.IsCasting != true) - { - RunTo(c); - } - - m_LastTarget = c; - m_LastTargetLoc = c.Location; - - return true; } - public override bool DoActionGuard() + if (!m_Mobile.InLOS(c)) { - if (m_LastTarget?.Hidden == true) + if (m_Mobile.Debug) { - var map = m_Mobile.Map; - - if (map == null || !m_Mobile.InRange(m_LastTargetLoc, Core.ML ? 10 : 12)) - { - m_LastTarget = null; - } - else if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0) - { - m_Mobile.DebugSay("I am going to reveal my last target"); - - m_RevealTarget = new LandTarget(m_LastTargetLoc, map); - Spell spell = new RevealSpell(m_Mobile); - - if (spell.Cast()) - { - m_LastTarget = null; // only do it once - } - - m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; - } + m_Mobile.DebugSay("I can't see my target"); } 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) + if (m_Mobile.Debug) { - ProcessTarget(); - - var spell = CheckCastHealingSpell(); - - spell?.Cast(); + m_Mobile.DebugSay($"I will switch to {m_Mobile.FocusMob.Name}"); } - 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)) - { - m_Mobile.DebugSay("I am scared of {0}", m_Mobile.FocusMob.Name); - - RunFrom(m_Mobile.FocusMob); + m_Mobile.Combatant = c = m_Mobile.FocusMob; m_Mobile.FocusMob = null; + } + } - if (m_Mobile.Poisoned && Utility.Random(0, 5) == 0) + if (!Core.AOS && SmartAI && !m_Mobile.StunReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && + m_Mobile.Skills.Anatomy.Value >= 80.0) + { + EventSink.InvokeStunRequest(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) + { + if (m_Mobile.Debug) { - new CureSpell(m_Mobile).Cast(); + 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.CanFlee && m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) + { + // We are low on health, should we flee? + // (10 + diff)% chance to flee + var fleeChance = 10 + Math.Max(0, c.Hits - m_Mobile.Hits); + + if (Utility.Random(0, 100) > fleeChance) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to flee from {c.Name}"); + } + + Action = ActionType.Flee; + return true; + } + } + + if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0 && m_Mobile.InRange(c, Core.ML ? 10 : 12)) + { + // We are ready to cast a spell + Spell spell; + var toDispel = FindDispelTarget(true); + + if (m_Mobile.Poisoned) // Top cast priority is cure + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am going to cure myself"); + } + + spell = new CureSpell(m_Mobile); + } + else if (toDispel != null) // Something dispellable is attacking us + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to dispel {toDispel}"); + } + + spell = DoDispel(toDispel); } else { - m_Mobile.DebugSay("Area seems clear, but my guard is up"); - - Action = ActionType.Guard; - m_Mobile.Warmode = true; + spell = SmartAI switch + { + // We are doing a spell combo + true when m_Combo != -1 => DoCombo(c), + // They have a heal spell out + true when + !c.Poisoned && + c.Spell is HealSpell or GreaterHealSpell => new PoisonSpell(m_Mobile), + _ => ChooseSpell(c) + }; } - return true; + // Now we have a spell picked + // Move first before casting + + if (!SmartAI || toDispel == null) + { + RunTo(c); + } + else if (m_Mobile.InRange(toDispel, 10)) + { + RunFrom(toDispel); + } + else if (!m_Mobile.InRange(toDispel, Core.ML ? 10 : 12)) + { + RunTo(toDispel); + } + + spell?.Cast(); + + m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; + } + else if (m_Mobile.Spell?.IsCasting != true) + { + RunTo(c); } - public Mobile FindDispelTarget(bool activeOnly) + m_LastTarget = c; + m_LastTargetLoc = c.Location; + + return true; + } + + public override bool DoActionGuard() + { + if (m_LastTarget?.Hidden == true) { - if (m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel(m_Mobile) || m_Mobile.AutoDispel) - { - return null; - } - - if (activeOnly) - { - var aggressed = m_Mobile.Aggressed; - var aggressors = m_Mobile.Aggressors; - - Mobile active = null; - var activePrio = 0.0; - - var comb = m_Mobile.Combatant; - - if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && - m_Mobile.InRange(comb, Core.ML ? 10 : 12) && CanDispel(comb)) - { - active = comb; - activePrio = m_Mobile.GetDistanceToSqrt(comb); - - if (activePrio <= 2) - { - return active; - } - } - - for (var i = 0; i < aggressed.Count; ++i) - { - var info = aggressed[i]; - var m = info.Defender; - - if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, Core.ML ? 10 : 12) && CanDispel(m)) - { - var prio = m_Mobile.GetDistanceToSqrt(m); - - if (active == null || prio < activePrio) - { - active = m; - activePrio = prio; - - if (activePrio <= 2) - { - return active; - } - } - } - } - - for (var i = 0; i < aggressors.Count; ++i) - { - var info = aggressors[i]; - var m = info.Attacker; - - if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, Core.ML ? 10 : 12) && CanDispel(m)) - { - var prio = m_Mobile.GetDistanceToSqrt(m); - - if (active == null || prio < activePrio) - { - active = m; - activePrio = prio; - - if (activePrio <= 2) - { - return active; - } - } - } - } - - return active; - } - var map = m_Mobile.Map; - if (map != null) + if (map == null || !m_Mobile.InRange(m_LastTargetLoc, Core.ML ? 10 : 12)) { - Mobile active = null, inactive = null; - double actPrio = 0.0, inactPrio = 0.0; - - var comb = m_Mobile.Combatant; - - if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb)) + m_LastTarget = null; + } + else if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0) + { + if (m_Mobile.Debug) { - active = inactive = comb; - actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb); + m_Mobile.DebugSay("I am going to reveal my last target"); } - foreach (var m in m_Mobile.GetMobilesInRange(Core.ML ? 10 : 12)) + m_RevealTarget = new LandTarget(m_LastTargetLoc, map); + Spell spell = new RevealSpell(m_Mobile); + + if (spell.Cast()) { - if (m != m_Mobile && CanDispel(m)) - { - var prio = m_Mobile.GetDistanceToSqrt(m); - - if (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; - } - } + m_LastTarget = null; // only do it once } - return active ?? inactive; + m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; + } + } + + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); } + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + if (!m_Mobile.Controlled) + { + ProcessTarget(); + + var spell = CheckCastHealingSpell(); + + 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) + { + if (m_Mobile.Debug) + { + 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 {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).Cast(); + } + } + else + { + if (m_Mobile.Debug) + { + 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; } - public bool CanDispel(Mobile m) => - m is BaseCreature creature && creature.Summoned && m_Mobile.CanBeHarmful(creature, false) && - !creature.IsAnimatedDead; - - private bool ProcessTarget() + if (activeOnly) { - var targ = m_Mobile.Target; + var aggressed = m_Mobile.Aggressed; + var aggressors = m_Mobile.Aggressors; - if (targ == null) + Mobile active = null; + var activePrio = 0.0; + + var comb = m_Mobile.Combatant; + + if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && + m_Mobile.InRange(comb, Core.ML ? 10 : 12) && CanDispel(comb)) { - return false; - } + active = comb; + activePrio = m_Mobile.GetDistanceToSqrt(comb); - var spellTarg = targ as ISpellTarget; - - var isReveal = spellTarg?.Spell is RevealSpell; - var isDispel = spellTarg?.Spell is DispelSpell; - var isParalyze = spellTarg?.Spell is ParalyzeSpell; - var isTeleport = spellTarg?.Spell is TeleportSpell; - var isInvisible = spellTarg?.Spell is InvisibilitySpell; - var teleportAway = false; - - Mobile toTarget; - - if (isInvisible) - { - toTarget = m_Mobile; - } - else if (isDispel) - { - toTarget = FindDispelTarget(false); - - if (!SmartAI && toTarget != null) + if (activePrio <= 2) { - RunTo(toTarget); - } - else if (toTarget != null && m_Mobile.InRange(toTarget, 10)) - { - RunFrom(toTarget); + return active; } } - else if (SmartAI && (isParalyze || isTeleport)) + + for (var i = 0; i < aggressed.Count; ++i) { - toTarget = FindDispelTarget(true); + var info = aggressed[i]; + var m = info.Defender; - if (toTarget == null) + if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, Core.ML ? 10 : 12) && CanDispel(m)) { - toTarget = m_Mobile.Combatant; + var prio = m_Mobile.GetDistanceToSqrt(m); - if (toTarget != null) + if (active == null || prio < activePrio) { - RunTo(toTarget); + active = m; + activePrio = prio; + + if (activePrio <= 2) + { + return active; + } } } - else if (m_Mobile.InRange(toTarget, 10)) + } + + for (var i = 0; i < aggressors.Count; ++i) + { + var info = aggressors[i]; + var m = info.Attacker; + + if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, Core.ML ? 10 : 12) && CanDispel(m)) { - RunFrom(toTarget); - teleportAway = true; - } - else - { - teleportAway = true; + var prio = m_Mobile.GetDistanceToSqrt(m); + + if (active == null || prio < activePrio) + { + active = m; + activePrio = prio; + + if (activePrio <= 2) + { + return active; + } + } } } - else + + return active; + } + + var map = m_Mobile.Map; + + if (map != null) + { + Mobile active = null, inactive = null; + double actPrio = 0.0, inactPrio = 0.0; + + var comb = m_Mobile.Combatant; + + if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb)) + { + active = inactive = comb; + actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb); + } + + foreach (var m in m_Mobile.GetMobilesInRange(Core.ML ? 10 : 12)) + { + if (m != m_Mobile && CanDispel(m)) + { + var prio = m_Mobile.GetDistanceToSqrt(m); + + if (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 ?? inactive; + } + + return null; + } + + public bool CanDispel(Mobile m) => + m is BaseCreature creature && creature.Summoned && m_Mobile.CanBeHarmful(creature, false) && + !creature.IsAnimatedDead; + + private bool ProcessTarget() + { + var targ = m_Mobile.Target; + + if (targ == null) + { + return false; + } + + var spellTarg = targ as ISpellTarget; + + var isReveal = spellTarg?.Spell is RevealSpell; + var isDispel = spellTarg?.Spell is DispelSpell; + var isParalyze = spellTarg?.Spell is ParalyzeSpell; + var isTeleport = spellTarg?.Spell is TeleportSpell; + var isInvisible = spellTarg?.Spell is InvisibilitySpell; + var teleportAway = false; + + Mobile toTarget; + + if (isInvisible) + { + toTarget = m_Mobile; + } + else 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; @@ -1108,93 +1092,111 @@ namespace Server.Mobiles RunTo(toTarget); } } - - if ((targ.Flags & TargetFlags.Harmful) != 0 && toTarget != null) + else if (m_Mobile.InRange(toTarget, 10)) { - 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 (isReveal && m_RevealTarget != null) - { - targ.Invoke(m_Mobile, m_RevealTarget); + RunFrom(toTarget); + teleportAway = true; } else { - var map = m_Mobile.Map; + teleportAway = true; + } + } + else + { + toTarget = m_Mobile.Combatant; - if (map != null && isTeleport && toTarget != null) + 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 (isReveal && m_RevealTarget != null) + { + targ.Invoke(m_Mobile, m_RevealTarget); + } + else + { + var map = m_Mobile.Map; + + if (map != null && isTeleport && toTarget != null) + { + var teleRange = targ.Range >= 0 ? targ.Range : + Core.ML ? 11 : 12; + + int px, py; + + if (teleportAway) { - var teleRange = targ.Range >= 0 ? targ.Range : - Core.ML ? 11 : 12; + var rx = m_Mobile.X - toTarget.X; + var ry = m_Mobile.Y - toTarget.Y; - int px, py; + var d = m_Mobile.GetDistanceToSqrt(toTarget); - if (teleportAway) + px = toTarget.X + (int)(rx * (10 / d)); + py = toTarget.Y + (int)(ry * (10 / d)); + } + else + { + px = toTarget.X; + py = toTarget.Y; + } + + for (var i = 0; i < m_Offsets.Length; i += 2) + { + int x = m_Offsets[i], y = m_Offsets[i + 1]; + + var p = new Point3D(px + x, py + y, 0); + + var 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)) { - var rx = m_Mobile.X - toTarget.X; - var ry = m_Mobile.Y - toTarget.Y; - - var 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 (var i = 0; i < m_Offsets.Length; i += 2) - { - int x = m_Offsets[i], y = m_Offsets[i + 1]; - - var p = new Point3D(px + x, py + y, 0); - - var 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; - } - } - - for (var i = 0; i < 10; ++i) - { - var randomPoint = new Point3D( - m_Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1), - m_Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1), - 0 - ); - - var 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.Invoke(m_Mobile, lt); + return true; } } - targ.Cancel(m_Mobile, TargetCancelType.Canceled); + for (var i = 0; i < 10; ++i) + { + var randomPoint = new Point3D( + m_Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1), + m_Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1), + 0 + ); + + var 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; + } + } } - return true; + targ.Cancel(m_Mobile, TargetCancelType.Canceled); } + + return true; } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index ba60c82a7..e3ef2da4d 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -1,175 +1,176 @@ -namespace Server.Mobiles -{ - public class MeleeAI : BaseAI - { - public MeleeAI(BaseCreature m) : base(m) - { - } +using System; - public override bool DoActionWander() +namespace Server.Mobiles; + +public class MeleeAI : BaseAI +{ + public MeleeAI(BaseCreature m) : base(m) + { + } + + public override bool DoActionWander() + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I have no combatant"); + } - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name); - } + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } - m_Mobile.Combatant = m_Mobile.FocusMob; - Action = ActionType.Combat; - } - else + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + var combatant = m_Mobile.Combatant; + + if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map || !combatant.Alive || + combatant.IsDeadBondedPet) + { + if (m_Mobile.Debug) { - base.DoActionWander(); + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); } + Action = ActionType.Guard; + return true; } - public override bool DoActionCombat() + if (!m_Mobile.InRange(combatant, m_Mobile.RangePerception)) { - var combatant = m_Mobile.Combatant; + // They are somewhat far away, can we find something else? - if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map || !combatant.Alive || - combatant.IsDeadBondedPet) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + 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) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant has fled, so I am on guard"); + } + + Action = ActionType.Guard; + + return true; + } + } + + if (!MoveTo(combatant, true, m_Mobile.RangeFight)) + { + 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 {m_Mobile.FocusMob!.Name}"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + + return true; + } + + if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); + } Action = ActionType.Guard; return true; } - if (!m_Mobile.InRange(combatant, m_Mobile.RangePerception)) + if (m_Mobile.Debug) { - // 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; - } + m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); } + } - if (!MoveTo(combatant, true, m_Mobile.RangeFight)) + if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) + { + if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + // We are low on health, should we flee? + + var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); // (10 + diff)% chance to flee; + if (Utility.Random(0, 100) < fleeChance) { if (m_Mobile.Debug) { - m_Mobile.DebugSay("My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob?.Name ?? "null"); + m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); } - m_Mobile.Combatant = m_Mobile.FocusMob; - Action = ActionType.Combat; - - return true; - } - - 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; - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I should be closer to {0}", combatant.Name); + Action = ActionType.Flee; } } - - if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) - { - if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) - { - // We are low on health, should we flee? - - bool flee; - - if (m_Mobile.Hits < combatant.Hits) - { - // We are more hurt than them - - var 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() + return true; + } + + public override bool DoActionGuard() + { + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (m_Mobile.Debug) { - 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(); + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); } - return true; + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); } - public override bool DoActionFlee() + return true; + } + + public override bool DoActionFlee() + { + if (m_Mobile.Hits > m_Mobile.HitsMax / 2) { - if (m_Mobile.Hits > m_Mobile.HitsMax / 2) + if (m_Mobile.Debug) { 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; + Action = ActionType.Combat; } + else + { + m_Mobile.FocusMob = m_Mobile.Combatant; + base.DoActionFlee(); + } + + return true; } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/PredatorAI.cs b/Projects/UOContent/Mobiles/AI/PredatorAI.cs index 8c4110293..bdfd5d890 100644 --- a/Projects/UOContent/Mobiles/AI/PredatorAI.cs +++ b/Projects/UOContent/Mobiles/AI/PredatorAI.cs @@ -1,82 +1,104 @@ -namespace Server.Mobiles -{ - public class PredatorAI : BaseAI - { - public PredatorAI(BaseCreature m) : base(m) - { - } +namespace Server.Mobiles; - public override bool DoActionWander() +public class PredatorAI : BaseAI +{ + public PredatorAI(BaseCreature m) : base(m) + { + } + + public override bool DoActionWander() + { + if (m_Mobile.Combatant != null) { - if (m_Mobile.Combatant != null) + if (m_Mobile.Debug) { 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(); } + Action = ActionType.Combat; + } + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("There is something near, I go away"); + } + + Action = ActionType.Backoff; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + var combatant = m_Mobile.Combatant; + + if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + } + + Action = ActionType.Wander; return true; } - public override bool DoActionCombat() + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - var combatant = m_Mobile.Combatant; - - if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map) + if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}"); + } + Action = ActionType.Wander; return true; } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (m_Mobile.Debug) { - if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) - { - m_Mobile.DebugSay("I cannot find {0}", combatant.Name); - - Action = ActionType.Wander; - return true; - } - - m_Mobile.DebugSay("I should be closer to {0}", combatant.Name); + m_Mobile.DebugSay($"I should be closer to {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; - } + 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)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Well, here I am safe"); + } + + Action = ActionType.Wander; + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my focus, lets relax"); + } + + Action = ActionType.Wander; + } + + return true; + } +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/SpeedInfo.cs b/Projects/UOContent/Mobiles/AI/SpeedInfo.cs deleted file mode 100644 index a6403922f..000000000 --- a/Projects/UOContent/Mobiles/AI/SpeedInfo.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Factions; -using Server.Mobiles; - -namespace Server -{ - public class SpeedInfo - { - // Should we use the new method of speeds? - private static readonly bool Enabled = true; - - private static Dictionary m_Table; - - private static readonly SpeedInfo[] m_Speeds = - { - /* Slow */ - new( - 0.3, - 0.6, - new[] - { - 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( - 0.2, - 0.4, - new[] - { - 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), typeof(RuneBeetle), - typeof(Changeling), typeof(LadyJennifyr), typeof(LadyMarai), typeof(MasterJonath), - typeof(MasterMikael), typeof(MasterTheophilus), typeof(RedDeath), - typeof(SirPatrick), typeof(Miasma), typeof(Rend), - typeof(Grobu), typeof(Gnaw), typeof(Guile), - typeof(Irk), typeof(Spite), typeof(LadyLissith), - typeof(LadySabrix), typeof(Malefic), typeof(Silk), - typeof(Virulent) - // TODO: Where to put Lurg, Putrefier, Swoop and Pyre? They seem slower. - } - ), - /* Very Fast */ - new( - 0.175, - 0.350, - new[] - { - typeof(Barracoon), typeof(Mephitis), typeof(Neira), - typeof(Rikktor), typeof(Semidar), typeof(EnergyVortex), - typeof(EliteNinja), 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(FactionDeathKnight) - } - ), - /* Medium */ - new( - 0.25, - 0.5, - new[] - { - typeof(AcidElemental), 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(Gaman), typeof(Crane), typeof(Beetle) - } - ) - }; - - public SpeedInfo(double activeSpeed, double passiveSpeed, Type[] types) - { - ActiveSpeed = activeSpeed; - PassiveSpeed = passiveSpeed; - Types = types; - } - - public double ActiveSpeed { get; set; } - - public double PassiveSpeed { get; set; } - - public Type[] Types { get; set; } - - public static bool Contains(object obj) - { - if (!Enabled) - { - return false; - } - - if (m_Table == null) - { - LoadTable(); - } - - return m_Table!.ContainsKey(obj.GetType()); - } - - public static bool GetSpeeds(object obj, ref double activeSpeed, ref double passiveSpeed) - { - if (!Enabled) - { - return false; - } - - if (m_Table == null) - { - LoadTable(); - } - - if (!m_Table!.TryGetValue(obj.GetType(), out var sp)) - { - return false; - } - - activeSpeed = sp.ActiveSpeed; - passiveSpeed = sp.PassiveSpeed; - - return true; - } - - private static void LoadTable() - { - m_Table = new Dictionary(); - - for (var i = 0; i < m_Speeds.Length; ++i) - { - var info = m_Speeds[i]; - var types = info.Types; - - for (var j = 0; j < types.Length; ++j) - { - m_Table[types[j]] = info; - } - } - } - } -} diff --git a/Projects/UOContent/Mobiles/AI/ThiefAI.cs b/Projects/UOContent/Mobiles/AI/ThiefAI.cs index 71347cb66..486e905f2 100644 --- a/Projects/UOContent/Mobiles/AI/ThiefAI.cs +++ b/Projects/UOContent/Mobiles/AI/ThiefAI.cs @@ -1,179 +1,214 @@ +using System; using Server.Items; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class ThiefAI : BaseAI { - public class ThiefAI : BaseAI + private Item m_toDisarm; + + public ThiefAI(BaseCreature m) : base(m) { - private Item m_toDisarm; + } - public ThiefAI(BaseCreature m) : base(m) - { - } - - public override bool DoActionWander() + public override bool DoActionWander() + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I have no combatant"); + } - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + 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.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } - m_Mobile.Combatant = m_Mobile.FocusMob; - Action = ActionType.Combat; - } - else + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + var combatant = m_Mobile.Combatant; + + if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map) + { + if (m_Mobile.Debug) { - base.DoActionWander(); + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); } + Action = ActionType.Guard; + return true; } - public override bool DoActionCombat() + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - var combatant = m_Mobile.Combatant; - - if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map) + if (m_Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); - - Action = ActionType.Guard; - - return true; + m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + } + } + else + { + if (m_toDisarm?.IsChildOf(m_Mobile.Backpack) != false) + { + m_toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded); } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (!Core.AOS && !m_Mobile.DisarmReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && + m_Mobile.Skills.ArmsLore.Value >= 80.0 && m_toDisarm != null) { - m_Mobile.DebugSay("I should be closer to {0}", combatant.Name); + EventSink.InvokeDisarmRequest(m_Mobile); } - else + + if (m_toDisarm?.IsChildOf(combatant.Backpack) == true && + Core.TickCount - m_Mobile.NextSkillTime >= 0 && m_toDisarm.LootType != LootType.Blessed && + m_toDisarm.LootType != LootType.Newbied) { - if (m_toDisarm?.IsChildOf(m_Mobile.Backpack) != false) - { - m_toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded); - } - - if (!Core.AOS && !m_Mobile.DisarmReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && - m_Mobile.Skills.ArmsLore.Value >= 80.0 && m_toDisarm != null) - { - EventSink.InvokeDisarmRequest(m_Mobile); - } - - if (m_toDisarm?.IsChildOf(combatant.Backpack) == true && - Core.TickCount - m_Mobile.NextSkillTime >= 0 && m_toDisarm.LootType != LootType.Blessed && - m_toDisarm.LootType != LootType.Newbied) + if (m_Mobile.Debug) { m_Mobile.DebugSay("Trying to steal from combatant."); - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, m_toDisarm); } - else if (m_toDisarm == null && Core.TickCount - m_Mobile.NextSkillTime >= 0) + + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, m_toDisarm); + } + else if (m_toDisarm == null && Core.TickCount - m_Mobile.NextSkillTime >= 0) + { + var cpack = combatant.Backpack; + + if (cpack != null) { - var cpack = combatant.Backpack; - - if (cpack != null) + Item steala = cpack.FindItemByType(); + if (steala != null) { - Item steala = cpack.FindItemByType(); - if (steala != null) + if (m_Mobile.Debug) { m_Mobile.DebugSay("Trying to steal from combatant."); - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, steala); } - Item stealb = cpack.FindItemByType(); - if (stealb != null) + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, steala); + } + + Item stealb = cpack.FindItemByType(); + if (stealb != null) + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("Trying to steal from combatant."); - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, stealb); } - Item stealc = cpack.FindItemByType(); - if (stealc != null) + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, stealb); + } + + Item stealc = cpack.FindItemByType(); + if (stealc != null) + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("Trying to steal from combatant."); - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, stealc); } - Item steald = cpack.FindItemByType(); - if (steald != null) + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, stealc); + } + + Item steald = cpack.FindItemByType(); + if (steald != null) + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("Trying to steal from combatant."); - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, steald); } - else if (steala == null && stealb == null && stealc == null) - { - m_Mobile.DebugSay("I am going to flee from {0}", combatant.Name); - Action = ActionType.Flee; + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, steald); + } + else if (steala == null && stealb == null && stealc == null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); } + + Action = ActionType.Flee; } } } + } - if (m_Mobile.Hits >= m_Mobile.HitsMax * 20 / 100 || !m_Mobile.CanFlee) - { - return true; - } - - // We are low on health, should we flee? - bool flee; - - if (m_Mobile.Hits < combatant.Hits) - { - // We are more hurt than them - var 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; - } - + if (m_Mobile.Hits >= m_Mobile.HitsMax * 20 / 100 || !m_Mobile.CanFlee) + { return true; } - public override bool DoActionGuard() + // We are low on health, should we flee? + // (10 + diff)% chance to flee + var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); + + if (Utility.Random(0, 100) > fleeChance) { - 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(); + m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); } - return true; + Action = ActionType.Flee; } - public override bool DoActionFlee() + return true; + } + + public override bool DoActionGuard() + { + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - if (m_Mobile.Hits > m_Mobile.HitsMax / 2) + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } + + 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) + { + if (m_Mobile.Debug) { 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; + Action = ActionType.Combat; } + else + { + m_Mobile.FocusMob = m_Mobile.Combatant; + base.DoActionFlee(); + } + + return true; } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/VendorAI.cs b/Projects/UOContent/Mobiles/AI/VendorAI.cs index ade3686e5..066be44e3 100644 --- a/Projects/UOContent/Mobiles/AI/VendorAI.cs +++ b/Projects/UOContent/Mobiles/AI/VendorAI.cs @@ -1,157 +1,158 @@ -namespace Server.Mobiles -{ - public class VendorAI : BaseAI - { - public VendorAI(BaseCreature m) : base(m) - { - } +namespace Server.Mobiles; - public override bool DoActionWander() +public class VendorAI : BaseAI +{ + public VendorAI(BaseCreature m) : base(m) + { + } + + public override bool DoActionWander() + { + if (m_Mobile.Debug) { m_Mobile.DebugSay("I'm fine"); + } - if (m_Mobile.Combatant != null) + if (m_Mobile.Combatant != null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); + } + + m_Mobile.Say(Utility.RandomBool() ? 1005305 : 501603); + Action = ActionType.Flee; + } + else + { + if (m_Mobile.FocusMob != null) { if (m_Mobile.Debug) { - m_Mobile.DebugSay("{0} is attacking me", m_Mobile.Combatant.Name); + m_Mobile.DebugSay($"{m_Mobile.FocusMob.Name} has talked to me"); } - m_Mobile.Say(Utility.RandomList(1005305, 501603)); - - Action = ActionType.Flee; + Action = ActionType.Interact; } else { - if (m_Mobile.FocusMob != null) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("{0} has talked to me", m_Mobile.FocusMob.Name); - } + m_Mobile.Warmode = false; - Action = ActionType.Interact; - } - else - { - m_Mobile.Warmode = false; - - base.DoActionWander(); - } + base.DoActionWander(); } + } + + return true; + } + + public override bool DoActionInteract() + { + var customer = m_Mobile.FocusMob; + + if (m_Mobile.Combatant != null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); + } + + m_Mobile.Say(Utility.RandomList(1005305, 501603)); + + Action = ActionType.Flee; return true; } - public override bool DoActionInteract() + if (customer?.Deleted != false || customer.Map != m_Mobile.Map) { - var 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?.Deleted != false || customer.Map != m_Mobile.Map) + if (m_Mobile.Debug) { m_Mobile.DebugSay("My customer have disapeared"); - m_Mobile.FocusMob = null; - - Action = ActionType.Wander; } - else + + m_Mobile.FocusMob = null; + + Action = ActionType.Wander; + } + else if (customer.InRange(m_Mobile, m_Mobile.RangeFight)) + { + if (m_Mobile.Debug) { - 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; - } + m_Mobile.DebugSay($"I am with {customer.Name}"); } + m_Mobile.Direction = m_Mobile.GetDirectionTo(customer); + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{customer.Name} is gone"); + } + + 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; } - public override bool DoActionGuard() - { - m_Mobile.FocusMob = m_Mobile.Combatant; - return base.DoActionGuard(); - } + return base.HandlesOnSpeech(from); + } - public override bool HandlesOnSpeech(Mobile from) + // Temporary + public override void OnSpeech(SpeechEventArgs e) + { + base.OnSpeech(e); + + var from = e.Mobile; + + if (m_Mobile is BaseVendor vendor && from.InRange(m_Mobile, Core.AOS ? 1 : 4) && !e.Handled) { - if (from.InRange(m_Mobile, 4)) + if (e.HasKeyword(0x14D)) // *vendor sell* { - return true; + e.Handled = true; + + vendor.VendorSell(from); + vendor.FocusMob = from; } - - return base.HandlesOnSpeech(from); - } - - // Temporary - public override void OnSpeech(SpeechEventArgs e) - { - base.OnSpeech(e); - - var from = e.Mobile; - - if (m_Mobile is BaseVendor vendor && from.InRange(m_Mobile, Core.AOS ? 1 : 4) && !e.Handled) + else if (e.HasKeyword(0x3C)) // *vendor buy* { - if (e.HasKeyword(0x14D)) // *vendor sell* + e.Handled = true; + + vendor.VendorBuy(from); + vendor.FocusMob = from; + } + else if (WasNamed(e.Speech)) + { + if (e.HasKeyword(0x177)) // *sell* { e.Handled = true; vendor.VendorSell(from); - vendor.FocusMob = from; } - else if (e.HasKeyword(0x3C)) // *vendor buy* + else if (e.HasKeyword(0x171)) // *buy* { e.Handled = true; vendor.VendorBuy(from); - vendor.FocusMob = from; } - else if (WasNamed(e.Speech)) - { - if (e.HasKeyword(0x177)) // *sell* - { - e.Handled = true; - vendor.VendorSell(from); - } - else if (e.HasKeyword(0x171)) // *buy* - { - e.Handled = true; - - vendor.VendorBuy(from); - } - - vendor.FocusMob = from; - } + vendor.FocusMob = from; } } } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs b/Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs index 536a9a97e..c692f9286 100644 --- a/Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs +++ b/Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class BlackBear : BaseCreature { [Constructible] - public BlackBear() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public BlackBear() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 211; BaseSoundID = 0xA3; diff --git a/Projects/UOContent/Mobiles/Animals/Bears/BrownBear.cs b/Projects/UOContent/Mobiles/Animals/Bears/BrownBear.cs index 1e3175411..781f7bb37 100644 --- a/Projects/UOContent/Mobiles/Animals/Bears/BrownBear.cs +++ b/Projects/UOContent/Mobiles/Animals/Bears/BrownBear.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class BrownBear : BaseCreature { [Constructible] - public BrownBear() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public BrownBear() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 167; BaseSoundID = 0xA3; diff --git a/Projects/UOContent/Mobiles/Animals/Bears/GrizzlyBear.cs b/Projects/UOContent/Mobiles/Animals/Bears/GrizzlyBear.cs index 56a40674c..709b2181a 100644 --- a/Projects/UOContent/Mobiles/Animals/Bears/GrizzlyBear.cs +++ b/Projects/UOContent/Mobiles/Animals/Bears/GrizzlyBear.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class GrizzlyBear : BaseCreature { [Constructible] - public GrizzlyBear() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public GrizzlyBear() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 212; BaseSoundID = 0xA3; diff --git a/Projects/UOContent/Mobiles/Animals/Bears/PolarBear.cs b/Projects/UOContent/Mobiles/Animals/Bears/PolarBear.cs index eccc1716d..2931c8931 100644 --- a/Projects/UOContent/Mobiles/Animals/Bears/PolarBear.cs +++ b/Projects/UOContent/Mobiles/Animals/Bears/PolarBear.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class PolarBear : BaseCreature { [Constructible] - public PolarBear() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public PolarBear() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 213; BaseSoundID = 0xA3; diff --git a/Projects/UOContent/Mobiles/Animals/Birds/Chicken.cs b/Projects/UOContent/Mobiles/Animals/Birds/Chicken.cs index 756de8844..bd8b7146b 100644 --- a/Projects/UOContent/Mobiles/Animals/Birds/Chicken.cs +++ b/Projects/UOContent/Mobiles/Animals/Birds/Chicken.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Chicken : BaseCreature { [Constructible] - public Chicken() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Chicken() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xD0; BaseSoundID = 0x6E; diff --git a/Projects/UOContent/Mobiles/Animals/Birds/Crane.cs b/Projects/UOContent/Mobiles/Animals/Birds/Crane.cs index 1cb10d33d..171a20fb5 100644 --- a/Projects/UOContent/Mobiles/Animals/Birds/Crane.cs +++ b/Projects/UOContent/Mobiles/Animals/Birds/Crane.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Crane : BaseCreature { [Constructible] - public Crane() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Crane() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 254; BaseSoundID = 0x4D7; diff --git a/Projects/UOContent/Mobiles/Animals/Birds/Eagle.cs b/Projects/UOContent/Mobiles/Animals/Birds/Eagle.cs index 593a2c694..4df82ef66 100644 --- a/Projects/UOContent/Mobiles/Animals/Birds/Eagle.cs +++ b/Projects/UOContent/Mobiles/Animals/Birds/Eagle.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Eagle : BaseCreature { [Constructible] - public Eagle() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Eagle() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 5; BaseSoundID = 0x2EE; diff --git a/Projects/UOContent/Mobiles/Animals/Birds/Phoenix.cs b/Projects/UOContent/Mobiles/Animals/Birds/Phoenix.cs index 49179359c..7c0befb94 100644 --- a/Projects/UOContent/Mobiles/Animals/Birds/Phoenix.cs +++ b/Projects/UOContent/Mobiles/Animals/Birds/Phoenix.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Phoenix : BaseCreature { [Constructible] - public Phoenix() : base(AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Phoenix() : base(AIType.AI_Mage, FightMode.Aggressor) { Body = 5; Hue = 0x674; diff --git a/Projects/UOContent/Mobiles/Animals/Canines/DireWolf.cs b/Projects/UOContent/Mobiles/Animals/Canines/DireWolf.cs index 2a6edf148..310338310 100644 --- a/Projects/UOContent/Mobiles/Animals/Canines/DireWolf.cs +++ b/Projects/UOContent/Mobiles/Animals/Canines/DireWolf.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class DireWolf : BaseCreature { [Constructible] - public DireWolf() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public DireWolf() : base(AIType.AI_Melee) { Body = 23; BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Mobiles/Animals/Canines/GreyWolf.cs b/Projects/UOContent/Mobiles/Animals/Canines/GreyWolf.cs index 195bcfadc..b0851992d 100644 --- a/Projects/UOContent/Mobiles/Animals/Canines/GreyWolf.cs +++ b/Projects/UOContent/Mobiles/Animals/Canines/GreyWolf.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class GreyWolf : BaseCreature { [Constructible] - public GreyWolf() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public GreyWolf() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = Utility.RandomList(25, 27); BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Mobiles/Animals/Canines/TimberWolf.cs b/Projects/UOContent/Mobiles/Animals/Canines/TimberWolf.cs index f808128cf..a33ad2b89 100644 --- a/Projects/UOContent/Mobiles/Animals/Canines/TimberWolf.cs +++ b/Projects/UOContent/Mobiles/Animals/Canines/TimberWolf.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class TimberWolf : BaseCreature { [Constructible] - public TimberWolf() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public TimberWolf() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 225; BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Mobiles/Animals/Canines/WhiteWolf.cs b/Projects/UOContent/Mobiles/Animals/Canines/WhiteWolf.cs index bb57d611d..9a5d08d06 100644 --- a/Projects/UOContent/Mobiles/Animals/Canines/WhiteWolf.cs +++ b/Projects/UOContent/Mobiles/Animals/Canines/WhiteWolf.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class WhiteWolf : BaseCreature { [Constructible] - public WhiteWolf() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public WhiteWolf() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = Utility.RandomList(34, 37); BaseSoundID = 0xE5; diff --git a/Projects/UOContent/Mobiles/Animals/Cows/Bull.cs b/Projects/UOContent/Mobiles/Animals/Cows/Bull.cs index 3a495b6b4..2430532a0 100644 --- a/Projects/UOContent/Mobiles/Animals/Cows/Bull.cs +++ b/Projects/UOContent/Mobiles/Animals/Cows/Bull.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Bull : BaseCreature { [Constructible] - public Bull() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Bull() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = Utility.RandomList(0xE8, 0xE9); BaseSoundID = 0x64; diff --git a/Projects/UOContent/Mobiles/Animals/Cows/Cow.cs b/Projects/UOContent/Mobiles/Animals/Cows/Cow.cs index 043012ed8..ea74e6425 100644 --- a/Projects/UOContent/Mobiles/Animals/Cows/Cow.cs +++ b/Projects/UOContent/Mobiles/Animals/Cows/Cow.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Cow : BaseCreature { [Constructible] - public Cow() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Cow() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = Utility.RandomList(0xD8, 0xE7); BaseSoundID = 0x78; diff --git a/Projects/UOContent/Mobiles/Animals/Felines/Cougar.cs b/Projects/UOContent/Mobiles/Animals/Felines/Cougar.cs index ede2b5ae5..0dc46458d 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/Cougar.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/Cougar.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Cougar : BaseCreature { [Constructible] - public Cougar() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Cougar() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 63; BaseSoundID = 0x73; diff --git a/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs b/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs index b1742f463..346b50747 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/HellCat.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class HellCat : BaseCreature { [Constructible] - public HellCat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public HellCat() : base(AIType.AI_Melee) { Body = 0xC9; Hue = Utility.RandomList(0x647, 0x650, 0x659, 0x662, 0x66B, 0x674); diff --git a/Projects/UOContent/Mobiles/Animals/Felines/Panther.cs b/Projects/UOContent/Mobiles/Animals/Felines/Panther.cs index 966d998a6..ff271a03b 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/Panther.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/Panther.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Panther : BaseCreature { [Constructible] - public Panther() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Panther() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xD6; Hue = 0x901; diff --git a/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs b/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs index 8dcf0286f..612497376 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/PredatorHellCat.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class PredatorHellCat : BaseCreature { [Constructible] - public PredatorHellCat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public PredatorHellCat() : base(AIType.AI_Melee) { Body = 127; BaseSoundID = 0xBA; diff --git a/Projects/UOContent/Mobiles/Animals/Felines/SnowLeopard.cs b/Projects/UOContent/Mobiles/Animals/Felines/SnowLeopard.cs index 28f86bb0e..e0d0366e9 100644 --- a/Projects/UOContent/Mobiles/Animals/Felines/SnowLeopard.cs +++ b/Projects/UOContent/Mobiles/Animals/Felines/SnowLeopard.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class SnowLeopard : BaseCreature { [Constructible] - public SnowLeopard() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public SnowLeopard() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = Utility.RandomList(64, 65); BaseSoundID = 0x73; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Boar.cs b/Projects/UOContent/Mobiles/Animals/Misc/Boar.cs index 57dfbb36c..6763885b3 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Boar.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Boar.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Boar : BaseCreature { [Constructible] - public Boar() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Boar() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0x122; BaseSoundID = 0xC4; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/BullFrog.cs b/Projects/UOContent/Mobiles/Animals/Misc/BullFrog.cs index 7ef3d5fcd..139db24ab 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/BullFrog.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/BullFrog.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class BullFrog : BaseCreature { [Constructible] - public BullFrog() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public BullFrog() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 81; Hue = Utility.RandomList(0x5AC, 0x5A3, 0x59A, 0x591, 0x588, 0x57F); diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Dolphin.cs b/Projects/UOContent/Mobiles/Animals/Misc/Dolphin.cs index 7aae6d76a..2ae29cbbb 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Dolphin.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Dolphin.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles { [Constructible] public Dolphin() - : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0x97; BaseSoundID = 0x8A; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Gaman.cs b/Projects/UOContent/Mobiles/Animals/Misc/Gaman.cs index 380890a2c..e852e697a 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Gaman.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Gaman.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Gaman : BaseCreature { [Constructible] - public Gaman() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Gaman() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 248; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/GiantToad.cs b/Projects/UOContent/Mobiles/Animals/Misc/GiantToad.cs index 6730d6b21..70342218a 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/GiantToad.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/GiantToad.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class GiantToad : BaseCreature { [Constructible] - public GiantToad() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GiantToad() : base(AIType.AI_Melee) { Body = 80; BaseSoundID = 0x26B; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Goat.cs b/Projects/UOContent/Mobiles/Animals/Misc/Goat.cs index 3e45ad600..67240f9fc 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Goat.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Goat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Goat : BaseCreature { [Constructible] - public Goat() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Goat() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xD1; BaseSoundID = 0x99; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Gorilla.cs b/Projects/UOContent/Mobiles/Animals/Misc/Gorilla.cs index 9978d0118..70447f247 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Gorilla.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Gorilla.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Gorilla : BaseCreature { [Constructible] - public Gorilla() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Gorilla() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0x1D; BaseSoundID = 0x9E; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/GreatHart.cs b/Projects/UOContent/Mobiles/Animals/Misc/GreatHart.cs index 470c773b3..3681bfda6 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/GreatHart.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/GreatHart.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class GreatHart : BaseCreature { [Constructible] - public GreatHart() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public GreatHart() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xEA; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Hind.cs b/Projects/UOContent/Mobiles/Animals/Misc/Hind.cs index 96ba6bc12..7aec10de9 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Hind.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Hind.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Hind : BaseCreature { [Constructible] - public Hind() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Hind() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xED; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Llama.cs b/Projects/UOContent/Mobiles/Animals/Misc/Llama.cs index e8f4976d5..b67b023ab 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Llama.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Llama.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Llama : BaseCreature { [Constructible] - public Llama() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Llama() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xDC; BaseSoundID = 0x3F3; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/MountainGoat.cs b/Projects/UOContent/Mobiles/Animals/Misc/MountainGoat.cs index a0d403404..979d09153 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/MountainGoat.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/MountainGoat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class MountainGoat : BaseCreature { [Constructible] - public MountainGoat() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public MountainGoat() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 88; BaseSoundID = 0x99; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs b/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs index dcb6b4f0d..ca5d8324e 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class PackHorse : BaseCreature { [Constructible] - public PackHorse() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public PackHorse() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 291; BaseSoundID = 0xA8; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/PackLlama.cs b/Projects/UOContent/Mobiles/Animals/Misc/PackLlama.cs index d916b6dba..8a523d840 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/PackLlama.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/PackLlama.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class PackLlama : BaseCreature { [Constructible] - public PackLlama() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public PackLlama() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 292; BaseSoundID = 0x3F3; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Pig.cs b/Projects/UOContent/Mobiles/Animals/Misc/Pig.cs index cbc1e4ae8..7c0d3a4d5 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Pig.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Pig.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Pig : BaseCreature { [Constructible] - public Pig() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Pig() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xCB; BaseSoundID = 0xC4; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs index 04e21dd45..61c54ec44 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles private DateTime m_NextWoolTime; [Constructible] - public Sheep() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Sheep() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xCF; BaseSoundID = 0xD6; diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Walrus.cs b/Projects/UOContent/Mobiles/Animals/Misc/Walrus.cs index 480744c5c..7fbd9001f 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Walrus.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Walrus.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Walrus : BaseCreature { [Constructible] - public Walrus() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Walrus() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xDD; BaseSoundID = 0xE0; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs b/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs index 944ef0272..fcffd9646 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs @@ -11,16 +11,9 @@ namespace Server.Mobiles private Mobile m_Rider; 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 - ) + string name, int bodyID, int itemID, AIType aiType, FightMode fightMode = FightMode.Closest, + int rangePerception = 10, int rangeFight = 1 + ) : base(aiType, fightMode, rangePerception, rangeFight) { Name = name; Body = bodyID; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Beetle.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Beetle.cs index 2a0ed620e..053385ab0 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Beetle.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Beetle.cs @@ -7,17 +7,7 @@ namespace Server.Mobiles public class Beetle : BaseMount { [Constructible] - public Beetle(string name = "a giant beetle") : base( - name, - 0x317, - 0x3EBC, - AIType.AI_Melee, - FightMode.Closest, - 10, - 1, - 0.25, - 0.5 - ) + public Beetle(string name = "a giant beetle") : base(name, 0x317, 0x3EBC, AIType.AI_Melee) { SetStr(300); SetDex(100); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/DesertOstard.cs b/Projects/UOContent/Mobiles/Animals/Mounts/DesertOstard.cs index ec67254a9..e2e7516f6 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/DesertOstard.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/DesertOstard.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class DesertOstard : BaseMount { [Constructible] - public DesertOstard(string name = "a desert ostard") : base( - name, - 0xD2, - 0x3EA3, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public DesertOstard(string name = "a desert ostard") : base(name, 0xD2, 0x3EA3, AIType.AI_Animal, FightMode.Aggressor) { BaseSoundID = 0x270; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs index d97216fce..079736046 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs @@ -13,8 +13,7 @@ namespace Server.Mobiles private Mobile m_Rider; [Constructible] - public EtherealMount(int itemID, int mountID) - : base(itemID) + public EtherealMount(int itemID, int mountID) : base(itemID) { m_MountedID = mountID; m_RegularID = itemID; @@ -129,7 +128,7 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public bool IsRewardItem { get; set; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs b/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs index cc5eba47f..5dfdd5e18 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/FireSteed.cs @@ -6,17 +6,7 @@ namespace Server.Mobiles public class FireSteed : BaseMount { [Constructible] - public FireSteed(string name = "a fire steed") : base( - name, - 0xBE, - 0x3E9E, - AIType.AI_Melee, - FightMode.Closest, - 10, - 1, - 0.2, - 0.4 - ) + public FireSteed(string name = "a fire steed") : base(name, 0xBE, 0x3E9E, AIType.AI_Melee) { BaseSoundID = 0xA8; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/ForestOstard.cs b/Projects/UOContent/Mobiles/Animals/Mounts/ForestOstard.cs index c8a7cce51..c7b764afe 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/ForestOstard.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/ForestOstard.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class ForestOstard : BaseMount { [Constructible] - public ForestOstard(string name = "a forest ostard") : base( - name, - 0xDB, - 0x3EA5, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public ForestOstard(string name = "a forest ostard") : base(name, 0xDB, 0x3EA5, AIType.AI_Animal, FightMode.Aggressor) { Hue = Utility.RandomSlimeHue() | 0x8000; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/FrenziedOstard.cs b/Projects/UOContent/Mobiles/Animals/Mounts/FrenziedOstard.cs index 33e7b144b..98425336e 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/FrenziedOstard.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/FrenziedOstard.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class FrenziedOstard : BaseMount { [Constructible] - public FrenziedOstard(string name = "a frenzied ostard") : base( - name, - 0xDA, - 0x3EA4, - AIType.AI_Melee, - FightMode.Closest, - 10, - 1, - 0.2, - 0.4 - ) + public FrenziedOstard(string name = "a frenzied ostard") : base(name, 0xDA, 0x3EA4, AIType.AI_Melee) { Hue = Race.Human.RandomHairHue() | 0x8000; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs b/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs index 1c2479baa..5d00d1ab3 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/HellSteed.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class HellSteed : BaseMount { [Constructible] - public HellSteed(string name = "a hellsteed") : base( - name, - 793, - 0x3EBB, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public HellSteed(string name = "a hellsteed") : base(name, 793, 0x3EBB, AIType.AI_Animal, FightMode.Aggressor) { SetStats(this); } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs index 5aac32a1f..aea42872d 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs @@ -10,8 +10,7 @@ namespace Server.Mobiles private static readonly Dictionary m_Table = new(); [Constructible] - public Hiryu() - : base("a hiryu", 243, 0x3E94, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Hiryu() : base("a hiryu", 243, 0x3E94, AIType.AI_Melee) { Hue = GetHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Horse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Horse.cs index 7a31777a4..297215d03 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Horse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Horse.cs @@ -17,17 +17,7 @@ namespace Server.Mobiles }; [Constructible] - public Horse(string name = "a horse") : base( - name, - 0xE2, - 0x3EA0, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public Horse(string name = "a horse") : base(name, 0xE2, 0x3EA0, AIType.AI_Animal, FightMode.Aggressor) { var random = Utility.Random(4); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Kirin.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Kirin.cs index 58ae0a3fc..842f4233e 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Kirin.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Kirin.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class Kirin : BaseMount { [Constructible] - public Kirin(string name = "a ki-rin") : base(name, 132, 0x3EAD, AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public Kirin(string name = "a ki-rin") : base(name, 132, 0x3EAD, AIType.AI_Mage, FightMode.Evil) { BaseSoundID = 0x3C5; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs b/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs index dafed75de..f6c0167fc 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs @@ -10,8 +10,7 @@ namespace Server.Mobiles private static readonly Dictionary m_Table = new(); [Constructible] - public LesserHiryu() - : base("a lesser hiryu", 243, 0x3E94, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public LesserHiryu() : base("a lesser hiryu", 243, 0x3E94, AIType.AI_Melee) { Hue = GetHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs index 447a0947c..e186bec7c 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Nightmare.cs @@ -9,17 +9,31 @@ namespace Server.Mobiles name, 0x74, 0x3EA7, - AIType.AI_Mage, - FightMode.Closest, - 10, - 1, - 0.2, - 0.4 + AIType.AI_Mage ) { BaseSoundID = Core.AOS ? 0xA8 : 0x16A; - SetStr(496, 525); + // Publish 97 + if (Core.TOL) + { + if (Utility.RandomDouble() < 0.3) + { + SetStr(296, 315); + ControlSlots = 2; + } + else + { + SetStr(496, 525); + ControlSlots = 3; + } + } + else + { + SetStr(496, 525); + ControlSlots = 2; + } + SetDex(86, 105); SetInt(86, 125); @@ -49,7 +63,6 @@ namespace Server.Mobiles VirtualArmor = 60; Tamable = true; - ControlSlots = 2; MinTameSkill = 95.1; switch (Utility.Random(3)) diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/RidableLlama.cs b/Projects/UOContent/Mobiles/Animals/Mounts/RidableLlama.cs index d35d8ef1a..7ba192f45 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/RidableLlama.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/RidableLlama.cs @@ -3,16 +3,7 @@ namespace Server.Mobiles public class RidableLlama : BaseMount { [Constructible] - public RidableLlama(string name = "a ridable llama") : base( - name, - 0xDC, - 0x3EA6, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 + public RidableLlama(string name = "a ridable llama") : base(name, 0xDC, 0x3EA6, AIType.AI_Animal, FightMode.Aggressor ) { BaseSoundID = 0x3F3; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Ridgeback.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Ridgeback.cs index f92bcebf7..93345b7c2 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Ridgeback.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Ridgeback.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class Ridgeback : BaseMount { [Constructible] - public Ridgeback(string name = "a ridgeback") : base( - name, - 187, - 0x3EBA, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public Ridgeback(string name = "a ridgeback") : base(name, 187, 0x3EBA, AIType.AI_Animal, FightMode.Aggressor) { BaseSoundID = 0x3F3; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SavageRidgeback.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SavageRidgeback.cs index ad1292e19..b8bfc6615 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SavageRidgeback.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SavageRidgeback.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class SavageRidgeback : BaseMount { [Constructible] - public SavageRidgeback(string name = "a savage ridgeback") : base( - name, - 188, - 0x3EB8, - AIType.AI_Melee, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public SavageRidgeback(string name = "a savage ridgeback") : base(name, 188, 0x3EB8, AIType.AI_Melee, FightMode.Aggressor) { BaseSoundID = 0x3F3; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/ScaledSwampDragon.cs b/Projects/UOContent/Mobiles/Animals/Mounts/ScaledSwampDragon.cs index 045dd8e25..4705c668a 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/ScaledSwampDragon.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/ScaledSwampDragon.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class ScaledSwampDragon : BaseMount { [Constructible] - public ScaledSwampDragon(string name = "a swamp dragon") : base( - name, - 0x31F, - 0x3EBE, - AIType.AI_Melee, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public ScaledSwampDragon(string name = "a swamp dragon") : base(name, 0x31F, 0x3EBE, AIType.AI_Melee, FightMode.Aggressor) { SetStr(201, 300); SetDex(66, 85); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SeaHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SeaHorse.cs index 0729514cd..4f799c9ad 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SeaHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SeaHorse.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class SeaHorse : BaseMount { [Constructible] - public SeaHorse(string name = "a sea horse") : base( - name, - 0x90, - 0x3EB3, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public SeaHorse(string name = "a sea horse") : base(name, 0x90, 0x3EB3, AIType.AI_Animal, FightMode.Aggressor) { InitStats(Utility.Random(50, 30), Utility.Random(50, 30), 10); Skills.MagicResist.Base = 25.0 + Utility.RandomDouble() * 5.0; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SilverSteed.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SilverSteed.cs index 3ffe7159a..43d32abe8 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SilverSteed.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SilverSteed.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class SilverSteed : BaseMount { [Constructible] - public SilverSteed(string name = "a silver steed") : base( - name, - 0x75, - 0x3EA8, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public SilverSteed(string name = "a silver steed") : base(name, 0x75, 0x3EA8, AIType.AI_Animal, FightMode.Aggressor) { InitStats(Utility.Random(50, 30), Utility.Random(50, 30), 10); Skills.MagicResist.Base = 25.0 + Utility.RandomDouble() * 5.0; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SkeletalMount.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SkeletalMount.cs index eb14bdb2f..2b2f76a3b 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SkeletalMount.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SkeletalMount.cs @@ -3,17 +3,7 @@ namespace Server.Mobiles public class SkeletalMount : BaseMount { [Constructible] - public SkeletalMount(string name = null) : base( - name, - 793, - 0x3EBB, - AIType.AI_Animal, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public SkeletalMount(string name = null) : base(name, 793, 0x3EBB, AIType.AI_Animal, FightMode.Aggressor) { SetStr(91, 100); SetDex(46, 55); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs index 44d6de6fd..984e8bedf 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs @@ -4,24 +4,14 @@ namespace Server.Mobiles { public class SwampDragon : BaseMount { - private Mobile m_BardingCrafter; + private string _bardingCraftedBy; private bool m_BardingExceptional; private int m_BardingHP; private CraftResource m_BardingResource; private bool m_HasBarding; [Constructible] - public SwampDragon(string name = "a swamp dragon") : base( - name, - 0x31A, - 0x3EBD, - AIType.AI_Melee, - FightMode.Aggressor, - 10, - 1, - 0.2, - 0.4 - ) + public SwampDragon(string name = "a swamp dragon") : base(name, 0x31A, 0x3EBD, AIType.AI_Melee, FightMode.Aggressor) { BaseSoundID = 0x16A; @@ -64,12 +54,12 @@ namespace Server.Mobiles public override string CorpseName => "a swamp dragon corpse"; [CommandProperty(AccessLevel.GameMaster)] - public Mobile BardingCrafter + public string BardingCraftedBy { - get => m_BardingCrafter; + get => _bardingCraftedBy; set { - m_BardingCrafter = value; + _bardingCraftedBy = value; InvalidateProperties(); } } @@ -162,13 +152,13 @@ namespace Server.Mobiles public override double GetControlChance(Mobile m, bool useBaseSkill = false) => 1.0; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - if (m_HasBarding && m_BardingExceptional && m_BardingCrafter != null) + if (m_HasBarding && m_BardingExceptional && _bardingCraftedBy != null) { - list.Add(1060853, m_BardingCrafter.Name); // armor exceptionally crafted by ~1_val~ + list.Add(1060853, _bardingCraftedBy); // armor exceptionally crafted by ~1_val~ } } @@ -176,10 +166,10 @@ namespace Server.Mobiles { base.Serialize(writer); - writer.Write(1); // version + writer.Write(2); // version writer.Write(m_BardingExceptional); - writer.Write(m_BardingCrafter); + writer.Write(_bardingCraftedBy); writer.Write(m_HasBarding); writer.Write(m_BardingHP); writer.Write((int)m_BardingResource); @@ -193,10 +183,21 @@ namespace Server.Mobiles switch (version) { + case 2: + { + m_BardingExceptional = reader.ReadBool(); + _bardingCraftedBy = reader.ReadString(); + m_HasBarding = reader.ReadBool(); + m_BardingHP = reader.ReadInt(); + m_BardingResource = (CraftResource)reader.ReadInt(); + break; + } case 1: { m_BardingExceptional = reader.ReadBool(); - m_BardingCrafter = reader.ReadEntity(); + var crafter = reader.ReadEntity(); + // Name might not be set during this deserialization + Timer.StartTimer(() => _bardingCraftedBy = crafter?.RawName); m_HasBarding = reader.ReadBool(); m_BardingHP = reader.ReadInt(); m_BardingResource = (CraftResource)reader.ReadInt(); diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Unicorn.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Unicorn.cs index aa4e46202..23b101675 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Unicorn.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Unicorn.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class Unicorn : BaseMount { [Constructible] - public Unicorn(string name = "a unicorn") : base(name, 0x7A, 0x3EB4, AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public Unicorn(string name = "a unicorn") : base(name, 0x7A, 0x3EB4, AIType.AI_Mage, FightMode.Evil) { BaseSoundID = 0x4BC; @@ -87,8 +87,8 @@ namespace Server.Mobiles if (chanceToCure > Utility.Random(100)) { - if (Rider.CurePoison(this) - ) // TODO: Confirm if mount is the one flagged for curing it or the rider is + // TODO: Confirm if mount is the one flagged for curing it or the rider is + if (Rider.CurePoison(this)) { Rider.LocalOverheadMessage( MessageType.Regular, diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs index a103a344d..26d9d646c 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs @@ -3,8 +3,8 @@ namespace Server.Mobiles public abstract class BaseWarHorse : BaseMount { public BaseWarHorse( - int bodyID, int itemID, AIType aiType, FightMode fightMode, int rangePerception, int rangeFight, - double activeSpeed, double passiveSpeed + int bodyID, int itemID, AIType aiType = AIType.AI_Melee, FightMode fightMode = FightMode.Aggressor, + int rangePerception = 10, int rangeFight = 1 ) : base( "a war horse", bodyID, @@ -12,9 +12,7 @@ namespace Server.Mobiles aiType, fightMode, rangePerception, - rangeFight, - activeSpeed, - passiveSpeed + rangeFight ) { BaseSoundID = 0xA8; diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs index 0b18db03f..c8d6c98ff 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class CoMWarHorse : BaseWarHorse { [Constructible] - public CoMWarHorse() : base(0x77, 0x3EB1, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public CoMWarHorse() : base(0x77, 0x3EB1) { } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs index e116dabdb..f05d4cf06 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class MinaxWarHorse : BaseWarHorse { [Constructible] - public MinaxWarHorse() : base(0x78, 0x3EAF, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public MinaxWarHorse() : base(0x78, 0x3EAF) { } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs index fb2e04c5d..14418a816 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SLWarHorse : BaseWarHorse { [Constructible] - public SLWarHorse() : base(0x79, 0x3EB0, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public SLWarHorse() : base(0x79, 0x3EB0) { } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs index aeced8675..d3fd30dbe 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class TBWarHorse : BaseWarHorse { [Constructible] - public TBWarHorse() : base(0x76, 0x3EB2, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public TBWarHorse() : base(0x76, 0x3EB2) { } diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/Alligator.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/Alligator.cs index 5522cdedc..35ae29e9a 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/Alligator.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/Alligator.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Alligator : BaseCreature { [Constructible] - public Alligator() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Alligator() : base(AIType.AI_Melee) { Body = 0xCA; BaseSoundID = 660; diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/GiantSerpent.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/GiantSerpent.cs index c4258032c..8f23649a2 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/GiantSerpent.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/GiantSerpent.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class GiantSerpent : BaseCreature { [Constructible] - public GiantSerpent() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GiantSerpent() : base(AIType.AI_Melee) { Body = 0x15; Hue = Utility.RandomSnakeHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/IceSerpent.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/IceSerpent.cs index 17183f19e..030656723 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/IceSerpent.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/IceSerpent.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class IceSerpent : BaseCreature { [Constructible] - public IceSerpent() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public IceSerpent() : base(AIType.AI_Melee) { Body = 89; BaseSoundID = 219; diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/IceSnake.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/IceSnake.cs index 751be390b..b4348cdc8 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/IceSnake.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/IceSnake.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class IceSnake : BaseCreature { [Constructible] - public IceSnake() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public IceSnake() : base(AIType.AI_Melee) { Body = 52; Hue = 0x480; diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs index ac0dfb0b0..caff4953f 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaLizard.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class LavaLizard : BaseCreature { [Constructible] - public LavaLizard() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public LavaLizard() : base(AIType.AI_Melee) { Body = 0xCE; Hue = Utility.RandomList(0x647, 0x650, 0x659, 0x662, 0x66B, 0x674); diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs index f97697978..5e963937c 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSerpent.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class LavaSerpent : BaseCreature { [Constructible] - public LavaSerpent() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public LavaSerpent() : base(AIType.AI_Melee) { Body = 90; BaseSoundID = 219; diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs index cd6434ba6..3b7cb2609 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/LavaSnake.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class LavaSnake : BaseCreature { [Constructible] - public LavaSnake() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public LavaSnake() : base(AIType.AI_Melee) { Body = 52; Hue = Utility.RandomList(0x647, 0x650, 0x659, 0x662, 0x66B, 0x674); diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/SilverSerpent.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/SilverSerpent.cs index 9be470ebe..9039c6281 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/SilverSerpent.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/SilverSerpent.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class SilverSerpent : BaseCreature { [Constructible] - public SilverSerpent() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SilverSerpent() : base(AIType.AI_Melee) { Body = 92; BaseSoundID = 219; diff --git a/Projects/UOContent/Mobiles/Animals/Reptiles/Snake.cs b/Projects/UOContent/Mobiles/Animals/Reptiles/Snake.cs index 4e8467de5..f89d0af66 100644 --- a/Projects/UOContent/Mobiles/Animals/Reptiles/Snake.cs +++ b/Projects/UOContent/Mobiles/Animals/Reptiles/Snake.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Snake : BaseCreature { [Constructible] - public Snake() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Snake() : base(AIType.AI_Melee) { Body = 52; Hue = Utility.RandomSnakeHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Rodents/GiantRat.cs b/Projects/UOContent/Mobiles/Animals/Rodents/GiantRat.cs index cda852b99..6704129c6 100644 --- a/Projects/UOContent/Mobiles/Animals/Rodents/GiantRat.cs +++ b/Projects/UOContent/Mobiles/Animals/Rodents/GiantRat.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class GiantRat : BaseCreature { [Constructible] - public GiantRat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GiantRat() : base(AIType.AI_Melee) { Body = 0xD7; BaseSoundID = 0x188; diff --git a/Projects/UOContent/Mobiles/Animals/Rodents/JackRabbit.cs b/Projects/UOContent/Mobiles/Animals/Rodents/JackRabbit.cs index 35504b199..e40cb6d41 100644 --- a/Projects/UOContent/Mobiles/Animals/Rodents/JackRabbit.cs +++ b/Projects/UOContent/Mobiles/Animals/Rodents/JackRabbit.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class JackRabbit : BaseCreature { [Constructible] - public JackRabbit() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public JackRabbit() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xCD; Hue = 0x1BB; diff --git a/Projects/UOContent/Mobiles/Animals/Rodents/Rabbit.cs b/Projects/UOContent/Mobiles/Animals/Rodents/Rabbit.cs index 3592cb5da..173753f13 100644 --- a/Projects/UOContent/Mobiles/Animals/Rodents/Rabbit.cs +++ b/Projects/UOContent/Mobiles/Animals/Rodents/Rabbit.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Rabbit : BaseCreature { [Constructible] - public Rabbit() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Rabbit() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 205; diff --git a/Projects/UOContent/Mobiles/Animals/Rodents/SewerRat.cs b/Projects/UOContent/Mobiles/Animals/Rodents/SewerRat.cs index d2148adb8..6ccf7ed15 100644 --- a/Projects/UOContent/Mobiles/Animals/Rodents/SewerRat.cs +++ b/Projects/UOContent/Mobiles/Animals/Rodents/SewerRat.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class SewerRat : BaseCreature { [Constructible] - public SewerRat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SewerRat() : base(AIType.AI_Melee) { Body = 238; BaseSoundID = 0xCC; diff --git a/Projects/UOContent/Mobiles/Animals/Slimes/Jwilson.cs b/Projects/UOContent/Mobiles/Animals/Slimes/Jwilson.cs index 2b0e90b42..5c6326e1b 100644 --- a/Projects/UOContent/Mobiles/Animals/Slimes/Jwilson.cs +++ b/Projects/UOContent/Mobiles/Animals/Slimes/Jwilson.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Jwilson : BaseCreature { [Constructible] - public Jwilson() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Jwilson() : base(AIType.AI_Melee) { Hue = Utility.RandomList(0x89C, 0x8A2, 0x8A8, 0x8AE); Body = 0x33; diff --git a/Projects/UOContent/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs b/Projects/UOContent/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs index 71cc1be60..c802c255f 100644 --- a/Projects/UOContent/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs +++ b/Projects/UOContent/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Parrot : BaseCreature { [Constructible] - public Parrot() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Parrot() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 831; VirtualArmor = Utility.Random(0, 6); diff --git a/Projects/UOContent/Mobiles/Animals/Town Critters/Bird.cs b/Projects/UOContent/Mobiles/Animals/Town Critters/Bird.cs index 4f99f8350..990c6a6bf 100644 --- a/Projects/UOContent/Mobiles/Animals/Town Critters/Bird.cs +++ b/Projects/UOContent/Mobiles/Animals/Town Critters/Bird.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Bird : BaseCreature { [Constructible] - public Bird() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Bird() : base(AIType.AI_Animal, FightMode.Aggressor) { if (Utility.RandomBool()) { @@ -82,7 +82,7 @@ namespace Server.Mobiles public class TropicalBird : BaseCreature { [Constructible] - public TropicalBird() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public TropicalBird() : base(AIType.AI_Animal, FightMode.Aggressor) { Hue = Utility.RandomBirdHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Town Critters/Cat.cs b/Projects/UOContent/Mobiles/Animals/Town Critters/Cat.cs index e27517958..7d533c429 100644 --- a/Projects/UOContent/Mobiles/Animals/Town Critters/Cat.cs +++ b/Projects/UOContent/Mobiles/Animals/Town Critters/Cat.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class Cat : BaseCreature { [Constructible] - public Cat() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Cat() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xC9; Hue = Utility.RandomAnimalHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Town Critters/Dog.cs b/Projects/UOContent/Mobiles/Animals/Town Critters/Dog.cs index cd2c4ce10..f88e2b00f 100644 --- a/Projects/UOContent/Mobiles/Animals/Town Critters/Dog.cs +++ b/Projects/UOContent/Mobiles/Animals/Town Critters/Dog.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Dog : BaseCreature { [Constructible] - public Dog() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Dog() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0xD9; Hue = Utility.RandomAnimalHue(); diff --git a/Projects/UOContent/Mobiles/Animals/Town Critters/Rat.cs b/Projects/UOContent/Mobiles/Animals/Town Critters/Rat.cs index fad7e803a..c47ab5880 100644 --- a/Projects/UOContent/Mobiles/Animals/Town Critters/Rat.cs +++ b/Projects/UOContent/Mobiles/Animals/Town Critters/Rat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Rat : BaseCreature { [Constructible] - public Rat() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Rat() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 238; BaseSoundID = 0xCC; diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index 1c0b91b4e..25e2a90a1 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Collections; using Server.ContextMenus; using Server.Engines.ConPVP; @@ -21,7 +22,6 @@ using Server.Spells.Necromancy; using Server.Spells.Sixth; using Server.Spells.Spellweaving; using Server.Targeting; -using Server.Utilities; namespace Server.Mobiles { @@ -152,7 +152,7 @@ namespace Server.Mobiles } } - public class BaseCreature : Mobile, IHonorTarget, IQuestGiver + public abstract class BaseCreature : Mobile, IHonorTarget, IQuestGiver { public enum Allegiance { @@ -197,16 +197,6 @@ namespace Server.Mobiles typeof(SkeletalMage), typeof(BoneMagi), typeof(PatchworkSkeleton) }; - private static readonly double[] m_StandardActiveSpeeds = - { - 0.175, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.8 - }; - - private static readonly double[] m_StandardPassiveSpeeds = - { - 0.350, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.6, 2.0 - }; - private static Mobile m_NoDupeGuards; private static readonly bool EnableRummaging = true; @@ -259,9 +249,6 @@ namespace Server.Mobiles typeof(Gold) }; - private readonly List m_SpellAttack; // List of attack spell/power - private readonly List m_SpellDefense; // List of defensive spell/power - private bool _summoned; private bool m_bTamable; @@ -332,11 +319,9 @@ namespace Server.Mobiles public BaseCreature( AIType ai, - FightMode mode, - int iRangePerception, - int iRangeFight, - double dActiveSpeed, - double dPassiveSpeed + FightMode mode = FightMode.Closest, + int iRangePerception = 10, + int iRangeFight = 1 ) { if (iRangePerception == OldRangePerception) @@ -354,19 +339,12 @@ namespace Server.Mobiles FightMode = mode; + ResetSpeeds(); + m_Team = 0; - SpeedInfo.GetSpeeds(this, ref dActiveSpeed, ref dPassiveSpeed); - - ActiveSpeed = dActiveSpeed; - PassiveSpeed = dPassiveSpeed; - m_CurrentSpeed = dPassiveSpeed; - Debug = false; - m_SpellAttack = new List(); - m_SpellDefense = new List(); - m_Controlled = false; m_ControlMaster = null; ControlTarget = null; @@ -394,9 +372,6 @@ namespace Server.Mobiles public BaseCreature(Serial serial) : base(serial) { - m_SpellAttack = new List(); - m_SpellDefense = new List(); - Debug = false; } @@ -690,16 +665,23 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public double PassiveSpeed { get; set; } + [CommandProperty(AccessLevel.GameMaster)] + public double SpeedMod { get; set; } + [CommandProperty(AccessLevel.GameMaster)] public double CurrentSpeed { - get => TargetLocation != null ? 0.3 : m_CurrentSpeed; + get => TargetLocation != null ? 0.3 : SpeedMod <= 0 ? m_CurrentSpeed : SpeedMod; set { if (m_CurrentSpeed != value) { m_CurrentSpeed = value; - AIObject?.OnCurrentSpeedChanged(); + + if (SpeedMod <= 0) + { + AIObject?.OnCurrentSpeedChanged(); + } } } } @@ -893,6 +875,8 @@ namespace Server.Mobiles public virtual bool ReturnsToHome => SeeksHome && Home != Point3D.Zero && !m_ReturnQueued && !Controlled && !Summoned; + public virtual bool ScaleSpeedByDex => NPCSpeeds.ScaleSpeedByDex && !IsMonster; + // used for deleting untamed creatures [in houses] [CommandProperty(AccessLevel.GameMaster)] public bool RemoveIfUntamed { get; set; } @@ -1375,6 +1359,15 @@ namespace Server.Mobiles } } + public override void OnRawDexChange(int oldValue) + { + // This only really happens for pets or when a GM modifies a mob. + if (oldValue != RawDex && ScaleSpeedByDex) + { + ResetSpeeds(); + } + } + public override void OnBeforeSpawn(Point3D location, Map m) { if (Paragon.CheckConvert(this, location, m)) @@ -1633,23 +1626,35 @@ namespace Server.Mobiles switch (sc) { case ScaleType.Red: - corpse.AddCarvedItem(new RedScales(scales), from); - break; + { + corpse.AddCarvedItem(new RedScales(scales), from); + break; + } case ScaleType.Yellow: - corpse.AddCarvedItem(new YellowScales(scales), from); - break; + { + corpse.AddCarvedItem(new YellowScales(scales), from); + break; + } case ScaleType.Black: - corpse.AddCarvedItem(new BlackScales(scales), from); - break; + { + corpse.AddCarvedItem(new BlackScales(scales), from); + break; + } case ScaleType.Green: - corpse.AddCarvedItem(new GreenScales(scales), from); - break; + { + corpse.AddCarvedItem(new GreenScales(scales), from); + break; + } case ScaleType.White: - corpse.AddCarvedItem(new WhiteScales(scales), from); - break; + { + corpse.AddCarvedItem(new WhiteScales(scales), from); + break; + } case ScaleType.Blue: - corpse.AddCarvedItem(new BlueScales(scales), from); - break; + { + corpse.AddCarvedItem(new BlueScales(scales), from); + break; + } case ScaleType.All: { corpse.AddCarvedItem(new RedScales(scales), from); @@ -1678,7 +1683,7 @@ namespace Server.Mobiles { base.Serialize(writer); - writer.Write(19); // version + writer.Write(20); // version writer.Write((int)m_CurrentAI); writer.Write((int)m_DefaultAI); @@ -1699,18 +1704,6 @@ namespace Server.Mobiles // Version 1 writer.Write(RangeHome); - writer.Write(m_SpellAttack.Count); - for (var i = 0; i < m_SpellAttack.Count; i++) - { - writer.Write(m_SpellAttack[i].ToString()); - } - - writer.Write(m_SpellDefense.Count); - for (var i = 0; i < m_SpellDefense.Count; i++) - { - writer.Write(m_SpellDefense[i].ToString()); - } - // Version 2 writer.Write((int)FightMode); @@ -1843,27 +1836,20 @@ namespace Server.Mobiles { RangeHome = reader.ReadInt(); - var iCount = reader.ReadInt(); - for (var i = 0; i < iCount; i++) + if (version < 20) { - var str = reader.ReadString(); - var type = Type.GetType(str); - - if (type != null) + // Spell Attacks + var iCount = reader.ReadInt(); // Count + for (var i = 0; i < iCount; i++) { - m_SpellAttack.Add(type); + reader.ReadString(); // Spell Type } - } - iCount = reader.ReadInt(); - for (var i = 0; i < iCount; i++) - { - var str = reader.ReadString(); - var type = Type.GetType(str); - - if (type != null) + // Spell Defenses + iCount = reader.ReadInt(); // Count + for (var i = 0; i < iCount; i++) { - m_SpellDefense.Add(type); + reader.ReadString(); // Spell Type } } } @@ -2005,42 +1991,6 @@ namespace Server.Mobiles Loyalty *= 10; } - var activeSpeed = ActiveSpeed; - var passiveSpeed = PassiveSpeed; - - SpeedInfo.GetSpeeds(this, ref activeSpeed, ref passiveSpeed); - - var isStandardActive = false; - for (var i = 0; !isStandardActive && i < m_StandardActiveSpeeds.Length; ++i) - { - isStandardActive = ActiveSpeed == m_StandardActiveSpeeds[i]; - } - - var isStandardPassive = false; - for (var i = 0; !isStandardPassive && i < m_StandardPassiveSpeeds.Length; ++i) - { - isStandardPassive = PassiveSpeed == m_StandardPassiveSpeeds[i]; - } - - if (isStandardActive && m_CurrentSpeed == ActiveSpeed) - { - m_CurrentSpeed = activeSpeed; - } - else if (isStandardPassive && m_CurrentSpeed == PassiveSpeed) - { - m_CurrentSpeed = passiveSpeed; - } - - if (isStandardActive && !m_Paragon) - { - ActiveSpeed = activeSpeed; - } - - if (isStandardPassive && !m_Paragon) - { - PassiveSpeed = passiveSpeed; - } - if (version >= 14) { RemoveIfUntamed = reader.ReadBool(); @@ -2322,20 +2272,11 @@ namespace Server.Mobiles base.OnAfterDelete(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void DebugSay(string text) { - if (Debug) - { - PublicOverheadMessage(MessageType.Regular, 41, false, text); - } - } - - public void DebugSay(string format, params object[] args) - { - if (Debug) - { - PublicOverheadMessage(MessageType.Regular, 41, false, string.Format(format, args)); - } + // Moved the debug check to implementation layer so we can avoid string formatting when we do not need it + PublicOverheadMessage(MessageType.Regular, 41, false, text); } /* @@ -2614,7 +2555,6 @@ namespace Server.Mobiles if (m_IdleReleaseTime > DateTime.MinValue) { // idling... - if (Core.Now >= m_IdleReleaseTime) { m_IdleReleaseTime = DateTime.MinValue; @@ -2629,18 +2569,23 @@ namespace Server.Mobiles return false; // not idling, but don't want to enter idle state } - m_IdleReleaseTime = Core.Now + TimeSpan.FromSeconds(Utility.RandomMinMax(15, 25)); + var idleSeconds = Utility.RandomMinMax(NPCSpeeds.MinIdleSeconds, NPCSpeeds.MaxIdleSeconds); + m_IdleReleaseTime = Core.Now + TimeSpan.FromSeconds(idleSeconds); if (Body.IsHuman) { switch (Utility.Random(2)) { case 0: - CheckedAnimate(5, 5, 1, true, true, 1); - break; + { + CheckedAnimate(5, 5, 1, true, true, 1); + break; + } case 1: - CheckedAnimate(6, 5, 1, true, false, 1); - break; + { + CheckedAnimate(6, 5, 1, true, false, 1); + break; + } } } else if (Body.IsAnimal) @@ -2648,14 +2593,20 @@ namespace Server.Mobiles switch (Utility.Random(3)) { case 0: - CheckedAnimate(3, 3, 1, true, false, 1); - break; + { + CheckedAnimate(3, 3, 1, true, false, 1); + break; + } case 1: - CheckedAnimate(9, 5, 1, true, false, 1); - break; + { + CheckedAnimate(9, 5, 1, true, false, 1); + break; + } case 2: - CheckedAnimate(10, 5, 1, true, false, 1); - break; + { + CheckedAnimate(10, 5, 1, true, false, 1); + break; + } } } else if (Body.IsMonster) @@ -2663,11 +2614,15 @@ namespace Server.Mobiles switch (Utility.Random(2)) { case 0: - CheckedAnimate(17, 5, 1, true, false, 1); - break; + { + CheckedAnimate(17, 5, 1, true, false, 1); + break; + } case 1: - CheckedAnimate(18, 5, 1, true, false, 1); - break; + { + CheckedAnimate(18, 5, 1, true, false, 1); + break; + } } } @@ -2795,43 +2750,6 @@ namespace Server.Mobiles } } - public void AddSpellAttack(Type type) - { - m_SpellAttack.Add(type); - } - - public void AddSpellDefense(Type type) - { - m_SpellDefense.Add(type); - } - - public Spell GetAttackSpellRandom() => m_SpellAttack.RandomElement()?.CreateInstance(this, null); - - public Spell GetDefenseSpellRandom() => m_SpellDefense.RandomElement()?.CreateInstance(this, null); - - public Spell GetSpellSpecific(Type type) - { - int i; - - for (i = 0; i < m_SpellAttack.Count; i++) - { - if (m_SpellAttack[i] == type) - { - return type.CreateInstance(this, null); - } - } - - for (i = 0; i < m_SpellDefense.Count; i++) - { - if (m_SpellDefense[i] == type) - { - return type.CreateInstance(this, null); - } - } - - return null; - } - public override void OnDoubleClick(Mobile from) { if (from.AccessLevel >= AccessLevel.GameMaster && !Body.IsHuman) @@ -2858,7 +2776,7 @@ namespace Server.Mobiles base.OnDoubleClick(from); } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); @@ -2871,7 +2789,7 @@ namespace Server.Mobiles { if (DisplayWeight) { - list.Add(TotalWeight == 1 ? 1072788 : 1072789, TotalWeight.ToString()); // Weight: ~1_WEIGHT~ stones + list.Add(TotalWeight == 1 ? 1072788 : 1072789, TotalWeight); // Weight: ~1_WEIGHT~ stones } if (m_ControlOrder == OrderType.Guard) @@ -2961,17 +2879,25 @@ namespace Server.Mobiles switch (Utility.Random(4)) { case 0: - PackItem(new CocoaButter()); - break; + { + PackItem(new CocoaButter()); + break; + } case 1: - PackItem(new CocoaLiquor()); - break; + { + PackItem(new CocoaLiquor()); + break; + } case 2: - PackItem(new SackOfSugar()); - break; + { + PackItem(new SackOfSugar()); + break; + } case 3: - PackItem(new Vanilla()); - break; + { + PackItem(new Vanilla()); + break; + } } } } @@ -3041,6 +2967,13 @@ namespace Server.Mobiles return null; } + public virtual bool IsMonster => !Controlled || (GetMaster() as BaseCreature)?.IsMonster == true; + + public bool InActivePVPCombat() => + ControlOrder != OrderType.Follow && + Combatant is PlayerMobile || + Combatant is BaseCreature { Controlled: true } bc && bc.GetMaster() is PlayerMobile; + public static List GetLootingRights(List damageEntries, int hitsMax) { var rights = new List(); @@ -3415,9 +3348,6 @@ namespace Server.Mobiles Controlled = false; ControlTarget = null; ControlOrder = OrderType.None; - Guild = null; - - Delta(MobileDelta.Noto); } else { @@ -3441,17 +3371,20 @@ namespace Server.Mobiles Controlled = true; ControlTarget = null; ControlOrder = OrderType.Come; - Guild = null; + if (m_DeleteTimer != null) { m_DeleteTimer.Stop(); m_DeleteTimer = null; } - - Delta(MobileDelta.Noto); } + Guild = null; + ResetSpeeds(); + + Delta(MobileDelta.Noto); + InvalidateProperties(); return true; @@ -4041,20 +3974,30 @@ namespace Server.Mobiles switch (Utility.Random(5)) { case 0: - physDamage += BreathChaosDamage; - break; + { + physDamage += BreathChaosDamage; + break; + } case 1: - fireDamage += BreathChaosDamage; - break; + { + fireDamage += BreathChaosDamage; + break; + } case 2: - coldDamage += BreathChaosDamage; - break; + { + coldDamage += BreathChaosDamage; + break; + } case 3: - poisDamage += BreathChaosDamage; - break; + { + poisDamage += BreathChaosDamage; + break; + } case 4: - nrgyDamage += BreathChaosDamage; - break; + { + nrgyDamage += BreathChaosDamage; + break; + } } } @@ -4596,6 +4539,13 @@ namespace Server.Mobiles return false; } + public void SetSpeed(double active, double passive) + { + ActiveSpeed = active; + PassiveSpeed = passive; + CurrentSpeed = PassiveSpeed; + } + public void SetDamage(int val) { m_DamageMin = val; @@ -4701,20 +4651,30 @@ namespace Server.Mobiles switch (type) { case ResistanceType.Physical: - PhysicalDamage = val; - break; + { + PhysicalDamage = val; + break; + } case ResistanceType.Fire: - FireDamage = val; - break; + { + FireDamage = val; + break; + } case ResistanceType.Cold: - ColdDamage = val; - break; + { + ColdDamage = val; + break; + } case ResistanceType.Poison: - PoisonDamage = val; - break; + { + PoisonDamage = val; + break; + } case ResistanceType.Energy: - EnergyDamage = val; - break; + { + EnergyDamage = val; + break; + } } } @@ -4728,20 +4688,30 @@ namespace Server.Mobiles switch (type) { case ResistanceType.Physical: - m_PhysicalResistance = val; - break; + { + m_PhysicalResistance = val; + break; + } case ResistanceType.Fire: - m_FireResistance = val; - break; + { + m_FireResistance = val; + break; + } case ResistanceType.Cold: - m_ColdResistance = val; - break; + { + m_ColdResistance = val; + break; + } case ResistanceType.Poison: - m_PoisonResistance = val; - break; + { + m_PoisonResistance = val; + break; + } case ResistanceType.Energy: - m_EnergyResistance = val; - break; + { + m_EnergyResistance = val; + break; + } } UpdateResistances(); @@ -4875,6 +4845,23 @@ namespace Server.Mobiles } } + // If this needs to be serialized, recommend creating a hash or registry id. Don't serialize strings. + public virtual string SpeedClass => null; + + public virtual void GetSpeeds(out double activeSpeed, out double passiveSpeed) + { + NPCSpeeds.GetSpeeds(this, out activeSpeed, out passiveSpeed); + } + + public void ResetSpeeds(bool currentUseActive = false) + { + GetSpeeds(out var activeSpeed, out var passiveSpeed); + + ActiveSpeed = activeSpeed; + PassiveSpeed = passiveSpeed; + CurrentSpeed = currentUseActive ? activeSpeed : passiveSpeed; + } + public virtual void DropBackpack() { if (Backpack?.Items.Count > 0) diff --git a/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs b/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs index 0f48c3587..8cad6a780 100644 --- a/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs +++ b/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs @@ -8,9 +8,9 @@ namespace Server.Mobiles { private bool m_LastHidden; - public BaseFamiliar() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, .1, .1) + public BaseFamiliar() : base(AIType.AI_Melee) { + SetSpeed(0.1, 0.11); } public BaseFamiliar(Serial serial) : base(serial) diff --git a/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs b/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs index e1be900ac..96bb1a49d 100644 --- a/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs +++ b/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; +using Server.Collections; using Server.ContextMenus; using Server.Gumps; using Server.Items; @@ -76,12 +76,24 @@ namespace Server.Mobiles return; } - var eable = GetItemsInRange(2).Where(item => item.Movable && item.Stackable); - - var pickedUp = 0; - + var eable = GetItemsInRange(2); + using var queue = PooledRefQueue.Create(); foreach (var item in eable) { + if (item.Movable && item.Stackable) + { + queue.Enqueue(item); + } + } + + eable.Free(); + + var pickedUp = 3; + + while (pickedUp > 0 && queue.Count > 0) + { + var item = queue.Dequeue(); + if (!pack.CheckHold(this, item, false, true)) { return; @@ -97,11 +109,7 @@ namespace Server.Mobiles } Drop(this, Point3D.Zero); - - if (++pickedUp == 3) - { - break; - } + pickedUp--; } } diff --git a/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs b/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs index 6ad276cc2..786c2d12b 100644 --- a/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs +++ b/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using Server.Collections; namespace Server.Mobiles { @@ -70,16 +70,20 @@ namespace Server.Mobiles return; } - var list = GetMobilesInRange(5) - .Where( - m => - m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 && m.AccessLevel < AccessLevel.Counselor - ) - .ToList(); - - for (var i = 0; i < list.Count; ++i) + var eable = GetMobilesInRange(5); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) { - var m = list[i]; + if (m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 && m.AccessLevel < AccessLevel.Counselor) + { + queue.Enqueue(m); + } + } + eable.Free(); + + while (queue.Count > 0) + { + var m = queue.Dequeue(); var friendly = true; for (var j = 0; friendly && j < caster.Aggressors.Count; ++j) diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/AbysmalHorror.cs b/Projects/UOContent/Mobiles/Monsters/AOS/AbysmalHorror.cs index 074700bfc..c83a03e5c 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/AbysmalHorror.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/AbysmalHorror.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class AbysmalHorror : BaseCreature { [Constructible] - public AbysmalHorror() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public AbysmalHorror() : base(AIType.AI_Mage) { Body = 312; BaseSoundID = 0x451; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/BoneDemon.cs b/Projects/UOContent/Mobiles/Monsters/AOS/BoneDemon.cs index adbc7f5a1..75c9ef030 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/BoneDemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/BoneDemon.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class BoneDemon : BaseCreature { [Constructible] - public BoneDemon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public BoneDemon() : base(AIType.AI_Mage) { Body = 308; BaseSoundID = 0x48D; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/CrystalElemental.cs b/Projects/UOContent/Mobiles/Monsters/AOS/CrystalElemental.cs index 3d1ba2821..d21249ff1 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/CrystalElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/CrystalElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class CrystalElemental : BaseCreature { [Constructible] - public CrystalElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public CrystalElemental() : base(AIType.AI_Mage) { Body = 300; BaseSoundID = 278; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/DarknightCreeper.cs b/Projects/UOContent/Mobiles/Monsters/AOS/DarknightCreeper.cs index 71c166e82..880b8835e 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/DarknightCreeper.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/DarknightCreeper.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class DarknightCreeper : BaseCreature { [Constructible] - public DarknightCreeper() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public DarknightCreeper() : base(AIType.AI_Mage) { Name = NameList.RandomName("darknight creeper"); Body = 313; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs b/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs index caa5c72e5..ef61460ef 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs @@ -8,7 +8,7 @@ namespace Server.Mobiles private static bool m_InHere; [Constructible] - public DemonKnight() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public DemonKnight() : base(AIType.AI_Mage) { Name = NameList.RandomName("demon knight"); Title = "the Dark Father"; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Devourer.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Devourer.cs index 6e8417836..1055d3198 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Devourer.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Devourer.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Devourer : BaseCreature { [Constructible] - public Devourer() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Devourer() : base(AIType.AI_Mage) { Body = 303; BaseSoundID = 357; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/FleshGolem.cs b/Projects/UOContent/Mobiles/Monsters/AOS/FleshGolem.cs index a4b26857b..57be294ee 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/FleshGolem.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/FleshGolem.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class FleshGolem : BaseCreature { [Constructible] - public FleshGolem() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FleshGolem() : base(AIType.AI_Melee) { Body = 304; BaseSoundID = 684; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/FleshRenderer.cs b/Projects/UOContent/Mobiles/Monsters/AOS/FleshRenderer.cs index e93ebdbed..f80fc7fde 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/FleshRenderer.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/FleshRenderer.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class FleshRenderer : BaseCreature { [Constructible] - public FleshRenderer() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FleshRenderer() : base(AIType.AI_Melee) { Body = 315; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Gibberling.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Gibberling.cs index d870f5f6a..b0eac7a40 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Gibberling.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Gibberling.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Gibberling : BaseCreature { [Constructible] - public Gibberling() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Gibberling() : base(AIType.AI_Melee) { Body = 307; BaseSoundID = 422; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/GoreFiend.cs b/Projects/UOContent/Mobiles/Monsters/AOS/GoreFiend.cs index ed3b5ccad..2eaa2318f 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/GoreFiend.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/GoreFiend.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class GoreFiend : BaseCreature { [Constructible] - public GoreFiend() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GoreFiend() : base(AIType.AI_Melee) { Body = 305; BaseSoundID = 224; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Impaler.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Impaler.cs index 4a80a1324..15272279a 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Impaler.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Impaler.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Impaler : BaseCreature { [Constructible] - public Impaler() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Impaler() : base(AIType.AI_Melee) { Name = NameList.RandomName("impaler"); Body = 306; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/MoundOfMaggots.cs b/Projects/UOContent/Mobiles/Monsters/AOS/MoundOfMaggots.cs index 84176bbd2..1e2a1321b 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/MoundOfMaggots.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/MoundOfMaggots.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class MoundOfMaggots : BaseCreature { [Constructible] - public MoundOfMaggots() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public MoundOfMaggots() : base(AIType.AI_Melee) { Body = 319; BaseSoundID = 898; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/PatchworkSkeleton.cs b/Projects/UOContent/Mobiles/Monsters/AOS/PatchworkSkeleton.cs index 3de36c43b..ea05afe33 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/PatchworkSkeleton.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/PatchworkSkeleton.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class PatchworkSkeleton : BaseCreature { [Constructible] - public PatchworkSkeleton() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public PatchworkSkeleton() : base(AIType.AI_Melee) { Body = 309; BaseSoundID = 0x48D; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Ravager.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Ravager.cs index 6e770b6f6..9c36f22b0 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Ravager.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Ravager.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Ravager : BaseCreature { [Constructible] - public Ravager() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Ravager() : base(AIType.AI_Melee) { Body = 314; BaseSoundID = 357; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs index 5ce83b7e4..057ad6c58 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs @@ -8,14 +8,7 @@ namespace Server.Mobiles private readonly DateTime m_ExpireTime; private readonly Mobile m_Target; - public Revenant(Mobile caster, Mobile target, TimeSpan duration) : base( - AIType.AI_Melee, - FightMode.Closest, - 10, - 1, - 0.18, - 0.36 - ) + public Revenant(Mobile caster, Mobile target, TimeSpan duration) : base(AIType.AI_Melee) { Body = 400; Hue = 1; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs b/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs index 232be7b9f..762b4c371 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private TimerExecutionToken _soundTimerToken; [Constructible] - public ShadowKnight() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public ShadowKnight() : base(AIType.AI_Mage) { Name = NameList.RandomName("shadow knight"); Title = "the Shadow Knight"; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/SkitteringHopper.cs b/Projects/UOContent/Mobiles/Monsters/AOS/SkitteringHopper.cs index c88a3a9f2..a8552f711 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/SkitteringHopper.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/SkitteringHopper.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SkitteringHopper : BaseCreature { [Constructible] - public SkitteringHopper() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public SkitteringHopper() : base(AIType.AI_Melee, FightMode.Aggressor) { Body = 302; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Treefellow.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Treefellow.cs index b2850d857..8c4aa6082 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Treefellow.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Treefellow.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Treefellow : BaseCreature { [Constructible] - public Treefellow() : base(AIType.AI_Melee, FightMode.Evil, 10, 1, 0.2, 0.4) + public Treefellow() : base(AIType.AI_Melee, FightMode.Evil) { Body = 301; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/VampireBat.cs b/Projects/UOContent/Mobiles/Monsters/AOS/VampireBat.cs index 9629f4a7d..d2ea234fb 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/VampireBat.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/VampireBat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class VampireBat : BaseCreature { [Constructible] - public VampireBat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public VampireBat() : base(AIType.AI_Melee) { Body = 317; BaseSoundID = 0x270; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/WailingBanshee.cs b/Projects/UOContent/Mobiles/Monsters/AOS/WailingBanshee.cs index ae86f37f7..0f1a447cd 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/WailingBanshee.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/WailingBanshee.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class WailingBanshee : BaseCreature { [Constructible] - public WailingBanshee() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public WailingBanshee() : base(AIType.AI_Melee) { Body = 310; BaseSoundID = 0x482; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/WandererOfTheVoid.cs b/Projects/UOContent/Mobiles/Monsters/AOS/WandererOfTheVoid.cs index 13051a7f8..8e3082de1 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/WandererOfTheVoid.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/WandererOfTheVoid.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class WandererOfTheVoid : BaseCreature { [Constructible] - public WandererOfTheVoid() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public WandererOfTheVoid() : base(AIType.AI_Mage) { Body = 316; BaseSoundID = 377; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/AntLion.cs b/Projects/UOContent/Mobiles/Monsters/Ants/AntLion.cs index 168d04296..47c4882a6 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/AntLion.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/AntLion.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class AntLion : BaseCreature { [Constructible] - public AntLion() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public AntLion() : base(AIType.AI_Melee) { Body = 787; BaseSoundID = 1006; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs index b3a787080..7d2bd5d5e 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class BlackSolenInfiltratorQueen : BaseCreature { [Constructible] - public BlackSolenInfiltratorQueen() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BlackSolenInfiltratorQueen() : base(AIType.AI_Melee) { Body = 807; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs index 6a89e2c20..9e2c8ed18 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class BlackSolenInfiltratorWarrior : BaseCreature { [Constructible] - public BlackSolenInfiltratorWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BlackSolenInfiltratorWarrior() : base(AIType.AI_Melee) { Body = 806; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenQueen.cs b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenQueen.cs index c6168f6dc..527b4b4ca 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenQueen.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenQueen.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class BlackSolenQueen : BaseCreature { [Constructible] - public BlackSolenQueen() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BlackSolenQueen() : base(AIType.AI_Melee) { Body = 807; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWarrior.cs index 4c493f49b..341c9fc51 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWarrior.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class BlackSolenWarrior : BaseCreature { [Constructible] - public BlackSolenWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BlackSolenWarrior() : base(AIType.AI_Melee) { Body = 806; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWorker.cs b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWorker.cs index 383784b1b..3ab873c82 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWorker.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/BlackSolenWorker.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class BlackSolenWorker : BaseCreature { [Constructible] - public BlackSolenWorker() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BlackSolenWorker() : base(AIType.AI_Melee) { Body = 805; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs index eaaf4105f..997f65955 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class RedSolenInfiltratorQueen : BaseCreature { [Constructible] - public RedSolenInfiltratorQueen() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RedSolenInfiltratorQueen() : base(AIType.AI_Melee) { Body = 783; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs index 71584f6de..afd41fd2a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class RedSolenInfiltratorWarrior : BaseCreature { [Constructible] - public RedSolenInfiltratorWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RedSolenInfiltratorWarrior() : base(AIType.AI_Melee) { Body = 782; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenQueen.cs b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenQueen.cs index fbdf85c47..4e491e070 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenQueen.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenQueen.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class RedSolenQueen : BaseCreature { [Constructible] - public RedSolenQueen() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RedSolenQueen() : base(AIType.AI_Melee) { Body = 783; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWarrior.cs index 4a0ba00d0..0f3ce8f46 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWarrior.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class RedSolenWarrior : BaseCreature { [Constructible] - public RedSolenWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RedSolenWarrior() : base(AIType.AI_Melee) { Body = 782; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWorker.cs b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWorker.cs index 896920266..6443c286b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWorker.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ants/RedSolenWorker.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class RedSolenWorker : BaseCreature { [Constructible] - public RedSolenWorker() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RedSolenWorker() : base(AIType.AI_Melee) { Body = 781; BaseSoundID = 959; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs index 8fe4d981d..794b52aab 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class DreadSpider : BaseCreature { [Constructible] - public DreadSpider() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public DreadSpider() : base(AIType.AI_Mage) { Body = 11; BaseSoundID = 1170; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs index 1dca6c6c4..7bac271bc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class TerathanAvenger : BaseCreature { [Constructible] - public TerathanAvenger() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public TerathanAvenger() : base(AIType.AI_Mage) { Body = 152; BaseSoundID = 0x24D; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs index fbec2457a..29a77e6fa 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class TerathanMatriarch : BaseCreature { [Constructible] - public TerathanMatriarch() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public TerathanMatriarch() : base(AIType.AI_Mage) { Body = 72; BaseSoundID = 599; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs index 09021bbba..2c551f41c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class FrostSpider : BaseCreature { [Constructible] - public FrostSpider() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FrostSpider() : base(AIType.AI_Melee) { Body = 20; BaseSoundID = 0x388; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs index 9649635ae..26d69c88a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GiantBlackWidow : BaseCreature { [Constructible] - public GiantBlackWidow() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GiantBlackWidow() : base(AIType.AI_Melee) { Body = 0x9D; BaseSoundID = 0x388; // TODO: validate diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs index dbe2c6852..1b967de50 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GiantSpider : BaseCreature { [Constructible] - public GiantSpider() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GiantSpider() : base(AIType.AI_Melee) { Body = 28; BaseSoundID = 0x388; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs index ddc2fe0a9..6d6b107b0 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class TerathanDrone : BaseCreature { [Constructible] - public TerathanDrone() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public TerathanDrone() : base(AIType.AI_Melee) { Body = 71; BaseSoundID = 594; diff --git a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs index aa48250f3..b26d22a95 100644 --- a/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class TerathanWarrior : BaseCreature { [Constructible] - public TerathanWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public TerathanWarrior() : base(AIType.AI_Melee) { Body = 70; BaseSoundID = 589; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AcidElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AcidElemental.cs index b0fb9541a..32377ffd4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AcidElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AcidElemental.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles public class AcidElemental : BaseCreature { [Constructible] - public AcidElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public AcidElemental() : base(AIType.AI_Mage) { Body = 0x9E; BaseSoundID = 278; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs index 2e292c837..fc9a25a53 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class AirElemental : BaseCreature { [Constructible] - public AirElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public AirElemental() : base(AIType.AI_Mage) { Body = 13; Hue = 0x4001; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs index 81898c98a..d7d9ab5bc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class BloodElemental : BaseCreature { [Constructible] - public BloodElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public BloodElemental() : base(AIType.AI_Mage) { Body = 159; BaseSoundID = 278; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/Efreet.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/Efreet.cs index 469f59827..85a47aa8c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/Efreet.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/Efreet.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Efreet : BaseCreature { [Constructible] - public Efreet() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Efreet() : base(AIType.AI_Mage) { Body = 131; BaseSoundID = 768; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs index e8ad5fb70..9549c1fe0 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class FireElemental : BaseCreature { [Constructible] - public FireElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public FireElemental() : base(AIType.AI_Mage) { Body = 15; BaseSoundID = 838; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/IceElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/IceElemental.cs index b55d93c93..f64ff2a4d 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/IceElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/IceElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class IceElemental : BaseCreature { [Constructible] - public IceElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public IceElemental() : base(AIType.AI_Mage) { Body = 161; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs index d0061309e..4aca1fb01 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class PoisonElemental : BaseCreature { [Constructible] - public PoisonElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public PoisonElemental() : base(AIType.AI_Mage) { Body = 162; BaseSoundID = 263; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs index f9ccba620..e3df50c68 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class WaterElemental : BaseCreature { [Constructible] - public WaterElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public WaterElemental() : base(AIType.AI_Mage) { Body = 16; BaseSoundID = 278; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs index 7af2c20c0..2ac62b633 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class EarthElemental : BaseCreature { [Constructible] - public EarthElemental() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public EarthElemental() : base(AIType.AI_Melee) { Body = 14; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs index 0b4ef83c4..123cac148 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class SnowElemental : BaseCreature { [Constructible] - public SnowElemental() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SnowElemental() : base(AIType.AI_Melee) { Body = 163; BaseSoundID = 263; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs index 9018c87e6..be0856d36 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class AncientLich : BaseCreature { [Constructible] - public AncientLich() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public AncientLich() : base(AIType.AI_Mage) { Name = NameList.RandomName("ancient lich"); Body = 78; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs index d283f8996..3666b3872 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ArcaneDaemon : BaseCreature { [Constructible] - public ArcaneDaemon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public ArcaneDaemon() : base(AIType.AI_Mage) { Body = 0x310; BaseSoundID = 0x47D; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Balron.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Balron.cs index c0132e5cb..1948e1ce1 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Balron.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Balron.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Balron : BaseCreature { [Constructible] - public Balron() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Balron() : base(AIType.AI_Mage) { Name = NameList.RandomName("balron"); Body = 40; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs index 79a0a4b31..d65b6bb5f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private bool m_Stunning; [Constructible] - public Betrayer() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Betrayer() : base(AIType.AI_Mage) { Body = 767; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Bogle.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Bogle.cs index 07831fb92..b90623cda 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Bogle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Bogle.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Bogle : BaseCreature { [Constructible] - public Bogle() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Bogle() : base(AIType.AI_Mage) { Body = 153; BaseSoundID = 0x482; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs index 2eabcc511..dcc897d7e 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class BoneMagi : BaseCreature { [Constructible] - public BoneMagi() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public BoneMagi() : base(AIType.AI_Mage) { Body = 148; BaseSoundID = 451; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs index 18de26c29..5d582c802 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class Daemon : BaseCreature { [Constructible] - public Daemon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Daemon() : base(AIType.AI_Mage) { Name = NameList.RandomName("daemon"); Body = 9; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs index 827953361..376ad67e2 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class ElderGazer : BaseCreature { [Constructible] - public ElderGazer() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public ElderGazer() : base(AIType.AI_Mage) { Body = 22; BaseSoundID = 377; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs index 508ee434b..110365c12 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs @@ -1,12 +1,13 @@ +using ModernUO.Serialization; using Server.Items; namespace Server.Mobiles { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class EvilMage : BaseCreature { [Constructible] - public EvilMage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public EvilMage() : base(AIType.AI_Mage) { Name = NameList.RandomName("evil mage"); Title = "the evil mage"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs index 897983ebe..14169921b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs @@ -1,12 +1,13 @@ +using ModernUO.Serialization; using Server.Items; namespace Server.Mobiles { - [Serializable(0, false)] + [SerializationGenerator(0, false)] public partial class EvilMageLord : BaseCreature { [Constructible] - public EvilMageLord() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public EvilMageLord() : base(AIType.AI_Mage) { Name = NameList.RandomName("evil mage lord"); Body = Core.UOR ? Utility.Random(125, 2) : 0x190; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs index b86c9eca3..8595d6001 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class FireGargoyle : BaseCreature { [Constructible] - public FireGargoyle() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public FireGargoyle() : base(AIType.AI_Mage) { Name = NameList.RandomName("fire gargoyle"); Body = 130; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs index 599390b49..b09605b71 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Gargoyle : BaseCreature { [Constructible] - public Gargoyle() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Gargoyle() : base(AIType.AI_Mage) { Body = 4; BaseSoundID = 372; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs index 26fa9b698..24b4bd053 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GargoyleDestroyer : BaseCreature { [Constructible] - public GargoyleDestroyer() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public GargoyleDestroyer() : base(AIType.AI_Mage) { Body = 0x2F3; BaseSoundID = 0x174; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs index 242c8dffc..9a0c01753 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GargoyleEnforcer : BaseCreature { [Constructible] - public GargoyleEnforcer() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public GargoyleEnforcer() : base(AIType.AI_Mage) { Body = 0x2F2; BaseSoundID = 0x174; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gazer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gazer.cs index 47aa1d08a..ed9e35dcb 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gazer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Gazer.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Gazer : BaseCreature { [Constructible] - public Gazer() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Gazer() : base(AIType.AI_Mage) { Body = 22; BaseSoundID = 377; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GolemController.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GolemController.cs index b76607323..87bbf8c41 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GolemController.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/GolemController.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GolemController : BaseCreature { [Constructible] - public GolemController() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public GolemController() : base(AIType.AI_Mage) { Name = NameList.RandomName("golem controller"); Title = "the controller"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs index 8fa4cf347..513386a59 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class IceFiend : BaseCreature { [Constructible] - public IceFiend() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public IceFiend() : base(AIType.AI_Mage) { Body = 43; BaseSoundID = 357; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Imp.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Imp.cs index 43747d769..6bc8a560b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Imp.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Imp.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Imp : BaseCreature { [Constructible] - public Imp() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Imp() : base(AIType.AI_Mage) { Body = 74; BaseSoundID = 422; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Lich.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Lich.cs index 75926f12b..96b93a502 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Lich.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Lich.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Lich : BaseCreature { [Constructible] - public Lich() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Lich() : base(AIType.AI_Mage) { Body = 24; BaseSoundID = 0x3E9; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs index 1717af145..3d29c14f9 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/LichLord.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class LichLord : BaseCreature { [Constructible] - public LichLord() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public LichLord() : base(AIType.AI_Mage) { Body = 79; BaseSoundID = 412; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs index 0eb72f21c..b45d3f8db 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class OrcishMage : BaseCreature { [Constructible] - public OrcishMage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public OrcishMage() : base(AIType.AI_Mage) { Body = 140; BaseSoundID = 0x45A; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs index c9c3944f3..64ed0626e 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class RatmanMage : BaseCreature { [Constructible] - public RatmanMage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public RatmanMage() : base(AIType.AI_Mage) { Name = NameList.RandomName("ratman"); Body = 0x8F; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs index 29904bf35..9952033a3 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs @@ -8,7 +8,7 @@ namespace Server.Mobiles public class SavageShaman : BaseCreature { [Constructible] - public SavageShaman() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SavageShaman() : base(AIType.AI_Mage) { Name = NameList.RandomName("savage shaman"); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Shade.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Shade.cs index 06fc00dd9..6a9a04e17 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Shade.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Shade.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Shade : BaseCreature { [Constructible] - public Shade() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Shade() : base(AIType.AI_Mage) { Body = 26; Hue = 0x4001; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs index ad767f10d..c40390a17 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class SkeletalMage : BaseCreature { [Constructible] - public SkeletalMage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SkeletalMage() : base(AIType.AI_Mage) { Body = 148; BaseSoundID = 451; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spectre.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spectre.cs index fdff55b9b..c1e311333 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spectre.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spectre.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Spectre : BaseCreature { [Constructible] - public Spectre() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Spectre() : base(AIType.AI_Mage) { Body = 26; Hue = 0x4001; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs index 2af283bf4..0b43262fe 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Succubus.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Succubus : BaseCreature { [Constructible] - public Succubus() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Succubus() : base(AIType.AI_Mage) { Body = 149; BaseSoundID = 0x4B0; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Titan.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Titan.cs index 3dba46e41..0cbf87790 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Titan.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Titan.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Titan : BaseCreature { [Constructible] - public Titan() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Titan() : base(AIType.AI_Mage) { Body = 76; BaseSoundID = 609; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Wraith.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Wraith.cs index 51d506ef4..0806091db 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Wraith.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Wraith.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Wraith : BaseCreature { [Constructible] - public Wraith() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Wraith() : base(AIType.AI_Mage) { Body = 26; Hue = 0x4001; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs index 0c516ccb8..3f6f75985 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class ArcticOgreLord : BaseCreature { [Constructible] - public ArcticOgreLord() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ArcticOgreLord() : base(AIType.AI_Melee) { Body = 135; BaseSoundID = 427; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs index e11ac3186..c64589ffc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class BoneKnight : BaseCreature { [Constructible] - public BoneKnight() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BoneKnight() : base(AIType.AI_Melee) { Body = 57; BaseSoundID = 451; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Brigand.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Brigand.cs index 936c0397a..8d687f6b6 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Brigand.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Brigand.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Brigand : BaseCreature { [Constructible] - public Brigand() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Brigand() : base(AIType.AI_Melee) { SpeechHue = Utility.RandomDyedHue(); Title = "the brigand"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs index 643516358..e2279f04b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ChaosDaemon : BaseCreature { [Constructible] - public ChaosDaemon() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ChaosDaemon() : base(AIType.AI_Melee) { Body = 792; BaseSoundID = 0x3E9; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cursed.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cursed.cs index 352cd3ae2..8bc329b6c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cursed.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cursed.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Cursed : BaseCreature { [Constructible] - public Cursed() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Cursed() : base(AIType.AI_Melee) { Title = "the Cursed"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs index edc0183ad..1317d9a9a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Cyclops : BaseCreature { [Constructible] - public Cyclops() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Cyclops() : base(AIType.AI_Melee) { Body = 75; BaseSoundID = 604; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs index 255977082..6f85f2edc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Doppleganger : BaseCreature { [Constructible] - public Doppleganger() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Doppleganger() : base(AIType.AI_Melee) { Body = 0x309; BaseSoundID = 0x451; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ElfBrigand.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ElfBrigand.cs index 6e12d56d4..9dee70647 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ElfBrigand.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ElfBrigand.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class ElfBrigand : BaseCreature { [Constructible] - public ElfBrigand() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ElfBrigand() : base(AIType.AI_Melee) { SpeechHue = Utility.RandomDyedHue(); Title = "the brigand"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs index 22111a70d..ec29a7df1 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class EnslavedGargoyle : BaseCreature { [Constructible] - public EnslavedGargoyle() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public EnslavedGargoyle() : base(AIType.AI_Melee) { Body = 0x2F1; BaseSoundID = 0x174; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ettin.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ettin.cs index e41162e7b..23641dd69 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ettin.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ettin.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Ettin : BaseCreature { [Constructible] - public Ettin() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Ettin() : base(AIType.AI_Melee) { Body = 18; BaseSoundID = 367; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Executioner.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Executioner.cs index e241fb044..e5b2a9ea8 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Executioner.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Executioner.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Executioner : BaseCreature { [Constructible] - public Executioner() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Executioner() : base(AIType.AI_Melee) { SpeechHue = Utility.RandomDyedHue(); Title = "the executioner"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs index e96b21cdf..da5904924 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class FrostTroll : BaseCreature { [Constructible] - public FrostTroll() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FrostTroll() : base(AIType.AI_Melee) { Body = 55; BaseSoundID = 461; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs index 7bbac091b..8a1209573 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GazerLarva : BaseCreature { [Constructible] - public GazerLarva() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GazerLarva() : base(AIType.AI_Melee) { Body = 778; BaseSoundID = 377; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs index f3130d9b7..2fb04ef1a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Ghoul : BaseCreature { [Constructible] - public Ghoul() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Ghoul() : base(AIType.AI_Melee) { Body = 153; BaseSoundID = 0x482; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs index 4fcf786af..fff738990 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/GreaterMongbat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class GreaterMongbat : BaseCreature { [Constructible] - public GreaterMongbat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GreaterMongbat() : base(AIType.AI_Melee) { Body = 39; BaseSoundID = 422; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Guardian.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Guardian.cs index cb868762e..d6cec9f56 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Guardian.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Guardian.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Guardian : BaseCreature { [Constructible] - public Guardian() : base(AIType.AI_Archer, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Guardian() : base(AIType.AI_Archer, FightMode.Aggressor) { InitStats(100, 125, 25); Title = "the guardian"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs index 0934f9f8c..5abf05236 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class HeadlessOne : BaseCreature { [Constructible] - public HeadlessOne() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public HeadlessOne() : base(AIType.AI_Melee) { Body = 31; Hue = Race.Human.RandomSkinHue() & 0x7FFF; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs index b1fc8822f..416792596 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class HordeMinion : BaseCreature { [Constructible] - public HordeMinion() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public HordeMinion() : base(AIType.AI_Melee) { Body = 776; BaseSoundID = 357; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs index 483d3a274..f241746f5 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles private bool m_Stunning; [Constructible] - public Juggernaut() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.3, 0.6) + public Juggernaut() : base(AIType.AI_Melee) { Body = 768; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs index 501300865..f3298de78 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs @@ -11,7 +11,7 @@ namespace Server.Mobiles private readonly Mobile m_Target; - public KhaldunRevenant(Mobile target) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.18, 0.36) + public KhaldunRevenant(Mobile target) : base(AIType.AI_Melee) { Body = 0x3CA; Hue = 0x41CE; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs index 1b35f0f50..b69b0b3ac 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class KhaldunSummoner : BaseCreature { [Constructible] - public KhaldunSummoner() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public KhaldunSummoner() : base(AIType.AI_Mage) { Body = 0x190; Title = "the Summoner"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs index da25b342c..45c5d3b62 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class KhaldunZealot : BaseCreature { [Constructible] - public KhaldunZealot() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public KhaldunZealot() : base(AIType.AI_Melee) { Body = 0x190; Title = "the Knight"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Moloch.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Moloch.cs index 75d8d80ba..b54a19af3 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Moloch.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Moloch.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Moloch : BaseCreature { [Constructible] - public Moloch() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Moloch() : base(AIType.AI_Melee) { Body = 0x311; BaseSoundID = 0x300; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs index b4628c2cc..fe691470f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Mongbat : BaseCreature { [Constructible] - public Mongbat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Mongbat() : base(AIType.AI_Melee) { Body = 39; BaseSoundID = 422; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mummy.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mummy.cs index d345cf974..ebef3ae20 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mummy.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Mummy.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class Mummy : BaseCreature { [Constructible] - public Mummy() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8) + public Mummy() : base(AIType.AI_Melee) { Body = 154; BaseSoundID = 471; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ogre.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ogre.cs index cb68c9464..06eaf0413 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ogre.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ogre.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Ogre : BaseCreature { [Constructible] - public Ogre() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Ogre() : base(AIType.AI_Melee) { Body = 1; BaseSoundID = 427; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs index a3305cd0c..e147daf55 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class OgreLord : BaseCreature { [Constructible] - public OgreLord() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OgreLord() : base(AIType.AI_Melee) { Body = 83; BaseSoundID = 427; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Orc.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Orc.cs index acb5c1e3b..1c9556b47 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Orc.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Orc.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class Orc : BaseCreature { [Constructible] - public Orc() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Orc() : base(AIType.AI_Melee) { Name = NameList.RandomName("orc"); Body = 17; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs index c55426b2f..1b2ff2afb 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private int m_Thrown; [Constructible] - public OrcBomber() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OrcBomber() : base(AIType.AI_Melee) { Body = 182; BaseSoundID = 0x45A; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs index d07b31a81..69deb37ae 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs @@ -1,4 +1,3 @@ -using System.Linq; using Server.Items; namespace Server.Mobiles @@ -6,7 +5,7 @@ namespace Server.Mobiles public class OrcBrute : BaseCreature { [Constructible] - public OrcBrute() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OrcBrute() : base(AIType.AI_Melee) { Body = 189; BaseSoundID = 0x45A; @@ -120,8 +119,16 @@ namespace Server.Mobiles } var eable = GetMobilesInRange(10); + var count = 0; + foreach (var m in eable) + { + if (++count == 10) + { + break; + } + } - if (eable.Count() < 10) + if (count < 10) { BaseCreature orc = new SpawnedOrcishLord { Team = Team }; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs index 417de8bca..2130ec3a6 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class OrcCaptain : BaseCreature { [Constructible] - public OrcCaptain() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OrcCaptain() : base(AIType.AI_Melee) { Name = NameList.RandomName("orc"); Body = 7; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs index e43631519..c6e6a0a62 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class OrcishLord : BaseCreature { [Constructible] - public OrcishLord() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OrcishLord() : base(AIType.AI_Melee) { Body = 138; BaseSoundID = 0x45A; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ratman.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ratman.cs index 9bac39e79..6eea3204a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ratman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Ratman.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Ratman : BaseCreature { [Constructible] - public Ratman() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Ratman() : base(AIType.AI_Melee) { Name = NameList.RandomName("ratman"); Body = 42; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs index b8ac7f09c..579932c2f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class RatmanArcher : BaseCreature { [Constructible] - public RatmanArcher() : base(AIType.AI_Archer, FightMode.Closest, 10, 1, 0.2, 0.4) + public RatmanArcher() : base(AIType.AI_Archer) { Name = NameList.RandomName("ratman"); Body = 0x8E; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs index dfcae9946..76b8cf239 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class RestlessSoul : BaseCreature { [Constructible] - public RestlessSoul() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8) + public RestlessSoul() : base(AIType.AI_Melee) { Body = 0x3CA; Hue = 0x453; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs index 48dc7bfe4..284dd8fba 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class RottingCorpse : BaseCreature { [Constructible] - public RottingCorpse() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RottingCorpse() : base(AIType.AI_Melee) { Body = 155; BaseSoundID = 471; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Savage.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Savage.cs index 54e899533..2b8a53ebb 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Savage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Savage.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class Savage : BaseCreature { [Constructible] - public Savage() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Savage() : base(AIType.AI_Melee) { Name = NameList.RandomName("savage"); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs index ad74f08ae..193e709fc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class SavageRider : BaseCreature { [Constructible] - public SavageRider() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.15, 0.4) + public SavageRider() : base(AIType.AI_Melee) { Name = NameList.RandomName("savage rider"); diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs index 1003a2724..064e2221c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs @@ -8,7 +8,7 @@ namespace Server.Mobiles private UnhideTimer m_Timer; [Constructible] - public ShadowFiend() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ShadowFiend() : base(AIType.AI_Melee) { Body = 0xA8; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs index 0d3fe161e..0f1bf65f5 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class SkeletalKnight : BaseCreature { [Constructible] - public SkeletalKnight() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SkeletalKnight() : base(AIType.AI_Melee) { Body = 147; BaseSoundID = 451; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs index b1df50d4c..9d8ff90ef 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Skeleton : BaseCreature { [Constructible] - public Skeleton() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Skeleton() : base(AIType.AI_Melee) { Body = Utility.RandomList(50, 56); BaseSoundID = 0x48D; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs index 7c46d85f5..8c4269d96 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class SpectralArmour : BaseCreature { [Constructible] - public SpectralArmour() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SpectralArmour() : base(AIType.AI_Melee) { Body = 637; Hue = 0x8026; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs index fe65c0fb5..e66d5796a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class StoneGargoyle : BaseCreature { [Constructible] - public StoneGargoyle() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public StoneGargoyle() : base(AIType.AI_Melee) { Body = 67; BaseSoundID = 0x174; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs index 1d942c9da..49cc7ae65 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class StrongMongbat : BaseCreature { [Constructible] - public StrongMongbat() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public StrongMongbat() : base(AIType.AI_Melee) { Body = 39; BaseSoundID = 422; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Troll.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Troll.cs index 5b47fd587..ff0bc02d4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Troll.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Troll.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Troll : BaseCreature { [Constructible] - public Troll() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Troll() : base(AIType.AI_Melee) { Body = Utility.RandomList(53, 54); BaseSoundID = 461; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Zombie.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Zombie.cs index dc886d409..bbb2f36b8 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Zombie.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Zombie.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Zombie : BaseCreature { [Constructible] - public Zombie() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Zombie() : base(AIType.AI_Melee) { Body = 3; BaseSoundID = 471; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs index 9d2cc5581..f70c42bb2 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ExodusMinion : BaseCreature { [Constructible] - public ExodusMinion() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ExodusMinion() : base(AIType.AI_Melee) { Body = 0x2F5; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs index 09aa5a9d7..18aeed356 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ExodusOverseer : BaseCreature { [Constructible] - public ExodusOverseer() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ExodusOverseer() : base(AIType.AI_Melee) { Body = 0x2F4; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs index c139720cb..fb3b38a7c 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoon.cs @@ -5,11 +5,13 @@ namespace Server.Mobiles public class ChaosDragoon : BaseCreature { [Constructible] - public ChaosDragoon() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.15, 0.4) + public ChaosDragoon() : base(AIType.AI_Melee) { Body = 0x190; Hue = Race.Human.RandomSkinHue(); + SetSpeed(0.15, 0.4); + SetStr(176, 225); SetDex(81, 95); SetInt(61, 85); diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs index 839f9b117..5e6bf3b5e 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/ChaosDragoonElite.cs @@ -5,12 +5,13 @@ namespace Server.Mobiles public class ChaosDragoonElite : BaseCreature { [Constructible] - public ChaosDragoonElite() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.15, 0.4) + public ChaosDragoonElite() : base(AIType.AI_Mage) { Body = 0x190; Hue = Race.Human.RandomSkinHue(); + SetSpeed(0.15, 0.4); + SetStr(276, 350); SetDex(66, 90); SetInt(126, 150); diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs index 55fa320db..aac42f25e 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class JukaLord : BaseCreature { [Constructible] - public JukaLord() : base(AIType.AI_Archer, FightMode.Closest, 10, 3, 0.2, 0.4) + public JukaLord() : base(AIType.AI_Archer, FightMode.Closest, 10, 3) { Body = 766; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs index eff006e42..cbbad4177 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private DateTime m_NextAbilityTime; [Constructible] - public JukaMage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public JukaMage() : base(AIType.AI_Mage) { Body = 765; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs index 57d256753..22e9160b1 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class JukaWarrior : BaseCreature { [Constructible] - public JukaWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public JukaWarrior() : base(AIType.AI_Melee) { Body = 764; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/EnragedCreatures.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/EnragedCreatures.cs index d6b4fbfb6..3bf779bc5 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/EnragedCreatures.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/EnragedCreatures.cs @@ -150,29 +150,18 @@ namespace Server.Mobiles public class BaseEnraged : BaseCreature { - public BaseEnraged(Mobile summoner) - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BaseEnraged(Mobile summoner) : base(AIType.AI_Melee) { SetStr(50, 200); SetDex(50, 200); - SetHits(50, 200); - SetStam(50, 200); /* - On OSI, all stats are random 50-200, but - str is never less than hits, and dex is never - less than stam. + * On OSI, all stats are random 50-200, but + * str is never less than hits, and dex is never + * less than stam. */ - - if (Str < Hits) - { - Str = Hits; - } - - if (Dex < Stam) - { - Dex = Stam; - } + SetHits(50, Str); + SetStam(50, Dex); Karma = -1000; Tamable = false; @@ -230,7 +219,7 @@ namespace Server.Mobiles PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1060768, from.NetState); // enraged } - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); list.Add(1060768); // enraged diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs index 0cbe3214b..6be4fcdc7 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles private DateTime m_NextAbilityTime; [Constructible] - public MeerCaptain() : base(AIType.AI_Archer, FightMode.Evil, 10, 1, 0.2, 0.4) + public MeerCaptain() : base(AIType.AI_Archer, FightMode.Evil) { Body = 773; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs index feb53f272..4b78c5108 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using Server.Collections; namespace Server.Mobiles { @@ -8,7 +8,7 @@ namespace Server.Mobiles private DateTime m_NextAbilityTime; [Constructible] - public MeerEternal() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public MeerEternal() : base(AIType.AI_Mage, FightMode.Evil) { Body = 772; @@ -84,45 +84,43 @@ namespace Server.Mobiles private void DoAreaLeech_Finish() { var eable = GetMobilesInRange(6); - var list = eable.Where(m => CanBeHarmful(m) && IsEnemy(m)).ToList(); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) + { + if (CanBeHarmful(m) && IsEnemy(m)) + { + queue.Enqueue(m); + } + } eable.Free(); - if (list.Count == 0) + if (queue.Count == 0) { Say(true, "Bah! You have escaped my grasp this time, mortal!"); + return; } - else + + double scalar = queue.Count switch { - double scalar; + 1 => 0.75, + 2 => 0.50, + _ => 0.25 + }; - if (list.Count == 1) - { - scalar = 0.75; - } - else if (list.Count == 2) - { - scalar = 0.50; - } - else - { - scalar = 0.25; - } + while (queue.Count > 0) + { + var m = queue.Dequeue(); - for (var i = 0; i < list.Count; ++i) - { - var m = list[i]; + var damage = (int)(m.Hits * scalar) + Utility.RandomMinMax(-5, 5); - var damage = (int)(m.Hits * scalar) + Utility.RandomMinMax(-5, 5); + 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); - 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); - - DoHarmful(m); - Hits += AOS.Damage(m, this, Math.Max(damage, 1), 100, 0, 0, 0, 0); - } - - Say(true, "If I cannot cleanse thy soul, I will destroy it!"); + DoHarmful(m); + Hits += AOS.Damage(m, this, Math.Max(damage, 1), 100, 0, 0, 0, 0); } + + Say(true, "If I cannot cleanse thy soul, I will destroy it!"); } private void DoFocusedLeech(Mobile combatant, string message) @@ -172,20 +170,28 @@ namespace Server.Mobiles switch (ability) { case 0: - DoFocusedLeech(combatant, "Thine essence will fill my withering body with strength!"); - break; + { + 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; + { + 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; + { + DoFocusedLeech(combatant, "I devour your life's essence to strengthen my resolve!"); + break; + } case 3: - DoAreaLeech(); - break; + { + DoAreaLeech(); + break; + } // TODO: Resurrect ability } } diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs index 3d171a090..ce509df11 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs @@ -12,7 +12,7 @@ namespace Server.Mobiles private DateTime m_NextAbilityTime; [Constructible] - public MeerMage() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public MeerMage() : base(AIType.AI_Mage, FightMode.Evil) { Body = 770; diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs index 5019d678f..c56872294 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class MeerWarrior : BaseCreature { [Constructible] - public MeerWarrior() : base(AIType.AI_Melee, FightMode.Evil, 10, 1, 0.2, 0.4) + public MeerWarrior() : base(AIType.AI_Melee, FightMode.Evil) { Body = 771; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Animal/CuSidhe.cs b/Projects/UOContent/Mobiles/Monsters/ML/Animal/CuSidhe.cs index ff7c2ff5f..b6ab6b169 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Animal/CuSidhe.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Animal/CuSidhe.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class CuSidhe : BaseMount { [Constructible] - public CuSidhe(string name = null) : base(name, 277, 0x3E91, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public CuSidhe(string name = null) : base(name, 277, 0x3E91, AIType.AI_Animal, FightMode.Aggressor) { var chance = Utility.RandomDouble() * 23301; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs b/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs index cb578515f..413886152 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs @@ -15,7 +15,7 @@ namespace Server.Mobiles private bool m_CanTalk; [Constructible] - public Ferret() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Ferret() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0x117; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Animal/RagingGrizzlyBear.cs b/Projects/UOContent/Mobiles/Monsters/ML/Animal/RagingGrizzlyBear.cs index dfceeec83..d02acc83e 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Animal/RagingGrizzlyBear.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Animal/RagingGrizzlyBear.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class RagingGrizzlyBear : BaseCreature { [Constructible] - public RagingGrizzlyBear() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public RagingGrizzlyBear() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 212; BaseSoundID = 0xA3; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Animal/Squirrel.cs b/Projects/UOContent/Mobiles/Monsters/ML/Animal/Squirrel.cs index a94614772..1749f7d73 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Animal/Squirrel.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Animal/Squirrel.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Squirrel : BaseCreature { [Constructible] - public Squirrel() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Squirrel() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 0x116; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs b/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs index 6a8b5c609..a9c563b0b 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Blighted Grove/Hydra.cs @@ -5,8 +5,7 @@ namespace Server.Mobiles public class Hydra : BaseCreature { [Constructible] - public Hydra() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Hydra() : base(AIType.AI_Melee) { Body = 0x109; BaseSoundID = 0x16A; @@ -67,7 +66,7 @@ namespace Server.Mobiles // TODO: uncomment once added if (Utility.RandomDouble() < 0.2) c.DropItem( new ParrotItem() ); - + if (Utility.RandomDouble() < 0.05) c.DropItem( new ThorvaldsMedallion() ); */ diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/FetidEssence.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/FetidEssence.cs index d5fd88ad8..59f9fdf57 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/FetidEssence.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/FetidEssence.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class FetidEssence : BaseCreature { [Constructible] - public FetidEssence() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public FetidEssence() : base(AIType.AI_Mage) { Body = 273; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs index 4b6df14d4..216656535 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class InterredGrizzle : BaseCreature { [Constructible] - public InterredGrizzle() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public InterredGrizzle() : base(AIType.AI_Mage) { Body = 259; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs index d3a1589ae..e5175f0b7 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private DateTime m_NextUndress; [Constructible] - public MLDryad() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public MLDryad() : base(AIType.AI_Mage, FightMode.Evil) { Body = 266; BaseSoundID = 0x57B; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs index 38ff955e2..c67242b21 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs @@ -15,7 +15,7 @@ namespace Server.Mobiles private DateTime m_NextUndress; [Constructible] - public Satyr() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Satyr() : base(AIType.AI_Animal, FightMode.Aggressor) { Body = 271; BaseSoundID = 0x586; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/CorruptedSoul.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/CorruptedSoul.cs index 9c1ab08a5..bed118762 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/CorruptedSoul.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/CorruptedSoul.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class CorruptedSoul : BaseCreature { [Constructible] - public CorruptedSoul() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, .1, 5) + public CorruptedSoul() : base(AIType.AI_Melee) { Body = 0x3CA; Hue = 0x453; @@ -14,6 +14,7 @@ namespace Server.Mobiles SetHits(61, 69); + SetSpeed(0.25, 2.5); SetDamage(4, 40); SetDamageType(ResistanceType.Physical, 100); diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/FeralTreefellow.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/FeralTreefellow.cs index f375eeb4e..9dd6f72ab 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/FeralTreefellow.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/FeralTreefellow.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class FeralTreefellow : BaseCreature { [Constructible] - public FeralTreefellow() : base(AIType.AI_Melee, FightMode.Evil, 10, 1, 0.2, 0.4) + public FeralTreefellow() : base(AIType.AI_Melee, FightMode.Evil) { Body = 301; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Minotaur.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Minotaur.cs index 7fb8be852..3df0abe45 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Minotaur.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Minotaur.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Minotaur : BaseCreature { [Constructible] - public Minotaur() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) // NEED TO CHECK + public Minotaur() : base(AIType.AI_Melee) // NEED TO CHECK { Body = 263; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurCaptain.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurCaptain.cs index 701dd75a7..8bf35b72e 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurCaptain.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurCaptain.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class MinotaurCaptain : BaseCreature { [Constructible] - public MinotaurCaptain() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) // NEED TO CHECK + public MinotaurCaptain() : base(AIType.AI_Melee) // NEED TO CHECK { Body = 280; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurScout.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurScout.cs index ca8842240..e61b6de4d 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurScout.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/MinotaurScout.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class MinotaurScout : BaseCreature { [Constructible] - public MinotaurScout() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) // NEED TO CHECK + public MinotaurScout() : base(AIType.AI_Melee) // NEED TO CHECK { Body = 281; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/PestilentBandage.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/PestilentBandage.cs index 321dd1f10..abe05b461 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/PestilentBandage.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/PestilentBandage.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class PestilentBandage : BaseCreature { [Constructible] - public PestilentBandage() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) // NEED TO CHECK + public PestilentBandage() : base(AIType.AI_Melee) // NEED TO CHECK { Body = 154; Hue = 0x515; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Tormented Minotaur.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Tormented Minotaur.cs index 25cbf5d27..703960d84 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Tormented Minotaur.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Tormented Minotaur.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class TormentedMinotaur : BaseCreature { [Constructible] - public TormentedMinotaur() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public TormentedMinotaur() : base(AIType.AI_Melee) { Body = 262; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Troglodyte.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Troglodyte.cs index e47f077c2..b2bfcffa1 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Troglodyte.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Melee/Troglodyte.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Troglodyte : BaseCreature { [Constructible] - public Troglodyte() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) // NEED TO CHECK + public Troglodyte() : base(AIType.AI_Melee) // NEED TO CHECK { Body = 267; BaseSoundID = 0x59F; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Labyrinth/Miasma.cs b/Projects/UOContent/Mobiles/Monsters/ML/Labyrinth/Miasma.cs index ab70964d3..456198c9e 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Labyrinth/Miasma.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Labyrinth/Miasma.cs @@ -44,7 +44,7 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.025) { switch ( Utility.Random( 16 ) ) diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs index d62c27d4f..671c1925f 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Magic/GreaterDragon.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class GreaterDragon : BaseCreature { [Constructible] - public GreaterDragon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.3, 0.5) + public GreaterDragon() : base(AIType.AI_Mage) { Body = Utility.RandomList(12, 59); BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/CorrosiveSlime.cs b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/CorrosiveSlime.cs index b54b44d78..b30c65035 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/CorrosiveSlime.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/CorrosiveSlime.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class CorrosiveSlime : BaseCreature { [Constructible] - public CorrosiveSlime() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public CorrosiveSlime() : base(AIType.AI_Melee) { Body = 51; BaseSoundID = 456; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs index 20c23967d..fe27e5c56 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Misc/Melee/Reptalon.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Reptalon : BaseMount { [Constructible] - public Reptalon() : base("a reptalon", 0x114, 0x3E90, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.35) + public Reptalon() : base("a reptalon", 0x114, 0x3E90, AIType.AI_Melee) { BaseSoundID = 0x16A; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CorporealBrume.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CorporealBrume.cs index 0c6c772c5..31a16b7dd 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CorporealBrume.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CorporealBrume.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles { [Constructible] public CorporealBrume() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Melee) { Body = 0x104; // TODO: Verify BaseSoundID = 0x56B; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalDaemon.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalDaemon.cs index b2e6726a5..a77d90e36 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalDaemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalDaemon.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles { [Constructible] public CrystalDaemon() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Mage) { Body = 0x310; Hue = 0x3E8; @@ -46,7 +46,7 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.4) c.DropItem( new ScatteredCrystals() ); } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs index c0af63279..20f7859ef 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalLatticeSeeker.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles { [Constructible] public CrystalLatticeSeeker() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Mage) { Body = 0x7B; Hue = 0x47E; @@ -52,10 +52,10 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.75) c.DropItem( new CrystallineFragments() ); - + if (Utility.RandomDouble() < 0.07) c.DropItem( new PiecesOfCrystal() ); } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalVortex.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalVortex.cs index e20773064..b2c70f1c0 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalVortex.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/CrystalVortex.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles { [Constructible] public CrystalVortex() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Melee) { Body = 0xD; Hue = 0x2B2; @@ -59,10 +59,10 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.75) c.DropItem( new CrystallineFragments() ); - + if (Utility.RandomDouble() < 0.06) c.DropItem( new JaggedCrystals() ); } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/MantraEffervescence.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/MantraEffervescence.cs index d1a0f67b2..7b6904de1 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/MantraEffervescence.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/MantraEffervescence.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles { [Constructible] public MantraEffervescence() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Mage) { Body = 0x111; BaseSoundID = 0x56E; diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs index 6b57008a1..88abdc8c4 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles { [Constructible] public Protector() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Melee) { Body = 401; Female = true; @@ -86,7 +86,7 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.4) c.DropItem( new ProtectorsEssence() ); } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/UnfrozenMummy.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/UnfrozenMummy.cs index 56ca7f095..453a7fdf4 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/UnfrozenMummy.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/UnfrozenMummy.cs @@ -4,7 +4,7 @@ namespace Server.Mobiles { [Constructible] public UnfrozenMummy() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.4, 0.8) + : base(AIType.AI_Mage) { Body = 0x9B; Hue = 0x480; @@ -46,10 +46,10 @@ namespace Server.Mobiles public override void OnDeath( Container c ) { base.OnDeath( c ); - + if (Utility.RandomDouble() < 0.6) c.DropItem( new BrokenCrystals() ); - + if (Utility.RandomDouble() < 0.1) c.DropItem( new ParrotItem() ); } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Changeling.cs b/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Changeling.cs index c57b62524..5a14803c2 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Changeling.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Twisted Weald/Changeling.cs @@ -27,7 +27,7 @@ namespace Server.Mobiles [Constructible] public Changeling() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + : base(AIType.AI_Mage) { Body = 264; Hue = DefaultHue; diff --git a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs index d98775042..9c9942d45 100644 --- a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs +++ b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/HellHound.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class HellHound : BaseCreature { [Constructible] - public HellHound() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public HellHound() : base(AIType.AI_Melee) { Body = 98; BaseSoundID = 229; diff --git a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs index c386b5cae..c62860bca 100644 --- a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs +++ b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class VorpalBunny : BaseCreature { [Constructible] - public VorpalBunny() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public VorpalBunny() : base(AIType.AI_Melee) { Body = 205; Hue = 0x480; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/DarkWisp.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/DarkWisp.cs index 0bbb5ee34..4dc452690 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/DarkWisp.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/DarkWisp.cs @@ -8,7 +8,7 @@ namespace Server.Mobiles public class DarkWisp : BaseCreature { [Constructible] - public DarkWisp() : base(AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public DarkWisp() : base(AIType.AI_Mage, FightMode.Aggressor) { Body = 165; BaseSoundID = 466; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs index 1a1317ea0..f09582850 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private DateTime m_NextResurrect; [Constructible] - public EtherealWarrior() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public EtherealWarrior() : base(AIType.AI_Mage, FightMode.Evil) { Name = NameList.RandomName("ethereal warrior"); Body = 123; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Pixie.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Pixie.cs index 245d76f30..abacd88f5 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Pixie.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Pixie.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Pixie : BaseCreature { [Constructible] - public Pixie() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public Pixie() : base(AIType.AI_Mage, FightMode.Evil) { Name = NameList.RandomName("pixie"); Body = 128; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs index 451acc6d8..6e8025f81 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ShadowWisp : BaseCreature { [Constructible] - public ShadowWisp() : base(AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.3, 0.6) + public ShadowWisp() : base(AIType.AI_Mage, FightMode.Aggressor) { Body = 165; BaseSoundID = 466; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Wisp.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Wisp.cs index 613b6b574..b4122c834 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Wisp.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/Wisp.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles public class Wisp : BaseCreature { [Constructible] - public Wisp() : base(AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Wisp() : base(AIType.AI_Mage, FightMode.Aggressor) { Body = 58; BaseSoundID = 466; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs index dc3d09d98..47844cae7 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs @@ -5,8 +5,7 @@ namespace Server.Mobiles public class AnimatedWeapon : BaseCreature { [Constructible] - public AnimatedWeapon(Mobile caster, int level) - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.3, 0.6) + public AnimatedWeapon(Mobile caster, int level) : base(AIType.AI_Melee) { Body = 692; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs index bcd9ed8ba..c0ea74690 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs @@ -1,106 +1,116 @@ using System; -using System.Linq; +using Server.Buffers; +using Server.Collections; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class BladeSpirits : BaseCreature { - public class BladeSpirits : BaseCreature + [Constructible] + public BladeSpirits() : base(AIType.AI_Melee) { - [Constructible] - public BladeSpirits() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.3, 0.6) + 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 BladeSpirits(Serial serial) : base(serial) + { + } + + public override string CorpseName => "a blade spirit corpse"; + public override bool DeleteCorpseOnDeath => Core.AOS; + public override bool IsHouseSummonable => true; + + public override double DispelDifficulty => 0.0; + public override double DispelFocus => 20.0; + + public override string DefaultName => "a blade spirit"; + + public override bool BleedImmune => true; + public override Poison PoisonImmune => Poison.Lethal; + + public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => + (m.Str + m.Skills.Tactics.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); + + public override int GetAngerSound() => 0x23A; + + public override int GetAttackSound() => 0x3B8; + + public override int GetHurtSound() => 0x23A; + + public override void OnThink() + { + if (Core.SE && Summoned) { - 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 BladeSpirits(Serial serial) : base(serial) - { - } - - public override string CorpseName => "a blade spirit corpse"; - public override bool DeleteCorpseOnDeath => Core.AOS; - public override bool IsHouseSummonable => true; - - public override double DispelDifficulty => 0.0; - public override double DispelFocus => 20.0; - - public override string DefaultName => "a blade spirit"; - - public override bool BleedImmune => true; - public override Poison PoisonImmune => Poison.Lethal; - - public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - (m.Str + m.Skills.Tactics.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); - - public override int GetAngerSound() => 0x23A; - - public override int GetAttackSound() => 0x3B8; - - public override int GetHurtSound() => 0x23A; - - public override void OnThink() - { - if (Core.SE && Summoned) + var eable = GetMobilesInRange(5); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) { - var eable = GetMobilesInRange(5); - var spiritsOrVortexes = eable - .Where(m => m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned) - .ToList(); - - eable.Free(); - - while (spiritsOrVortexes.Count > 6) + if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned) { - var random = spiritsOrVortexes.RandomElement(); - Dispel(random); - spiritsOrVortexes.Remove(random); + queue.Enqueue(m); } } + eable.Free(); - base.OnThink(); + var amount = queue.Count - 6; + if (amount > 0) + { + var mobs = queue.ToPooledArray(); + mobs.Shuffle(); + + while (amount > 0) + { + Dispel(mobs[amount--]); + } + + STArrayPool.Shared.Return(mobs, true); + } } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); + base.OnThink(); + } - writer.Write(0); // version - } + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); + writer.Write(0); // version + } - var version = reader.ReadInt(); - } + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); } } diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Centaur.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Centaur.cs index 7ac9b6286..4df4c37ef 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Centaur.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Centaur.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Centaur : BaseCreature { [Constructible] - public Centaur() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Centaur() : base(AIType.AI_Melee, FightMode.Aggressor) { Name = NameList.RandomName("centaur"); Body = 101; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs index a69586bc3..ddc689b87 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs @@ -1,118 +1,128 @@ using System; -using System.Linq; +using Server.Buffers; +using Server.Collections; -namespace Server.Mobiles +namespace Server.Mobiles; + +public class EnergyVortex : BaseCreature { - public class EnergyVortex : BaseCreature + [Constructible] + public EnergyVortex() : base(AIType.AI_Melee) { - [Constructible] - public EnergyVortex() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + if (Core.SE && Utility.Random(500) == 0) // Per OSI FoF, it's a 1/500 chance. { - if (Core.SE && Utility.RandomDouble() < 0.002) // Per OSI FoF, it's a 1/500 chance. - { - // 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, 100.0); - SetSkill(SkillName.Wrestling, 120.0); - - Fame = 0; - Karma = 0; - - VirtualArmor = 40; - ControlSlots = Core.SE ? 2 : 1; + // Llama vortex! + Body = 0xDC; + Hue = 0x76; + } + else + { + Body = 164; } - public EnergyVortex(Serial serial) - : base(serial) + 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, 100.0); + SetSkill(SkillName.Wrestling, 120.0); + + Fame = 0; + Karma = 0; + + VirtualArmor = 40; + ControlSlots = Core.SE ? 2 : 1; + } + + public EnergyVortex(Serial serial) + : base(serial) + { + } + + public override string CorpseName => "an energy vortex corpse"; + public override bool DeleteCorpseOnDeath => Summoned; + public override bool AlwaysMurderer => true; // Or Llama vortices will appear gray. + + public override double DispelDifficulty => 80.0; + public override double DispelFocus => 20.0; + + public override string DefaultName => "an energy vortex"; + + public override bool BleedImmune => true; + public override Poison PoisonImmune => Poison.Lethal; + + public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => + (m.Int + m.Skills.Magery.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); + + public override int GetAngerSound() => 0x15; + + public override int GetAttackSound() => 0x28; + + public override void OnThink() + { + if (Core.SE && Summoned) { - } - - public override string CorpseName => "an energy vortex corpse"; - public override bool DeleteCorpseOnDeath => Summoned; - public override bool AlwaysMurderer => true; // Or Llama vortices will appear gray. - - public override double DispelDifficulty => 80.0; - public override double DispelFocus => 20.0; - - public override string DefaultName => "an energy vortex"; - - public override bool BleedImmune => true; - public override Poison PoisonImmune => Poison.Lethal; - - public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - (m.Int + m.Skills.Magery.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); - - public override int GetAngerSound() => 0x15; - - public override int GetAttackSound() => 0x28; - - public override void OnThink() - { - if (Core.SE && Summoned) + var eable = GetMobilesInRange(5); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) { - var eable = GetMobilesInRange(5); - var spiritsOrVortexes = eable - .Where(m => m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned) - .ToList(); - - eable.Free(); - - while (spiritsOrVortexes.Count > 6) + if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned) { - var random = spiritsOrVortexes.RandomElement(); - Dispel(random); - spiritsOrVortexes.Remove(random); + queue.Enqueue(m); } } + eable.Free(); - base.OnThink(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - if (BaseSoundID == 263) + var amount = queue.Count - 6; + if (amount > 0) { - BaseSoundID = 0; + var mobs = queue.ToPooledArray(); + mobs.Shuffle(); + + while (amount > 0) + { + Dispel(mobs[amount--]); + } + + STArrayPool.Shared.Return(mobs, true); } } + + base.OnThink(); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + if (BaseSoundID == 263) + { + BaseSoundID = 0; + } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/FrostOoze.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/FrostOoze.cs index b4e4e5577..52e5704aa 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/FrostOoze.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/FrostOoze.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class FrostOoze : BaseCreature { [Constructible] - public FrostOoze() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FrostOoze() : base(AIType.AI_Melee) { Body = 94; BaseSoundID = 456; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs index 46f1a0535..7bd41f3f4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles private bool m_Stunning; [Constructible] - public Golem(bool summoned = false, double scalar = 1.0) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8) + public Golem(bool summoned = false, double scalar = 1.0) : base(AIType.AI_Melee) { Body = 752; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs index 263e86462..aec74b455 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private int m_DevourGoal; [Constructible] - public PlagueBeast() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public PlagueBeast() : base(AIType.AI_Melee) { Body = 775; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs index 0e69a4bb1..bd71a9d14 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles private DecayTimer m_Timer; [Constructible] - public PlagueBeastLord() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public PlagueBeastLord() : base(AIType.AI_Melee) { Body = 775; BaseSoundID = 679; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs index e1ca92246..856dd233c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class PlagueSpawn : BaseCreature { [Constructible] - public PlagueSpawn(Mobile owner = null) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public PlagueSpawn(Mobile owner = null) : base(AIType.AI_Melee) { Owner = owner; ExpireTime = Core.Now + TimeSpan.FromMinutes(1.0); diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/SandVortex.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/SandVortex.cs index 6674e685e..289149080 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/SandVortex.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/SandVortex.cs @@ -8,7 +8,7 @@ namespace Server.Mobiles private DateTime m_NextAttack; [Constructible] - public SandVortex() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SandVortex() : base(AIType.AI_Melee) { Body = 790; BaseSoundID = 263; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Slime.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Slime.cs index 1ba5420a2..5e43b8a29 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Slime.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Slime.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Slime : BaseCreature { [Constructible] - public Slime() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Slime() : base(AIType.AI_Melee) { Body = 51; BaseSoundID = 456; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs index 600e7f2b2..4afc602e6 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class AgapiteElemental : BaseCreature { [Constructible] - public AgapiteElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public AgapiteElemental(int oreAmount = 2) : base(AIType.AI_Melee) { Body = 107; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs index a9da2c0f7..3ff35fc5d 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class BronzeElemental : BaseCreature { [Constructible] - public BronzeElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public BronzeElemental(int oreAmount = 2) : base(AIType.AI_Melee) { // TODO: Gas attack Body = 108; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs index 52c7319f4..e076e2c3f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class CopperElemental : BaseCreature { [Constructible] - public CopperElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public CopperElemental(int oreAmount = 2) : base(AIType.AI_Melee) { Body = 109; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs index 6ba9e2e5d..e5c12b5e4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class DullCopperElemental : BaseCreature { [Constructible] - public DullCopperElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public DullCopperElemental(int oreAmount = 2) : base(AIType.AI_Melee) { Body = 110; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs index b412dc8de..33efe5d66 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class GoldenElemental : BaseCreature { [Constructible] - public GoldenElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public GoldenElemental(int oreAmount = 2) : base(AIType.AI_Melee) { Body = 166; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs index c3d4b32e2..41c978361 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ShadowIronElemental : BaseCreature { [Constructible] - public ShadowIronElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ShadowIronElemental(int oreAmount = 2) : base(AIType.AI_Melee) { Body = 111; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs index 6c4654249..7bad63bfd 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class ValoriteElemental : BaseCreature { [Constructible] - public ValoriteElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public ValoriteElemental(int oreAmount = 2) : base(AIType.AI_Melee) { // TODO: Gas attack Body = 112; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs index 23ecc6a0e..8aaf413fc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class VeriteElemental : BaseCreature { [Constructible] - public VeriteElemental(int oreAmount = 2) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public VeriteElemental(int oreAmount = 2) : base(AIType.AI_Melee) { Body = 113; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Magic/Reaper.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Magic/Reaper.cs index ce16242dc..66dc20fe8 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Magic/Reaper.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Magic/Reaper.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Reaper : BaseCreature { [Constructible] - public Reaper() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Reaper() : base(AIType.AI_Mage) { Body = 47; BaseSoundID = 442; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs index 8f7528997..b2b1cbc93 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/BogThing.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class BogThing : BaseCreature { [Constructible] - public BogThing() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.6, 1.2) + public BogThing() : base(AIType.AI_Melee) { Body = 780; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Bogling.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Bogling.cs index acfc3cfae..f736e7563 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Bogling.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Bogling.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class Bogling : BaseCreature { [Constructible] - public Bogling() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Bogling() : base(AIType.AI_Melee) { Body = 779; BaseSoundID = 422; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Corpser.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Corpser.cs index 16917fa89..5fa465692 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Corpser.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Corpser.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Corpser : BaseCreature { [Constructible] - public Corpser() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Corpser() : base(AIType.AI_Melee) { Body = 8; BaseSoundID = 684; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Quagmire.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Quagmire.cs index 851e8789d..42ab21e28 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Quagmire.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/Quagmire.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Quagmire : BaseCreature { [Constructible] - public Quagmire() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8) + public Quagmire() : base(AIType.AI_Melee) { Body = 789; BaseSoundID = 352; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs index 119bc2aa8..8f9b68779 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SwampTentacle : BaseCreature { [Constructible] - public SwampTentacle() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SwampTentacle() : base(AIType.AI_Melee) { Body = 66; BaseSoundID = 352; diff --git a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/WhippingVine.cs b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/WhippingVine.cs index fe57af0ba..8fdf40f0d 100644 --- a/Projects/UOContent/Mobiles/Monsters/Plant/Melee/WhippingVine.cs +++ b/Projects/UOContent/Mobiles/Monsters/Plant/Melee/WhippingVine.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class WhippingVine : BaseCreature { [Constructible] - public WhippingVine() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public WhippingVine() : base(AIType.AI_Melee) { Body = 8; Hue = 0x851; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs index 092ce3d13..d98ed9fb7 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class AncientWyrm : BaseCreature { [Constructible] - public AncientWyrm() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public AncientWyrm() : base(AIType.AI_Mage) { Body = 46; BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs index f96df532b..6d4d2dc50 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class DeepSeaSerpent : BaseCreature { [Constructible] - public DeepSeaSerpent() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public DeepSeaSerpent() : base(AIType.AI_Mage) { Body = 150; BaseSoundID = 447; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs index cfdd61f66..9847941cc 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Dragon.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Dragon : BaseCreature { [Constructible] - public Dragon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Dragon() : base(AIType.AI_Mage) { Body = Utility.RandomList(12, 59); BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs index a66b989df..272b8a85c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class Leviathan : BaseCreature { [Constructible] - public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage) { Fisher = fisher; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs index aee35bbdf..783d73265 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles }; [Constructible] - public OphidianArchmage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public OphidianArchmage() : base(AIType.AI_Mage) { Name = m_Names.RandomElement(); Body = 85; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs index ddbe1ad58..e1964962e 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles }; [Constructible] - public OphidianMage() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public OphidianMage() : base(AIType.AI_Mage) { Name = m_Names.RandomElement(); Body = 85; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs index 4847acc1d..3da099941 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class OphidianMatriarch : BaseCreature { [Constructible] - public OphidianMatriarch() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public OphidianMatriarch() : base(AIType.AI_Mage) { Body = 87; BaseSoundID = 644; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs index e142dfb0b..8fe8eca12 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs @@ -6,7 +6,7 @@ namespace Server.Mobiles public class SeaSerpent : BaseCreature { [Constructible] - public SeaSerpent() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SeaSerpent() : base(AIType.AI_Mage) { Body = 150; BaseSoundID = 447; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs index 16e103d6f..0da6c0d54 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class SerpentineDragon : BaseCreature { [Constructible] - public SerpentineDragon() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public SerpentineDragon() : base(AIType.AI_Mage, FightMode.Evil) { Body = 103; BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs index 3043112b4..dae5c0e0b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class ShadowWyrm : BaseCreature { [Constructible] - public ShadowWyrm() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public ShadowWyrm() : base(AIType.AI_Mage) { Body = 106; BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs index 1ca9c3303..646aafd59 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SkeletalDragon : BaseCreature { [Constructible] - public SkeletalDragon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SkeletalDragon() : base(AIType.AI_Mage) { Body = 104; BaseSoundID = 0x488; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs index a3bb53046..37524dbe6 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class WhiteWyrm : BaseCreature { [Constructible] - public WhiteWyrm() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public WhiteWyrm() : base(AIType.AI_Mage) { Body = Utility.RandomBool() ? 180 : 49; BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs index 2b4af117b..ff14ed9c0 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Drake.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Drake : BaseCreature { [Constructible] - public Drake() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Drake() : base(AIType.AI_Melee) { Body = Utility.RandomList(60, 61); BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Harpy.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Harpy.cs index fe6ca4a7d..c3c490f84 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Harpy.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Harpy.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Harpy : BaseCreature { [Constructible] - public Harpy() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Harpy() : base(AIType.AI_Melee) { Body = 30; BaseSoundID = 402; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Kraken.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Kraken.cs index 119930286..19f3281a2 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Kraken.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Kraken.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Kraken : BaseCreature { [Constructible] - public Kraken() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Kraken() : base(AIType.AI_Melee) { Body = 77; BaseSoundID = 353; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Lizardman.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Lizardman.cs index 7b79bf063..b8fd3ee4f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Lizardman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Lizardman.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Lizardman : BaseCreature { [Constructible] - public Lizardman() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Lizardman() : base(AIType.AI_Melee) { Name = NameList.RandomName("lizardman"); Body = Utility.RandomList(35, 36); diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs index f530a516e..90a1b36e9 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs @@ -12,7 +12,7 @@ namespace Server.Mobiles }; [Constructible] - public OphidianKnight() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OphidianKnight() : base(AIType.AI_Melee) { Name = m_Names.RandomElement(); Body = 86; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs index 5a76ecb86..afce1e001 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles }; [Constructible] - public OphidianWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public OphidianWarrior() : base(AIType.AI_Melee) { Name = m_Names.RandomElement(); Body = 86; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Scorpion.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Scorpion.cs index 14db2e1e6..c3d172d7b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Scorpion.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Scorpion.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Scorpion : BaseCreature { [Constructible] - public Scorpion() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Scorpion() : base(AIType.AI_Melee) { Body = 48; BaseSoundID = 397; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs index 3af776b96..39861b586 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class StoneHarpy : BaseCreature { [Constructible] - public StoneHarpy() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public StoneHarpy() : base(AIType.AI_Melee) { Body = 73; BaseSoundID = 402; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Wyvern.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Wyvern.cs index 25cb30493..36361e746 100644 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Wyvern.cs +++ b/Projects/UOContent/Mobiles/Monsters/Reptile/Melee/Wyvern.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Wyvern : BaseCreature { [Constructible] - public Wyvern() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Wyvern() : base(AIType.AI_Melee) { Body = 62; BaseSoundID = 362; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs index a4d405e88..0bc626613 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs @@ -12,7 +12,7 @@ namespace Server.Mobiles private TimerExecutionToken _disguiseTimerToken; [Constructible] - public BakeKitsune() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public BakeKitsune() : base(AIType.AI_Mage) { Body = 246; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetle.cs b/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetle.cs index 90d36b4fc..37b92ee25 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetle.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetle.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class DeathwatchBeetle : BaseCreature { [Constructible] - public DeathwatchBeetle() : base(AIType.AI_Melee, Core.ML ? FightMode.Aggressor : FightMode.Closest, 10, 1, 0.2, 0.4) + public DeathwatchBeetle() : base(AIType.AI_Melee, Core.ML ? FightMode.Aggressor : FightMode.Closest) { Body = 242; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs b/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs index 07033c5c6..83302820e 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs @@ -6,14 +6,7 @@ namespace Server.Mobiles public class DeathwatchBeetleHatchling : BaseCreature { [Constructible] - public DeathwatchBeetleHatchling() : base( - AIType.AI_Melee, - Core.ML ? FightMode.Aggressor : FightMode.Closest, - 10, - 1, - 0.2, - 0.4 - ) + public DeathwatchBeetleHatchling() : base(AIType.AI_Melee, Core.ML ? FightMode.Aggressor : FightMode.Closest) { Body = 242; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/EliteNinja.cs b/Projects/UOContent/Mobiles/Monsters/SE/EliteNinja.cs index dc87d356d..a16c794c0 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/EliteNinja.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/EliteNinja.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class EliteNinja : BaseCreature { [Constructible] - public EliteNinja() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public EliteNinja() : base(AIType.AI_Melee) { SpeechHue = Utility.RandomDyedHue(); Hue = Race.Human.RandomSkinHue(); @@ -16,7 +16,7 @@ namespace Server.Mobiles SetHits(251, 350); SetStr(126, 225); - SetDex(81, 95); + SetDex(175, 275); SetInt(151, 165); SetDamage(12, 20); diff --git a/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs b/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs index 49b738f19..637af3571 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/FanDancer.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private static readonly HashSet m_Table = new(); [Constructible] - public FanDancer() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FanDancer() : base(AIType.AI_Melee) { Body = 247; BaseSoundID = 0x372; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/FireBeetle.cs b/Projects/UOContent/Mobiles/Monsters/SE/FireBeetle.cs index 9e24b2336..7f4daea42 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/FireBeetle.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/FireBeetle.cs @@ -7,10 +7,11 @@ namespace Server.Mobiles public class FireBeetle : BaseMount { [Constructible] - public FireBeetle() : base("a fire beetle", 0xA9, 0x3E95, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public FireBeetle() : base("a fire beetle", 0xA9, 0x3E95, AIType.AI_Melee) { + SetStam(100); SetStr(300); - SetDex(100); + SetDex(65, 100); SetInt(500); SetHits(200); diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs b/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs index 5abb5deb5..f10e68ba1 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private static readonly Dictionary m_Table = new(); [Constructible] - public Kappa() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Kappa() : base(AIType.AI_Melee) { Body = 240; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs b/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs index 05ebec43e..d4810186c 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/KazeKemono.cs @@ -7,12 +7,10 @@ namespace Server.Mobiles { private static readonly Dictionary m_FlurryOfTwigsTable = new(); - private static readonly Dictionary m_ChlorophylBlastTable = - new(); + private static readonly Dictionary m_ChlorophylBlastTable = new(); [Constructible] - public KazeKemono() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public KazeKemono() : base(AIType.AI_Mage) { Body = 196; BaseSoundID = 655; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs b/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs index cd3af17f3..46d841731 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/LadyOfTheSnow.cs @@ -10,8 +10,7 @@ namespace Server.Mobiles private static readonly Dictionary m_Table = new(); [Constructible] - public LadyOfTheSnow() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public LadyOfTheSnow() : base(AIType.AI_Mage) { Body = 252; BaseSoundID = 0x482; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Oni.cs b/Projects/UOContent/Mobiles/Monsters/SE/Oni.cs index a75708399..fa5c6a6bc 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Oni.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Oni.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Oni : BaseCreature { [Constructible] - public Oni() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public Oni() : base(AIType.AI_Mage) { Body = 241; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs b/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs index b0d92e70c..73019d70c 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs @@ -8,7 +8,7 @@ namespace Server.Mobiles private static readonly HashSet m_Table = new(); [Constructible] - public RaiJu() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public RaiJu() : base(AIType.AI_Melee) { Body = 199; BaseSoundID = 0x346; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/RevenantLion.cs b/Projects/UOContent/Mobiles/Monsters/SE/RevenantLion.cs index 50e336676..52da6b4dc 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/RevenantLion.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/RevenantLion.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class RevenantLion : BaseCreature { [Constructible] - public RevenantLion() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public RevenantLion() : base(AIType.AI_Mage) { Body = 251; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Ronin.cs b/Projects/UOContent/Mobiles/Monsters/SE/Ronin.cs index b7c8e51b6..91ca622e6 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Ronin.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Ronin.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Ronin : BaseCreature { [Constructible] - public Ronin() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Ronin() : base(AIType.AI_Melee) { SpeechHue = Utility.RandomDyedHue(); Hue = Race.Human.RandomSkinHue(); diff --git a/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs b/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs index b705c518f..8825b753d 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/RuneBeetle.cs @@ -10,7 +10,7 @@ namespace Server.Mobiles private static readonly Dictionary m_Table = new(); [Constructible] - public RuneBeetle() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public RuneBeetle() : base(AIType.AI_Mage) { Body = 244; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs b/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs index 54ed25ea7..6ff33c378 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/TsukiWolf.cs @@ -10,8 +10,7 @@ namespace Server.Mobiles private static readonly Dictionary m_Table = new(); [Constructible] - public TsukiWolf() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public TsukiWolf() : base(AIType.AI_Melee) { Body = 250; Hue = Utility.Random(3) == 0 ? Utility.RandomNeutralHue() : 0; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs b/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs index 7e7856efb..b475c769c 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Yamandon.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class Yamandon : BaseCreature { [Constructible] - public Yamandon() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public Yamandon() : base(AIType.AI_Melee) { Body = 249; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs index b323bbb82..4a6a2e4c6 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuElder.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class YomotsuElder : BaseCreature { [Constructible] - public YomotsuElder() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public YomotsuElder() : base(AIType.AI_Melee) { Body = 255; BaseSoundID = 0x452; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs index e443a15a4..00dd628c6 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuPriest.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class YomotsuPriest : BaseCreature { [Constructible] - public YomotsuPriest() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public YomotsuPriest() : base(AIType.AI_Mage) { Body = 253; BaseSoundID = 0x452; diff --git a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs index b4bcac395..22973b4aa 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/YomotsuWarrior.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles public class YomotsuWarrior : BaseCreature { [Constructible] - public YomotsuWarrior() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public YomotsuWarrior() : base(AIType.AI_Melee) { Body = 245; BaseSoundID = 0x452; diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs index bc733ae92..d430af376 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SummonedAirElemental : BaseCreature { [Constructible] - public SummonedAirElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SummonedAirElemental() : base(AIType.AI_Mage) { Body = 13; Hue = 0x4001; diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs index 776d5dba8..0f4bbcbd3 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SummonedDaemon : BaseCreature { [Constructible] - public SummonedDaemon() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SummonedDaemon() : base(AIType.AI_Mage) { Name = NameList.RandomName("daemon"); Body = Core.AOS ? 10 : 9; diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs index b7d499ed7..d1fd54a2b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SummonedEarthElemental : BaseCreature { [Constructible] - public SummonedEarthElemental() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public SummonedEarthElemental() : base(AIType.AI_Melee) { Body = 14; BaseSoundID = 268; diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs index e76e2a472..19d8f5662 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class SummonedFireElemental : BaseCreature { [Constructible] - public SummonedFireElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SummonedFireElemental() : base(AIType.AI_Mage) { Body = 15; BaseSoundID = 838; diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs index 1228bb03d..1a08bedd0 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class SummonedWaterElemental : BaseCreature { [Constructible] - public SummonedWaterElemental() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public SummonedWaterElemental() : base(AIType.AI_Mage) { Body = 16; BaseSoundID = 278; diff --git a/Projects/UOContent/Mobiles/NPCSpeeds.cs b/Projects/UOContent/Mobiles/NPCSpeeds.cs new file mode 100644 index 000000000..e6317c91f --- /dev/null +++ b/Projects/UOContent/Mobiles/NPCSpeeds.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using Server.Json; + +namespace Server.Mobiles; + +public static class NPCSpeeds +{ + private const string _tablePath = "Data/npc-speeds.json"; + private static Dictionary _speedsByType = new(); + private static Dictionary _speedsByName = new(); + + // Enabled for pets on HS+ + public static bool ScaleSpeedByDex { get; private set; } + public static double MinDelay { get; private set; } + public static double MaxDelay { get; private set; } + public static int MinDex { get; private set; } + public static int MaxDex { get; private set; } + + // Time period to lock NPCs into idling + public static int MinIdleSeconds { get; private set; } + public static int MaxIdleSeconds { get; private set; } + + public static void GetSpeeds(BaseCreature bc, out double activeSpeed, out double passiveSpeed) + { + // Used for scaling pet's speed by dex in HS+ + if (bc.ScaleSpeedByDex) + { + var maxDex = MaxDex; + double min = MinDelay; + double max = MaxDelay; + var dex = Math.Clamp(bc.Dex, MinDex, maxDex); + + activeSpeed = Math.Max(max - (max - min) * ((double)dex / maxDex), min); + passiveSpeed = activeSpeed * 2; + return; + } + + if (bc.SpeedClass != null && _speedsByName.TryGetValue(bc.SpeedClass, out var sp) || + _speedsByType.TryGetValue(bc.GetType(), out sp)) + { + activeSpeed = sp.ActiveSpeed; + passiveSpeed = sp.PassiveSpeed; + return; + } + + // "Fast" + activeSpeed = 0.2; + passiveSpeed = 0.4; + } + + public static void RegisterSpeed(SpeedClassEntry entry) + { + _speedsByName[entry.Name] = entry; + + foreach (var type in entry.Types) + { + _speedsByType[type] = entry; + } + } + + public static void Configure() + { + ScaleSpeedByDex = ServerConfiguration.GetSetting("movement.delay.scaleSpeedByDex", Core.HS); + MinDelay = ServerConfiguration.GetSetting("movement.delay.npcMinDelay", 0.1); + MaxDelay = ServerConfiguration.GetSetting("movement.delay.npcMaxDelay", 0.4); + MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMinDex", 50); + MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMaxDex", 200); + MinIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMinIdle", 15); + MaxIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMaxIdle", 25); + + var path = Path.Combine(Core.BaseDirectory, _tablePath); + if (!File.Exists(path)) + { + return; + } + + var speeds = JsonConfig.Deserialize(path); + + for (var i = 0; i < speeds.Length; i++) + { + RegisterSpeed(speeds[i]); + } + } + + public record SpeedClassEntry + { + [JsonPropertyName("name")] + public string Name { get; init; } + + [JsonPropertyName("active")] + public double ActiveSpeed { get; init; } + + [JsonPropertyName("passive")] + public double PassiveSpeed { get; init; } + + [JsonPropertyName("types")] + public HashSet Types { get; init; } + } +} diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 3e9895002..9db9e7d14 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Server.Accounting; using Server.ContextMenus; using Server.Engines.BulkOrders; @@ -181,7 +180,7 @@ namespace Server.Mobiles private DateTime m_LastYoungMessage = DateTime.MinValue; private TimeSpan m_LongTermElapse; - private MountBlock m_MountBlock; + private MountBlock _mountBlock; private DateTime m_NextJustAward; @@ -240,7 +239,7 @@ namespace Server.Mobiles public DesignContext DesignContext { get; set; } - public BlockMountType MountBlockReason => CheckBlock(m_MountBlock) ? m_MountBlock.m_Type : BlockMountType.None; + public BlockMountType MountBlockReason => _mountBlock?.MountBlockReason ?? BlockMountType.None; public override int MaxWeight => (Core.ML && Race == Race.Human ? 100 : 40) + (int)(3.5 * Str); @@ -394,7 +393,7 @@ namespace Server.Mobiles public bool NinjaWepCooldown { get; set; } - public List AllFollowers => m_AllFollowers ?? (m_AllFollowers = new List()); + public List AllFollowers => m_AllFollowers ??= new List(); public RankDefinition GuildRank { @@ -1102,8 +1101,8 @@ namespace Server.Mobiles } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool CheckBlock(MountBlock block) => block?._timerToken.Running == true; + public void SetMountBlock(BlockMountType type, bool dismount) => + SetMountBlock(type, TimeSpan.MaxValue, dismount); public void SetMountBlock(BlockMountType type, TimeSpan duration, bool dismount) { @@ -1119,9 +1118,10 @@ namespace Server.Mobiles } } - if (!CheckBlock(m_MountBlock) || m_MountBlock._timerToken.Next < Core.Now + duration) + if (!_mountBlock.CheckBlock() || _mountBlock.Expiration < Core.Now + duration) { - m_MountBlock = new MountBlock(duration, type, this); + _mountBlock?.RemoveBlock(this); + _mountBlock = new MountBlock(duration, type, this); } } @@ -3510,7 +3510,7 @@ namespace Server.Mobiles DisguiseTimers.RemoveTimer(this); } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -3524,38 +3524,33 @@ namespace Server.Mobiles if (faction.Commander == this) { - list.Add(1042733, faction.Definition.PropName); // Commanding Lord of the ~1_FACTION_NAME~ + // Commanding Lord of the ~1_FACTION_NAME~ + list.Add(1042733, $"{faction.Definition.PropName}"); } 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~ + 1042734, // The Sheriff of ~1_CITY~, ~2_FACTION_NAME~ + $"{pl.Sheriff.Definition.FriendlyName}\t{faction.Definition.PropName}" + ); } 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~ + 1042735, // The Finance Minister of ~1_CITY~, ~2_FACTION_NAME~ + $"{pl.Finance.Definition.FriendlyName}\t{faction.Definition.PropName}" + ); } else if (pl.MerchantTitle != MerchantTitle.None) { list.Add( - 1060776, - "{0}\t{1}", - MerchantTitles.GetInfo(pl.MerchantTitle).Title, - faction.Definition.PropName - ); // ~1_val~, ~2_val~ + 1060776, // ~1_val~, ~2_val~ + $"{MerchantTitles.GetInfo(pl.MerchantTitle).Title}\t{faction.Definition.PropName}" + ); } else { - list.Add(1060776, "{0}\t{1}", pl.Rank.Title, faction.Definition.PropName); // ~1_val~, ~2_val~ + list.Add(1060776, $"{pl.Rank.Title}\t{faction.Definition.PropName}"); // ~1_val~, ~2_val~ } } } @@ -4634,21 +4629,30 @@ namespace Server.Mobiles private class MountBlock { - public TimerExecutionToken _timerToken; - public readonly BlockMountType m_Type; + private TimerExecutionToken _timerToken; + private BlockMountType _type; public MountBlock(TimeSpan duration, BlockMountType type, Mobile mobile) { - m_Type = type; + _type = type; - Timer.StartTimer(duration, () => RemoveBlock(mobile), out _timerToken); + if (duration < TimeSpan.MaxValue) + { + Timer.StartTimer(duration, () => RemoveBlock(mobile), out _timerToken); + } } - private void RemoveBlock(Mobile mobile) + public DateTime Expiration => _timerToken.Next; + + public BlockMountType MountBlockReason => CheckBlock() ? _type : BlockMountType.None; + + public bool CheckBlock() => _timerToken.Next == DateTime.MinValue || _timerToken.Running; + + public void RemoveBlock(Mobile mobile) { if (mobile is PlayerMobile pm) { - pm.m_MountBlock = null; + pm._mountBlock = null; } _timerToken.Cancel(); diff --git a/Projects/UOContent/Mobiles/Special/BaseChampion.cs b/Projects/UOContent/Mobiles/Special/BaseChampion.cs index 732016313..0085781a7 100644 --- a/Projects/UOContent/Mobiles/Special/BaseChampion.cs +++ b/Projects/UOContent/Mobiles/Special/BaseChampion.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles { public abstract class BaseChampion : BaseCreature { - public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18, 1, 0.1, 0.2) + public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18) { } diff --git a/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs b/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs index 85eaaaba4..3ca4c3232 100644 --- a/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs +++ b/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs @@ -5,13 +5,13 @@ namespace Server.Mobiles { public abstract class BaseShieldGuard : BaseCreature { - public BaseShieldGuard() : base(AIType.AI_Melee, FightMode.Aggressor, 14, 1, 0.8, 1.6) + public BaseShieldGuard() : base(AIType.AI_Melee, FightMode.Aggressor, 14) { InitStats(1000, 1000, 1000); Title = "the guard"; + SetSpeed(0.5, 2.0); SpeechHue = Utility.RandomDyedHue(); - Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) diff --git a/Projects/UOContent/Mobiles/Special/DarkGuardian.cs b/Projects/UOContent/Mobiles/Special/DarkGuardian.cs index 2e6bbeca2..6f2326973 100644 --- a/Projects/UOContent/Mobiles/Special/DarkGuardian.cs +++ b/Projects/UOContent/Mobiles/Special/DarkGuardian.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class DarkGuardian : BaseCreature { [Constructible] - public DarkGuardian() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public DarkGuardian() : base(AIType.AI_Mage) { Body = 78; BaseSoundID = 0x3E9; diff --git a/Projects/UOContent/Mobiles/Special/Dummy.cs b/Projects/UOContent/Mobiles/Special/Dummy.cs deleted file mode 100644 index c56d03f61..000000000 --- a/Projects/UOContent/Mobiles/Special/Dummy.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using Server.Items; - -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; - - [Constructible] - public Dummy( - AIType iAI, FightMode iFightMode, int iRangePerception, int iRangeFight, double dActiveSpeed, - double dPassiveSpeed - ) : base(iAI, iFightMode, iRangePerception, iRangeFight, dActiveSpeed, dPassiveSpeed) - { - Body = 400 + Utility.Random(2); - Hue = Race.Human.RandomSkinHue(); - - Skills.DetectHidden.Base = 100; - Skills.MagicResist.Base = 120; - - Team = Utility.Random(3); - - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - Utility.AssignRandomHair(this, iHue); - - var 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(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var 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() - { - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - var 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 readonly Dummy m_Owner; - - public AutokillTimer(Dummy owner) : base(TimeSpan.FromMinutes(5.0)) - { - m_Owner = owner; - } - - protected override void OnTick() - { - m_Owner.Kill(); - Stop(); - } - } - } -} diff --git a/Projects/UOContent/Mobiles/Special/DummySpecific.cs b/Projects/UOContent/Mobiles/Special/DummySpecific.cs deleted file mode 100644 index fc064c378..000000000 --- a/Projects/UOContent/Mobiles/Special/DummySpecific.cs +++ /dev/null @@ -1,784 +0,0 @@ -using Server.Items; -using Server.Spells.First; -using Server.Spells.Third; - -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 - { - [Constructible] - public DummyMace() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Macer - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(125, 125, 90); - Skills.Macing.Base = 120; - Skills.Anatomy.Base = 120; - Skills.Healing.Base = 120; - Skills.Tactics.Base = 120; - - // Equip - var war = new WarHammer(); - war.Movable = true; - war.Crafter = this; - war.Quality = WeaponQuality.Regular; - AddItem(war); - - var bts = new Boots(); - bts.Hue = iHue; - AddItem(bts); - - var cht = new ChainChest(); - cht.Movable = false; - cht.LootType = LootType.Newbied; - cht.Crafter = this; - cht.Quality = ArmorQuality.Regular; - AddItem(cht); - - var chl = new ChainLegs(); - chl.Movable = false; - chl.LootType = LootType.Newbied; - chl.Crafter = this; - chl.Quality = ArmorQuality.Regular; - AddItem(chl); - - var pla = new PlateArms(); - pla.Movable = false; - pla.LootType = LootType.Newbied; - pla.Crafter = this; - pla.Quality = ArmorQuality.Regular; - AddItem(pla); - - var band = new Bandage(50); - AddToBackpack(band); - } - - public DummyMace(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Macer"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummyFence : Dummy - { - [Constructible] - public DummyFence() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Fencer - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(125, 125, 90); - Skills.Fencing.Base = 120; - Skills.Anatomy.Base = 120; - Skills.Healing.Base = 120; - Skills.Tactics.Base = 120; - - // Equip - var ssp = new Spear(); - ssp.Movable = true; - ssp.Crafter = this; - ssp.Quality = WeaponQuality.Regular; - AddItem(ssp); - - var snd = new Boots(); - snd.Hue = iHue; - snd.LootType = LootType.Newbied; - AddItem(snd); - - var cht = new ChainChest(); - cht.Movable = false; - cht.LootType = LootType.Newbied; - cht.Crafter = this; - cht.Quality = ArmorQuality.Regular; - AddItem(cht); - - var chl = new ChainLegs(); - chl.Movable = false; - chl.LootType = LootType.Newbied; - chl.Crafter = this; - chl.Quality = ArmorQuality.Regular; - AddItem(chl); - - var pla = new PlateArms(); - pla.Movable = false; - pla.LootType = LootType.Newbied; - pla.Crafter = this; - pla.Quality = ArmorQuality.Regular; - AddItem(pla); - - var band = new Bandage(50); - AddToBackpack(band); - } - - public DummyFence(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Fencer"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummySword : Dummy - { - [Constructible] - public DummySword() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Swordsman - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(125, 125, 90); - Skills.Swords.Base = 120; - Skills.Anatomy.Base = 120; - Skills.Healing.Base = 120; - Skills.Tactics.Base = 120; - Skills.Parry.Base = 120; - - // Equip - var kat = new Katana(); - kat.Crafter = this; - kat.Movable = true; - kat.Quality = WeaponQuality.Regular; - AddItem(kat); - - var bts = new Boots(); - bts.Hue = iHue; - AddItem(bts); - - var cht = new ChainChest(); - cht.Movable = false; - cht.LootType = LootType.Newbied; - cht.Crafter = this; - cht.Quality = ArmorQuality.Regular; - AddItem(cht); - - var chl = new ChainLegs(); - chl.Movable = false; - chl.LootType = LootType.Newbied; - chl.Crafter = this; - chl.Quality = ArmorQuality.Regular; - AddItem(chl); - - var pla = new PlateArms(); - pla.Movable = false; - pla.LootType = LootType.Newbied; - pla.Crafter = this; - pla.Quality = ArmorQuality.Regular; - AddItem(pla); - - var band = new Bandage(50); - AddToBackpack(band); - } - - public DummySword(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Swordsman"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummyNox : Dummy - { - [Constructible] - public DummyNox() : base(AIType.AI_Mage, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Nox or Pure Mage - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(90, 90, 125); - Skills.Magery.Base = 120; - Skills.EvalInt.Base = 120; - Skills.Inscribe.Base = 100; - Skills.Wrestling.Base = 120; - Skills.Meditation.Base = 120; - Skills.Poisoning.Base = 100; - - // Equip - var book = new Spellbook(); - book.Movable = false; - book.LootType = LootType.Newbied; - book.Content = 0xFFFFFFFFFFFFFFFF; - AddItem(book); - - var kilt = new Kilt(); - kilt.Hue = jHue; - AddItem(kilt); - - var snd = new Sandals(); - snd.Hue = iHue; - snd.LootType = LootType.Newbied; - AddItem(snd); - - var skc = new SkullCap(); - skc.Hue = iHue; - AddItem(skc); - - // Spells - AddSpellAttack(typeof(MagicArrowSpell)); - AddSpellAttack(typeof(WeakenSpell)); - AddSpellAttack(typeof(FireballSpell)); - AddSpellDefense(typeof(WallOfStoneSpell)); - AddSpellDefense(typeof(HealSpell)); - } - - public DummyNox(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Nox Mage"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummyStun : Dummy - { - [Constructible] - public DummyStun() : base(AIType.AI_Mage, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Stun Mage - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(90, 90, 125); - Skills.Magery.Base = 100; - Skills.EvalInt.Base = 120; - Skills.Anatomy.Base = 80; - Skills.Wrestling.Base = 80; - Skills.Meditation.Base = 100; - Skills.Poisoning.Base = 100; - - // Equip - var book = new Spellbook(); - book.Movable = false; - book.LootType = LootType.Newbied; - book.Content = 0xFFFFFFFFFFFFFFFF; - AddItem(book); - - var lea = new LeatherArms(); - lea.Movable = false; - lea.LootType = LootType.Newbied; - lea.Crafter = this; - lea.Quality = ArmorQuality.Regular; - AddItem(lea); - - var lec = new LeatherChest(); - lec.Movable = false; - lec.LootType = LootType.Newbied; - lec.Crafter = this; - lec.Quality = ArmorQuality.Regular; - AddItem(lec); - - var leg = new LeatherGorget(); - leg.Movable = false; - leg.LootType = LootType.Newbied; - leg.Crafter = this; - leg.Quality = ArmorQuality.Regular; - AddItem(leg); - - var lel = new LeatherLegs(); - lel.Movable = false; - lel.LootType = LootType.Newbied; - lel.Crafter = this; - lel.Quality = ArmorQuality.Regular; - AddItem(lel); - - var bts = new Boots(); - bts.Hue = iHue; - AddItem(bts); - - var cap = new Cap(); - cap.Hue = iHue; - AddItem(cap); - - // Spells - AddSpellAttack(typeof(MagicArrowSpell)); - AddSpellAttack(typeof(WeakenSpell)); - AddSpellAttack(typeof(FireballSpell)); - AddSpellDefense(typeof(WallOfStoneSpell)); - AddSpellDefense(typeof(HealSpell)); - } - - public DummyStun(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Stun Mage"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummySuper : Dummy - { - [Constructible] - public DummySuper() : base(AIType.AI_Mage, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Super Mage - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(125, 125, 125); - Skills.Magery.Base = 120; - Skills.EvalInt.Base = 120; - Skills.Anatomy.Base = 120; - Skills.Wrestling.Base = 120; - Skills.Meditation.Base = 120; - Skills.Poisoning.Base = 100; - Skills.Inscribe.Base = 100; - - // Equip - var book = new Spellbook(); - book.Movable = false; - book.LootType = LootType.Newbied; - book.Content = 0xFFFFFFFFFFFFFFFF; - AddItem(book); - - var lea = new LeatherArms(); - lea.Movable = false; - lea.LootType = LootType.Newbied; - lea.Crafter = this; - lea.Quality = ArmorQuality.Regular; - AddItem(lea); - - var lec = new LeatherChest(); - lec.Movable = false; - lec.LootType = LootType.Newbied; - lec.Crafter = this; - lec.Quality = ArmorQuality.Regular; - AddItem(lec); - - var leg = new LeatherGorget(); - leg.Movable = false; - leg.LootType = LootType.Newbied; - leg.Crafter = this; - leg.Quality = ArmorQuality.Regular; - AddItem(leg); - - var lel = new LeatherLegs(); - lel.Movable = false; - lel.LootType = LootType.Newbied; - lel.Crafter = this; - lel.Quality = ArmorQuality.Regular; - AddItem(lel); - - var snd = new Sandals(); - snd.Hue = iHue; - snd.LootType = LootType.Newbied; - AddItem(snd); - - var jhat = new JesterHat(); - jhat.Hue = iHue; - AddItem(jhat); - - var dblt = new Doublet(); - dblt.Hue = iHue; - AddItem(dblt); - - // Spells - AddSpellAttack(typeof(MagicArrowSpell)); - AddSpellAttack(typeof(WeakenSpell)); - AddSpellAttack(typeof(FireballSpell)); - AddSpellDefense(typeof(WallOfStoneSpell)); - AddSpellDefense(typeof(HealSpell)); - } - - public DummySuper(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Super Mage"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummyHealer : Dummy - { - [Constructible] - public DummyHealer() : base(AIType.AI_Healer, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Healer Mage - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(125, 125, 125); - Skills.Magery.Base = 120; - Skills.EvalInt.Base = 120; - Skills.Anatomy.Base = 120; - Skills.Wrestling.Base = 120; - Skills.Meditation.Base = 120; - Skills.Healing.Base = 100; - - // Equip - var book = new Spellbook(); - book.Movable = false; - book.LootType = LootType.Newbied; - book.Content = 0xFFFFFFFFFFFFFFFF; - AddItem(book); - - var lea = new LeatherArms(); - lea.Movable = false; - lea.LootType = LootType.Newbied; - lea.Crafter = this; - lea.Quality = ArmorQuality.Regular; - AddItem(lea); - - var lec = new LeatherChest(); - lec.Movable = false; - lec.LootType = LootType.Newbied; - lec.Crafter = this; - lec.Quality = ArmorQuality.Regular; - AddItem(lec); - - var leg = new LeatherGorget(); - leg.Movable = false; - leg.LootType = LootType.Newbied; - leg.Crafter = this; - leg.Quality = ArmorQuality.Regular; - AddItem(leg); - - var lel = new LeatherLegs(); - lel.Movable = false; - lel.LootType = LootType.Newbied; - lel.Crafter = this; - lel.Quality = ArmorQuality.Regular; - AddItem(lel); - - var snd = new Sandals(); - snd.Hue = iHue; - snd.LootType = LootType.Newbied; - AddItem(snd); - - var cap = new Cap(); - cap.Hue = iHue; - AddItem(cap); - - var robe = new Robe(); - robe.Hue = iHue; - AddItem(robe); - } - - public DummyHealer(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Healer"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class DummyAssassin : Dummy - { - [Constructible] - public DummyAssassin() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Hybrid Assassin - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(105, 105, 105); - Skills.Magery.Base = 120; - Skills.EvalInt.Base = 120; - Skills.Swords.Base = 120; - Skills.Tactics.Base = 120; - Skills.Meditation.Base = 120; - Skills.Poisoning.Base = 100; - - // Equip - var book = new Spellbook(); - book.Movable = false; - book.LootType = LootType.Newbied; - book.Content = 0xFFFFFFFFFFFFFFFF; - AddToBackpack(book); - - var 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); - - var lea = new LeatherArms(); - lea.Movable = false; - lea.LootType = LootType.Newbied; - lea.Crafter = this; - lea.Quality = ArmorQuality.Regular; - AddItem(lea); - - var lec = new LeatherChest(); - lec.Movable = false; - lec.LootType = LootType.Newbied; - lec.Crafter = this; - lec.Quality = ArmorQuality.Regular; - AddItem(lec); - - var leg = new LeatherGorget(); - leg.Movable = false; - leg.LootType = LootType.Newbied; - leg.Crafter = this; - leg.Quality = ArmorQuality.Regular; - AddItem(leg); - - var lel = new LeatherLegs(); - lel.Movable = false; - lel.LootType = LootType.Newbied; - lel.Crafter = this; - lel.Quality = ArmorQuality.Regular; - AddItem(lel); - - var snd = new Sandals(); - snd.Hue = iHue; - snd.LootType = LootType.Newbied; - AddItem(snd); - - var cap = new Cap(); - cap.Hue = iHue; - AddItem(cap); - - var robe = new Robe(); - robe.Hue = iHue; - AddItem(robe); - - var pota = new DeadlyPoisonPotion(); - pota.LootType = LootType.Newbied; - AddToBackpack(pota); - - var potb = new DeadlyPoisonPotion(); - potb.LootType = LootType.Newbied; - AddToBackpack(potb); - - var potc = new DeadlyPoisonPotion(); - potc.LootType = LootType.Newbied; - AddToBackpack(potc); - - var potd = new DeadlyPoisonPotion(); - potd.LootType = LootType.Newbied; - AddToBackpack(potd); - - var band = new Bandage(50); - AddToBackpack(band); - } - - public DummyAssassin(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Hybrid Assassin"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [TypeAlias("Server.Mobiles.DummyTheif")] - public class DummyThief : Dummy - { - [Constructible] - public DummyThief() : base(AIType.AI_Thief, FightMode.Closest, 15, 1, 0.2, 0.6) - { - // A Dummy Hybrid Thief - var iHue = 20 + Team * 40; - var jHue = 25 + Team * 40; - - // Skills and Stats - InitStats(105, 105, 105); - Skills.Healing.Base = 120; - Skills.Anatomy.Base = 120; - Skills.Stealing.Base = 120; - Skills.ArmsLore.Base = 100; - Skills.Meditation.Base = 120; - Skills.Wrestling.Base = 120; - - // Equip - var book = new Spellbook(); - book.Movable = false; - book.LootType = LootType.Newbied; - book.Content = 0xFFFFFFFFFFFFFFFF; - AddItem(book); - - var lea = new LeatherArms(); - lea.Movable = false; - lea.LootType = LootType.Newbied; - lea.Crafter = this; - lea.Quality = ArmorQuality.Regular; - AddItem(lea); - - var lec = new LeatherChest(); - lec.Movable = false; - lec.LootType = LootType.Newbied; - lec.Crafter = this; - lec.Quality = ArmorQuality.Regular; - AddItem(lec); - - var leg = new LeatherGorget(); - leg.Movable = false; - leg.LootType = LootType.Newbied; - leg.Crafter = this; - leg.Quality = ArmorQuality.Regular; - AddItem(leg); - - var lel = new LeatherLegs(); - lel.Movable = false; - lel.LootType = LootType.Newbied; - lel.Crafter = this; - lel.Quality = ArmorQuality.Regular; - AddItem(lel); - - var snd = new Sandals(); - snd.Hue = iHue; - snd.LootType = LootType.Newbied; - AddItem(snd); - - var cap = new Cap(); - cap.Hue = iHue; - AddItem(cap); - - var robe = new Robe(); - robe.Hue = iHue; - AddItem(robe); - - var band = new Bandage(50); - AddToBackpack(band); - } - - public DummyThief(Serial serial) : base(serial) - { - } - - public override string DefaultName => "Hybrid Thief"; - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } -} diff --git a/Projects/UOContent/Mobiles/Special/Harrower.cs b/Projects/UOContent/Mobiles/Special/Harrower.cs index 967f2fb3a..ad0779270 100644 --- a/Projects/UOContent/Mobiles/Special/Harrower.cs +++ b/Projects/UOContent/Mobiles/Special/Harrower.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Server.Items; using Server.Spells; @@ -48,7 +47,7 @@ namespace Server.Mobiles private bool m_TrueForm; [Constructible] - public Harrower() : base(AIType.AI_Mage, FightMode.Closest, 18, 1, 0.2, 0.4) + public Harrower() : base(AIType.AI_Mage, FightMode.Closest, 18) { Instances.Add(this); Body = 146; @@ -570,8 +569,17 @@ namespace Server.Mobiles return; } - var toTeleport = m_Owner.GetMobilesInRange(16) - .FirstOrDefault(mob => mob != m_Owner && mob.Player && m_Owner.CanBeHarmful(mob) && m_Owner.CanSee(mob)); + var eable = m_Owner.GetMobilesInRange(16); + Mobile toTeleport = null; + foreach (var m in eable) + { + if (m != m_Owner && m.Player && m_Owner.CanBeHarmful(m) && m_Owner.CanSee(m)) + { + toTeleport = m; + break; + } + } + eable.Free(); if (toTeleport == null) { @@ -602,33 +610,31 @@ namespace Server.Mobiles } } - var m = toTeleport; + var from = toTeleport.Location; - var from = m.Location; - - m.Location = to; + toTeleport.Location = to; SpellHelper.Turn(m_Owner, toTeleport); SpellHelper.Turn(toTeleport, m_Owner); - m.ProcessDelta(); + toTeleport.ProcessDelta(); Effects.SendLocationParticles( - EffectItem.Create(from, m.Map, EffectItem.DefaultDuration), + EffectItem.Create(from, toTeleport.Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 2023 ); Effects.SendLocationParticles( - EffectItem.Create(to, m.Map, EffectItem.DefaultDuration), + EffectItem.Create(to, toTeleport.Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 5023 ); - m.PlaySound(0x1FE); + toTeleport.PlaySound(0x1FE); m_Owner.Combatant = toTeleport; } diff --git a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs index d0521bb08..eb98639d4 100644 --- a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs +++ b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles private DrainTimer m_Timer; [Constructible] - public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee) { Harrower = harrower; Body = 129; diff --git a/Projects/UOContent/Mobiles/Special/Paragon.cs b/Projects/UOContent/Mobiles/Special/Paragon.cs index feb1c4b84..be9c805e3 100644 --- a/Projects/UOContent/Mobiles/Special/Paragon.cs +++ b/Projects/UOContent/Mobiles/Special/Paragon.cs @@ -6,15 +6,15 @@ namespace Server.Mobiles { public static class Paragon { - public static double ChestChance = .10; // Chance that a paragon will carry a paragon chest - public static double ChocolateIngredientChance = .20; // Chance that a paragon will drop a chocolatiering ingredient + public const double ChestChance = 0.10; // Chance that a paragon will carry a paragon chest + public const double ChocolateIngredientChance = 0.20; // Chance that a paragon will drop a chocolatiering ingredient public static Map[] Maps = { Map.Ilshenar }; - private static readonly TimeSpan FastRegenRate = TimeSpan.FromSeconds(.5); + private static readonly TimeSpan FastRegenRate = TimeSpan.FromSeconds(0.5); private static readonly TimeSpan CPUSaverRate = TimeSpan.FromSeconds(2); public static Type[] Artifacts = @@ -36,15 +36,15 @@ namespace Server.Mobiles 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 const double HitsBuff = 5.0; + public const double StrBuff = 1.05; + public const double IntBuff = 1.20; + public const double DexBuff = 1.20; + public const double SkillsBuff = 1.20; + public const double SpeedBuff = 1.20; + public const double FameBuff = 1.40; + public const double KarmaBuff = 1.40; + public const int DamageBuff = 5; public static void Convert(BaseCreature bc) { @@ -186,7 +186,7 @@ namespace Server.Mobiles fame = 32000; } - var chance = 1 / Math.Round(20.0 - fame / 3200); + var chance = 1 / Math.Round(20.0 - fame / 3200.0); return chance > Utility.RandomDouble(); } diff --git a/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs b/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs index d7cd010ba..cf9836831 100644 --- a/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs +++ b/Projects/UOContent/Mobiles/Special/ServantOfSemidar.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class ServantOfSemidar : BaseCreature { [Constructible] - public ServantOfSemidar() : base(AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4) => Body = 0x26; + public ServantOfSemidar() : base(AIType.AI_Melee, FightMode.None) => Body = 0x26; public ServantOfSemidar(Serial serial) : base(serial) { @@ -17,7 +17,7 @@ namespace Server.Mobiles public override bool CanBeDamaged() => false; - public override void AddNameProperties(ObjectPropertyList list) + public override void AddNameProperties(IPropertyList list) { base.AddNameProperties(list); diff --git a/Projects/UOContent/Mobiles/Special/Silvani.cs b/Projects/UOContent/Mobiles/Special/Silvani.cs index d23bd0221..c103169e8 100644 --- a/Projects/UOContent/Mobiles/Special/Silvani.cs +++ b/Projects/UOContent/Mobiles/Special/Silvani.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class Silvani : BaseCreature { [Constructible] - public Silvani() : base(AIType.AI_Mage, FightMode.Evil, 18, 1, 0.1, 0.2) + public Silvani() : base(AIType.AI_Mage, FightMode.Evil, 18) { Body = 176; BaseSoundID = 0x467; diff --git a/Projects/UOContent/Mobiles/Special/Wanderer.cs b/Projects/UOContent/Mobiles/Special/Wanderer.cs deleted file mode 100644 index 62bbd553a..000000000 --- a/Projects/UOContent/Mobiles/Special/Wanderer.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; - -namespace Server.Mobiles -{ - public class Wanderer : Mobile - { - private readonly Timer m_Timer; - - [Constructible] - public Wanderer() - { - Name = "Me"; - Body = 0x1; - 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(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - - private class InternalTimer : Timer - { - private readonly Wanderer m_Owner; - private int m_Count; - - 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); - } - } - } -} diff --git a/Projects/UOContent/Mobiles/Townfolk/Actor.cs b/Projects/UOContent/Mobiles/Townfolk/Actor.cs index 60feea63f..7dbe6eec6 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Actor.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Actor.cs @@ -5,12 +5,12 @@ namespace Server.Mobiles public class Actor : BaseCreature { [Constructible] - public Actor() : base(AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4) + public Actor() : base(AIType.AI_Animal, FightMode.None) { InitStats(31, 41, 51); + SetSpeed(0.2, 0.4); SpeechHue = Utility.RandomDyedHue(); - Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) diff --git a/Projects/UOContent/Mobiles/Townfolk/Artist.cs b/Projects/UOContent/Mobiles/Townfolk/Artist.cs index 2945fa96b..de9567ab7 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Artist.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Artist.cs @@ -5,13 +5,12 @@ namespace Server.Mobiles public class Artist : BaseCreature { [Constructible] - public Artist() - : base(AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4) + public Artist() : base(AIType.AI_Animal, FightMode.None) { InitStats(31, 41, 51); - SetSkill(SkillName.Healing, 36, 68); + SetSpeed(0.2, 0.4); SpeechHue = Utility.RandomDyedHue(); Title = "the artist"; Hue = Race.Human.RandomSkinHue(); diff --git a/Projects/UOContent/Mobiles/Townfolk/Banker.cs b/Projects/UOContent/Mobiles/Townfolk/Banker.cs index 25248dd74..636867bdd 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Banker.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Banker.cs @@ -360,10 +360,8 @@ namespace Server.Mobiles } else { - Say( - 1042759, - GetBalance(e.Mobile).ToString("#,0") - ); // Thy current bank balance is ~1_AMOUNT~ gold. + // Thy current bank balance is ~1_AMOUNT~ gold. + Say(1042759, $"{GetBalance(e.Mobile):N0}"); } break; diff --git a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs index 21dbb152b..9cecc6c78 100644 --- a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs +++ b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs @@ -92,14 +92,15 @@ namespace Server.Mobiles private MLQuest m_MLQuest; [Constructible] - public BaseEscortable() - : base(AIType.AI_Melee, FightMode.Aggressor, 22, 1, 0.2, 1.0) + public BaseEscortable() : base(AIType.AI_Melee, FightMode.Aggressor, 22) { InitBody(); InitOutfit(); Fame = 200; Karma = 4000; + + SetSpeed(0.2, 1.0); } public BaseEscortable(Serial serial) diff --git a/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs b/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs index ca5ee2ed6..b15e221d2 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs @@ -5,17 +5,15 @@ namespace Server.Mobiles public class Gypsy : BaseCreature { [Constructible] - public Gypsy() - : base(AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4) + public Gypsy() : base(AIType.AI_Animal, FightMode.None) { InitStats(31, 41, 51); - - SpeechHue = Utility.RandomDyedHue(); - SetSkill(SkillName.Cooking, 65, 88); SetSkill(SkillName.Snooping, 65, 88); SetSkill(SkillName.Stealing, 65, 88); + SetSpeed(0.2, 0.4); + SpeechHue = Utility.RandomDyedHue(); Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) diff --git a/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs b/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs index 2d03f6099..844eb7876 100644 --- a/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs +++ b/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs @@ -5,13 +5,12 @@ namespace Server.Mobiles public class HarborMaster : BaseCreature { [Constructible] - public HarborMaster() - : base(AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4) + public HarborMaster() : base(AIType.AI_Animal, FightMode.None) { InitStats(31, 41, 51); - SetSkill(SkillName.Mining, 36, 68); + SetSpeed(0.2, 0.4); SpeechHue = Utility.RandomDyedHue(); Hue = Race.Human.RandomSkinHue(); Blessed = true; diff --git a/Projects/UOContent/Mobiles/Townfolk/Ninja.cs b/Projects/UOContent/Mobiles/Townfolk/Ninja.cs index 91f4e0667..0026e20d1 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Ninja.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Ninja.cs @@ -5,10 +5,9 @@ namespace Server.Mobiles public class Ninja : BaseCreature { [Constructible] - public Ninja() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Ninja() : base(AIType.AI_Melee, FightMode.Aggressor) { Title = "the ninja"; - InitStats(100, 100, 25); SetSkill(SkillName.Fencing, 64.0, 80.0); @@ -18,8 +17,8 @@ namespace Server.Mobiles SetSkill(SkillName.Tactics, 64.0, 85.0); SetSkill(SkillName.Swords, 64.0, 85.0); + SetSpeed(0.2, 0.4); SpeechHue = Utility.RandomDyedHue(); - Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) diff --git a/Projects/UOContent/Mobiles/Townfolk/Samurai.cs b/Projects/UOContent/Mobiles/Townfolk/Samurai.cs index 86d28bae2..14e9f1072 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Samurai.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Samurai.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles public class Samurai : BaseCreature { [Constructible] - public Samurai() : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4) + public Samurai() : base(AIType.AI_Melee, FightMode.Aggressor) { Title = "the samurai"; @@ -16,8 +16,8 @@ namespace Server.Mobiles SetSkill(SkillName.Parry, 64.0, 80.0); SetSkill(SkillName.Swords, 64.0, 85.0); + SetSpeed(0.2, 0.4); SpeechHue = Utility.RandomDyedHue(); - Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) diff --git a/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs b/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs index 00b6de015..15c126cdb 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs @@ -5,11 +5,11 @@ namespace Server.Mobiles public class Sculptor : BaseCreature { [Constructible] - public Sculptor() - : base(AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4) + public Sculptor() : base(AIType.AI_Animal, FightMode.None) { InitStats(31, 41, 51); + SetSpeed(0.2, 0.4); SpeechHue = Utility.RandomDyedHue(); Title = "the sculptor"; Hue = Race.Human.RandomSkinHue(); diff --git a/Projects/UOContent/Mobiles/Townfolk/SeekerOfAdventure.cs b/Projects/UOContent/Mobiles/Townfolk/SeekerOfAdventure.cs index 59bd0b575..fd14c5a38 100644 --- a/Projects/UOContent/Mobiles/Townfolk/SeekerOfAdventure.cs +++ b/Projects/UOContent/Mobiles/Townfolk/SeekerOfAdventure.cs @@ -26,15 +26,7 @@ namespace Server.Mobiles public override bool ClickTitle => false; // Do not display 'the seeker of adventure' when single-clicking - public override string[] GetPossibleDestinations() - { - if (Core.ML) - { - return m_MLDestinations; - } - - return m_Dungeons; - } + public override string[] GetPossibleDestinations() => Core.ML ? m_MLDestinations : m_Dungeons; private static int GetRandomHue() { diff --git a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs index 34e7814ed..50e7a9b6b 100644 --- a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs +++ b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs @@ -28,14 +28,9 @@ namespace Server.Mobiles public TownCrierEntry GetRandomEntry() { - if (Entries == null || Entries.Count == 0) + for (var i = (Entries?.Count ?? 0) - 1; i >= 0; --i) { - return null; - } - - for (var i = Entries.Count - 1; Entries != null && i >= 0; --i) - { - if (i >= Entries.Count) + if (i >= Entries!.Count) { continue; } @@ -366,14 +361,10 @@ namespace Server.Mobiles public TownCrierEntry GetRandomEntry() { - if (Entries == null || Entries.Count == 0) + var count = Entries?.Count ?? 0; + for (var i = count - 1; i >= 0; --i) { - return GlobalTownCrierEntryList.Instance.GetRandomEntry(); - } - - for (var i = Entries.Count - 1; Entries != null && i >= 0; --i) - { - if (i >= Entries.Count) + if (i >= Entries!.Count) { continue; } @@ -387,8 +378,12 @@ namespace Server.Mobiles } var entry = GlobalTownCrierEntryList.Instance.GetRandomEntry(); + if (count > 0 && entry != null && Utility.RandomBool()) + { + return entry; + } - return entry ?? (Entries?.Count > 0 && Utility.RandomBool() ? Entries.RandomElement() : null); + return Entries.RandomElement(); } public TownCrierEntry AddEntry(string[] lines, TimeSpan duration) @@ -420,7 +415,7 @@ namespace Server.Mobiles if (Entries == null && GlobalTownCrierEntryList.Instance.IsEmpty) { - _autoShoutTimer.Stop(); + _autoShoutTimer?.Stop(); _autoShoutTimer = null; } } diff --git a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs index 8cb8eb5c2..f9e05c63a 100644 --- a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Server.Collections; using Server.ContextMenus; using Server.Engines.BulkOrders; using Server.Factions; @@ -52,14 +53,13 @@ namespace Server.Mobiles } } - public BaseVendor(string title = null) - : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) + public BaseVendor(string title = null) : base(AIType.AI_Vendor, FightMode.None, 2) { LoadSBInfo(); - Title = title; InitBody(); InitOutfit(); + SetSpeed(0.5, 2.0); // these packs MUST exist, or the client will crash when the packets are sent Container pack = new Backpack { Layer = Layer.ShopBuy, Movable = false, Visible = false }; @@ -877,7 +877,7 @@ namespace Server.Mobiles var list = new List(buyInfo.Length); var cont = BuyPack; - var opls = EnableVendorBuyOPL ? new List(buyInfo.Length) : null; + using var opls = PooledRefQueue.Create(EnableVendorBuyOPL ? buyInfo.Length : 0); for (var idx = 0; idx < buyInfo.Length; idx++) { @@ -907,9 +907,9 @@ namespace Server.Mobiles ) ); - if (disp is IPropertyListObject obj) + if (disp is IObjectPropertyListEntity obj) { - opls?.Add(obj.PropertyList); + opls.Enqueue(obj.PropertyList); } } @@ -950,7 +950,7 @@ namespace Server.Mobiles if (name != null && list.Count < 250) { list.Add(new BuyItemState(name, cont.Serial, item.Serial, price, item.Amount, item.ItemID, item.Hue)); - opls?.Add(item.PropertyList); + opls.Enqueue(item.PropertyList); } } @@ -969,7 +969,7 @@ namespace Server.Mobiles var ns = from.NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -979,12 +979,9 @@ namespace Server.Mobiles from.NetState.SendDisplayBuyList(Serial); from.NetState.SendMobileStatus(from); // make sure their gold amount is sent - if (opls != null) + while (opls.Count > 0) { - for (var i = 0; i < opls.Count; ++i) - { - from.NetState?.Send(opls[i].Buffer); - } + from.NetState?.Send(opls.Dequeue().Buffer); } SayTo(from, 500186); // Greetings. Have a look around. diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs index 11c3e340d..6bb78d9c5 100644 --- a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs @@ -155,7 +155,7 @@ namespace Server.Mobiles } } - public override void GetChildNameProperties(ObjectPropertyList list, Item item) + public override void GetChildNameProperties(IPropertyList list, Item item) { base.GetChildNameProperties(list, item); @@ -182,7 +182,7 @@ namespace Server.Mobiles } } - public override void GetChildProperties(ObjectPropertyList list, Item item) + public override void GetChildProperties(IPropertyList list, Item item) { base.GetChildProperties(list, item); @@ -735,7 +735,7 @@ namespace Server.Mobiles public override bool IsSnoop(Mobile from) => false; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -1652,7 +1652,7 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public PlayerVendor Vendor { get; private set; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs index 10ede3f19..d2192f64e 100644 --- a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs @@ -1,390 +1,359 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.ContextMenus; using Server.Gumps; using Server.Misc; using Server.Multis; using Server.Prompts; -namespace Server.Mobiles -{ - public class VendorRentalDuration - { - public static readonly VendorRentalDuration[] Instances = - { - new(TimeSpan.FromDays(7.0), 1062361), // 1 Week - new(TimeSpan.FromDays(14.0), 1062362), // 2 Weeks - new(TimeSpan.FromDays(21.0), 1062363), // 3 Weeks - new(TimeSpan.FromDays(28.0), 1062364) // 1 Month - }; +namespace Server.Mobiles; - private VendorRentalDuration(TimeSpan duration, int name) +public class VendorRentalDuration +{ + public static readonly VendorRentalDuration[] Instances = + { + new(TimeSpan.FromDays(7.0), 1062361), // 1 Week + new(TimeSpan.FromDays(14.0), 1062362), // 2 Weeks + new(TimeSpan.FromDays(21.0), 1062363), // 3 Weeks + new(TimeSpan.FromDays(28.0), 1062364) // 1 Month + }; + + private VendorRentalDuration(TimeSpan duration, int name) + { + Duration = duration; + Name = name; + } + + public TimeSpan Duration { get; } + + public int Name { get; } + + public int ID + { + get { - Duration = duration; - Name = name; + for (var i = 0; i < Instances.Length; i++) + { + if (Instances[i] == this) + { + return i; + } + } + + return 0; + } + } +} + +[SerializationGenerator(0)] +public partial class RentedVendor : PlayerVendor +{ + private Timer _rentalExpireTimer; + + public RentedVendor( + Mobile owner, BaseHouse house, VendorRentalDuration duration, int rentalPrice, + bool landlordRenew, int rentalGold + ) : base(owner, house) + { + RentalDuration = duration; + RentalPrice = RenewalPrice = rentalPrice; + LandlordRenew = landlordRenew; + RenterRenew = false; + + RentalGold = rentalGold; + + RentalExpireTime = Core.Now + duration.Duration; + _rentalExpireTimer = new RentalExpireTimer(this, duration.Duration); + _rentalExpireTimer.Start(); + } + + public VendorRentalDuration RentalDuration { get; private set; } + + [SerializableField(0, getter: "private", setter: "private")] + private int _rentalDurationId; // TODO: Replace this with something more robust + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _rentalPrice; + + [SerializableField(2)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _landlordRenew; + + [SerializableField(3)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private bool _renterRenew; + + [SerializableField(4)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _renewalPrice; + + [SerializableField(5)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private int _rentalGold; + + [DeltaDateTime] + [SerializableField(6)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private DateTime _rentalExpireTime; + + [CommandProperty(AccessLevel.GameMaster)] + public Mobile Landlord => House?.Owner; + + [CommandProperty(AccessLevel.GameMaster)] + public bool Renew => LandlordRenew && RenterRenew && House != null && House.DecayType != DecayType.Condemned; + + public override bool IsOwner(Mobile m) => m == Owner || m.AccessLevel >= AccessLevel.GameMaster || + Core.ML && AccountHandler.CheckAccount(m, Owner); + + public bool IsLandlord(Mobile m) => House?.IsOwner(m) == true; + + public void ComputeRentalExpireDelay(out int days, out int hours) + { + var delay = RentalExpireTime - Core.Now; + + if (delay <= TimeSpan.Zero) + { + days = 0; + hours = 0; + } + else + { + days = delay.Days; + hours = delay.Hours; + } + } + + public void SendRentalExpireMessage(Mobile to) + { + ComputeRentalExpireDelay(out var days, out var hours); + + to.SendLocalizedMessage( + 1062464, + $"{days}\t{hours}" + ); // The rental contract on this vendor will expire in ~1_DAY~ day(s) and ~2_HOUR~ hour(s). + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + _rentalExpireTimer.Stop(); + } + + public override void Destroy(bool toBackpack) + { + if (RentalGold > 0 && House?.IsAosRules == true) + { + House.MovingCrate ??= new MovingCrate(House); + + Banker.Deposit(House.MovingCrate, RentalGold); + RentalGold = 0; } - public TimeSpan Duration { get; } + base.Destroy(toBackpack); + } - public int Name { get; } - - public int ID + public override void GetContextMenuEntries(Mobile from, List list) + { + if (from.Alive) { - get + if (IsOwner(from)) { - for (var i = 0; i < Instances.Length; i++) + list.Add(new ContractOptionsEntry(this)); + } + else if (IsLandlord(from)) + { + if (RentalGold > 0) { - if (Instances[i] == this) - { - return i; - } + list.Add(new CollectRentEntry(this)); } - return 0; + list.Add(new TerminateContractEntry(this)); + list.Add(new ContractOptionsEntry(this)); + } + } + + base.GetContextMenuEntries(from, list); + } + + [AfterDeserialization] + private void AfterDeserialization() + { + var index = Math.Clamp(_rentalDurationId, 0, VendorRentalDuration.Instances.Length - 1); + RentalDuration = VendorRentalDuration.Instances[index]; + + var delay = _rentalExpireTime - Core.Now; + _rentalExpireTimer = new RentalExpireTimer(this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero).Start(); + } + + private class ContractOptionsEntry : ContextMenuEntry + { + private readonly RentedVendor m_Vendor; + + public ContractOptionsEntry(RentedVendor vendor) : base(6209) => m_Vendor = vendor; + + public override void OnClick() + { + var from = Owner.From; + + if (m_Vendor.Deleted || !from.CheckAlive()) + { + return; + } + + if (m_Vendor.IsOwner(from)) + { + from.CloseGump(); + from.SendGump(new RenterVendorRentalGump(m_Vendor)); + + m_Vendor.SendRentalExpireMessage(from); + } + else if (m_Vendor.IsLandlord(from)) + { + from.CloseGump(); + from.SendGump(new LandlordVendorRentalGump(m_Vendor)); + + m_Vendor.SendRentalExpireMessage(from); } } } - public class RentedVendor : PlayerVendor + private class CollectRentEntry : ContextMenuEntry { - private Timer m_RentalExpireTimer; + private readonly RentedVendor m_Vendor; - public RentedVendor( - Mobile owner, BaseHouse house, VendorRentalDuration duration, int rentalPrice, - bool landlordRenew, int rentalGold - ) : base(owner, house) + public CollectRentEntry(RentedVendor vendor) : base(6212) => m_Vendor = vendor; + + public override void OnClick() { - RentalDuration = duration; - RentalPrice = RenewalPrice = rentalPrice; - LandlordRenew = landlordRenew; - RenterRenew = false; + var from = Owner.From; - RentalGold = rentalGold; - - RentalExpireTime = Core.Now + duration.Duration; - m_RentalExpireTimer = new RentalExpireTimer(this, duration.Duration); - m_RentalExpireTimer.Start(); - } - - public RentedVendor(Serial serial) : base(serial) - { - } - - public VendorRentalDuration RentalDuration { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int RentalPrice { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool LandlordRenew { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool RenterRenew { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool Renew => LandlordRenew && RenterRenew && House != null && House.DecayType != DecayType.Condemned; - - [CommandProperty(AccessLevel.GameMaster)] - public int RenewalPrice { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public int RentalGold { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public DateTime RentalExpireTime { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Landlord => House?.Owner; - - public override bool IsOwner(Mobile m) => m == Owner || m.AccessLevel >= AccessLevel.GameMaster || - Core.ML && AccountHandler.CheckAccount(m, Owner); - - public bool IsLandlord(Mobile m) => House?.IsOwner(m) == true; - - public void ComputeRentalExpireDelay(out int days, out int hours) - { - var delay = RentalExpireTime - Core.Now; - - if (delay <= TimeSpan.Zero) + if (m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord(from)) { - days = 0; - hours = 0; - } - else - { - days = delay.Days; - hours = delay.Hours; - } - } - - public void SendRentalExpireMessage(Mobile to) - { - ComputeRentalExpireDelay(out var days, out var hours); - - to.SendLocalizedMessage( - 1062464, - $"{days}\t{hours}" - ); // 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?.IsAosRules == true) - { - House.MovingCrate ??= new MovingCrate(House); - - Banker.Deposit(House.MovingCrate, RentalGold); - RentalGold = 0; + return; } - base.Destroy(toBackpack); - } - - public override void GetContextMenuEntries(Mobile from, List list) - { - if (from.Alive) + if (m_Vendor.RentalGold > 0) { - if (IsOwner(from)) + var depositedGold = Banker.DepositUpTo(from, m_Vendor.RentalGold); + m_Vendor.RentalGold -= depositedGold; + + if (depositedGold > 0) { - 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); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - - writer.WriteEncodedInt(RentalDuration.ID); - - writer.Write(RentalPrice); - writer.Write(LandlordRenew); - writer.Write(RenterRenew); - writer.Write(RenewalPrice); - - writer.Write(RentalGold); - - writer.WriteDeltaTime(RentalExpireTime); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - var durationID = reader.ReadEncodedInt(); - if (durationID < VendorRentalDuration.Instances.Length) - { - RentalDuration = VendorRentalDuration.Instances[durationID]; - } - else - { - RentalDuration = VendorRentalDuration.Instances[0]; - } - - RentalPrice = reader.ReadInt(); - LandlordRenew = reader.ReadBool(); - RenterRenew = reader.ReadBool(); - RenewalPrice = reader.ReadInt(); - - RentalGold = reader.ReadInt(); - - RentalExpireTime = reader.ReadDeltaTime(); - - var delay = RentalExpireTime - Core.Now; - m_RentalExpireTimer = new RentalExpireTimer(this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero); - m_RentalExpireTimer.Start(); - } - - private class ContractOptionsEntry : ContextMenuEntry - { - private readonly RentedVendor m_Vendor; - - public ContractOptionsEntry(RentedVendor vendor) : base(6209) => m_Vendor = vendor; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Vendor.Deleted || !from.CheckAlive()) - { - return; - } - - if (m_Vendor.IsOwner(from)) - { - from.CloseGump(); - from.SendGump(new RenterVendorRentalGump(m_Vendor)); - - m_Vendor.SendRentalExpireMessage(from); - } - else if (m_Vendor.IsLandlord(from)) - { - from.CloseGump(); - from.SendGump(new LandlordVendorRentalGump(m_Vendor)); - - m_Vendor.SendRentalExpireMessage(from); - } - } - } - - private class CollectRentEntry : ContextMenuEntry - { - private readonly RentedVendor m_Vendor; - - public CollectRentEntry(RentedVendor vendor) : base(6212) => m_Vendor = vendor; - - public override void OnClick() - { - var from = Owner.From; - - if (m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord(from)) - { - return; + from.SendLocalizedMessage( + 1060397, + depositedGold.ToString() + ); // ~1_AMOUNT~ gold has been deposited into your bank box. } if (m_Vendor.RentalGold > 0) { - var 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 readonly RentedVendor m_Vendor; - - public TerminateContractEntry(RentedVendor vendor) : base(6218) => m_Vendor = vendor; - - public override void OnClick() - { - var 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 readonly 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(); - - if (!int.TryParse(text, out var amount)) - { - amount = -1; - } - - var 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(); - owner.SendGump(new VendorRentalRefundGump(m_Vendor, from, amount)); - } - } - } - - private class RentalExpireTimer : Timer - { - private readonly RentedVendor m_Vendor; - - public RentalExpireTimer(RentedVendor vendor, TimeSpan delay) : base(delay, vendor.RentalDuration.Duration) - { - m_Vendor = vendor; - } - - protected override void OnTick() - { - var 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.RentalExpireTime = Core.Now + m_Vendor.RentalDuration.Duration; - } - else - { - m_Vendor.Destroy(false); + from.SendLocalizedMessage(500390); // Your bank box is full. } } } } + + private class TerminateContractEntry : ContextMenuEntry + { + private readonly RentedVendor m_Vendor; + + public TerminateContractEntry(RentedVendor vendor) : base(6218) => m_Vendor = vendor; + + public override void OnClick() + { + var 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 readonly 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(); + + if (!int.TryParse(text, out var amount)) + { + amount = -1; + } + + var 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(); + owner.SendGump(new VendorRentalRefundGump(m_Vendor, from, amount)); + } + } + } + + private class RentalExpireTimer : Timer + { + private readonly RentedVendor m_Vendor; + + public RentalExpireTimer(RentedVendor vendor, TimeSpan delay) : base(delay, vendor.RentalDuration.Duration) + { + m_Vendor = vendor; + } + + protected override void OnTick() + { + var 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.RentalExpireTime = Core.Now + m_Vendor.RentalDuration.Duration; + } + else + { + m_Vendor.Destroy(false); + } + } + } } diff --git a/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs b/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs index eabf02198..3c00b4b6e 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs @@ -176,7 +176,7 @@ namespace Server.Multis { if (ip is Item item) { - ip = item.GetWorldTop(); + ip = from; } var p = new Point3D(ip); diff --git a/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs b/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs index f4c681837..f2b18daa5 100644 --- a/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs @@ -102,7 +102,7 @@ namespace Server.Multis } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (m_ShipName != null) { @@ -194,29 +194,31 @@ namespace Server.Multis protected override void OnTarget(Mobile from, object o) { - if (o is IPoint3D ip) + if (o is not IPoint3D ip) { - if (ip is Item item) - { - ip = item.GetWorldTop(); - } + return; + } - var p = new Point3D(ip); + Point3D p = ip switch + { + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; - var region = Region.Find(p, from.Map); + var region = Region.Find(p, from.Map); - if (region.IsPartOf()) - { - from.SendLocalizedMessage(502488); // You can not place a ship inside a dungeon. - } - else if (region.IsPartOf() || region.IsPartOf()) - { - from.SendLocalizedMessage(1042549); // A boat may not be placed in this area. - } - else - { - m_Model.OnPlacement(from, p); - } + if (region.IsPartOf()) + { + from.SendLocalizedMessage(502488); // You can not place a ship inside a dungeon. + } + else if (region.IsPartOf() || region.IsPartOf()) + { + from.SendLocalizedMessage(1042549); // A boat may not be placed in this area. + } + else + { + m_Model.OnPlacement(from, p); } } } diff --git a/Projects/UOContent/Multis/Boats/TillerMan.cs b/Projects/UOContent/Multis/Boats/TillerMan.cs index be04517b2..cef485f9c 100644 --- a/Projects/UOContent/Multis/Boats/TillerMan.cs +++ b/Projects/UOContent/Multis/Boats/TillerMan.cs @@ -29,7 +29,7 @@ namespace Server.Items }; } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -46,7 +46,7 @@ namespace Server.Items PublicOverheadMessage(MessageType.Regular, 0x3B2, number, args); } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { if (m_Boat?.ShipName != null) { diff --git a/Projects/UOContent/Multis/Deeds.cs b/Projects/UOContent/Multis/Deeds.cs index 7b9f41356..b537cec5e 100644 --- a/Projects/UOContent/Multis/Deeds.cs +++ b/Projects/UOContent/Multis/Deeds.cs @@ -11,41 +11,41 @@ namespace Server.Multis.Deeds protected override void OnTarget(Mobile from, object o) { - if (o is IPoint3D ip) + if (o is not IPoint3D ip) { - if (ip is Item item) - { - ip = item.GetWorldTop(); - } + return; + } - var p = new Point3D(ip); + Point3D p = ip switch + { + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; - var reg = Region.Find(new Point3D(p), from.Map); + var reg = Region.Find(p, from.Map); - if (from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing(from, p)) - { - m_Deed.OnPlacement(from, p); - } - else if (reg.IsPartOf()) - { - from.SendLocalizedMessage( - 501270 - ); // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. - } - else if (reg.IsPartOf() || reg.IsPartOf()) - { - 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 if (reg.IsPartOf()) - { - from.SendLocalizedMessage(1150493); // You must have a deed for this plot of land in order to build here. - } - else - { - from.SendLocalizedMessage(501265); // Housing can not be created in this area. - } + if (from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing(from, p)) + { + m_Deed.OnPlacement(from, p); + } + else if (reg.IsPartOf()) + { + // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. + from.SendLocalizedMessage(501270); + } + else if (reg.IsPartOf() || reg.IsPartOf()) + { + // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + from.SendLocalizedMessage(1043287); + } + else if (reg.IsPartOf()) + { + from.SendLocalizedMessage(1150493); // You must have a deed for this plot of land in order to build here. + } + else + { + from.SendLocalizedMessage(501265); // Housing can not be created in this area. } } } @@ -184,9 +184,8 @@ namespace Server.Multis.Deeds 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. + // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + from.SendLocalizedMessage(1043287); break; } case HousePlacementResult.NoSurface: @@ -203,16 +202,14 @@ namespace Server.Multis.Deeds } case HousePlacementResult.BadRegionTemp: { - from.SendLocalizedMessage( - 501270 - ); // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. + // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. + from.SendLocalizedMessage(501270); break; } case HousePlacementResult.BadRegionRaffle: { - from.SendLocalizedMessage( - 1150493 - ); // You must have a deed for this plot of land in order to build here. + // You must have a deed for this plot of land in order to build here. + from.SendLocalizedMessage(1150493); break; } } diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index 66cef968e..b11216e86 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Server.Accounting; +using Server.Collections; using Server.ContextMenus; using Server.Ethics; using Server.Guilds; @@ -3227,16 +3228,21 @@ namespace Server.Multis private void FixLockdowns_Sandbox() { - var conts = LockDowns?.Where(item => item is Container).ToList(); - - if (conts == null) + if (LockDowns?.Count > 0) { - return; - } + using var queue = PooledRefQueue.Create(); + foreach (var item in LockDowns) + { + if (item is Container) + { + queue.Enqueue(item); + } + } - foreach (var cont in conts) - { - SetLockdown(cont, true, true); + while (queue.Count > 0) + { + SetLockdown(queue.Dequeue(), true, true); + } } } @@ -3694,7 +3700,7 @@ namespace Server.Multis public override string DefaultName => "a house transfer contract"; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -3715,12 +3721,17 @@ namespace Server.Multis ref ySouth ); - var location = - valid ? $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}° {xMins}'{(xEast ? "E" : "W")}" : "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~ + if (valid) + { + // Location: ~1_val~ + list.Add(1061114, $"{yLat}° {yMins}'{(ySouth ? "S" : "N")}, {xLong}° {xMins}'{(xEast ? "E" : "W")}"); + } + else + { + list.Add(1061114, "unknown"); // Location: ~1_val~ + } } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Multis/Houses/ContestHouses.cs b/Projects/UOContent/Multis/Houses/ContestHouses.cs index 18df0537e..06382205e 100644 --- a/Projects/UOContent/Multis/Houses/ContestHouses.cs +++ b/Projects/UOContent/Multis/Houses/ContestHouses.cs @@ -215,11 +215,11 @@ namespace Server.Multis { if (value.Count > 2) { - logger.Warning("More than 2 teleporters detected for {0:X}!", key); + logger.Warning("More than 2 teleporters detected for {ItemId:X}!", key); } else if (value.Count <= 1) { - logger.Warning("1 or less teleporters detected for {0:X}!", key); + logger.Warning("1 or less teleporters detected for {ItemId:X}!", key); continue; } diff --git a/Projects/UOContent/Multis/Houses/HouseFoundation.cs b/Projects/UOContent/Multis/Houses/HouseFoundation.cs index bd63938ec..f8b114e5c 100644 --- a/Projects/UOContent/Multis/Houses/HouseFoundation.cs +++ b/Projects/UOContent/Multis/Houses/HouseFoundation.cs @@ -878,9 +878,9 @@ namespace Server.Multis DesignState.SendDetailedInfoTo(ns); } - public override void SendInfoTo(NetState ns, ReadOnlySpan world = default, Span opl = default) + public override void SendInfoTo(NetState ns, ReadOnlySpan world = default) { - base.SendInfoTo(ns, world, opl); + base.SendInfoTo(ns, world); var stateToSend = DesignContext.Find(ns?.Mobile)?.Foundation == this ? DesignState : CurrentState; stateToSend.SendGeneralInfoTo(ns); @@ -972,25 +972,25 @@ namespace Server.Multis public bool IsHiddenToCustomizer(Item item) => item == Signpost || item == SignHanger || item == Sign || IsFixture(item); - public static void Initialize() + public static unsafe void Initialize() { - IncomingExtendedCommandPackets.RegisterExtended(0x1E, true, QueryDesignDetails); + IncomingExtendedCommandPackets.RegisterExtended(0x1E, true, &QueryDesignDetails); - IncomingPackets.RegisterEncoded(0x02, true, Designer_Backup); - IncomingPackets.RegisterEncoded(0x03, true, Designer_Restore); - IncomingPackets.RegisterEncoded(0x04, true, Designer_Commit); - IncomingPackets.RegisterEncoded(0x05, true, Designer_Delete); - IncomingPackets.RegisterEncoded(0x06, true, Designer_Build); - IncomingPackets.RegisterEncoded(0x0C, true, Designer_Close); - IncomingPackets.RegisterEncoded(0x0D, true, Designer_Stairs); - IncomingPackets.RegisterEncoded(0x0E, true, Designer_Sync); - IncomingPackets.RegisterEncoded(0x10, true, Designer_Clear); - IncomingPackets.RegisterEncoded(0x12, true, Designer_Level); + IncomingPackets.RegisterEncoded(0x02, true, &Designer_Backup); + IncomingPackets.RegisterEncoded(0x03, true, &Designer_Restore); + IncomingPackets.RegisterEncoded(0x04, true, &Designer_Commit); + IncomingPackets.RegisterEncoded(0x05, true, &Designer_Delete); + IncomingPackets.RegisterEncoded(0x06, true, &Designer_Build); + IncomingPackets.RegisterEncoded(0x0C, true, &Designer_Close); + IncomingPackets.RegisterEncoded(0x0D, true, &Designer_Stairs); + IncomingPackets.RegisterEncoded(0x0E, true, &Designer_Sync); + IncomingPackets.RegisterEncoded(0x10, true, &Designer_Clear); + IncomingPackets.RegisterEncoded(0x12, true, &Designer_Level); - IncomingPackets.RegisterEncoded(0x13, true, Designer_Roof); // Samurai Empire roof - IncomingPackets.RegisterEncoded(0x14, true, Designer_RoofDelete); // Samurai Empire roof + IncomingPackets.RegisterEncoded(0x13, true, &Designer_Roof); // Samurai Empire roof + IncomingPackets.RegisterEncoded(0x14, true, &Designer_RoofDelete); // Samurai Empire roof - IncomingPackets.RegisterEncoded(0x1A, true, Designer_Revert); + IncomingPackets.RegisterEncoded(0x1A, true, &Designer_Revert); EventSink.Speech += EventSink_Speech; } @@ -1766,7 +1766,7 @@ namespace Server.Multis context.Foundation.SendInfoTo(state); } - public static void QueryDesignDetails(NetState state, CircularBufferReader reader, ref int packetLength) + public static void QueryDesignDetails(NetState state, CircularBufferReader reader, int packetLength) { var from = state.Mobile; diff --git a/Projects/UOContent/Multis/Houses/HousePackets.cs b/Projects/UOContent/Multis/Houses/HousePackets.cs index 98e8a675e..f8c41a47f 100644 --- a/Projects/UOContent/Multis/Houses/HousePackets.cs +++ b/Projects/UOContent/Multis/Houses/HousePackets.cs @@ -29,7 +29,7 @@ namespace Server.Multis public static void SendBeginHouseCustomization(this NetState ns, Serial house) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -50,7 +50,7 @@ namespace Server.Multis public static void SendEndHouseCustomization(this NetState ns, Serial house) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -71,7 +71,7 @@ namespace Server.Multis public static void SendDesignStateGeneral(this NetState ns, Serial house, int revision) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -251,7 +251,7 @@ namespace Server.Multis if (ce != ZlibError.Okay) { - logger.Warning("ZLib error: {0} (#{1})", ce, (int)ce); + logger.Warning("ZLib error: {Error} (#{ErrorCode})", ce, (int)ce); length = 0; size = 0; } diff --git a/Projects/UOContent/Multis/Houses/HousePlacementTool.cs b/Projects/UOContent/Multis/Houses/HousePlacementTool.cs index 56817bf22..cc8d5aa31 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacementTool.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacementTool.cs @@ -103,8 +103,9 @@ namespace Server.Items { case 1: // Classic Houses { - // TODO: Add flag to use ClassicHouses or EJ - m_From.SendGump(new HousePlacementListGump(m_From, HousePlacementEntry.HousesEJ)); + var entry = Core.EJ ? HousePlacementEntry.HousesEJ : HousePlacementEntry.ClassicHouses; + m_From.SendGump(new HousePlacementListGump(m_From, entry)); + break; } case 2: // 2-Story Customizable Houses @@ -249,46 +250,46 @@ namespace Server.Items protected override void OnTarget(Mobile from, object o) { + if (o is not IPoint3D ip) + { + return; + } + if (!from.CheckAlive() || from.Backpack?.FindItemByType() == null) { return; } - if (o is IPoint3D ip) + Point3D p = ip switch { - if (ip is Item item) - { - ip = item.GetWorldTop(); - } + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; - var p = new Point3D(ip); + var reg = Region.Find(p, from.Map); - var 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()) - { - from.SendLocalizedMessage( - 501270 - ); // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. - } - else if (reg.IsPartOf() || reg.IsPartOf()) - { - 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 if (reg.IsPartOf()) - { - from.SendLocalizedMessage(1150493); // You must have a deed for this plot of land in order to build here. - } - else - { - from.SendLocalizedMessage(501265); // Housing can not be created in this area. - } + if (from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing(from, p)) + { + m_Placed = m_Entry.OnPlacement(from, p); + } + else if (reg.IsPartOf()) + { + // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. + from.SendLocalizedMessage(501270); + } + else if (reg.IsPartOf() || reg.IsPartOf()) + { + // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + from.SendLocalizedMessage(1043287); + } + else if (reg.IsPartOf()) + { + from.SendLocalizedMessage(1150493); // You must have a deed for this plot of land in order to build here. + } + else + { + from.SendLocalizedMessage(501265); // Housing can not be created in this area. } } @@ -318,7 +319,7 @@ namespace Server.Items { m_Table = new Dictionary(); - FillTable(ClassicHouses); + FillTable(Core.EJ ? HousesEJ : ClassicHouses); FillTable(TwoStoryFoundations); FillTable(ThreeStoryFoundations); } diff --git a/Projects/UOContent/Multis/Houses/HouseSign.cs b/Projects/UOContent/Multis/Houses/HouseSign.cs index 9341915e4..2df270fb8 100644 --- a/Projects/UOContent/Multis/Houses/HouseSign.cs +++ b/Projects/UOContent/Multis/Houses/HouseSign.cs @@ -52,12 +52,12 @@ namespace Server.Multis } } - public override void AddNameProperty(ObjectPropertyList list) + public override void AddNameProperty(IPropertyList list) { list.Add(1061638); // A House Sign } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); @@ -83,7 +83,7 @@ namespace Server.Multis level = DecayLevel.IDOC; } - list.Add(1062028, $"#{1043009 + (int)level}"); // Condition: This structure is ... + list.AddLocalized(1062028, 1043009 + (int)level); // Condition: This structure is ... } } } diff --git a/Projects/UOContent/Multis/Houses/HouseTeleporter.cs b/Projects/UOContent/Multis/Houses/HouseTeleporter.cs index e1ba1320d..5e7826120 100644 --- a/Projects/UOContent/Multis/Houses/HouseTeleporter.cs +++ b/Projects/UOContent/Multis/Houses/HouseTeleporter.cs @@ -155,9 +155,7 @@ namespace Server.Items return; } - var m = m_Mobile; - - if (m.Location != m_Teleporter.Location || m.Map != m_Teleporter.Map) + if (m_Mobile.Location != m_Teleporter.Location || m_Mobile.Map != m_Teleporter.Map) { return; } @@ -165,11 +163,11 @@ namespace Server.Items var p = target.GetWorldTop(); var map = target.Map; - BaseCreature.TeleportPets(m, p, map); + BaseCreature.TeleportPets(m_Mobile, p, map); - m.MoveToWorld(p, map); + m_Mobile.MoveToWorld(p, map); - if (m.Hidden && m.AccessLevel != AccessLevel.Player) + if (m_Mobile.Hidden && m_Mobile.AccessLevel != AccessLevel.Player) { return; } diff --git a/Projects/UOContent/Network/ConnectUO.cs b/Projects/UOContent/Network/ConnectUO.cs deleted file mode 100644 index 44749b378..000000000 --- a/Projects/UOContent/Network/ConnectUO.cs +++ /dev/null @@ -1,122 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ConnectUO.cs * - * * - * 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 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers; -using Server.Accounting; -using Server.Logging; -using Server.Text; - -namespace Server.Network -{ - public static class ConnectUO - { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ConnectUO)); - - public enum ConnectUOServerType - { - RunUO, - ServUO, - UOX3, - POL, - Sphere, - ModernUO - } - - public const byte ConnectUOProtocolVersion = 0; - private const int _connectUOTokenLength = 32; - private const ConnectUOServerType _serverType = ConnectUOServerType.ModernUO; - private static byte[] _token; - - public static void Configure() - { - var enabled = ServerConfiguration.GetOrUpdateSetting("connectuo.enabled", true); - var token = ServerConfiguration.GetOrUpdateSetting("connectuo.token", ""); - - if (enabled) - { - try - { - if (!string.IsNullOrWhiteSpace(token)) - { - if (token.Length != _connectUOTokenLength * 2) - { - throw new Exception("Invalid length for ConnectUO token"); - } - - _token = GC.AllocateUninitializedArray(_connectUOTokenLength); - token.ToUpperInvariant().GetBytes(_token); - } - } - catch - { - logger.Warning("ConnectUO token could not be parsed. Make sure modernuo.json is properly configured"); - _token = null; - } - - FreeshardProtocol.Register(0xC0, false, PollInfo); - } - } - - public static void PollInfo(NetState ns, CircularBufferReader reader, ref int packetLength) - { - var version = reader.ReadByte(); - - if (_token != null) - { - unsafe { - byte* tok = stackalloc byte[_token.Length]; - var span = new Span(tok, _token.Length); - reader.Read(span); - - if (!span.SequenceEqual(_token)) - { - ns.Disconnect("Invalid token sent for ConnectUO"); - return; - } - } - } - - ns.LogInfo($"ConnectUO (v{version}) is requesting stats."); - if (version > ConnectUOProtocolVersion) - { - Utility.PushColor(ConsoleColor.Yellow); - ns.LogInfo("Warning! ConnectUO (v{version}) is newer than what is supported."); - Utility.PopColor(); - } - - ns.SendServerPollInfo(); - } - - public static void SendServerPollInfo(this NetState ns) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[15]); - writer.Write((byte)0xC0); // Packet ID - writer.Write((ushort)17); // Length - writer.Write(ConnectUOProtocolVersion); // Version - writer.Write((byte)_serverType); - writer.Write((int)(Core.TickCount / 1000)); - writer.Write(Accounts.Count); // Shame if you modify this! - writer.Write(TcpServer.Instances.Count - 1); // Shame if you modify this! - - ns.Send(writer.Span); - } - } -} diff --git a/Projects/UOContent/Network/FastwalkDetection.cs b/Projects/UOContent/Network/FastwalkDetection.cs new file mode 100644 index 000000000..4328f0f12 --- /dev/null +++ b/Projects/UOContent/Network/FastwalkDetection.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using Server.Commands.Generic; + +namespace Server.Network; + +public static class FastwalkDetection +{ + private static readonly HashSet _debugFastwalk = new(); + + public static void Initialize() + { + TargetCommands.Register(new DebugFastwalk()); + EventSink.FastWalk += OnFastwalk; + } + + private static void OnFastwalk(FastWalkEventArgs e) + { + var from = e.NetState.Mobile; + if (from == null) + { + return; + } + + if (!_debugFastwalk.Contains(from)) + { + return; + } + + from.PublicOverheadMessage(MessageType.Emote, from.EmoteHue, false, "Fastwalk Detected", accessLevel: AccessLevel.GameMaster); + } + + public class DebugFastwalk : BaseCommand + { + public DebugFastwalk() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllMobiles; + Commands = new[] { "DebugFastwalk" }; + ObjectTypes = ObjectTypes.Mobiles; + ListOptimized = true; + Usage = "DebugFastwalk "; + Description = "Enables fastwalk debug messages"; + } + + public override void ExecuteList(CommandEventArgs e, List list) + { + var on = e.Arguments.Length == 0 || e.GetBoolean(0); + + foreach (var o in list) + { + if (o is not Mobile m) + { + continue; + } + + if (on) + { + _debugFastwalk.Add(m); + } + else + { + _debugFastwalk.Remove(m); + } + } + } + } +} diff --git a/Projects/UOContent/Network/FreeshardProtocol.cs b/Projects/UOContent/Network/FreeshardProtocol.cs index 93b6a49b3..62efd6abd 100644 --- a/Projects/UOContent/Network/FreeshardProtocol.cs +++ b/Projects/UOContent/Network/FreeshardProtocol.cs @@ -22,10 +22,15 @@ namespace Server.Network [CallPriority(10)] public static void Configure() { - _handlers = ProtocolExtensions.Register(0xF1); + _handlers = ProtocolExtensions.Register(new FreeshardProtocolInfo()); } - public static void Register(int cmd, bool ingame, OnPacketReceive onReceive) => + public static unsafe void Register(int cmd, bool ingame, delegate* onReceive) => _handlers[cmd] = new PacketHandler(cmd, 0, ingame, onReceive); + + private struct FreeshardProtocolInfo : IProtocolExtensionsInfo + { + public int PacketId => 0xF1; + } } } diff --git a/Projects/UOContent/Network/MapUO.cs b/Projects/UOContent/Network/MapUO.cs index c5d4649a2..751d0402f 100644 --- a/Projects/UOContent/Network/MapUO.cs +++ b/Projects/UOContent/Network/MapUO.cs @@ -19,146 +19,138 @@ using System.IO; using Server.Engines.PartySystem; using Server.Guilds; -namespace Server.Network +namespace Server.Network; + +public static class MapUO { - public static class MapUO + public static unsafe void Configure() { - private static PacketHandler[] _handlers; + AssistantProtocol.Register(0x00, true, &QueryPartyMemberLocations); + AssistantProtocol.Register(0x01, true, &QueryGuildMemberLocations); + } - public static void Configure() + public static void QueryGuildMemberLocations(NetState state, CircularBufferReader reader, int packetLength) + { + Mobile from = state.Mobile; + + state.SendGuildMemberLocations(from, from.Guild as Guild, reader.ReadBoolean()); + } + + public static void QueryPartyMemberLocations(NetState state, CircularBufferReader reader, int packetLength) + { + Mobile from = state.Mobile; + var party = Party.Get(from); + + if (party != null) { - _handlers = ProtocolExtensions.Register(0xF0); - - Register(0x00, true, QueryPartyMemberLocations); - Register(0x01, true, QueryGuildMemberLocations); - } - - public static void Register(int cmd, bool ingame, OnPacketReceive onReceive) => - _handlers[cmd] = new PacketHandler(cmd, 0, ingame, onReceive); - - public static void QueryGuildMemberLocations(NetState state, CircularBufferReader reader, ref int packetLength) - { - Mobile from = state.Mobile; - - state.SendGuildMemberLocations(from, from.Guild as Guild, reader.ReadBoolean()); - } - - public static void QueryPartyMemberLocations(NetState state, CircularBufferReader reader, ref int packetLength) - { - Mobile from = state.Mobile; - var party = Party.Get(from); - - if (party != null) - { - state.SendPartyMemberLocations(from, party); - } - } - - public static void SendGuildMemberLocations(this NetState ns, Mobile from, Guild guild, bool sendLocations) - { - if (ns == null) - { - return; - } - - var count = guild?.Members.Count ?? 0; - var maxLength = 9 + (count > 1 ? (count - 1) * (sendLocations ? 10 : 4) : 0); - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0xF0); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write((byte)0x02); // Command - writer.Write(count > 0 && sendLocations); - - bool sendPacket = false; - for (var i = 0; i < count; i++) - { - var m = guild!.Members[i]; - - if (m?.NetState == null || m == from) - { - continue; - } - - if (sendLocations && Utility.InUpdateRange(from.Location, m.Location) && from.CanSee(m)) - { - continue; - } - - sendPacket = true; - writer.Write(m.Serial); - - if (sendLocations) - { - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((byte)(m.Map?.MapID ?? 0)); - - if (m.Alive) - { - writer.Write((byte)(m.Hits * 100 / Math.Max(m.HitsMax, 1))); - } - else - { - writer.Write((byte)0); - } - } - } - - if (!sendPacket) - { - return; - } - - writer.Write(0); - writer.WritePacketLength(); - ns.Send(writer.Span); - } - - public static void SendPartyMemberLocations(this NetState ns, Mobile from, Party party) - { - if (ns == null) - { - return; - } - - var count = party?.Members.Count ?? 0; - var maxLength = 9 + (count > 1 ? (count - 1) * 9 : 0); - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0xF0); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write((byte)0x01); // Command - - bool sendPacket = false; - for (var i = 0; i < count; i++) - { - var pmi = party!.Members[i]; - Mobile mob = pmi?.Mobile; - - if (mob?.NetState == null || mob == from) - { - continue; - } - - if (Utility.InUpdateRange(from.Location, mob.Location) && from.CanSee(mob)) - { - continue; - } - - sendPacket = true; - writer.Write(mob.Serial); - writer.Write((short)mob.X); - writer.Write((short)mob.Y); - writer.Write((byte)(mob.Map?.MapID ?? 0)); - } - - if (!sendPacket) - { - return; - } - - writer.Write(0); - writer.WritePacketLength(); - ns.Send(writer.Span); + state.SendPartyMemberLocations(from, party); } } + + public static void SendGuildMemberLocations(this NetState ns, Mobile from, Guild guild, bool sendLocations) + { + if (ns.CannotSendPackets()) + { + return; + } + + var count = guild?.Members.Count ?? 0; + var maxLength = 9 + (count > 1 ? (count - 1) * (sendLocations ? 10 : 4) : 0); + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0xF0); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write((byte)0x02); // Command + writer.Write(count > 0 && sendLocations); + + bool sendPacket = false; + for (var i = 0; i < count; i++) + { + var m = guild!.Members[i]; + + if (m?.NetState == null || m == from) + { + continue; + } + + if (sendLocations && Utility.InUpdateRange(from.Location, m.Location) && from.CanSee(m)) + { + continue; + } + + sendPacket = true; + writer.Write(m.Serial); + + if (sendLocations) + { + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((byte)(m.Map?.MapID ?? 0)); + + if (m.Alive) + { + writer.Write((byte)(m.Hits * 100 / Math.Max(m.HitsMax, 1))); + } + else + { + writer.Write((byte)0); + } + } + } + + if (!sendPacket) + { + return; + } + + writer.Write(0); + writer.WritePacketLength(); + ns.Send(writer.Span); + } + + public static void SendPartyMemberLocations(this NetState ns, Mobile from, Party party) + { + if (ns.CannotSendPackets()) + { + return; + } + + var count = party?.Members.Count ?? 0; + var maxLength = 9 + (count > 1 ? (count - 1) * 9 : 0); + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0xF0); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write((byte)0x01); // Command + + bool sendPacket = false; + for (var i = 0; i < count; i++) + { + var pmi = party!.Members[i]; + Mobile mob = pmi?.Mobile; + + if (mob?.NetState == null || mob == from) + { + continue; + } + + if (Utility.InUpdateRange(from.Location, mob.Location) && from.CanSee(mob)) + { + continue; + } + + sendPacket = true; + writer.Write(mob.Serial); + writer.Write((short)mob.X); + writer.Write((short)mob.Y); + writer.Write((byte)(mob.Map?.MapID ?? 0)); + } + + if (!sendPacket) + { + return; + } + + writer.Write(0); + writer.WritePacketLength(); + ns.Send(writer.Span); + } } diff --git a/Projects/UOContent/Network/ProtocolExtensions.cs b/Projects/UOContent/Network/ProtocolExtensions.cs index c012bdb11..6df2a6a8b 100644 --- a/Projects/UOContent/Network/ProtocolExtensions.cs +++ b/Projects/UOContent/Network/ProtocolExtensions.cs @@ -15,40 +15,48 @@ namespace Server.Network { - public static class ProtocolExtensions + public interface IProtocolExtensionsInfo { - public static PacketHandler[] Register(byte packetId) + public int PacketId { get; } + } + + public static class ProtocolExtensions where T : struct, IProtocolExtensionsInfo + { + private static readonly PacketHandler[] packetHandlers = new PacketHandler[0x100]; + private static int packetId; + + public static unsafe PacketHandler[] Register(T info) { - var packetHandlers = new PacketHandler[0x100]; + packetId = info.PacketId; + IncomingPackets.Register(packetId, 0, false, &DecodeBundledPacket); - void DecodeBundledPacket(NetState state, CircularBufferReader reader, ref int packetLength) + return packetHandlers; + } + + private static unsafe void DecodeBundledPacket(NetState state, CircularBufferReader reader, int packetLength) + { + int cmd = reader.ReadByte(); + + PacketHandler ph = packetHandlers[cmd]; + + if (ph == null) { - int cmd = reader.ReadByte(); - - PacketHandler ph = cmd >= 0 && cmd < packetHandlers.Length ? packetHandlers[cmd] : null; - - if (ph == null) - { - return; - } - - if (ph.Ingame && state.Mobile == null) - { - state.LogInfo("Sent in-game packet (0x{0:X2}x{1:X2}) before having been attached to a mobile", packetId, cmd); - state.Disconnect("Sent in-game packet before being attached to a mobile."); - } - else if (ph.Ingame && state.Mobile.Deleted) - { - state.Disconnect(string.Empty); - } - else - { - ph.OnReceive(state, reader, ref packetLength); - } + return; } - IncomingPackets.Register(packetId, 0, false, DecodeBundledPacket); - return packetHandlers; + if (ph.Ingame && state.Mobile == null) + { + state.LogInfo($"Sent in-game packet (0x{packetId:X2}x{cmd:X2}) before having been attached to a mobile"); + state.Disconnect("Sent in-game packet before being attached to a mobile."); + } + else if (ph.Ingame && state.Mobile.Deleted) + { + state.Disconnect(string.Empty); + } + else + { + ph.OnReceive(state, reader, packetLength); + } } } } diff --git a/Projects/UOContent/Network/UOGateway.cs b/Projects/UOContent/Network/UOGateway.cs index 97ecf3a22..b3b3bdcc4 100644 --- a/Projects/UOContent/Network/UOGateway.cs +++ b/Projects/UOContent/Network/UOGateway.cs @@ -22,20 +22,20 @@ namespace Server.Network { public static class UOGateway { - public static void Configure() + public static unsafe void Configure() { var enabled = ServerConfiguration.GetOrUpdateSetting("uogateway.enabled", true); if (enabled) { - FreeshardProtocol.Register(0xFE, false, QueryCompactShardStats); - FreeshardProtocol.Register(0xFF, false, QueryExtendedShardStats); + FreeshardProtocol.Register(0xFE, false, &QueryCompactShardStats); + FreeshardProtocol.Register(0xFF, false, &QueryExtendedShardStats); } } - public static void QueryCompactShardStats(NetState ns, CircularBufferReader reader, ref int packetLength) + public static void QueryCompactShardStats(NetState state, CircularBufferReader reader, int packetLength) { - ns.SendCompactShardStats( + state.SendCompactShardStats( (uint)(Core.TickCount / 1000), TcpServer.Instances.Count - 1, // Shame if you modify this! World.Items.Count, @@ -44,10 +44,10 @@ namespace Server.Network ); } - public static void QueryExtendedShardStats(NetState ns, CircularBufferReader reader, ref int packetLength) + public static void QueryExtendedShardStats(NetState state, CircularBufferReader reader, int packetLength) { const long ticksInHour = 1000 * 60 * 60; - ns.SendExtendedShardStats( + state.SendExtendedShardStats( ServerList.ServerName, (int)(Core.TickCount / ticksInHour), TcpServer.Instances.Count - 1, // Shame if you modify this! @@ -61,7 +61,7 @@ namespace Server.Network this NetState ns, uint age, int clients, int items, int mobiles, long mem ) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -82,7 +82,7 @@ namespace Server.Network this NetState ns, string name, int age, int clients, int items, int mobiles, int mem ) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Regions/GuardedRegion.cs b/Projects/UOContent/Regions/GuardedRegion.cs index d714c1719..dc1879ab5 100644 --- a/Projects/UOContent/Regions/GuardedRegion.cs +++ b/Projects/UOContent/Regions/GuardedRegion.cs @@ -34,7 +34,7 @@ namespace Server.Regions if (!typeof(BaseGuard).IsAssignableFrom(m_GuardType)) { - logger.Warning("Invalid guard type for region '{0}'", this); + logger.Warning("Invalid guard type for region '{Region}'", this); m_GuardType = DefaultGuardType; } } diff --git a/Projects/UOContent/Skills/AnimalTaming.cs b/Projects/UOContent/Skills/AnimalTaming.cs index 99b1293ef..b7a9ffed3 100644 --- a/Projects/UOContent/Skills/AnimalTaming.cs +++ b/Projects/UOContent/Skills/AnimalTaming.cs @@ -486,16 +486,13 @@ namespace Server.SkillHandlers if (m_Creature is GreaterDragon) { ScaleSkills(m_Creature, 0.72, 0.90); // 72% of original skills trainable to 90% - m_Creature.Skills.Magery.Base = - m_Creature.Skills.Magery - .Cap; // Greater dragons have a 90% cap reduction and 90% skill reduction on magery + // Greater dragons have a 90% cap reduction and 90% skill reduction on magery + m_Creature.Skills.Magery.Base = m_Creature.Skills.Magery.Cap; } else if (m_Paralyzed) { - ScaleSkills( - m_Creature, - 0.86 - ); // 86% of original skills if they were paralyzed during the taming + // 86% of original skills if they were paralyzed during the taming + ScaleSkills(m_Creature, 0.86); } else { diff --git a/Projects/UOContent/Skills/SpiritSpeak.cs b/Projects/UOContent/Skills/SpiritSpeak.cs index 69b40fcf8..4a2ac0ab0 100644 --- a/Projects/UOContent/Skills/SpiritSpeak.cs +++ b/Projects/UOContent/Skills/SpiritSpeak.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Server.Items; using Server.Network; using Server.Spells; @@ -134,7 +133,15 @@ namespace Server.SkillHandlers public override void OnCast() { var eable = Caster.GetItemsInRange(3); - var toChannel = eable.FirstOrDefault(corpse => !corpse.Channeled); + Corpse toChannel = null; + foreach (var corpse in eable) + { + if (!corpse.Channeled) + { + toChannel = corpse; + break; + } + } eable.Free(); var min = 1 + (int)(Caster.Skills.SpiritSpeak.Value * 0.25); diff --git a/Projects/UOContent/Skills/Stealing.cs b/Projects/UOContent/Skills/Stealing.cs index 7a65b98cd..f2aa47c63 100644 --- a/Projects/UOContent/Skills/Stealing.cs +++ b/Projects/UOContent/Skills/Stealing.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Server.Engines.ConPVP; +using Server.Engines.Stealables; using Server.Factions; using Server.Items; using Server.Mobiles; @@ -79,11 +80,9 @@ namespace Server.SkillHandlers var root = toSteal.RootParent; var mobRoot = root as Mobile; - StealableArtifactsSpawner.StealableInstance si = null; - if (toSteal.Parent == null || !toSteal.Movable) - { - si = StealableArtifactsSpawner.GetStealableInstance(toSteal); - } + StealableArtifacts.StealableInstance si = toSteal.Parent == null || !toSteal.Movable + ? StealableArtifacts.GetStealableInstance(toSteal) + : null; if (!IsEmptyHanded(m_Thief)) { diff --git a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs index 0006e8413..6bf666de4 100644 --- a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs +++ b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs @@ -28,7 +28,7 @@ namespace Server.Network public static void SendArrow(this NetState ns, byte command, int x, int y, Serial s) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Skills/Tracking/Tracking.cs b/Projects/UOContent/Skills/Tracking/Tracking.cs index ee7bfa99a..ad62def69 100644 --- a/Projects/UOContent/Skills/Tracking/Tracking.cs +++ b/Projects/UOContent/Skills/Tracking/Tracking.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Server.Gumps; using Server.Mobiles; using Server.Network; @@ -13,13 +12,13 @@ namespace Server.SkillHandlers { private static readonly Dictionary m_Table = new(); - public static void Configure() + public static unsafe void Configure() { - IncomingExtendedCommandPackets.RegisterExtended(0x07, true, QuestArrow); + IncomingExtendedCommandPackets.RegisterExtended(0x07, true, &QuestArrow); SkillInfo.Table[(int)SkillName.Tracking].Callback = OnUse; } - public static void QuestArrow(NetState state, CircularBufferReader reader, ref int packetLength) + public static void QuestArrow(NetState state, CircularBufferReader reader, int packetLength) { if (state.Mobile is PlayerMobile from) { @@ -210,13 +209,18 @@ namespace Server.SkillHandlers var range = 10 + (int)(from.Skills.Tracking.Value / 10); - var list = from.GetMobilesInRange(range) - .Where( - m => m != from && (!Core.AOS || m.Alive) && - (!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) && - check(m) && CheckDifficulty(from, m) - ) - .ToList(); + var eable = from.GetMobilesInRange(range); + var list = new List(); + foreach (var m in eable) + { + 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); + } + } + eable.Free(); if (list.Count > 0) { diff --git a/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs b/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs index 076ef0d77..f33a1197f 100644 --- a/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs +++ b/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs @@ -30,17 +30,17 @@ namespace Server.Items public override string DefaultName => "a gambling stone"; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add("Jackpot: {0}gp", m_GamblePot); + list.Add($"Jackpot: {m_GamblePot}gp"); } public override void OnSingleClick(Mobile from) { base.OnSingleClick(from); - LabelTo(from, "Jackpot: {0}gp", m_GamblePot); + LabelTo(from, $"Jackpot: {m_GamblePot}gp"); } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Spells/Base/MagerySpell.cs b/Projects/UOContent/Spells/Base/MagerySpell.cs index 7905a7d65..0648ac6c1 100644 --- a/Projects/UOContent/Spells/Base/MagerySpell.cs +++ b/Projects/UOContent/Spells/Base/MagerySpell.cs @@ -7,9 +7,14 @@ namespace Server.Spells { private static readonly int[] _manaTable = { 4, 6, 9, 11, 14, 20, 40, 50 }; - // Starts at Circle -2 to account for scrolls + /* + * Starts at Circle -2 to account for scrolls + * Mana requirements formula: (14 * (circle - 1)) + 2 = 50% probability + * Add or subtract 20 for max or min limits + */ + private static readonly double[] _requiredSkill = Core.ML ? - new[] { -46.0, -32.0, 0.0, -4.0, 10.0, 24.0, 38.0, 52.0, 66.0, 80.0 } : + new[] { -46.0, -32.0, -18.0, -4.0, 10.0, 24.0, 38.0, 52.0, 66.0, 80.0 } : new[] { -50.0, -30.0, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0 }; public MagerySpell(Mobile caster, Item scroll, SpellInfo info) : base(caster, scroll, info) diff --git a/Projects/UOContent/Spells/Base/Spell.cs b/Projects/UOContent/Spells/Base/Spell.cs index 78eea7bdc..c7631a7ee 100644 --- a/Projects/UOContent/Spells/Base/Spell.cs +++ b/Projects/UOContent/Spells/Base/Spell.cs @@ -459,6 +459,8 @@ namespace Server.Spells } } + private static ClientVersion _insufficientManaClientChange = new ClientVersion("7.0.65.4"); + public bool Cast() { StartCastTime = Core.TickCount; @@ -473,13 +475,19 @@ namespace Server.Spells return false; } - if (Scroll is BaseWand && Caster.Spell?.IsCasting == true) + var isCasting = Caster.Spell?.IsCasting == true; + var isWand = Scroll is BaseWand; + + if (isCasting) { - Caster.SendLocalizedMessage(502643); // You can not cast a spell while frozen. - } - else if (Caster.Spell?.IsCasting == true) - { - Caster.SendLocalizedMessage(502642); // You are already casting a spell. + if (isWand) + { + Caster.SendLocalizedMessage(502643); // You can not cast a spell while frozen. + } + else + { + Caster.SendLocalizedMessage(502642); // You are already casting a spell. + } } else if (BlockedByHorrificBeast && TransformationSpellHelper.UnderTransformation(Caster, typeof(HorrificBeastSpell)) || @@ -487,7 +495,7 @@ namespace Server.Spells { Caster.SendLocalizedMessage(1061091); // You cannot cast that spell in this form. } - else if (Scroll is not BaseWand && (Caster.Paralyzed || Caster.Frozen)) + else if (!isWand && (Caster.Paralyzed || Caster.Frozen)) { Caster.SendLocalizedMessage(502643); // You can not cast a spell while frozen. } @@ -502,76 +510,84 @@ namespace Server.Spells else if ((Caster as PlayerMobile)?.DuelContext?.AllowSpellCast(Caster, this) == false) { } - else if (Caster.Mana >= ScaleMana(GetMana())) - { - if (Caster.Spell == null && Caster.CheckSpellCast(this) && CheckCast() && - Caster.Region.OnBeginSpellCast(Caster, this)) - { - State = SpellState.Casting; - Caster.Spell = this; - - if (Scroll is not BaseWand && RevealOnCast) - { - Caster.RevealingAction(); - } - - SayMantra(); - - var castDelay = GetCastDelay(); - - if (ShowHandMovement && (Caster.Body.IsHuman || Caster.Player && Caster.Body.IsMonster)) - { - var count = (int)Math.Ceiling(castDelay.TotalSeconds / AnimateDelay.TotalSeconds); - - if (count != 0) - { - _animTimer = new AnimTimer(this, count); - _animTimer.Start(); - } - - if (Info.LeftHandEffect > 0) - { - Caster.FixedParticles(0, 10, 5, Info.LeftHandEffect, EffectLayer.LeftHand); - } - - if (Info.RightHandEffect > 0) - { - Caster.FixedParticles(0, 10, 5, Info.RightHandEffect, EffectLayer.RightHand); - } - } - - if (ClearHandsOnCast) - { - Caster.ClearHands(); - } - - if (Core.ML) - { - WeaponAbility.ClearCurrentAbility(Caster); - } - - _castTimer = new CastTimer(this, castDelay); - // m_CastTimer.Start(); - - OnBeginCast(); - - if (castDelay > TimeSpan.Zero) - { - _castTimer.Start(); - } - else - { - _castTimer.Tick(); - } - - return true; - } - - return false; - } else { - Caster.LocalOverheadMessage(MessageType.Regular, 0x22, 502625); // Insufficient mana + var requiredMana = ScaleMana(GetMana()); + + if (Caster.Mana >= requiredMana) + { + if (Caster.Spell == null && Caster.CheckSpellCast(this) && CheckCast() && + Caster.Region.OnBeginSpellCast(Caster, this)) + { + State = SpellState.Casting; + Caster.Spell = this; + + if (!isWand && RevealOnCast) + { + Caster.RevealingAction(); + } + + SayMantra(); + + var castDelay = GetCastDelay(); + + if (ShowHandMovement && (Caster.Body.IsHuman || Caster.Player && Caster.Body.IsMonster)) + { + var count = (int)Math.Ceiling(castDelay.TotalSeconds / AnimateDelay.TotalSeconds); + + if (count != 0) + { + _animTimer = new AnimTimer(this, count); + _animTimer.Start(); + } + + if (Info.LeftHandEffect > 0) + { + Caster.FixedParticles(0, 10, 5, Info.LeftHandEffect, EffectLayer.LeftHand); + } + + if (Info.RightHandEffect > 0) + { + Caster.FixedParticles(0, 10, 5, Info.RightHandEffect, EffectLayer.RightHand); + } + } + + if (ClearHandsOnCast) + { + Caster.ClearHands(); + } + + if (Core.ML) + { + WeaponAbility.ClearCurrentAbility(Caster); + } + + _castTimer = new CastTimer(this, castDelay); + // m_CastTimer.Start(); + + OnBeginCast(); + + if (castDelay > TimeSpan.Zero) + { + _castTimer.Start(); + } + else + { + _castTimer.Tick(); + } + + return true; + } + } + else if (Caster.NetState?.Version >= _insufficientManaClientChange) + { + // Insufficient mana. You must have at least ~1_MANA_REQUIREMENT~ Mana to use this spell. + Caster.LocalOverheadMessage(MessageType.Regular, 0x22, 502625, requiredMana.ToString()); + } + else + { + Caster.LocalOverheadMessage(MessageType.Regular, 0x22, 502625); // Insufficient mana + } } return false; diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index ea9ec4131..787fddb03 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -256,14 +256,14 @@ namespace Server.Spells return false; } - public static bool GetEastToWest(IPoint3D from,IPoint3D target) + public static bool GetEastToWest(Point3D from, Point3D target) { var dx = from.X - target.X; var dy = from.Y - target.Y; var rx = (dx - dy) * 44; var ry = (dx + dy) * 44; - return (rx >= 0 && ry < 0) || (ry >= 0 && rx < 0); + return rx >= 0 && ry < 0 || ry >= 0 && rx < 0; } public static bool CanRevealCaster(Mobile m) => m is BaseCreature { Controlled: false }; @@ -303,7 +303,7 @@ namespace Server.Spells public static bool AddStatBonus(Mobile caster, Mobile target, StatType type, int bonus, TimeSpan duration) { var offset = bonus; - var name = $"[Magic] {type} Offset"; + var name = $"[Magic] {type} Buff"; var mod = target.GetStatMod(name); diff --git a/Projects/UOContent/Spells/Bushido/MomentumStrike.cs b/Projects/UOContent/Spells/Bushido/MomentumStrike.cs index 5513c5429..c3b291d01 100644 --- a/Projects/UOContent/Spells/Bushido/MomentumStrike.cs +++ b/Projects/UOContent/Spells/Bushido/MomentumStrike.cs @@ -1,4 +1,4 @@ -using System.Linq; +using Server.Collections; namespace Server.Spells.Bushido { @@ -21,12 +21,17 @@ namespace Server.Spells.Bushido var weapon = attacker.Weapon; - var targets = attacker.GetMobilesInRange(weapon.MaxRange) - .Where(m => m != defender) - .Where(m => m.Combatant == attacker) - .ToList(); + var eable = attacker.GetMobilesInRange(weapon.MaxRange); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) + { + if (m != defender && m.Combatant == attacker) + { + queue.Enqueue(m); + } + } - if (targets.Count <= 0) + if (queue.Count <= 0) { attacker.SendLocalizedMessage(1063123); // There are no valid targets to attack! return; @@ -37,7 +42,7 @@ namespace Server.Spells.Bushido return; } - var target = targets.RandomElement(); + Mobile target = queue.PeekRandom(); var damageBonus = attacker.Skills.Bushido.Value / 100.0; @@ -47,7 +52,7 @@ namespace Server.Spells.Bushido } 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!.SendLocalizedMessage(1063172); // You were hit by the momentum of a Samurai's weapon! target.FixedParticles(0x37B9, 1, 4, 0x251D, 0, 0, EffectLayer.Waist); diff --git a/Projects/UOContent/Spells/Chivalry/DispelEvil.cs b/Projects/UOContent/Spells/Chivalry/DispelEvil.cs index d70ef6859..b09d6a2dd 100644 --- a/Projects/UOContent/Spells/Chivalry/DispelEvil.cs +++ b/Projects/UOContent/Spells/Chivalry/DispelEvil.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using Server.Collections; using Server.Items; using Server.Mobiles; using Server.Spells.Necromancy; @@ -46,11 +46,21 @@ namespace Server.Spells.Chivalry var chiv = Caster.Skills.Chivalry.Value; - var targets = Caster.GetMobilesInRange(8) - .Where(m => Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false)); - - foreach (var m in targets) + var eable = Caster.GetMobilesInRange(8); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) { + if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false)) + { + queue.Enqueue(m); + } + } + eable.Free(); + + while (queue.Count > 0) + { + var m = queue.Dequeue(); + if (m is BaseCreature bc) { if (bc.Summoned && !bc.IsAnimatedDead) diff --git a/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs b/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs index 9e8e4e4c7..f44066b35 100644 --- a/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs +++ b/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; using Server.Gumps; +using Server.Items; using Server.Mobiles; +using Server.Spells.Fourth; +using Server.Spells.Mysticism; using Server.Spells.Necromancy; namespace Server.Spells.Chivalry @@ -83,7 +86,7 @@ namespace Server.Spells.Chivalry } else { - var sendEffect = false; + bool sendEffect = false; if (m.Poisoned && m.CurePoison(Caster)) { @@ -108,26 +111,24 @@ namespace Server.Spells.Chivalry sendEffect = true; } - StatMod mod; - - mod = m.GetStatMod("[Magic] Str Offset"); + var mod = m.GetStatMod("[Magic] Str Curse"); if (mod?.Offset < 0) { - m.RemoveStatMod("[Magic] Str Offset"); + m.RemoveStatMod("[Magic] Str Curse"); sendEffect = true; } - mod = m.GetStatMod("[Magic] Dex Offset"); + mod = m.GetStatMod("[Magic] Dex Curse"); if (mod?.Offset < 0) { - m.RemoveStatMod("[Magic] Dex Offset"); + m.RemoveStatMod("[Magic] Dex Curse"); sendEffect = true; } - mod = m.GetStatMod("[Magic] Int Offset"); + mod = m.GetStatMod("[Magic] Int Curse"); if (mod?.Offset < 0) { - m.RemoveStatMod("[Magic] Int Offset"); + m.RemoveStatMod("[Magic] Int Curse"); sendEffect = true; } @@ -137,22 +138,24 @@ namespace Server.Spells.Chivalry sendEffect = true; } - if (EvilOmenSpell.TryEndEffect(m)) - { - sendEffect = true; - } + sendEffect = EvilOmenSpell.TryEndEffect(m) || sendEffect; + sendEffect = StrangleSpell.RemoveCurse(m) || sendEffect; + sendEffect = CorpseSkinSpell.RemoveCurse(m) || sendEffect; + sendEffect = CurseSpell.RemoveEffect(m) || sendEffect; + sendEffect = MortalStrike.EndWound(m) || sendEffect; + sendEffect = MindRotSpell.ClearMindRotScalar(m) || sendEffect; + sendEffect = BloodOathSpell.RemoveCurse(m) || sendEffect; + sendEffect = SpellPlagueSpell.RemoveEffect(m) || sendEffect; - if (StrangleSpell.RemoveCurse(m)) - { - sendEffect = true; - } - - if (CorpseSkinSpell.RemoveCurse(m)) - { - sendEffect = true; - } - - // TODO: Should this remove blood oath? Pain spike? + // TODO: Move these into their respective end effect methods + BuffInfo.RemoveBuff(m, BuffIcon.Clumsy); + BuffInfo.RemoveBuff(m, BuffIcon.FeebleMind); + BuffInfo.RemoveBuff(m, BuffIcon.Weaken); + BuffInfo.RemoveBuff(m, BuffIcon.Curse); + BuffInfo.RemoveBuff(m, BuffIcon.MassCurse); + BuffInfo.RemoveBuff(m, BuffIcon.MortalStrike); + BuffInfo.RemoveBuff(m, BuffIcon.Strangle); + BuffInfo.RemoveBuff(m, BuffIcon.EvilOmen); if (sendEffect) { diff --git a/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs b/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs index 21357ec64..bd0561773 100644 --- a/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs +++ b/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs @@ -2,6 +2,7 @@ using System; using Server.Engines.ConPVP; using Server.Items; using Server.Spells.Fourth; +using Server.Spells.Mysticism; using Server.Spells.Necromancy; using Server.Targeting; @@ -44,24 +45,13 @@ namespace Server.Spells.Chivalry * Chance of removing curse is affected by Caster's Karma. */ - int chance; - - if (Caster.Karma < -5000) + int chance = Caster.Karma switch { - 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; - } + < -5000 => 0, + < 0 => (int)Math.Sqrt(20000 + Caster.Karma) - 122, + < 5625 => (int)Math.Sqrt(Caster.Karma) + 25, + _ => 100 + }; if (chance > Utility.Random(100)) { @@ -88,22 +78,22 @@ namespace Server.Spells.Chivalry 0x100 ); - var mod = m.GetStatMod("[Magic] Str Offset"); + var mod = m.GetStatMod("[Magic] Str Curse"); if (mod?.Offset < 0) { - m.RemoveStatMod("[Magic] Str Offset"); + m.RemoveStatMod("[Magic] Str Curse"); } - mod = m.GetStatMod("[Magic] Dex Offset"); + mod = m.GetStatMod("[Magic] Dex Curse"); if (mod?.Offset < 0) { - m.RemoveStatMod("[Magic] Dex Offset"); + m.RemoveStatMod("[Magic] Dex Curse"); } - mod = m.GetStatMod("[Magic] Int Offset"); + mod = m.GetStatMod("[Magic] Int Curse"); if (mod?.Offset < 0) { - m.RemoveStatMod("[Magic] Int Offset"); + m.RemoveStatMod("[Magic] Int Curse"); } m.Paralyzed = false; @@ -113,22 +103,19 @@ namespace Server.Spells.Chivalry CorpseSkinSpell.RemoveCurse(m); CurseSpell.RemoveEffect(m); MortalStrike.EndWound(m); - if (Core.ML) - { - BloodOathSpell.RemoveCurse(m); - } - MindRotSpell.ClearMindRotScalar(m); + BloodOathSpell.RemoveCurse(m); + SpellPlagueSpell.RemoveEffect(m); + // TODO: Move these into their respective end effect methods BuffInfo.RemoveBuff(m, BuffIcon.Clumsy); BuffInfo.RemoveBuff(m, BuffIcon.FeebleMind); BuffInfo.RemoveBuff(m, BuffIcon.Weaken); BuffInfo.RemoveBuff(m, BuffIcon.Curse); BuffInfo.RemoveBuff(m, BuffIcon.MassCurse); BuffInfo.RemoveBuff(m, BuffIcon.MortalStrike); - BuffInfo.RemoveBuff(m, BuffIcon.Mindrot); - - // TODO: Should this remove blood oath? Pain spike? + BuffInfo.RemoveBuff(m, BuffIcon.Strangle); + BuffInfo.RemoveBuff(m, BuffIcon.EvilOmen); } else { diff --git a/Projects/UOContent/Spells/Chivalry/SacredJourney.cs b/Projects/UOContent/Spells/Chivalry/SacredJourney.cs index 0b8667796..8245c88de 100644 --- a/Projects/UOContent/Spells/Chivalry/SacredJourney.cs +++ b/Projects/UOContent/Spells/Chivalry/SacredJourney.cs @@ -16,9 +16,12 @@ namespace Server.Spells.Chivalry ); private readonly Runebook m_Book; - private readonly RunebookEntry m_Entry; + public SacredJourneySpell(Mobile caster, Item scroll) : base(caster, scroll, _info) + { + } + public SacredJourneySpell( Mobile caster, RunebookEntry entry = null, Runebook book = null, Item scroll = null ) : base(caster, scroll, _info) diff --git a/Projects/UOContent/Spells/Eighth/Earthquake.cs b/Projects/UOContent/Spells/Eighth/Earthquake.cs index ffa480feb..f821a49fc 100644 --- a/Projects/UOContent/Spells/Eighth/Earthquake.cs +++ b/Projects/UOContent/Spells/Eighth/Earthquake.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using Server.Collections; namespace Server.Spells.Eighth { @@ -37,14 +37,21 @@ namespace Server.Spells.Eighth return; } - var targets = Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0)) - .Where( - m => Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false) && - (!Core.AOS || Caster.InLOS(m)) - ); - - foreach (var m in targets) + var eable = Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0)); + using var queue = PooledRefQueue.Create(); + foreach (var m in eable) { + if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false) && + (!Core.AOS || Caster.InLOS(m))) + { + queue.Enqueue(m); + } + } + + while (queue.Count > 0) + { + var m = queue.Dequeue(); + int damage; if (Core.AOS) diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs index 8a55f0d0b..80bada423 100644 --- a/Projects/UOContent/Spells/Fifth/PoisonField.cs +++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs @@ -30,22 +30,21 @@ namespace Server.Spells.Fifth if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) { SpellHelper.Turn(Caster, p); - SpellHelper.GetSurfaceTop(ref p); - var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); + var loc = new Point3D(p); + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc); - Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B); + Effects.PlaySound(loc, Caster.Map, 0x20B); var itemID = eastToWest ? 0x3915 : 0x3922; - var duration = TimeSpan.FromSeconds(3 + Caster.Skills.Magery.Fixed / 25); for (var i = -2; i <= 2; ++i) { - var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z); + var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z); - new InternalItem(itemID, loc, Caster, Caster.Map, duration, i); + new InternalItem(itemID, targetLoc, Caster, Caster.Map, duration, i); } } diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs index c3979af90..91ca1b8bb 100644 --- a/Projects/UOContent/Spells/Fourth/FireField.cs +++ b/Projects/UOContent/Spells/Fourth/FireField.cs @@ -33,28 +33,23 @@ namespace Server.Spells.Fourth SpellHelper.GetSurfaceTop(ref p); - var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); + var loc = new Point3D(p); - Effects.PlaySound(new Point3D(p), Caster.Map, 0x20C); + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc); + + Effects.PlaySound(loc, Caster.Map, 0x20C); var itemID = eastToWest ? 0x398C : 0x3996; - TimeSpan duration; - - if (Core.AOS) - { - duration = TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5.0) / 4.0); - } - else - { - duration = TimeSpan.FromSeconds(4.0 + Caster.Skills.Magery.Value * 0.5); - } + var duration = Core.AOS + ? TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5.0) / 4.0) + : TimeSpan.FromSeconds(4.0 + Caster.Skills.Magery.Value * 0.5); for (var i = -2; i <= 2; ++i) { - var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z); + var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z); - new FireFieldItem(itemID, loc, Caster, Caster.Map, duration, i); + new FireFieldItem(itemID, targetLoc, Caster, Caster.Map, duration, i); } } diff --git a/Projects/UOContent/Spells/Fourth/Recall.cs b/Projects/UOContent/Spells/Fourth/Recall.cs index c2f556e33..f9ea8cd1e 100644 --- a/Projects/UOContent/Spells/Fourth/Recall.cs +++ b/Projects/UOContent/Spells/Fourth/Recall.cs @@ -22,6 +22,10 @@ namespace Server.Spells.Fourth private readonly RunebookEntry m_Entry; + public RecallSpell(Mobile caster, Item scroll) : base(caster, scroll, _info) + { + } + public RecallSpell(Mobile caster, RunebookEntry entry = null, Runebook book = null, Item scroll = null) : base( caster, scroll, diff --git a/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs b/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs index 23c991a4d..eb6f708ea 100644 --- a/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs @@ -80,12 +80,16 @@ namespace Server.Spells.Mysticism public static bool UnderEffect(Mobile m) => _table.ContainsKey(m); - public static void RemoveEffect(Mobile m) + public static bool RemoveEffect(Mobile m) { - if (_table.TryGetValue(m, out var context)) + if (_table.Remove(m, out var context)) { - context.EndPlague(false); + context.Stop(); + BuffInfo.RemoveBuff(m, BuffIcon.SpellPlague); + return true; } + + return false; } public static void CheckPlague(Mobile m) @@ -179,9 +183,9 @@ namespace Server.Spells.Mysticism } } - public void EndPlague(bool restart = true) + public void EndPlague() { - if (restart && m_Next != null) + if (m_Next != null) { _table[m_Target] = m_Next; m_Next.StartPlague(); @@ -191,6 +195,8 @@ namespace Server.Spells.Mysticism _table.Remove(m_Target); BuffInfo.RemoveBuff(m_Target, BuffIcon.SpellPlague); } + + Stop(); } } diff --git a/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs b/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs index 0b6b1aadf..d1209838d 100644 --- a/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs +++ b/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs @@ -65,8 +65,7 @@ namespace Server.Spells.Necromancy * ((ss-rm)/8)+8 */ - _table.TryGetValue(m, out var timer); - timer?.DoExpire(); + RemoveCurse(m); m_OathTable[Caster] = Caster; m_OathTable[m] = Caster; @@ -84,7 +83,7 @@ namespace Server.Spells.Necromancy var duration = TimeSpan.FromSeconds((GetDamageSkill(Caster) - GetResistSkill(m)) / 8 + 8); m.CheckSkill(SkillName.MagicResist, 0.0, 120.0); // Skill check for gain - timer = new ExpireTimer(Caster, m, duration); + var timer = new ExpireTimer(Caster, m, duration); timer.Start(); BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.BloodOathCaster, 1075659, duration, Caster, m.Name)); @@ -102,10 +101,30 @@ namespace Server.Spells.Necromancy Caster.Target = new SpellTargetMobile(this, TargetFlags.Harmful, Core.ML ? 10 : 12); } - public static void RemoveCurse(Mobile m) + public static bool RemoveCurse(Mobile target) { - _table.TryGetValue(m, out var t); - t?.DoExpire(); + if (_table.Remove(target, out var timer)) + { + var caster = timer.Caster; + if (m_OathTable.Remove(caster)) + { + caster.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. + } + + if (m_OathTable.Remove(target)) + { + target.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. + } + + timer.Stop(); + + BuffInfo.RemoveBuff(caster, BuffIcon.BloodOathCaster); + BuffInfo.RemoveBuff(target, BuffIcon.BloodOathCurse); + + return true; + } + + return false; } public static Mobile GetBloodOath(Mobile m) => @@ -113,48 +132,29 @@ namespace Server.Spells.Necromancy private class ExpireTimer : Timer { - private readonly Mobile m_Caster; - private readonly DateTime m_End; - private readonly Mobile m_Target; + private Mobile _target; + private DateTime _end; + + public Mobile Caster { get; } 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 = Core.Now + delay; + Caster = caster; + _target = target; + _end = Core.Now + delay; } protected override void OnTick() { - if (m_Caster.Deleted || m_Target.Deleted || !m_Caster.Alive || !m_Target.Alive || - Core.Now >= m_End) + if (Caster.Deleted || _target.Deleted || !Caster.Alive || !_target.Alive || + Core.Now >= _end) { - DoExpire(); + RemoveCurse(_target); } } - - public void DoExpire() - { - if (m_OathTable.Remove(m_Caster)) - { - m_Caster.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. - } - - if (m_OathTable.Remove(m_Target)) - { - m_Target.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. - } - - Stop(); - - BuffInfo.RemoveBuff(m_Caster, BuffIcon.BloodOathCaster); - BuffInfo.RemoveBuff(m_Target, BuffIcon.BloodOathCurse); - - _table.Remove(m_Caster); - } } } } diff --git a/Projects/UOContent/Spells/Necromancy/MindRot.cs b/Projects/UOContent/Spells/Necromancy/MindRot.cs index 784fcaa11..c541d8896 100644 --- a/Projects/UOContent/Spells/Necromancy/MindRot.cs +++ b/Projects/UOContent/Spells/Necromancy/MindRot.cs @@ -71,15 +71,17 @@ namespace Server.Spells.Necromancy Caster.Target = new SpellTargetMobile(this, TargetFlags.Harmful, Core.ML ? 10 : 12); } - public static void ClearMindRotScalar(Mobile m) + public static bool ClearMindRotScalar(Mobile m) { if (_table.Remove(m, out var tmpB)) { tmpB.m_MRExpireTimer.Stop(); m.SendLocalizedMessage(1060872); // Your mind feels normal again. + BuffInfo.RemoveBuff(m, BuffIcon.Mindrot); + return true; } - BuffInfo.RemoveBuff(m, BuffIcon.Mindrot); + return false; } public static bool HasMindRotScalar(Mobile m) => _table.ContainsKey(m); @@ -127,7 +129,6 @@ namespace Server.Spells.Necromancy if (m_Target.Deleted || !m_Target.Alive || Core.Now >= m_End) { MindRotSpell.ClearMindRotScalar(m_Target); - Stop(); } } } diff --git a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs index 041a10eab..680718eb5 100644 --- a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs +++ b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs @@ -462,9 +462,9 @@ namespace Server.Spells.Ninjitsu entries[i].ItemID, entries[i].Hue, 40 - b.Width / 2 - b.X, - 30 - b.Height / 2 - b.Y, - entries[i].Tooltip + 30 - b.Height / 2 - b.Y ); + AddTooltip(entries[i].Tooltip); AddHtmlLocalized(x + 84, y, 250, 60, entries[i].Name, 0x7FFF); current++; @@ -589,8 +589,7 @@ namespace Server.Spells.Ninjitsu m_LastTarget = m_Mobile.Combatant; } - if (m_Mobile.Warmode && m_LastTarget?.Alive == true && m_LastTarget?.Deleted != true && - m_Counter-- <= 0) + if (m_Mobile.Warmode && m_LastTarget is { Alive: true, Deleted: false } && m_Counter-- <= 0) { if (m_Mobile.CanBeHarmful(m_LastTarget) && m_LastTarget.Map == m_Mobile.Map && m_LastTarget.InRange(m_Mobile.Location, BaseCreature.DefaultRangePerception) && diff --git a/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs b/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs index 4444a19c0..896d01456 100644 --- a/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs +++ b/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs @@ -128,7 +128,7 @@ namespace Server.Mobiles { private Mobile m_Caster; - public Clone(Mobile caster) : base(AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4) + public Clone(Mobile caster) : base(AIType.AI_Melee, FightMode.None) { m_Caster = caster; diff --git a/Projects/UOContent/Spells/Seventh/EnergyField.cs b/Projects/UOContent/Spells/Seventh/EnergyField.cs index bf5c5c019..5c5900a08 100644 --- a/Projects/UOContent/Spells/Seventh/EnergyField.cs +++ b/Projects/UOContent/Spells/Seventh/EnergyField.cs @@ -30,42 +30,35 @@ namespace Server.Spells.Seventh if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) { SpellHelper.Turn(Caster, p); - SpellHelper.GetSurfaceTop(ref p); - var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); + var loc = new Point3D(p); - Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B); + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc); - TimeSpan duration; + Effects.PlaySound(loc, Caster.Map, 0x20B); - if (Core.AOS) - { - duration = TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5) / 7.0); - } - else - { - // (28% of magery) + 2.0 seconds - duration = TimeSpan.FromSeconds(Caster.Skills.Magery.Value * 0.28 + 2.0); - } + TimeSpan duration = Core.AOS + ? TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5) / 7.0) + : TimeSpan.FromSeconds(Caster.Skills.Magery.Value * 0.28 + 2.0); var itemID = eastToWest ? 0x3946 : 0x3956; for (var i = -2; i <= 2; ++i) { - var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z); - var canFit = SpellHelper.AdjustField(ref loc, Caster.Map, 12, false); + var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z); + var canFit = SpellHelper.AdjustField(ref targetLoc, Caster.Map, 12, false); if (!canFit) { continue; } - Item item = new InternalItem(loc, Caster.Map, duration, itemID, Caster); + Item item = new InternalItem(targetLoc, Caster.Map, duration, itemID, Caster); item.ProcessDelta(); Effects.SendLocationParticles( - EffectItem.Create(loc, Caster.Map, EffectItem.DefaultDuration), + EffectItem.Create(targetLoc, Caster.Map, EffectItem.DefaultDuration), 0x376A, 9, 10, diff --git a/Projects/UOContent/Spells/Seventh/GateTravel.cs b/Projects/UOContent/Spells/Seventh/GateTravel.cs index 13a171c04..5377ddb22 100644 --- a/Projects/UOContent/Spells/Seventh/GateTravel.cs +++ b/Projects/UOContent/Spells/Seventh/GateTravel.cs @@ -20,6 +20,10 @@ namespace Server.Spells.Seventh private readonly RunebookEntry m_Entry; + public GateTravelSpell(Mobile caster, Item scroll) : base(caster, scroll, _info) + { + } + public GateTravelSpell(Mobile caster, RunebookEntry entry = null, Item scroll = null) : base(caster, scroll, _info) => m_Entry = entry; diff --git a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs index 8a018caba..0edd387b3 100644 --- a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs +++ b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs @@ -29,12 +29,12 @@ namespace Server.Spells.Sixth if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) { SpellHelper.Turn(Caster, p); - SpellHelper.GetSurfaceTop(ref p); - var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); + var loc = new Point3D(p); + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc); - Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B); + Effects.PlaySound(loc, Caster.Map, 0x20B); var itemID = eastToWest ? 0x3967 : 0x3979; @@ -42,18 +42,18 @@ namespace Server.Spells.Sixth for (var i = -2; i <= 2; ++i) { - var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z); + var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z); - if (!SpellHelper.AdjustField(ref loc, Caster.Map, 12, false)) + if (!SpellHelper.AdjustField(ref targetLoc, Caster.Map, 12, false)) { continue; } - Item item = new InternalItem(Caster, itemID, loc, Caster.Map, duration); + Item item = new InternalItem(Caster, itemID, targetLoc, Caster.Map, duration); item.ProcessDelta(); Effects.SendLocationParticles( - EffectItem.Create(loc, Caster.Map, EffectItem.DefaultDuration), + EffectItem.Create(targetLoc, Caster.Map, EffectItem.DefaultDuration), 0x376A, 9, 10, diff --git a/Projects/UOContent/Spells/Spellweaving/Items/ArcaneFocus.cs b/Projects/UOContent/Spells/Spellweaving/Items/ArcaneFocus.cs index f38d180d2..56676d627 100644 --- a/Projects/UOContent/Spells/Spellweaving/Items/ArcaneFocus.cs +++ b/Projects/UOContent/Spells/Spellweaving/Items/ArcaneFocus.cs @@ -34,11 +34,11 @@ namespace Server.Items public override TextDefinition InvalidTransferMessage => 1073480; // Your arcane focus disappears. public override bool Nontransferable => true; - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); - list.Add(1060485, StrengthBonus.ToString()); // strength bonus ~1_val~ + list.Add(1060485, StrengthBonus); // strength bonus ~1_val~ } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs index 49781eae5..8f5bec909 100644 --- a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs +++ b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs @@ -53,9 +53,9 @@ namespace Server.Items public virtual void SendTimeRemainingMessage(Mobile to) { to.SendLocalizedMessage( - 1072516, + 1072516, // ~1_name~ will expire in ~2_val~ seconds! $"{Name ?? $"#{LabelNumber}"}\t{(int)LifeSpan.TotalSeconds}" - ); // ~1_name~ will expire in ~2_val~ seconds! + ); } public override void OnDelete() @@ -76,13 +76,13 @@ namespace Server.Items } } - public override void GetProperties(ObjectPropertyList list) + public override void GetProperties(IPropertyList list) { base.GetProperties(list); var remaining = CreationTime + LifeSpan - Core.Now; - list.Add(1072517, ((int)remaining.TotalSeconds).ToString()); // Lifespan: ~1_val~ seconds + list.Add(1072517, $"{(int)remaining.TotalSeconds}"); // Lifespan: ~1_val~ seconds } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFey.cs b/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFey.cs index d93e03c44..3f1defbde 100644 --- a/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFey.cs +++ b/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFey.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class ArcaneFey : BaseCreature { [Constructible] - public ArcaneFey() : base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4) + public ArcaneFey() : base(AIType.AI_Mage, FightMode.Evil) { Name = NameList.RandomName("pixie"); Body = 128; diff --git a/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFiend.cs b/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFiend.cs index 3b44cca14..6041979af 100644 --- a/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFiend.cs +++ b/Projects/UOContent/Spells/Spellweaving/Mobiles/ArcaneFiend.cs @@ -3,7 +3,7 @@ namespace Server.Mobiles public class ArcaneFiend : BaseCreature { [Constructible] - public ArcaneFiend() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) + public ArcaneFiend() : base(AIType.AI_Mage) { Body = 74; BaseSoundID = 422; diff --git a/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs b/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs index 0c0d7f7ac..d91aa4318 100644 --- a/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs +++ b/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs @@ -5,8 +5,7 @@ namespace Server.Mobiles public class NatureFury : BaseCreature { [Constructible] - public NatureFury() - : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) + public NatureFury() : base(AIType.AI_Melee) { Body = 0x33; Hue = 0x4001; diff --git a/Projects/UOContent/Spells/Third/MagicLock.cs b/Projects/UOContent/Spells/Third/MagicLock.cs index c9cfef434..2f8e23c59 100644 --- a/Projects/UOContent/Spells/Third/MagicLock.cs +++ b/Projects/UOContent/Spells/Third/MagicLock.cs @@ -30,13 +30,10 @@ namespace Server.Spells.Third } else if (BaseHouse.CheckLockedDownOrSecured(cont)) { - Caster.LocalOverheadMessage( - MessageType.Regular, - 0x22, - 501761 - ); // You cannot cast this on a locked down item. + // You cannot cast this on a locked down item. + Caster.LocalOverheadMessage(MessageType.Regular, 0x22, 501761); } - else if (cont.Locked || cont.LockLevel == 0 || cont is ParagonChest) + else if (cont.Locked || cont.LockLevel == ILockpickable.CannotPick || cont is ParagonChest) { Caster.SendLocalizedMessage(501762); // Target must be an unlocked chest. } @@ -59,7 +56,7 @@ namespace Server.Spells.Third // The chest is now locked! Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 501763); - cont.LockLevel = -255; // signal magic lock + cont.LockLevel = ILockpickable.MagicLock; // signal magic lock cont.Locked = true; } diff --git a/Projects/UOContent/Spells/Third/Unlock.cs b/Projects/UOContent/Spells/Third/Unlock.cs index a29040d06..445b23e23 100644 --- a/Projects/UOContent/Spells/Third/Unlock.cs +++ b/Projects/UOContent/Spells/Third/Unlock.cs @@ -40,52 +40,44 @@ namespace Server.Spells.Third if (p is Mobile) { - Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 503101); // That did not need to be unlocked. + // That did not need to be unlocked. + Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 503101); } else if (p is not LockableContainer cont) { Caster.SendLocalizedMessage(501666); // You can't unlock that! } + else if (BaseHouse.CheckSecured(cont)) + { + Caster.SendLocalizedMessage(503098); // You cannot cast this on a secure item. + } + else if (!cont.Locked) + { + // That did not need to be unlocked. + Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 503101); + } + else if (cont.LockLevel == ILockpickable.CannotPick) + { + Caster.SendLocalizedMessage(501666); // You can't unlock that! + } else { - if (BaseHouse.CheckSecured(cont)) + var level = (int)(Caster.Skills.Magery.Value * 0.8) - 4; + + if (level >= cont.RequiredSkill && + !(cont is TreasureMapChest chest && chest.Level > 2)) { - Caster.SendLocalizedMessage(503098); // You cannot cast this on a secure item. - } - else if (!cont.Locked) - { - Caster.LocalOverheadMessage( - MessageType.Regular, - 0x3B2, - 503101 - ); // That did not need to be unlocked. - } - else if (cont.LockLevel == 0) - { - Caster.SendLocalizedMessage(501666); // You can't unlock that! + cont.Locked = false; + + if (cont.LockLevel == ILockpickable.MagicLock) + { + cont.LockLevel = cont.RequiredSkill - 10; + } } else { - var level = (int)(Caster.Skills.Magery.Value * 0.8) - 4; - - if (level >= cont.RequiredSkill && - !(cont is TreasureMapChest chest && chest.Level > 2)) - { - cont.Locked = false; - - if (cont.LockLevel == -255) - { - cont.LockLevel = cont.RequiredSkill - 10; - } - } - else - { - Caster.LocalOverheadMessage( - MessageType.Regular, - 0x3B2, - 503099 - ); // My spell does not seem to have an effect on that lock. - } + // My spell does not seem to have an effect on that lock. + Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 503099); } } } diff --git a/Projects/UOContent/Spells/Third/WallOfStone.cs b/Projects/UOContent/Spells/Third/WallOfStone.cs index 98d239a07..094374a86 100644 --- a/Projects/UOContent/Spells/Third/WallOfStone.cs +++ b/Projects/UOContent/Spells/Third/WallOfStone.cs @@ -30,14 +30,16 @@ namespace Server.Spells.Third SpellHelper.GetSurfaceTop(ref p); - var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); + var loc = new Point3D(p); - Effects.PlaySound(new Point3D(p), Caster.Map, 0x1F6); + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc); + + Effects.PlaySound(loc, Caster.Map, 0x1F6); for (var i = -1; i <= 1; ++i) { - var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z); - var canFit = SpellHelper.AdjustField(ref loc, Caster.Map, 22, true); + var targetLoc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z); + var canFit = SpellHelper.AdjustField(ref targetLoc, Caster.Map, 22, true); // Effects.SendLocationParticles( EffectItem.Create( loc, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5025 ); @@ -46,7 +48,7 @@ namespace Server.Spells.Third continue; } - Item item = new InternalItem(loc, Caster.Map, Caster); + Item item = new InternalItem(targetLoc, Caster.Map, Caster); Effects.SendLocationParticles(item, 0x376A, 9, 10, 5025); diff --git a/Projects/UOContent/Targets/MoveTarget.cs b/Projects/UOContent/Targets/MoveTarget.cs index 078fa5437..884f9367a 100644 --- a/Projects/UOContent/Targets/MoveTarget.cs +++ b/Projects/UOContent/Targets/MoveTarget.cs @@ -12,41 +12,45 @@ namespace Server.Targets protected override void OnTarget(Mobile from, object o) { - if (o is IPoint3D p) + if (o is not IPoint3D ip) { - if (!BaseCommand.IsAccessible(from, m_Object)) - { - from.SendLocalizedMessage(500447); // That is not accessible. - return; - } + return; + } - if (p is Item pItem) - { - p = pItem.GetWorldTop(); - } + if (!BaseCommand.IsAccessible(from, m_Object)) + { + from.SendLocalizedMessage(500447); // That is not accessible. + return; + } - CommandLogging.WriteLine( - from, - "{0} {1} moving {2} to {3}", - from.AccessLevel, - CommandLogging.Format(from), - CommandLogging.Format(m_Object), - new Point3D(p) - ); + Point3D p = ip switch + { + Item i => i.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; - if (m_Object is Item item) + CommandLogging.WriteLine( + from, + "{0} {1} moving {2} to {3}", + from.AccessLevel, + CommandLogging.Format(from), + CommandLogging.Format(m_Object), + p + ); + + if (m_Object is Item item) + { + if (!item.Deleted) { - if (!item.Deleted) - { - item.MoveToWorld(new Point3D(p), from.Map); - } + item.MoveToWorld(p, from.Map); } - else if (m_Object is Mobile m) + } + else if (m_Object is Mobile m) + { + if (!m.Deleted) { - if (!m.Deleted) - { - m.MoveToWorld(new Point3D(p), from.Map); - } + m.MoveToWorld(p, from.Map); } } } diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index f260b9a4c..bd101f145 100755 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -38,20 +38,15 @@ false - + - - - - - - - TargetFramework=netstandard2.0 - Analyzer - false - all - + + + + + + diff --git a/README.md b/README.md index 0983a8826..558a44ef1 100644 --- a/README.md +++ b/README.md @@ -21,21 +21,22 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc [![Ubuntu 16/18/20 LTS](https://img.shields.io/badge/-20LTS-E95420?logo=ubuntu&logoColor=white)](https://ubuntu.com/download/server) [![Linux Mint 17/18/19/20](https://img.shields.io/badge/-20-87CF3E?logo=linux%20mint&logoColor=white)](https://linuxmint.com/download.php) [![CentOS 7/8](https://img.shields.io/badge/-8.5-262577?logo=centos&logoColor=white)](https://www.centos.org/download/) -[![Fedora 32/33/34](https://img.shields.io/badge/-fedora%2034-0B57A4)](https://getfedora.org/en/server/download/) +[![Fedora 32/33/34/35/36](https://img.shields.io/badge/-36-51a2da?logo=fedora&logoColor=white)](https://getfedora.org/en/server/download/) [![RedHat 7/8](https://img.shields.io/badge/-8-BE0000?logo=red%20hat&logoColor=white)](https://access.redhat.com/downloads) #### Running the server -[![.NET](https://img.shields.io/badge/-6.0-5C2D91?logo=.NET)](https://dotnet.microsoft.com/download/dotnet/6.0) +[![.NET](https://img.shields.io/badge/-6.0.6-5C2D91?logo=.NET)](https://dotnet.microsoft.com/download/dotnet/6.0) #### Development [![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=white)](https://git-scm.com/downloads) -[![.NET](https://img.shields.io/badge/-%206.0%20SDK-5C2D91?logo=.NET)](https://dotnet.microsoft.com/download/dotnet/6.0) +[![.NET](https://img.shields.io/badge/-%206.0.6%20SDK-5C2D91?logo=.NET)](https://dotnet.microsoft.com/download/dotnet/6.0) #### Supported IDEs     -[Jetbrains Rider 2021.3](https://www.jetbrains.com/rider/download) +[Jetbrains Rider 2021.3](https://www.jetbrains.com/rider/download)                           -[Visual Studio 2022](https://visualstudio.microsoft.com/downloads) +[Visual Studio 2022](https://visualstudio.microsoft.com/downloads)
Rider 2021.3+             Visual Studio 2022+ ###### Note: VS Code is not currently supported. @@ -55,13 +56,10 @@ Rider 2021.3+           & - `linuxmint.17`, `linuxmint.18`, `linuxmint.19` - Linux Mint - `debian.10`, `debian.11` - Debian - `centos.7`, `centos.8` - CentOS - - `fedora.32`, `fedora.33`, `fedora.34` - Fedora + - `fedora.32`, `fedora.33`, `fedora.34`, `fedora.35`, `fedora.36` - Fedora - `rhel.7`, `rhel.8` - Redhat - If blank, the operating system running the build is used. Linux Mint 20 is not supported directly yet, so build explicitly against `ubuntu.20.04` instead. -**Note:** Building in Visual Studio (or Rider) will not run the schema migration. The schema migration ensures future changes -to the code will be backward compatible. - ## Running the Server - Follow the [publish](https://github.com/modernuo/ModernUO#publishing-builds) instructions - Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory on the server @@ -71,7 +69,7 @@ to the code will be backward compatible. ## Thanks - RunUO Team & Community -- ServUO Team & Community +- [Voxpire](https://github.com/Voxpire), the ServUO Team & Community - [Karasho](https://github.com/andreakarasho), [Jaedan](https://github.com/jaedan) and the ClassicUO Community ## Troubleshooting / FAQ @@ -81,4 +79,6 @@ to the code will be backward compatible. Thank you for supporting us! You can find out how by visiting the [sponsors](./SPONSORS.md) page.

-

Development Tools provided with ♥ by

+

Development Tools & Plugins provided with ♥ by
JetBrains +Material Theme +

diff --git a/RUNUO_MIGRATIONS.md b/RUNUO_MIGRATIONS.md deleted file mode 100644 index 4c7b890d8..000000000 --- a/RUNUO_MIGRATIONS.md +++ /dev/null @@ -1,73 +0,0 @@ -### Migrating from RunUO 2.0 -There are several considerations before migration from RunUO 2.0 to ModernUO. -Many of these issues can be fixed by writing scripts that do world file fixes. -Others will require careful code review to fix compile/runtime issues. - -We will introduce templates to create migration scripts in a future iteration. - -#### Deserialization Issues (Startup Issues) -* All BODs will fail to deserialize due to the introduction of BaseBOD - * Recommend a migration script to fix this. -* Serial is now a `uint` which could break certain scripts expecting negative numbers or fixed encoding. - -#### Constructor Inheritance (Runtime Errors, Corruption, or Crashes) -* Items that inherit `Food` will pass the wrong arguments to their parent constructor. -* Items that inherit spells or scrolls will pass the wrong arguments to their parent constructors. - -#### Code Changes (Possible Compile Errors) -* The following functions no longer take a Type as their argument and now utilize generics: - * CanBeginAction, BeginAction and EndAction - * FindRegion and IsPartOf - * FindGump, HasGump, and CloseGump -* Timers use lambda functions instead of callback objects with generic `object state` argument. -* Various functions such as `OnAdded` which previously took `object parent` now take `IEntity` -* Functions that utilized non-generic structures (ArrayList, etc) have been changed to use generics. - -### Profiling -* Disabled - -#### Reports -* Removed - -#### RemoteAdmin -* Removed - -#### MyRunUO -* Removed - -#### EventLog -* Removed - -### Future Migration Concerns - -#### Account System -* Accounts from the XML format will need to be migrated the new interface/format. -* IP Logs from the Accounts XML will need to be offloaded to a new database. - -#### Deserialization -* All resources including but not limited to the following will need to be migrated: - * Gold, Silver, Platinum - * Arrow, Bolt, Feature, Shaft - * BaseOre, BaseIngot, BaseScale and all children - * BigFish, Fish, MagicFish - * Granite, Sand - * BaseReagent and all children - * BoltOfCloth, Bone, Cloth, Cotton, Flax, BaseHides, BaseLeather, UncutCloth, Wool, YarnAndThreads and all children - * ML Resources (Check Scripts/Items/Resources/MiscMLResources.cs on RunUO 2.0) - * Log and Board - * Bottle - * Solen Items (e.g. ZoogiFungus) - * Other commodity types I can't remember -* Spawners will need to be migrated since the old Spawner has been replaced and deserialization is not guaranteed. - -#### Timers -* Scripts that rely on Timers, TimerPriority, etc will need to be fixed to work with the format. - -#### Fastwalk -* Other fastwalk/speedhack detection systems will need to be deleted or fixed. - -#### Packets -* Packet, ByteQueue, PacketWriter, and PacketReader have been removed. -* `ns.Send(packet)` has been changed to `Packets.Send(ns, PacketFunc, arg1, arg2...)` -* Packets that are not used in the core have been moved to content distribution -* God client packets have been removed diff --git a/RUNUO_TO_MODERNUO.md b/RUNUO_TO_MODERNUO.md new file mode 100644 index 000000000..6afb0d8ec --- /dev/null +++ b/RUNUO_TO_MODERNUO.md @@ -0,0 +1,118 @@ +# From RunUO to ModernUO +RunUO was built using C# from .NET 1.1 in 2002. There have been massive changes to technology in the past 20+ years. +We believe it is time for Ultima Online to take advantage of this technology so a server can provide a richer experience with never before seen scale. + +While it is possible (and many have done it) to migrate an _active server_ from RunUO to ModernUO, it is a daunting task. +Please ask for help in our discord! + +## Technology +| | RunUO | ModernUO | +|:-------------|:------------------------------------------------------------------------------------------------------|:------------------------------------------------------------| +| Language | C# 4 | C# 11 (.NET 6/7) | +| Supported OS | 32 & 64bit Windows or Mono | 64bit Windows, MacOS & Linux | +| IDEs | [VS](https://visualstudio.microsoft.com/downloads/), [VSCode](https://code.visualstudio.com/download) | VS 2022+ or [Rider 2022+](https://www.jetbrains.com/rider/) | + +## Code API Changes +* **ModernUO can use source generators to [serialization/deserialization](https://github.com/modernuo/SerializationGenerator#basic-usage) automatically.** + * This is the biggest change to the API! While intimidating and different, it unlocks the ability for ModernUO to only serialize _data that has changed_. + * We estimate a world save with 10mill objects on a busy server will take less than 1 second. + * This feature is _optional and will remain optional indefinitely_. +* `Serialize(GenericWriter writer)` and equiv deserialize was changed to `Serialize(IGenericWriter writer)`. +* The following now use generics, e.g. `BeginAction(typeof(X))` is now `BeginAction`. + * CanBeginAction, BeginAction and EndAction + * FindRegion and IsPartOf + * FindGump, HasGump, and CloseGump +* Functions such as `OnAdded(object)` for both Items/Mobiles are now `OnAdded(IEntity)`. +* Most `delegate` have been changed to `Action`. + * Example: `EventSink.PlayerDeath += new PlayerDeathEventHandler(EventSink_PlayerDeath);` is now `EventSink.PlayerDeath += EventSink_PlayerDeath;`. +* `ObjectPropertyList` is now `IPropertyList`, + * Example: `GetProperties(ObjectPropertyList list)` is now `GetProperties(IPropertyList list)`. +* `[Constructable]` attribute is now `[Constructible]`. + +### Object Property List API Changes +ObjectPropertyList has been drastically optimized, which means the API has been modernized: + +❌ _Not valid_ +```cs +list.Add(1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges); +``` +✅ +```cs +list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); +``` + +Clilocs that use arguments: + +❌ _Not valid_ +```cs +list.Add(1060659, "Level\t{1}", m_Level); +``` +✅ - _Note the string as an argument, this is mandatory!_ +```cs +list.Add(1060659, $"{"Level"}\t{Level}"); // ~1_val~: ~2_val~ +``` + +Clilocs that use other clilocs as an argument: + +❌ _Do not prepend #_ +```cs +list.Add(1060830, $"#{dirt.ToString()}"); +``` +✅ _Use the new custom cliloc argument formatter_ +```cs +list.Add(1060830, $"{dirt:#}"); +``` + +## Core Changes +* ModernUO is not inherently thread safe. Overall infrastructure improved with CPU and minimizing memory garbage in mind. +* Timer system improved drastically by using [Timer Wheels](http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf). +* Networking is 5-10x faster by using fixed [Circular Buffers](https://en.wikipedia.org/wiki/Circular_buffer). +* Network packets are no longer objects and write directly to the network buffer, improving performance by 10x. +* World saves are 10x faster by saving to memory and flushing to disk in the background. +* Improved RNG accuracy and performance by 5x using [Xoshiro256++](https://prng.di.unimi.it/) +* Converted quite a bit of configuration to JSON with a central settings file. +* Logging changed from Console.WriteLine to Serilog (still work in progress). +* Eliminated calling `DateTime.Now` which had a huge performance penalty. +* Accounts are now saved to a binary file (will eventually change to a databse) to improve world save performance by eliminating XML. +* Strings are now built using the new interpolated string syntax, and highly performant string builders. + +## New Features +* IPv6 support. +* Generic serialization that is automatically done in parallel with the world save. + * The [faction system](https://github.com/modernuo/ModernUO/blob/7adf52ef48df7ae2b034c27e67b0c332b37fb053/Projects/UOContent/Engines/Factions/Core/FactionSystem.cs#L15) is no longer powered by a single item and instead uses generic persistence. +* Timezone support for custom scripts that might need it. +* Hourly/daily/weekly/monthly backups & archiving using [Z-Standard](https://facebook.github.io/zstd). +* Client version detection (including CUO) for easy configuration. +* Localization (Cliloc) support. +* Better encryption for passwords using [Argon2](https://en.wikipedia.org/wiki/Argon2). +* Packet throttling that is configurable per packet and per connection. +* Enable packet logging per connection. +* Owner accounts can be protected from being locked out. The first account created is automatically added to this list. +* Use optional arguments from constructors for Add command. +* Captures Razor version and displays it in client gump +* Spawners can be exported/imported using commands and use a GUID for replacement. + +## Major Feature Changes +* By default, world saves are now every 5th minute of a real world hour. + * E.g. 5:00, 5:05, 5:10, regardless of when the server is booted. +* Some items have their constructor arguments rearranged. + +## Removed Features +* Reporting +* Remote Admin +* My RunUO +* Event Log +* DocsGen command + +## New Development Features & Changes +* Added a shared list/queue for temporary processing such as accumulating players to damage/kill. + * [PooledRefList](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledRefList.cs) + * [PooledRefQueue](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledRefQueue.cs) +* Adds HashSet that is ordered by insertion time + * [OrderedHashSet](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/OrderedHashSet.cs) + * [PooledOrderedHashSet](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledOrderedHashSet.cs) +* Adds [StringBuilder](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Buffers/ValueStringBuilder.cs) that is fast and does not impose garbage collection. +* Adds [BitArray](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/BitArray.cs) that can be serialized/deserialized directly to a buffer. +* Adds performant [JSON](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Json/JsonConfig.cs) support with simple API. +* Adds performant, thread _unsafe_, [ArrayPool](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Buffers/STArrayPool.cs) +* Adds highly performant and easy to use converters for [HexString](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Text/HexStringConverter.cs) representation of data diff --git a/SPONSORS.md b/SPONSORS.md index 769bced1f..70c0deede 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -3,7 +3,6 @@ Thank you to all of our generous sponsors that make ModernUO possible. **A special thank you to the following sponsors for their considerable contributions:** -* [Hephaestus Games](https://hephaestusgames.com) * [UO Outlands](https://uooutlands.com) * Prayer ([MagnUm-Opus](https://discord.gg/CzDEq3vv2N)) @@ -14,16 +13,23 @@ We greatly appreciate the support! Use one of the following platforms below: [Github Sponsors | ModernUO](https://github.com/sponsors/modernuo) #### Patreon -[Patreon | ModernUO](https://patreon.com/modernuo) +[Patreon | ModernUO](https://patreon.com/modernuo) #### Paypal -[Paypal | Sabresite](https://paypal.me/sabresite) +[Paypal | Sabresite](https://paypal.me/sabresite) #### Venmo -[Venmo | Sabresite](https://venmo.com/code?user_id=1834446441414656966) +[Venmo | Sabresite](https://venmo.com/code?user_id=1834446441414656966) -#### Bitcoin -37QmRWTCjVoNWycMpo1t5JnYVDmJTGSJ9W +#### Bitcoin (37QmRWTCjVoNWycMpo1t5JnYVDmJTGSJ9W) +Bitcoin -#### Ethereum -0x7A9D76F497d4Ee150Ada0ea0455fF3f6e8F3b6b8 + +#### Ethereum (0x7A9D76F497d4Ee150Ada0ea0455fF3f6e8F3b6b8) +Bitcoin + +#### Decentraland MANA (0xE425CF5aD90d193c4EF1eE6c5aFA8150834ABb1d) +Decrentraland + +#### Solana (AhVtRy9YXMBRf5snTRg6ybvMbGnzSryNx65BJxzg7fqa) +Solana diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 68a241499..a9a7cc38f 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -6,9 +6,9 @@ bring it to our attention. Post an issue or email us: hi@modernuo.com -The attached notices are provided for information only. +The attached notices are provided for informational purposes only. -License notice for corefx +License notice for dotnet --------------------------- The MIT License (MIT) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 38df04622..9820ec29a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ jobs: displayName: 'Install .NET 6' inputs: packageType: sdk - version: 6.0.100 + version: 6.0.300 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release win displayName: 'Build' @@ -40,15 +40,12 @@ jobs: 'Ubuntu 20': containerImage: mcr.microsoft.com/dotnet/sdk:6.0-focal os: ubuntu.20.04 - 'Fedora 32': - containerImage: fedora:32 - os: fedora.32 - 'Fedora 33': - containerImage: fedora:33 - os: fedora.33 - 'Fedora 34': - containerImage: fedora:34 - os: fedora.34 + 'Fedora 35': + containerImage: fedora:35 + os: fedora.35 + 'Fedora 36': + containerImage: fedora:36 + os: fedora.36 displayName: Linux @@ -62,7 +59,7 @@ jobs: displayName: 'Install .NET 6' inputs: packageType: sdk - version: 6.0.100 + version: 6.0.300 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release $(os) displayName: 'Build' diff --git a/docs/scripting-guide/serialization.md b/docs/scripting-guide/serialization.md new file mode 100644 index 000000000..095930751 --- /dev/null +++ b/docs/scripting-guide/serialization.md @@ -0,0 +1,163 @@ +--- +title: Serialization +--- + +# Serialization / Savings + +=== "Generic persistence" + ```Persistence.Serialize``` and ```Persistence.Deserialize``` is replaced with GenericPersistence class + + Example of how to persist a custom system. + + ```cs + namespace Server.ExampleSystem + { + public static class ExampleSerialization + { + public static void Configure() + { + GenericPersistence.Register("ExampleSystem", Serialize, Deserialize); + } + + public static void Serialize(IGenericWriter writer) + { + // Do serialization here + writer.WriteEncodedInt(0); // version + } + + public static void Deserialize(IGenericReader reader) + { + // Do deserialization here + var version = reader.ReadEncodedInt(); + } + } + } + ``` + +=== "Codegen" + ### Basic info + ModernUO can programatically generate migrations. This feature is based on internal C# Source generators [More info](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/) + + Old way of serializing objects: + ```cs + public class ExampleItem : Item + { + public string ExampleText { get; set; } + + [Constructible] + public ExampleItem() : base(0) + { + + } + public ExampleItem(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); //Version + writer.Write(ExampleText); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + ExampleText = reader.ReadString(); + } + } + ``` + + Same class serialized with codegen + ```cs + [Serializable(0)] + public partial class ExampleItem : Item + { + [SerializableField(0)] + public string ExampleText { get; set; } + + [Constructible] + public ExampleItem() : base(0) + { + + } + } + ``` + + ### Step by step + 1. Add ```SerializableAttribute(versionNumber)``` to your class and make it ```partial``` + ```cs + [Serializable(0)] + public partial class ExampleItem : Item + ``` + 1. Delete constructors with ```Serial serial```, ```Serialize``` and ```Deserialize``` methods. + 1. Add ```SerializableField(fieldOrder)``` attribute to all field you want to serialize. + ```cs + [SerializableField(0)] + public string ExampleText { get; set; } + ``` + 1. Build project "Run Schema Migrations". ModernUO will create migration files for you. In this case "Server.Items.ExampleItem.v0.json" and "Server.Items.ExampleItem.Serialization.cs" + These files contains all information and classes needed for MUO to serialize/deserialize your objects. + + ### Migrations + When new field is added to serialization, you need to increment versionNumber and make migration files. Here is little example. + + New class code will look like this: + ```cs + [Serializable(1)] + public partial class ExampleItem : Item + { + [SerializableField(0)] + public string ExampleText { get; set; } + + [SerializableField(1)] + public string AddedExampleTest { get; set; } + + [Constructible] + public ExampleItem() : base(0) + { + } + } + ``` + + After building "Run Schema Migrations" project, MUO will generate V0Content in serialization class. + + This Content contains all fields from V0. + Now create MigrateFrom for each version you make, in this case V0. + + !!! Tip + When you have more versions, create standalone file for migrations only. For example "ExampleItem.Migrations.cs" + + ```cs + private void MigrateFrom(V0Content content) + { + ExampleText = content.ExampleText; + } + ``` + + Your migration is now completed. + + ### Migrating from pre-codegen + For migration from pre-codegen code, use method + ```cs + private void Deserialize(IGenericReader reader, int version) + ``` + + this method is called when codegen doesnt have VXContent for deserialized object or version of Content is lower than deserialized. + In this method you can make old fashioned deserialization as before codegen. + + ### After deserialization + For some code changes after world load, you can use AfterDeserializationAttribute. + ```cs + [AfterDeserialization] + private void AfterDeserialization() + { + // Some code here + } + ``` + + ### Embedded serialization + Sometimes you need to serialize object inside object. For this you should use "EmbeddedSerializableAttribute". Nice example to understand it is ["AquariumState"](https://github.com/modernuo/ModernUO/blob/main/Projects/UOContent/Items/Aquarium/AquariumState.cs) diff --git a/docs/scripting-guide/timers.md b/docs/scripting-guide/timers.md new file mode 100644 index 000000000..443291e14 --- /dev/null +++ b/docs/scripting-guide/timers.md @@ -0,0 +1,8 @@ +--- +title: Timers +--- + +# Timers + +ModernUO completely changed the timer system to use an optimized data structure called a timer wheel. This will allow shards to add thousands of timers without slowing down the server. Traditionally the timer system used a thread and locked to add/remove/process timers. All of this is gone. +With the new timer system there is no TimerPriority. This can be deleted entirely from your scripts. diff --git a/mkdocs.yml b/mkdocs.yml index dcb2d826d..9ab434a9c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,7 +9,7 @@ edit_uri: edit/main/docs/ repo_name: modernuo/modernuo repo_url: https://github.com/modernuo/modernuo site_description: The Ultima Online Server Emulator for the modern era! -copyright: Copyright 2019-2020 ModernUO Development Team +copyright: Copyright 2019-2022 ModernUO Development Team theme: name: material favicon: branding/favicon.png @@ -72,3 +72,6 @@ nav: - 'Get Started': - 'installation.md' - 'building-server.md' + - 'Scripting guide': + - 'scripting-guide/timers.md' + - 'scripting-guide/serialization.md' diff --git a/publish.cmd b/publish.cmd index 0490b16a8..6ce7daf6e 100755 --- a/publish.cmd +++ b/publish.cmd @@ -12,8 +12,7 @@ elif [[ $(uname) = "Darwin" ]]; then os="-r osx-x64" elif [[ -f /etc/os-release ]]; then . /etc/os-release - NAME="$(tr '[:upper:]' '[:lower:]' <<< $NAME | tr -d [:blank:])" - os="-r $NAME.$VERSION_ID-x64" + os="-r $(tr '[:upper:]' '[:lower:]' <<< $ID).$VERSION_ID-x64" fi if [[ $config ]]; then @@ -27,21 +26,19 @@ if [[ $os == *'centos'* || $os == *'rhel'* ]]; then export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 fi +echo dotnet tool restore +dotnet tool restore + echo dotnet clean --verbosity quiet dotnet clean --verbosity quiet echo dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json -echo dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj -dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj - echo dotnet publish ${config} ${os} --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj dotnet publish ${config} ${os} --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj -echo dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj -dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj -echo Generating serialization schemas -dotnet Projects/SerializationSchemaGenerator/Output/SerializationSchemaGenerator.dll ModernUO.sln +echo Generating serialization migration schema... +dotnet tool run ModernUOSchemaGenerator -- ModernUO.sln exit $? @@ -63,18 +60,16 @@ IF "%~2" == "" ( SET os=-r %~2-x64 ) +echo dotnet tool restore +dotnet tool restore + echo dotnet clean --verbosity quiet dotnet clean --verbosity quiet echo dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json -echo dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj -dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj - echo dotnet publish %config% %os% --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj dotnet publish %config% %os% --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj -echo dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj -dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj -echo Generating serialization schemas -dotnet Projects/SerializationSchemaGenerator/Output/SerializationSchemaGenerator.dll ModernUO.sln +echo Generating serialization migration schema... +dotnet tool run ModernUOSchemaGenerator -- ModernUO.sln diff --git a/version.json b/version.json index dd101024d..b5377358e 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.8.3" + "version": "0.9.2" }