diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index dffe0944e..cf746c82e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET 5 uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.302 + dotnet-version: 5.0.401 - name: Build run: ./publish.cmd - name: Test diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index a74350987..f08889d15 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - token: ${{ secrets.ACCESS_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} - name: Install NGBV uses: dotnet/nbgv@master id: nbgv diff --git a/.gitignore b/.gitignore index f2e5b33e2..325476c99 100644 --- a/.gitignore +++ b/.gitignore @@ -2,13 +2,16 @@ /Distribution/ModernUO /Distribution/ModernUO.* /Distribution/Assemblies +/Distribution/bsdtar /Distribution/Configuration/modernuo.json /Distribution/Configuration/email-settings.json /Distribution/Configuration/throttles.json /Distribution/Logs +/Distribution/Archives /Distribution/Backups /Distribution/Saves /Distribution/docs +/Distribution/temp /Distribution/*.dylib /Distribution/*.so /Distribution/*.dll diff --git a/Directory.Build.props b/Directory.Build.props index ac42d2e11..0ac231599 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -57,9 +57,9 @@ - + + 3.4.240 all - runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Distribution/Data/Decoration/Britannia/_hythloth.cfg b/Distribution/Data/Decoration/Britannia/_hythloth.cfg index f85e11cf2..b462966b2 100644 --- a/Distribution/Data/Decoration/Britannia/_hythloth.cfg +++ b/Distribution/Data/Decoration/Britannia/_hythloth.cfg @@ -701,10 +701,6 @@ MetalDoor2 0x06C7 (Facing=EastCCW) 5988 159 0 6060 167 0 -# Blood Moss -Static 0x0F7B -2678 712 0 - # garbage Static 0x10F0 5988 46 22 diff --git a/Distribution/Data/Professions/AOS/prof.txt b/Distribution/Data/Professions/AOS/prof.txt new file mode 100644 index 000000000..50dc7dec1 --- /dev/null +++ b/Distribution/Data/Professions/AOS/prof.txt @@ -0,0 +1,85 @@ +Begin + Name Paladin + TrueName "Paladin" + NameId 1061177 + DescId 1061227 + Desc 5 + TopLevel true + Gump 5587 + Type Profession + Skill Chivalry 50 + Skill Tactics 50 + Skill Wrestling 0 + Stat Str 30 + Stat Dex 25 + Stat Int 25 +End + +Begin + Name Necromancer + TrueName "Necromancer" + NameId 1061178 + DescId 1061228 + Desc 4 + TopLevel true + Gump 5557 + Type Profession + Skill Necromancy 50 + Skill Swordsmanship 30 + Skill Wrestling 0 + Skill Tactics 20 + Stat Str 25 + Stat Dex 20 + Stat Int 45 +End + +Begin + Name Warrior + TrueName "Warrior" + NameId 1061180 + DescId 1061230 + Desc 1 + TopLevel true + Gump 5577 + Type Profession + Skill Tactics 50 + Skill Healing 45 + Skill Swordsmanship 5 + Stat Str 35 + Stat Dex 35 + Stat Int 10 +End + +Begin + Name Mage + TrueName "Mage" + NameId 1061179 + DescId 1061229 + Desc 2 + TopLevel true + Gump 5569 + Type Profession + Skill Magery 50 + Skill Meditation 50 + Skill Wrestling 0 + Stat Str 25 + Stat Dex 10 + Stat Int 45 +End + +Begin + Name Blacksmith + TrueName "Blacksmith" + NameId 1061181 + DescId 1061231 + Desc 3 + TopLevel true + Gump 5555 + Type Profession + Skill Blacksmith 50 + Skill Tinkering 45 + Skill Mining 5 + Stat Str 60 + Stat Dex 10 + Stat Int 10 +End \ No newline at end of file diff --git a/Distribution/Data/Professions/None/prof.txt b/Distribution/Data/Professions/None/prof.txt new file mode 100644 index 000000000..2225b8b16 --- /dev/null +++ b/Distribution/Data/Professions/None/prof.txt @@ -0,0 +1,50 @@ +Begin + Name Warrior + TrueName "Warrior" + NameId 1061180 + DescId 1061230 + Desc 1 + TopLevel true + Gump 5577 + Type Profession + Skill Tactics 50 + Skill Healing 45 + Skill Swordsmanship 5 + Stat Str 35 + Stat Dex 35 + Stat Int 10 +End + +Begin + Name Mage + TrueName "Mage" + NameId 1061179 + DescId 1061229 + Desc 2 + TopLevel true + Gump 5569 + Type Profession + Skill Magery 50 + Skill Meditation 50 + Skill Wrestling 0 + Stat Str 25 + Stat Dex 10 + Stat Int 45 +End + +Begin + Name Blacksmith + TrueName "Blacksmith" + NameId 1061181 + DescId 1061231 + Desc 3 + TopLevel true + Gump 5555 + Type Profession + Skill Blacksmith 50 + Skill Tinkering 45 + Skill Mining 5 + Stat Str 60 + Stat Dex 10 + Stat Int 10 +End diff --git a/Distribution/Data/Professions/SA/Prof.txt b/Distribution/Data/Professions/SA/Prof.txt new file mode 100644 index 000000000..d73afe23f --- /dev/null +++ b/Distribution/Data/Professions/SA/Prof.txt @@ -0,0 +1,125 @@ +Begin + Name Samurai + TrueName "Samurai" + NameId 1062948 + DescId 1062950 + Desc 6 + TopLevel true + Gump 5591 + Type Profession + Skill Bushido 30 + Skill Swordsmanship 30 + Skill Focus 30 + Skill Parrying 30 + Stat Str 40 + Stat Dex 30 + Stat Int 20 +End + +Begin + Name Ninja + TrueName "Ninja" + NameId 1062949 + DescId 1062951 + Desc 7 + TopLevel true + Gump 5589 + Type Profession + Skill Ninjitsu 30 + Skill Hiding 30 + Skill Fencing 30 + Skill Stealth 30 + Stat Str 40 + Stat Dex 30 + Stat Int 20 +End + +Begin + Name Paladin + TrueName "Paladin" + NameId 1061177 + DescId 1061227 + Desc 5 + TopLevel true + Gump 5587 + Type Profession + Skill Chivalry 30 + Skill Tactics 30 + Skill Focus 30 + Skill Swordsmanship 30 + Stat Str 45 + Stat Dex 20 + Stat Int 25 +End + +Begin + Name Necromancer + TrueName "Necromancer" + NameId 1061178 + DescId 1061228 + Desc 4 + TopLevel true + Gump 5557 + Type Profession + Skill Necromancy 30 + Skill Spirit Speak 30 + Skill Swordsmanship 30 + Skill Meditation 30 + Stat Str 25 + Stat Dex 20 + Stat Int 45 +End + +Begin + Name Warrior + TrueName "Warrior" + NameId 1061180 + DescId 1061230 + Desc 1 + TopLevel true + Gump 5577 + Type Profession + Skill Tactics 30 + Skill Healing 30 + Skill Swordsmanship 30 + Skill Anatomy 30 + Stat Str 45 + Stat Dex 35 + Stat Int 10 +End + +Begin + Name Mage + TrueName "Mage" + NameId 1061179 + DescId 1061229 + Desc 2 + TopLevel true + Gump 5569 + Type Profession + Skill Magery 30 + Skill Meditation 30 + Skill Evaluate Intelligence 30 + Skill Wrestling 30 + Stat Str 25 + Stat Dex 20 + Stat Int 45 +End + +Begin + Name Blacksmith + TrueName "Blacksmith" + NameId 1061181 + DescId 1061231 + Desc 3 + TopLevel true + Gump 5555 + Type Profession + Skill Blacksmith 30 + Skill Tinkering 30 + Skill Mining 30 + Skill Tailoring 30 + Stat Str 60 + Stat Dex 15 + Stat Int 15 +End \ No newline at end of file diff --git a/Distribution/Data/Professions/SE/Prof.txt b/Distribution/Data/Professions/SE/Prof.txt new file mode 100644 index 000000000..dc01538c0 --- /dev/null +++ b/Distribution/Data/Professions/SE/Prof.txt @@ -0,0 +1,118 @@ +Begin + Name Samurai + TrueName "Samurai" + NameId 1062948 + DescId 1062950 + Desc 6 + TopLevel true + Gump 5591 + Type Profession + Skill Bushido 50 + Skill Swordsmanship 50 + Skill Wrestling 0 + Stat Str 40 + Stat Dex 30 + Stat Int 10 +End + +Begin + Name Ninja + TrueName "Ninja" + NameId 1062949 + DescId 1062951 + Desc 7 + TopLevel true + Gump 5589 + Type Profession + Skill Ninjitsu 50 + Skill Hiding 50 + Skill Wrestling 0 + Stat Str 40 + Stat Dex 30 + Stat Int 10 +End + +Begin + Name Paladin + TrueName "Paladin" + NameId 1061177 + DescId 1061227 + Desc 5 + TopLevel true + Gump 5587 + Type Profession + Skill Chivalry 50 + Skill Tactics 50 + Skill Wrestling 0 + Stat Str 45 + Stat Dex 20 + Stat Int 15 +End + +Begin + Name Necromancer + TrueName "Necromancer" + NameId 1061178 + DescId 1061228 + Desc 4 + TopLevel true + Gump 5557 + Type Profession + Skill Necromancy 50 + Skill Swordsmanship 30 + Skill Tactics 20 + Stat Str 25 + Stat Dex 20 + Stat Int 35 +End + +Begin + Name Warrior + TrueName "Warrior" + NameId 1061180 + DescId 1061230 + Desc 1 + TopLevel true + Gump 5577 + Type Profession + Skill Tactics 50 + Skill Healing 45 + Skill Swordsmanship 5 + Stat Str 35 + Stat Dex 35 + Stat Int 10 +End + +Begin + Name Mage + TrueName "Mage" + NameId 1061179 + DescId 1061229 + Desc 2 + TopLevel true + Gump 5569 + Type Profession + Skill Magery 50 + Skill Meditation 50 + Skill Wrestling 0 + Stat Str 25 + Stat Dex 10 + Stat Int 45 +End + +Begin + Name Blacksmith + TrueName "Blacksmith" + NameId 1061181 + DescId 1061231 + Desc 3 + TopLevel true + Gump 5555 + Type Profession + Skill Blacksmith 50 + Skill Tinkering 45 + Skill Mining 5 + Stat Str 60 + Stat Dex 10 + Stat Int 10 +End \ No newline at end of file diff --git a/Distribution/Data/Professions/all-profession-skill-names.txt b/Distribution/Data/Professions/all-profession-skill-names.txt new file mode 100644 index 000000000..57f93a935 --- /dev/null +++ b/Distribution/Data/Professions/all-profession-skill-names.txt @@ -0,0 +1,58 @@ +Alchemy, +Anatomy, +AnimalLore, +ItemID, +ArmsLore, +Parrying, +Begging, +Blacksmith, +Bowcraft, +Peacemaking, +Camping, +Carpentry, +Cartography, +Cooking, +DetectHidden, +Enticement, +EvaluateIntelligence, +Healing, +Fishing, +ForensicEvaluation, +Herding, +Hiding, +Provocation, +Inscription, +Lockpicking, +Magery, +ResistingSpells, +Tactics, +Snooping, +Musicianship, +Poisoning, +Archery, +SpiritSpeak, +Stealing, +Tailoring, +AnimalTaming, +TasteIdentification, +Tinkering, +Tracking, +Veterinary, +Swordsmanship, +MaceFighting, +Fencing, +Wrestling, +Lumberjacking, +Mining, +Meditation, +Stealth, +Disarm, +Necromancy, +Focus, +Chivalry, +Bushido, +Ninjitsu, +Spellweaving, +Mysticism, +Imbuing, +Throwing diff --git a/Distribution/Data/expansion.json b/Distribution/Data/expansion.json new file mode 100644 index 000000000..e0b036f61 --- /dev/null +++ b/Distribution/Data/expansion.json @@ -0,0 +1,794 @@ +[ + { + "Name": "None", + "ClientVersion": null, + "ClientFlags": null, + "FeatureFlags": { + "None": true, + "T2A": false, + "UOR": false, + "UOTD": false, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": true, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": false, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "The Second Age", + "ClientVersion": null, + "ClientFlags": "Felucca", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": false, + "UOTD": false, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Renaissance", + "ClientVersion": null, + "ClientFlags": "Trammel", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": false, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Third Dawn", + "ClientVersion": null, + "ClientFlags": "Ilshenar", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Blackthorn's Revenge", + "ClientVersion": null, + "ClientFlags": "Ilshenar", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Age of Shadows", + "ClientVersion": null, + "ClientFlags": "Malas", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Samurai Empire", + "ClientVersion": null, + "ClientFlags": "Tokuno", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Mondain's Legacy", + "ClientVersion": "5.0.0a", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Stygian Abyss", + "ClientVersion": null, + "ClientFlags": "TerMur", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": false, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": false, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "High Seas", + "ClientVersion": "7.0.9.0", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Time of Legends", + "ClientVersion": "7.0.45.65", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": false + } + }, + { + "Name": "Endless Journey", + "ClientVersion": "7.0.61.0", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": true + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": true + } + } +] diff --git a/Distribution/Data/timezones.json b/Distribution/Data/timezones.json new file mode 100644 index 000000000..e61680c1e --- /dev/null +++ b/Distribution/Data/timezones.json @@ -0,0 +1,1157 @@ +/* Generated from https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml */ +[ + { + "timezone": "Dateline Standard Time", + "regions": [ + "Etc/GMT+12" + ] + }, + { + "timezone": "UTC-11", + "regions": [ + "Etc/GMT+11", + "Pacific/Pago_Pago", + "Pacific/Niue", + "Pacific/Midway" + ] + }, + { + "timezone": "Aleutian Standard Time", + "regions": [ + "America/Adak" + ] + }, + { + "timezone": "Hawaiian Standard Time", + "regions": [ + "Pacific/Honolulu", + "Pacific/Rarotonga", + "Pacific/Tahiti", + "Pacific/Johnston", + "Etc/GMT+10" + ] + }, + { + "timezone": "Marquesas Standard Time", + "regions": [ + "Pacific/Marquesas" + ] + }, + { + "timezone": "Alaskan Standard Time", + "regions": [ + "America/Anchorage", + "America/Juneau", + "America/Metlakatla", + "America/Nome", + "America/Sitka", + "America/Yakutat" + ] + }, + { + "timezone": "UTC-09", + "regions": [ + "Etc/GMT+9", + "Pacific/Gambier" + ] + }, + { + "timezone": "Pacific Standard Time (Mexico)", + "regions": [ + "America/Tijuana", + "America/Santa_Isabel" + ] + }, + { + "timezone": "UTC-08", + "regions": [ + "Etc/GMT+8", + "Pacific/Pitcairn" + ] + }, + { + "timezone": "Pacific Standard Time", + "regions": [ + "America/Los_Angeles", + "America/Vancouver", + "PST8PDT" + ] + }, + { + "timezone": "US Mountain Standard Time", + "regions": [ + "America/Phoenix", + "America/Creston", + "America/Dawson_Creek", + "America/Fort_Nelson", + "America/Hermosillo", + "Etc/GMT+7" + ] + }, + { + "timezone": "Mountain Standard Time (Mexico)", + "regions": [ + "America/Chihuahua", + "America/Mazatlan" + ] + }, + { + "timezone": "Mountain Standard Time", + "regions": [ + "America/Denver", + "America/Edmonton", + "America/Cambridge_Bay", + "America/Inuvik", + "America/Yellowknife", + "America/Ojinaga", + "America/Boise", + "MST7MDT" + ] + }, + { + "timezone": "Yukon Standard Time", + "regions": [ + "America/Whitehorse", + "America/Dawson" + ] + }, + { + "timezone": "Central America Standard Time", + "regions": [ + "America/Guatemala", + "America/Belize", + "America/Costa_Rica", + "Pacific/Galapagos", + "America/Tegucigalpa", + "America/Managua", + "America/El_Salvador", + "Etc/GMT+6" + ] + }, + { + "timezone": "Central Standard Time", + "regions": [ + "America/Chicago", + "America/Winnipeg", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Resolute", + "America/Matamoros", + "America/Indiana/Knox", + "America/Indiana/Tell_City", + "America/Menominee", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "CST6CDT" + ] + }, + { + "timezone": "Easter Island Standard Time", + "regions": [ + "Pacific/Easter" + ] + }, + { + "timezone": "Central Standard Time (Mexico)", + "regions": [ + "America/Mexico_City", + "America/Bahia_Banderas", + "America/Merida", + "America/Monterrey" + ] + }, + { + "timezone": "Canada Central Standard Time", + "regions": [ + "America/Regina", + "America/Swift_Current" + ] + }, + { + "timezone": "SA Pacific Standard Time", + "regions": [ + "America/Bogota", + "America/Rio_Branco", + "America/Eirunepe", + "America/Coral_Harbour", + "America/Guayaquil", + "America/Jamaica", + "America/Cayman", + "America/Panama", + "America/Lima", + "Etc/GMT+5" + ] + }, + { + "timezone": "Eastern Standard Time (Mexico)", + "regions": [ + "America/Cancun" + ] + }, + { + "timezone": "Eastern Standard Time", + "regions": [ + "America/New_York", + "America/Nassau", + "America/Toronto", + "America/Iqaluit", + "America/Montreal", + "America/Nipigon", + "America/Pangnirtung", + "America/Thunder_Bay", + "America/Detroit", + "America/Indiana/Petersburg", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Kentucky/Monticello", + "America/Louisville", + "EST5EDT" + ] + }, + { + "timezone": "Haiti Standard Time", + "regions": [ + "America/Port-au-Prince" + ] + }, + { + "timezone": "Cuba Standard Time", + "regions": [ + "America/Havana" + ] + }, + { + "timezone": "US Eastern Standard Time", + "regions": [ + "America/Indianapolis", + "America/Indiana/Marengo", + "America/Indiana/Vevay" + ] + }, + { + "timezone": "Turks And Caicos Standard Time", + "regions": [ + "America/Grand_Turk" + ] + }, + { + "timezone": "Paraguay Standard Time", + "regions": [ + "America/Asuncion" + ] + }, + { + "timezone": "Atlantic Standard Time", + "regions": [ + "America/Halifax", + "Atlantic/Bermuda", + "America/Glace_Bay", + "America/Goose_Bay", + "America/Moncton", + "America/Thule" + ] + }, + { + "timezone": "Venezuela Standard Time", + "regions": [ + "America/Caracas" + ] + }, + { + "timezone": "Central Brazilian Standard Time", + "regions": [ + "America/Cuiaba", + "America/Campo_Grande" + ] + }, + { + "timezone": "SA Western Standard Time", + "regions": [ + "America/La_Paz", + "America/Antigua", + "America/Anguilla", + "America/Aruba", + "America/Barbados", + "America/St_Barthelemy", + "America/Kralendijk", + "America/Manaus", + "America/Boa_Vista", + "America/Porto_Velho", + "America/Blanc-Sablon", + "America/Curacao", + "America/Dominica", + "America/Santo_Domingo", + "America/Grenada", + "America/Guadeloupe", + "America/Guyana", + "America/St_Kitts", + "America/St_Lucia", + "America/Marigot", + "America/Martinique", + "America/Montserrat", + "America/Puerto_Rico", + "America/Lower_Princes", + "America/Port_of_Spain", + "America/St_Vincent", + "America/Tortola", + "America/St_Thomas", + "Etc/GMT+4" + ] + }, + { + "timezone": "Pacific SA Standard Time", + "regions": [ + "America/Santiago" + ] + }, + { + "timezone": "Newfoundland Standard Time", + "regions": [ + "America/St_Johns" + ] + }, + { + "timezone": "Tocantins Standard Time", + "regions": [ + "America/Araguaina" + ] + }, + { + "timezone": "E. South America Standard Time", + "regions": [ + "America/Sao_Paulo" + ] + }, + { + "timezone": "SA Eastern Standard Time", + "regions": [ + "America/Cayenne", + "Antarctica/Rothera", + "Antarctica/Palmer", + "America/Fortaleza", + "America/Belem", + "America/Maceio", + "America/Recife", + "America/Santarem", + "Atlantic/Stanley", + "America/Paramaribo", + "Etc/GMT+3" + ] + }, + { + "timezone": "Argentina Standard Time", + "regions": [ + "America/Buenos_Aires", + "America/Argentina/La_Rioja", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Catamarca", + "America/Cordoba", + "America/Jujuy", + "America/Mendoza" + ] + }, + { + "timezone": "Greenland Standard Time", + "regions": [ + "America/Godthab" + ] + }, + { + "timezone": "Montevideo Standard Time", + "regions": [ + "America/Montevideo" + ] + }, + { + "timezone": "Magallanes Standard Time", + "regions": [ + "America/Punta_Arenas" + ] + }, + { + "timezone": "Saint Pierre Standard Time", + "regions": [ + "America/Miquelon" + ] + }, + { + "timezone": "Bahia Standard Time", + "regions": [ + "America/Bahia" + ] + }, + { + "timezone": "UTC-02", + "regions": [ + "Etc/GMT+2", + "America/Noronha", + "Atlantic/South_Georgia" + ] + }, + { + "timezone": "Azores Standard Time", + "regions": [ + "Atlantic/Azores", + "America/Scoresbysund" + ] + }, + { + "timezone": "Cape Verde Standard Time", + "regions": [ + "Atlantic/Cape_Verde", + "Etc/GMT+1" + ] + }, + { + "timezone": "UTC", + "regions": [ + "Etc/UTC", + "Etc/GMT" + ] + }, + { + "timezone": "GMT Standard Time", + "regions": [ + "Europe/London", + "Atlantic/Canary", + "Atlantic/Faeroe", + "Europe/Guernsey", + "Europe/Dublin", + "Europe/Isle_of_Man", + "Europe/Jersey", + "Europe/Lisbon", + "Atlantic/Madeira" + ] + }, + { + "timezone": "Greenwich Standard Time", + "regions": [ + "Atlantic/Reykjavik", + "Africa/Ouagadougou", + "Africa/Abidjan", + "Africa/Accra", + "America/Danmarkshavn", + "Africa/Banjul", + "Africa/Conakry", + "Africa/Bissau", + "Africa/Monrovia", + "Africa/Bamako", + "Africa/Nouakchott", + "Atlantic/St_Helena", + "Africa/Freetown", + "Africa/Dakar", + "Africa/Lome" + ] + }, + { + "timezone": "Sao Tome Standard Time", + "regions": [ + "Africa/Sao_Tome" + ] + }, + { + "timezone": "Morocco Standard Time", + "regions": [ + "Africa/Casablanca", + "Africa/El_Aaiun" + ] + }, + { + "timezone": "W. Europe Standard Time", + "regions": [ + "Europe/Berlin", + "Europe/Andorra", + "Europe/Vienna", + "Europe/Zurich", + "Europe/Busingen", + "Europe/Gibraltar", + "Europe/Rome", + "Europe/Vaduz", + "Europe/Luxembourg", + "Europe/Monaco", + "Europe/Malta", + "Europe/Amsterdam", + "Europe/Oslo", + "Europe/Stockholm", + "Arctic/Longyearbyen", + "Europe/San_Marino", + "Europe/Vatican" + ] + }, + { + "timezone": "Central Europe Standard Time", + "regions": [ + "Europe/Budapest", + "Europe/Tirane", + "Europe/Prague", + "Europe/Podgorica", + "Europe/Belgrade", + "Europe/Ljubljana", + "Europe/Bratislava" + ] + }, + { + "timezone": "Romance Standard Time", + "regions": [ + "Europe/Paris", + "Europe/Brussels", + "Europe/Copenhagen", + "Europe/Madrid", + "Africa/Ceuta" + ] + }, + { + "timezone": "Central European Standard Time", + "regions": [ + "Europe/Warsaw", + "Europe/Sarajevo", + "Europe/Zagreb", + "Europe/Skopje" + ] + }, + { + "timezone": "W. Central Africa Standard Time", + "regions": [ + "Africa/Lagos", + "Africa/Luanda", + "Africa/Porto-Novo", + "Africa/Kinshasa", + "Africa/Bangui", + "Africa/Brazzaville", + "Africa/Douala", + "Africa/Algiers", + "Africa/Libreville", + "Africa/Malabo", + "Africa/Niamey", + "Africa/Ndjamena", + "Africa/Tunis", + "Etc/GMT-1" + ] + }, + { + "timezone": "Jordan Standard Time", + "regions": [ + "Asia/Amman" + ] + }, + { + "timezone": "GTB Standard Time", + "regions": [ + "Europe/Bucharest", + "Asia/Nicosia", + "Asia/Famagusta", + "Europe/Athens" + ] + }, + { + "timezone": "Middle East Standard Time", + "regions": [ + "Asia/Beirut" + ] + }, + { + "timezone": "Egypt Standard Time", + "regions": [ + "Africa/Cairo" + ] + }, + { + "timezone": "E. Europe Standard Time", + "regions": [ + "Europe/Chisinau" + ] + }, + { + "timezone": "Syria Standard Time", + "regions": [ + "Asia/Damascus" + ] + }, + { + "timezone": "West Bank Standard Time", + "regions": [ + "Asia/Hebron", + "Asia/Gaza" + ] + }, + { + "timezone": "South Africa Standard Time", + "regions": [ + "Africa/Johannesburg", + "Africa/Bujumbura", + "Africa/Gaborone", + "Africa/Lubumbashi", + "Africa/Maseru", + "Africa/Blantyre", + "Africa/Maputo", + "Africa/Kigali", + "Africa/Mbabane", + "Africa/Lusaka", + "Africa/Harare", + "Etc/GMT-2" + ] + }, + { + "timezone": "FLE Standard Time", + "regions": [ + "Europe/Kiev", + "Europe/Mariehamn", + "Europe/Sofia", + "Europe/Tallinn", + "Europe/Helsinki", + "Europe/Vilnius", + "Europe/Riga", + "Europe/Uzhgorod", + "Europe/Zaporozhye" + ] + }, + { + "timezone": "Israel Standard Time", + "regions": [ + "Asia/Jerusalem" + ] + }, + { + "timezone": "South Sudan Standard Time", + "regions": [ + "Africa/Juba" + ] + }, + { + "timezone": "Kaliningrad Standard Time", + "regions": [ + "Europe/Kaliningrad" + ] + }, + { + "timezone": "Sudan Standard Time", + "regions": [ + "Africa/Khartoum" + ] + }, + { + "timezone": "Libya Standard Time", + "regions": [ + "Africa/Tripoli" + ] + }, + { + "timezone": "Namibia Standard Time", + "regions": [ + "Africa/Windhoek" + ] + }, + { + "timezone": "Arabic Standard Time", + "regions": [ + "Asia/Baghdad" + ] + }, + { + "timezone": "Turkey Standard Time", + "regions": [ + "Europe/Istanbul" + ] + }, + { + "timezone": "Arab Standard Time", + "regions": [ + "Asia/Riyadh", + "Asia/Bahrain", + "Asia/Kuwait", + "Asia/Qatar", + "Asia/Aden" + ] + }, + { + "timezone": "Belarus Standard Time", + "regions": [ + "Europe/Minsk" + ] + }, + { + "timezone": "Russian Standard Time", + "regions": [ + "Europe/Moscow", + "Europe/Kirov", + "Europe/Simferopol" + ] + }, + { + "timezone": "E. Africa Standard Time", + "regions": [ + "Africa/Nairobi", + "Antarctica/Syowa", + "Africa/Djibouti", + "Africa/Asmera", + "Africa/Addis_Ababa", + "Indian/Comoro", + "Indian/Antananarivo", + "Africa/Mogadishu", + "Africa/Dar_es_Salaam", + "Africa/Kampala", + "Indian/Mayotte", + "Etc/GMT-3" + ] + }, + { + "timezone": "Iran Standard Time", + "regions": [ + "Asia/Tehran" + ] + }, + { + "timezone": "Arabian Standard Time", + "regions": [ + "Asia/Dubai", + "Asia/Muscat", + "Etc/GMT-4" + ] + }, + { + "timezone": "Astrakhan Standard Time", + "regions": [ + "Europe/Astrakhan", + "Europe/Ulyanovsk" + ] + }, + { + "timezone": "Azerbaijan Standard Time", + "regions": [ + "Asia/Baku" + ] + }, + { + "timezone": "Russia Time Zone 3", + "regions": [ + "Europe/Samara" + ] + }, + { + "timezone": "Mauritius Standard Time", + "regions": [ + "Indian/Mauritius", + "Indian/Reunion", + "Indian/Mahe" + ] + }, + { + "timezone": "Saratov Standard Time", + "regions": [ + "Europe/Saratov" + ] + }, + { + "timezone": "Georgian Standard Time", + "regions": [ + "Asia/Tbilisi" + ] + }, + { + "timezone": "Volgograd Standard Time", + "regions": [ + "Europe/Volgograd" + ] + }, + { + "timezone": "Caucasus Standard Time", + "regions": [ + "Asia/Yerevan" + ] + }, + { + "timezone": "Afghanistan Standard Time", + "regions": [ + "Asia/Kabul" + ] + }, + { + "timezone": "West Asia Standard Time", + "regions": [ + "Asia/Tashkent", + "Antarctica/Mawson", + "Asia/Oral", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Atyrau", + "Indian/Maldives", + "Indian/Kerguelen", + "Asia/Dushanbe", + "Asia/Ashgabat", + "Asia/Samarkand", + "Etc/GMT-5" + ] + }, + { + "timezone": "Ekaterinburg Standard Time", + "regions": [ + "Asia/Yekaterinburg" + ] + }, + { + "timezone": "Pakistan Standard Time", + "regions": [ + "Asia/Karachi" + ] + }, + { + "timezone": "Qyzylorda Standard Time", + "regions": [ + "Asia/Qyzylorda" + ] + }, + { + "timezone": "India Standard Time", + "regions": [ + "Asia/Calcutta" + ] + }, + { + "timezone": "Sri Lanka Standard Time", + "regions": [ + "Asia/Colombo" + ] + }, + { + "timezone": "Nepal Standard Time", + "regions": [ + "Asia/Katmandu" + ] + }, + { + "timezone": "Central Asia Standard Time", + "regions": [ + "Asia/Almaty", + "Antarctica/Vostok", + "Asia/Urumqi", + "Indian/Chagos", + "Asia/Bishkek", + "Asia/Qostanay", + "Etc/GMT-6" + ] + }, + { + "timezone": "Bangladesh Standard Time", + "regions": [ + "Asia/Dhaka", + "Asia/Thimphu" + ] + }, + { + "timezone": "Omsk Standard Time", + "regions": [ + "Asia/Omsk" + ] + }, + { + "timezone": "Myanmar Standard Time", + "regions": [ + "Asia/Rangoon", + "Indian/Cocos" + ] + }, + { + "timezone": "SE Asia Standard Time", + "regions": [ + "Asia/Bangkok", + "Antarctica/Davis", + "Indian/Christmas", + "Asia/Jakarta", + "Asia/Pontianak", + "Asia/Phnom_Penh", + "Asia/Vientiane", + "Asia/Saigon", + "Etc/GMT-7" + ] + }, + { + "timezone": "Altai Standard Time", + "regions": [ + "Asia/Barnaul" + ] + }, + { + "timezone": "W. Mongolia Standard Time", + "regions": [ + "Asia/Hovd" + ] + }, + { + "timezone": "North Asia Standard Time", + "regions": [ + "Asia/Krasnoyarsk", + "Asia/Novokuznetsk" + ] + }, + { + "timezone": "N. Central Asia Standard Time", + "regions": [ + "Asia/Novosibirsk" + ] + }, + { + "timezone": "Tomsk Standard Time", + "regions": [ + "Asia/Tomsk" + ] + }, + { + "timezone": "China Standard Time", + "regions": [ + "Asia/Shanghai", + "Asia/Hong_Kong", + "Asia/Macau" + ] + }, + { + "timezone": "North Asia East Standard Time", + "regions": [ + "Asia/Irkutsk" + ] + }, + { + "timezone": "Singapore Standard Time", + "regions": [ + "Asia/Singapore", + "Asia/Brunei", + "Asia/Makassar", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Manila", + "Etc/GMT-8" + ] + }, + { + "timezone": "W. Australia Standard Time", + "regions": [ + "Australia/Perth" + ] + }, + { + "timezone": "Taipei Standard Time", + "regions": [ + "Asia/Taipei" + ] + }, + { + "timezone": "Ulaanbaatar Standard Time", + "regions": [ + "Asia/Ulaanbaatar", + "Asia/Choibalsan" + ] + }, + { + "timezone": "Aus Central W. Standard Time", + "regions": [ + "Australia/Eucla" + ] + }, + { + "timezone": "Transbaikal Standard Time", + "regions": [ + "Asia/Chita" + ] + }, + { + "timezone": "Tokyo Standard Time", + "regions": [ + "Asia/Tokyo", + "Asia/Jayapura", + "Pacific/Palau", + "Asia/Dili", + "Etc/GMT-9" + ] + }, + { + "timezone": "North Korea Standard Time", + "regions": [ + "Asia/Pyongyang" + ] + }, + { + "timezone": "Korea Standard Time", + "regions": [ + "Asia/Seoul" + ] + }, + { + "timezone": "Yakutsk Standard Time", + "regions": [ + "Asia/Yakutsk", + "Asia/Khandyga" + ] + }, + { + "timezone": "Cen. Australia Standard Time", + "regions": [ + "Australia/Adelaide", + "Australia/Broken_Hill" + ] + }, + { + "timezone": "AUS Central Standard Time", + "regions": [ + "Australia/Darwin" + ] + }, + { + "timezone": "E. Australia Standard Time", + "regions": [ + "Australia/Brisbane", + "Australia/Lindeman" + ] + }, + { + "timezone": "AUS Eastern Standard Time", + "regions": [ + "Australia/Sydney", + "Australia/Melbourne" + ] + }, + { + "timezone": "West Pacific Standard Time", + "regions": [ + "Pacific/Port_Moresby", + "Antarctica/DumontDUrville", + "Pacific/Truk", + "Pacific/Guam", + "Pacific/Saipan", + "Etc/GMT-10" + ] + }, + { + "timezone": "Tasmania Standard Time", + "regions": [ + "Australia/Hobart", + "Australia/Currie", + "Antarctica/Macquarie" + ] + }, + { + "timezone": "Vladivostok Standard Time", + "regions": [ + "Asia/Vladivostok", + "Asia/Ust-Nera" + ] + }, + { + "timezone": "Lord Howe Standard Time", + "regions": [ + "Australia/Lord_Howe" + ] + }, + { + "timezone": "Bougainville Standard Time", + "regions": [ + "Pacific/Bougainville" + ] + }, + { + "timezone": "Russia Time Zone 10", + "regions": [ + "Asia/Srednekolymsk" + ] + }, + { + "timezone": "Magadan Standard Time", + "regions": [ + "Asia/Magadan" + ] + }, + { + "timezone": "Norfolk Standard Time", + "regions": [ + "Pacific/Norfolk" + ] + }, + { + "timezone": "Sakhalin Standard Time", + "regions": [ + "Asia/Sakhalin" + ] + }, + { + "timezone": "Central Pacific Standard Time", + "regions": [ + "Pacific/Guadalcanal", + "Antarctica/Casey", + "Pacific/Ponape", + "Pacific/Kosrae", + "Pacific/Noumea", + "Pacific/Efate", + "Etc/GMT-11" + ] + }, + { + "timezone": "Russia Time Zone 11", + "regions": [ + "Asia/Kamchatka", + "Asia/Anadyr" + ] + }, + { + "timezone": "New Zealand Standard Time", + "regions": [ + "Pacific/Auckland", + "Antarctica/McMurdo" + ] + }, + { + "timezone": "UTC+12", + "regions": [ + "Etc/GMT-12", + "Pacific/Tarawa", + "Pacific/Majuro", + "Pacific/Kwajalein", + "Pacific/Nauru", + "Pacific/Funafuti", + "Pacific/Wake", + "Pacific/Wallis" + ] + }, + { + "timezone": "Fiji Standard Time", + "regions": [ + "Pacific/Fiji" + ] + }, + { + "timezone": "Chatham Islands Standard Time", + "regions": [ + "Pacific/Chatham" + ] + }, + { + "timezone": "UTC+13", + "regions": [ + "Etc/GMT-13", + "Pacific/Enderbury", + "Pacific/Fakaofo" + ] + }, + { + "timezone": "Tonga Standard Time", + "regions": [ + "Pacific/Tongatapu" + ] + }, + { + "timezone": "Samoa Standard Time", + "regions": [ + "Pacific/Apia" + ] + }, + { + "timezone": "Line Islands Standard Time", + "regions": [ + "Pacific/Kiritimati", + "Etc/GMT-14" + ] + } +] diff --git a/FAQ.md b/FAQ.md index 1449ae9fe..4b7d5c3e4 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,15 +1,21 @@ # 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) +## 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. +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. + ## 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 Core does not support dynamic compiling. Scripts are now compiled separately and moved to the `Distribution/Assemblies` directory. -This also allows content to be divided into `modules` that can be included by dropping in the DLL. +.NET 5 does not support dynamic compiling newer versions of C#. Scripts are now compiled separately 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. +ModernUO is optimized for performance and modern hardware. For this reason we no longer support 32-bit operating systems diff --git a/Projects/Benchmarks/Benchmarks.csproj b/Projects/Benchmarks/Benchmarks.csproj index cd7d4a26c..2ed8e89fd 100644 --- a/Projects/Benchmarks/Benchmarks.csproj +++ b/Projects/Benchmarks/Benchmarks.csproj @@ -9,7 +9,7 @@ true - + diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs index 584515369..7611d5799 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs @@ -190,7 +190,7 @@ namespace SerializationGenerator ) != null ) : null; - var serializablePropertySet = new SortedSet(new SerializablePropertyComparer()); + var serializablePropertySet = new SortedDictionary(new SerializablePropertyComparer()); foreach (var fieldOrPropertySymbol in fieldsAndProperties) { @@ -267,10 +267,16 @@ namespace SerializationGenerator serializableFieldSaveFlagMethods ); - serializablePropertySet.Add(serializableProperty); + serializablePropertySet.Add(serializableProperty, fieldOrPropertySymbol); } - var serializableProperties = serializablePropertySet.ToImmutableArray(); + 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)) @@ -323,6 +329,7 @@ namespace SerializationGenerator indent, isOverride, encodedVersion, + serializableFields, serializableProperties, serializableFieldSaveFlags ); @@ -337,6 +344,7 @@ namespace SerializationGenerator version, encodedVersion, migrations, + serializableFields, serializableProperties, parentFieldOrProperty, serializableFieldSaveFlags diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs index 6a0665b9a..59048a125 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs @@ -33,6 +33,7 @@ namespace SerializationGenerator int version, bool encodedVersion, ImmutableArray migrations, + ImmutableArray fields, ImmutableArray properties, ISymbol parentFieldOrProperty, SortedDictionary serializableFieldSaveFlagMethodsDictionary @@ -124,11 +125,12 @@ namespace SerializationGenerator source.AppendLine($"{bodyIndent}var saveFlags = reader.ReadEnum();"); } - foreach (var property in properties) + 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 @@ -138,7 +140,7 @@ namespace SerializationGenerator // Special case if (property.Type == "bool") { - source.AppendLine($"{bodyIndent}{property.Name} = (saveFlags & SaveFlag.{property.Name}) != 0;"); + source.AppendLine($"{bodyIndent}{field.Name} = (saveFlags & SaveFlag.{property.Name}) != 0;"); } else { @@ -146,16 +148,22 @@ namespace SerializationGenerator rule.GenerateDeserializationMethod( source, innerIndent, - property, + field, parentFieldOrProperty?.Name ?? "this" ); - (rule as IPostDeserializeMethod)?.PostDeserializeMethod(source, innerIndent, property, compilation, classSymbol); + (rule as IPostDeserializeMethod)?.PostDeserializeMethod( + source, + innerIndent, + field, + compilation, + classSymbol + ); if (serializableFieldSaveFlagMethods.GetFieldDefaultValue != null) { source.AppendLine($"{bodyIndent}}}\n{bodyIndent}else\n{bodyIndent}{{"); source.AppendLine( - $"{bodyIndent} {property.Name} = {serializableFieldSaveFlagMethods.GetFieldDefaultValue.Name}();" + $"{bodyIndent} {field.Name} = {serializableFieldSaveFlagMethods.GetFieldDefaultValue.Name}();" ); } @@ -168,10 +176,16 @@ namespace SerializationGenerator rule.GenerateDeserializationMethod( source, bodyIndent, - property, + field, parentFieldOrProperty?.Name ?? "this" ); - (rule as IPostDeserializeMethod)?.PostDeserializeMethod(source, bodyIndent, property, compilation, classSymbol); + (rule as IPostDeserializeMethod)?.PostDeserializeMethod( + source, + bodyIndent, + field, + compilation, + classSymbol + ); } } diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs index d41a278b4..6aff93f5e 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs @@ -29,6 +29,7 @@ namespace SerializationGenerator string indent, bool isOverride, bool encodedVersion, + ImmutableArray fields, ImmutableArray properties, SortedDictionary serializableFieldSaveFlagMethodsDictionary ) @@ -74,30 +75,34 @@ namespace SerializationGenerator source.AppendLine($"{bodyIndent}writer.WriteEnum(saveFlags);"); } - foreach (var property in properties) + 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, - property - ); + SerializableMigrationRulesEngine.Rules[property.Rule] + .GenerateSerializationMethod( + source, + innerIndent, + field + ); source.AppendLine($"{bodyIndent}}}"); } } else { source.AppendLine(); - SerializableMigrationRulesEngine.Rules[property.Rule].GenerateSerializationMethod( - source, - bodyIndent, - property - ); + SerializableMigrationRulesEngine.Rules[property.Rule] + .GenerateSerializationMethod( + source, + bodyIndent, + field + ); } } diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs b/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs index 37fccfe5a..15abef66a 100644 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs +++ b/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationRulesEngine.cs @@ -64,7 +64,7 @@ namespace SerializableMigration if (fieldOrPropertySymbol is IFieldSymbol fieldSymbol) { - propertyName = fieldSymbol.GetPropertyName(); + propertyName = fieldSymbol.Name; propertyType = fieldSymbol.Type; } else if (fieldOrPropertySymbol is IPropertySymbol propertySymbol) diff --git a/Projects/SerializationGenerator/SerializationGenerator.csproj b/Projects/SerializationGenerator/SerializationGenerator.csproj index 1d8674e5b..1839dfeb3 100755 --- a/Projects/SerializationGenerator/SerializationGenerator.csproj +++ b/Projects/SerializationGenerator/SerializationGenerator.csproj @@ -7,7 +7,7 @@ - + diff --git a/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj b/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj old mode 100644 new mode 100755 index 87098f422..31e89d561 --- a/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj +++ b/Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj @@ -14,8 +14,7 @@ - - + + - diff --git a/Projects/Server.Tests/Fixtures/ServerFixture.cs b/Projects/Server.Tests/Fixtures/ServerFixture.cs index 4b0b7cae6..231f3ad25 100644 --- a/Projects/Server.Tests/Fixtures/ServerFixture.cs +++ b/Projects/Server.Tests/Fixtures/ServerFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; namespace Server.Tests { @@ -7,8 +8,8 @@ namespace Server.Tests // Global setup static ServerFixture() { + Core.Assembly = Assembly.GetExecutingAssembly(); Core.LoopContext = new EventLoopContext(); - Core.Expansion = Expansion.EJ; // Load Configurations diff --git a/Projects/Server.Tests/Helpers/PacketTestUtilities.cs b/Projects/Server.Tests/Helpers/PacketTestUtilities.cs index b90c457e9..afcbc6423 100644 --- a/Projects/Server.Tests/Helpers/PacketTestUtilities.cs +++ b/Projects/Server.Tests/Helpers/PacketTestUtilities.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Net.Sockets; -using Moq; using Server.Network; namespace Server.Tests.Network @@ -11,18 +9,8 @@ namespace Server.Tests.Network public static Span Compile(this Packet p) => p.Compile(false, out var length).AsSpan(0, length); - public static NetState CreateTestNetState() - { - var socket = new Mock(); - socket - .Setup(s => s.SendAsync(It.IsAny>>(), SocketFlags.None)) - .ReturnsAsync(() => 0); - - socket - .Setup(s => s.ReceiveAsync(It.IsAny>>(), SocketFlags.None)) - .ReturnsAsync(() => 0); - - return new NetState(socket.Object); - } + public static NetState CreateTestNetState() => new( + new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) + ); } } diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 4360b0770..b6339a9a0 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -3,12 +3,19 @@ false - + + + + Data\Professions\LBR\prof.txt + + + + diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs index 36e79adf9..a6eb94b47 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs @@ -9,7 +9,7 @@ namespace Server.Tests.Network [Fact] public void TestGlobalLightLevel() { - byte lightLevel = 5; + const byte lightLevel = 5; var expected = new GlobalLightLevel(lightLevel).Compile(); var ns = PacketTestUtilities.CreateTestNetState(); diff --git a/Projects/Server/AssemblyHandler.cs b/Projects/Server/AssemblyHandler.cs index 2cccc222a..4cb69f497 100644 --- a/Projects/Server/AssemblyHandler.cs +++ b/Projects/Server/AssemblyHandler.cs @@ -173,14 +173,6 @@ namespace Server return null; } - - public static string EnsureDirectory(string dir) - { - var path = Path.Combine(Core.BaseDirectory, dir); - Directory.CreateDirectory(path); - - return path; - } } public class TypeCache diff --git a/Projects/Server/Comparers/DescendingComparer.cs b/Projects/Server/Comparers/DescendingComparer.cs new file mode 100644 index 000000000..4a53e9f32 --- /dev/null +++ b/Projects/Server/Comparers/DescendingComparer.cs @@ -0,0 +1,24 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: DescendingComparer.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; + +namespace Server +{ + public class DescendingComparer : IComparer where T : IComparable { + public int Compare(T x, T y) => y?.CompareTo(x) ?? 1; + } +} diff --git a/Projects/Server/Configuration/ServerConfiguration.cs b/Projects/Server/Configuration/ServerConfiguration.cs index f2e785b1f..7463aadff 100644 --- a/Projects/Server/Configuration/ServerConfiguration.cs +++ b/Projects/Server/Configuration/ServerConfiguration.cs @@ -169,6 +169,17 @@ namespace Server return value; } + 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; diff --git a/Projects/Server/EventLoopTasks.cs b/Projects/Server/EventLoopTasks.cs index 3ead229cb..12f5ac664 100644 --- a/Projects/Server/EventLoopTasks.cs +++ b/Projects/Server/EventLoopTasks.cs @@ -55,7 +55,7 @@ namespace Server evt.WaitOne(); } - public int ExecuteTasks() + public void ExecuteTasks() { if (Thread.CurrentThread != _mainThread) { @@ -66,15 +66,11 @@ namespace Server for (int i = 0; i < count; i++) { - if (!_queue.TryDequeue(out var a)) + if (_queue.TryDequeue(out var a)) { - return count; + a(); } - - a(); } - - return count; } } } diff --git a/Projects/Server/Events/CharacterCreatedEvent.cs b/Projects/Server/Events/CharacterCreatedEvent.cs index b919193ea..dc089daae 100644 --- a/Projects/Server/Events/CharacterCreatedEvent.cs +++ b/Projects/Server/Events/CharacterCreatedEvent.cs @@ -23,9 +23,9 @@ namespace Server public class CharacterCreatedEventArgs { public CharacterCreatedEventArgs( - NetState state, IAccount a, string name, bool female, int hue, int str, int dex, - int intel, CityInfo city, SkillNameValue[] skills, int shirtHue, int pantsHue, int hairID, int hairHue, - int beardID, int beardHue, int profession, Race race + NetState state, IAccount a, string name, bool female, int hue, StatNameValue[] stats, CityInfo city, + SkillNameValue[] skills, int shirtHue, int pantsHue, int hairID, int hairHue, int beardID, int beardHue, + int profession, Race race ) { State = state; @@ -33,9 +33,7 @@ namespace Server Name = name; Female = female; Hue = hue; - Str = str; - Dex = dex; - Int = intel; + Stats = stats; City = city; Skills = skills; ShirtHue = shirtHue; @@ -60,11 +58,7 @@ namespace Server public int Hue { get; } - public int Str { get; } - - public int Dex { get; } - - public int Int { get; } + public StatNameValue[] Stats { get; } public CityInfo City { get; } @@ -87,18 +81,8 @@ namespace Server public Race Race { get; } } - public struct SkillNameValue - { - public SkillName Name { get; } - - public int Value { get; } - - public SkillNameValue(SkillName name, int value) - { - Name = name; - Value = value; - } - } + public readonly record struct SkillNameValue(SkillName Name, int Value); + public readonly record struct StatNameValue(StatType Name, int Value); public static partial class EventSink { diff --git a/Projects/Server/ExpansionInfo.cs b/Projects/Server/ExpansionInfo.cs index 4ed98bea1..18407c052 100644 --- a/Projects/Server/ExpansionInfo.cs +++ b/Projects/Server/ExpansionInfo.cs @@ -1,4 +1,23 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ExpansionInfo.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.Text.Json.Serialization; +using Server.Json; namespace Server { @@ -139,107 +158,63 @@ namespace Server public class ExpansionInfo { + public static string GetEraFolder(string parentDirectory) + { + var expansion = Core.Expansion; + var folders = Directory.GetDirectories( + parentDirectory, + "*", + new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive } + ); + + while (expansion-- >= 0) + { + foreach (var folder in folders) + { + var di = new DirectoryInfo(folder); + if (di.Name.InsensitiveEquals(expansion.ToString())) + { + return folder; + } + } + } + + return null; + } + static ExpansionInfo() { - Table = new[] + var path = Path.Combine(Core.BaseDirectory, "Data/expansion.json"); + var expansions = JsonConfig.Deserialize>(path); + + Table = new ExpansionInfo[expansions.Count]; + + for (var i = 0; i < expansions.Count; i++) { - new ExpansionInfo( - 0, - "None", - ClientFlags.None, - FeatureFlags.ExpansionNone, - CharacterListFlags.ExpansionNone, - HousingFlags.None - ), - new ExpansionInfo( - 1, - "The Second Age", - ClientFlags.Felucca, - FeatureFlags.ExpansionT2A, - CharacterListFlags.ExpansionT2A, - HousingFlags.None - ), - new ExpansionInfo( - 2, - "Renaissance", - ClientFlags.Trammel, - FeatureFlags.ExpansionUOR, - CharacterListFlags.ExpansionUOR, - HousingFlags.None - ), - new ExpansionInfo( - 3, - "Third Dawn", - ClientFlags.Ilshenar, - FeatureFlags.ExpansionUOTD, - CharacterListFlags.ExpansionUOTD, - HousingFlags.None - ), - new ExpansionInfo( - 4, - "Blackthorn's Revenge", - ClientFlags.Ilshenar, - FeatureFlags.ExpansionLBR, - CharacterListFlags.ExpansionLBR, - HousingFlags.None - ), - new ExpansionInfo( - 5, - "Age of Shadows", - ClientFlags.Malas, - FeatureFlags.ExpansionAOS, - CharacterListFlags.ExpansionAOS, - HousingFlags.HousingAOS - ), - new ExpansionInfo( - 6, - "Samurai Empire", - ClientFlags.Tokuno, - FeatureFlags.ExpansionSE, - CharacterListFlags.ExpansionSE, - HousingFlags.HousingSE - ), - new ExpansionInfo( - 7, - "Mondain's Legacy", - new ClientVersion("5.0.0a"), - FeatureFlags.ExpansionML, - CharacterListFlags.ExpansionML, - HousingFlags.HousingML - ), - new ExpansionInfo( - 8, - "Stygian Abyss", - ClientFlags.TerMur, - FeatureFlags.ExpansionSA, - CharacterListFlags.ExpansionSA, - HousingFlags.HousingSA - ), - new ExpansionInfo( - 9, - "High Seas", - new ClientVersion("7.0.9.0"), - FeatureFlags.ExpansionHS, - CharacterListFlags.ExpansionHS, - HousingFlags.HousingHS - ), - new ExpansionInfo( - 10, - "Time of Legends", - new ClientVersion("7.0.45.65"), - FeatureFlags.ExpansionTOL, - CharacterListFlags.ExpansionTOL, - HousingFlags.HousingTOL - ), - new ExpansionInfo( - 11, - "Endless Journey", - new ClientVersion("7.0.61.0"), - FeatureFlags.ExpansionEJ, - CharacterListFlags.ExpansionEJ, - HousingFlags.HousingEJ - ) - }; + var expansion = expansions[i]; + if (expansion.ClientVersion != null) + { + Table[i] = new ExpansionInfo( + i, + expansion.Name, + expansion.ClientVersion, + expansion.FeatureFlags, + expansion.CharacterListFlags, + expansion.HousingFlags + ); + } + else + { + Table[i] = new ExpansionInfo( + i, + expansion.Name, + expansion.ClientFlags ?? ClientFlags.None, + expansion.FeatureFlags, + expansion.CharacterListFlags, + expansion.HousingFlags + ); + } + } } public ExpansionInfo( @@ -331,4 +306,22 @@ namespace Server public override string ToString() => Name; } + + public record ExpansionConfig + { + public string Name { get; init; } + + public ClientVersion? ClientVersion { get; init; } + + public ClientFlags? ClientFlags { get; init; } + + [JsonConverter(typeof(FlagsConverter))] + public FeatureFlags FeatureFlags { get; init; } + + [JsonConverter(typeof(FlagsConverter))] + public CharacterListFlags CharacterListFlags { get; init; } + + [JsonConverter(typeof(FlagsConverter))] + public HousingFlags HousingFlags { get; init; } + } } diff --git a/Projects/Server/Guild.cs b/Projects/Server/Guild.cs index e9e6cc84a..5ff38abd9 100644 --- a/Projects/Server/Guild.cs +++ b/Projects/Server/Guild.cs @@ -63,13 +63,21 @@ namespace Server.Guilds [CommandProperty(AccessLevel.Counselor)] public Serial Serial { get; } + [CommandProperty(AccessLevel.GameMaster, readOnly: true)] + public DateTime Created { get; set; } = Core.Now; + + [CommandProperty(AccessLevel.GameMaster)] + DateTime ISerializable.LastSerialized { get; set; } = Core.Now; + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } public int TypeRef { get; private set; } + public abstract void BeforeSerialize(); public abstract void Serialize(IGenericWriter writer); + public abstract void Deserialize(IGenericReader reader); public abstract void OnDelete(Mobile mob); diff --git a/Projects/Server/IEntity.cs b/Projects/Server/IEntity.cs index 1d2dbcf62..f5b36b0d9 100644 --- a/Projects/Server/IEntity.cs +++ b/Projects/Server/IEntity.cs @@ -47,6 +47,10 @@ namespace Server { } + DateTime ISerializable.Created { get; set; } = Core.Now; + + DateTime ISerializable.LastSerialized { get; set; } = DateTime.MaxValue; + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } @@ -103,6 +107,10 @@ namespace Server && p.Y >= Location.m_Y - range && p.Y <= Location.m_Y + range; + public void BeforeSerialize() + { + } + public void Deserialize(IGenericReader reader) { // Should not actually be saved diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index f0dd8e630..43a2ba939 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -763,7 +763,7 @@ namespace Server.Items { if (Core.ML) { - if (ParentsContain()) // Root Parent is the Mobile. Parent could be another containter. + if (ParentsContain()) // Root Parent is the Mobile. Parent could be another container. { list.Add( 1073841, // Contents: ~1_COUNT~/~2_MAXCOUNT~ items, ~3_WEIGHT~ stones diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index cfb7e14b8..0e49d5568 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -771,6 +771,12 @@ namespace Server AddNameProperties(list); } + [CommandProperty(AccessLevel.GameMaster, readOnly: true)] + public DateTime Created { get; set; } = Core.Now; + + [CommandProperty(AccessLevel.GameMaster)] + DateTime ISerializable.LastSerialized { get; set; } = Core.Now; + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } @@ -790,12 +796,12 @@ namespace Server if (x != 0 || y != 0 || z != 0) { - if (x >= short.MinValue && x <= short.MaxValue && y >= short.MinValue && y <= short.MaxValue && - z >= sbyte.MinValue && z <= sbyte.MaxValue) + if (x is >= short.MinValue and <= short.MaxValue && y is >= short.MinValue and <= short.MaxValue && + z is >= sbyte.MinValue and <= sbyte.MaxValue) { if (x != 0 || y != 0) { - if (x >= byte.MinValue && x <= byte.MaxValue && y >= byte.MinValue && y <= byte.MaxValue) + if (x is >= byte.MinValue and <= byte.MaxValue && y is >= byte.MinValue and <= byte.MaxValue) { flags |= SaveFlag.LocationByteXY; } @@ -1285,7 +1291,7 @@ namespace Server var worldLoc = GetWorldLocation(); var update = (flags & ItemDelta.Update) != 0; - if (update && m_Parent is Container contParent && !contParent.IsPublicContainer) + if (update && m_Parent is Container { IsPublicContainer: false } contParent) { var rootParent = contParent.RootParent as Mobile; Mobile tradeRecip = null; @@ -1332,39 +1338,36 @@ namespace Server if (openers != null) { - lock (openers) + for (var i = 0; i < openers.Count; ++i) { - for (var i = 0; i < openers.Count; ++i) + var mob = openers[i]; + + var range = GetUpdateRange(mob); + + if (mob.Map != map || !mob.InRange(worldLoc, range)) { - var mob = openers[i]; - - var range = GetUpdateRange(mob); - - if (mob.Map != map || !mob.InRange(worldLoc, range)) + openers.RemoveAt(i--); + } + else + { + if (mob == rootParent || mob == tradeRecip) { - openers.RemoveAt(i--); + continue; } - else + + var ns = mob.NetState; + + if (ns != null && mob.CanSee(this)) { - if (mob == rootParent || mob == tradeRecip) - { - continue; - } - - var ns = mob.NetState; - - if (ns != null && mob.CanSee(this)) - { - ns.SendContainerContentUpdate(this); - SendOPLPacketTo(ns); - } + ns.SendContainerContentUpdate(this); + SendOPLPacketTo(ns); } } + } - if (openers.Count == 0) - { - contParent.Openers = null; - } + if (openers.Count == 0) + { + contParent.Openers = null; } } @@ -1719,6 +1722,7 @@ namespace Server } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool GetFlag(ImplFlag flag) => (m_Flags & flag) != 0; public BounceInfo GetBounce() => LookupCompactInfo()?.m_Bounce; @@ -1748,12 +1752,9 @@ namespace Server parentItem.OnItemBounceCleared(this); } } - else if (bounce.Parent is Mobile parentMobile) + else if (bounce.Parent is Mobile { Deleted: false } parentMobile) { - if (!parentMobile.Deleted) - { - parentMobile.OnItemBounceCleared(this); - } + parentMobile.OnItemBounceCleared(this); } VerifyCompactInfo(); @@ -2344,7 +2345,7 @@ namespace Server var itemID = m_ItemID; var doubled = m_Amount > 1; - if (itemID >= 0xEEA && itemID <= 0xEF2) // Are we coins? + if (itemID is >= 0xEEA and <= 0xEF2) // Are we coins? { var coinBase = (itemID - 0xEEA) / 3; coinBase *= 3; @@ -2408,7 +2409,7 @@ namespace Server } #nullable enable - public void InvalidateProperties() + public virtual void InvalidateProperties() { if (!ObjectPropertyList.Enabled) { @@ -2573,6 +2574,10 @@ namespace Server } } + public virtual void BeforeSerialize() + { + } + public virtual void Deserialize(IGenericReader reader) { var version = reader.ReadInt(); @@ -3251,10 +3256,8 @@ namespace Server m_DeltaFlags &= ~flags; } - public static int ProcessDeltaQueue() + public static void ProcessDeltaQueue() { - int count = 0; - var limit = m_DeltaQueue.Count; while (m_DeltaQueue.Count > 0 && --limit >= 0) @@ -3266,8 +3269,6 @@ namespace Server continue; } - count++; - item.SetFlag(ImplFlag.InQueue, false); try @@ -3288,8 +3289,6 @@ namespace Server Console.WriteLine("Warning: {0} items left in delta queue after processing.", m_DeltaQueue.Count); Utility.PopColor(); } - - return count; } public virtual void OnDelete() diff --git a/Projects/Server/Json/Converters/ClientVersionConverter.cs b/Projects/Server/Json/Converters/ClientVersionConverter.cs new file mode 100644 index 000000000..c1dc8ecb9 --- /dev/null +++ b/Projects/Server/Json/Converters/ClientVersionConverter.cs @@ -0,0 +1,37 @@ +/************************************************************************* + * ModernUO * + * Copyright 2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ClientVersionConverter.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.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()); + } + + throw new JsonException($"Value must be a string"); + } + + public override void Write(Utf8JsonWriter writer, ClientVersion value, JsonSerializerOptions options) => + writer.WriteStringValue(value.ToString()); + } +} diff --git a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs new file mode 100644 index 000000000..3e778d591 --- /dev/null +++ b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs @@ -0,0 +1,30 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ClientVersionConverterFactory.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.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); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new ClientVersionConverter(); + } +} diff --git a/Projects/Server/Json/Converters/FlagsConverter.cs b/Projects/Server/Json/Converters/FlagsConverter.cs new file mode 100644 index 000000000..38584d05a --- /dev/null +++ b/Projects/Server/Json/Converters/FlagsConverter.cs @@ -0,0 +1,149 @@ +/************************************************************************* + * ModernUO * + * Copyright 2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: FlagsConverter.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.Json; +using System.Text.Json.Serialization; + +namespace Server.Json +{ + public class FlagsConverter : JsonConverter where T : struct, Enum + { + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var flags = 0ul; + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + + while (true) + { + 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); + } + + switch (Type.GetTypeCode(underlyingType)) + { + 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); + } + } + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + 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) + { + writer.WriteBoolean(flagName, (intValue & flag) != 0); + } + } + + 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 + }; + } +} diff --git a/Projects/Server/Json/Converters/GuidConverter.cs b/Projects/Server/Json/Converters/GuidConverter.cs index 3f61742b8..b6c373094 100644 --- a/Projects/Server/Json/Converters/GuidConverter.cs +++ b/Projects/Server/Json/Converters/GuidConverter.cs @@ -14,7 +14,6 @@ *************************************************************************/ using System; -using System.Net; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Projects/Server/Json/Converters/GuidConverterFactory.cs b/Projects/Server/Json/Converters/GuidConverterFactory.cs index 92acf4183..e0ba7c4ce 100644 --- a/Projects/Server/Json/Converters/GuidConverterFactory.cs +++ b/Projects/Server/Json/Converters/GuidConverterFactory.cs @@ -14,7 +14,6 @@ *************************************************************************/ using System; -using System.Net; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Projects/Server/Json/JsonConfig.cs b/Projects/Server/Json/JsonConfig.cs index c44ba874c..163776d84 100644 --- a/Projects/Server/Json/JsonConfig.cs +++ b/Projects/Server/Json/JsonConfig.cs @@ -39,6 +39,7 @@ namespace Server.Json Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + options.Converters.Add(new ClientVersionConverterFactory()); options.Converters.Add(new GuidConverterFactory()); options.Converters.Add(new JsonStringEnumConverter()); options.Converters.Add(new MapConverterFactory()); diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 18804b003..1eac5d8e8 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime; using System.Runtime.InteropServices; @@ -55,7 +56,8 @@ namespace Server public static readonly bool IsDarwin = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static readonly bool IsFreeBSD = RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); public static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || IsFreeBSD; - public static readonly bool Unix = IsDarwin || IsFreeBSD || IsLinux; + public static readonly bool IsBSD = IsDarwin || IsFreeBSD; + public static readonly bool Unix = IsBSD || IsLinux; private const string AssembliesConfiguration = "Data/assemblies.json"; @@ -155,6 +157,11 @@ namespace Server set => _now = value; } + private static long _cycleIndex = 1; + private static float[] _cyclesPerSecond = new float[100]; + + public static float CyclesPerSecond => _cyclesPerSecond[(_cycleIndex - 1) % _cyclesPerSecond.Length]; + public static bool MultiProcessor { get; private set; } public static int ProcessorCount { get; private set; } @@ -242,6 +249,17 @@ namespace Server { fullPath = Path.Combine(p, path); + if (IsLinux) + { + var fi = new FileInfo(fullPath); + fullPath = fi.Directory!.EnumerateFiles( + fi.Name, + new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive } + ).FirstOrDefault()?.FullName; + + break; + } + if (File.Exists(fullPath)) { break; @@ -488,40 +506,51 @@ namespace Server { try { +#if DEBUG + const bool idleCPU = true; +#else + var idleCPU = ServerConfiguration.GetOrUpdateSetting("core.enableIdleCPU", false); +#endif + + long last = TickCount; const int interval = 100; - int idleCount = 0; + const float ticksPerSecond = 1000 * interval; + + int sample = 0; while (!Closing) { _tickCount = TickCount; _now = DateTime.UtcNow; - var events = Mobile.ProcessDeltaQueue(); - events += Item.ProcessDeltaQueue(); - events += Timer.Slice(_tickCount); + Mobile.ProcessDeltaQueue(); + Item.ProcessDeltaQueue(); + Timer.Slice(_tickCount); // Handle networking - events += TcpServer.Slice(); - events += NetState.HandleAllReceives(); - events += NetState.Slice(); + TcpServer.Slice(); + NetState.Slice(); // Execute captured post-await methods (like Timer.Pause) - events += LoopContext.ExecuteTasks(); + LoopContext.ExecuteTasks(); Timer.CheckTimerPool(); // Check for pool depletion so we can async refill it. _tickCount = 0; _now = DateTime.MinValue; - if (events > 0) + if (idleCPU && ++sample % interval == 0) { - idleCount = 0; - continue; - } + var now = TickCount; - if (++idleCount > interval) - { - Thread.Sleep(1); + var cyclesPerSecond = ticksPerSecond / (now - last); + _cyclesPerSecond[_cycleIndex++ % _cyclesPerSecond.Length] = cyclesPerSecond; + last = now; + + if (cyclesPerSecond > 80) + { + Thread.Sleep(2); + } } } } diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index 0c0e489ec..8dfe26e66 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -692,7 +692,7 @@ namespace Server var tile = staticTiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - if (id.Surface || (id.Flags & TileFlag.Wet) != 0) + if (id.Surface || id.Wet) { var tileZ = tile.Z + id.CalcHeight; @@ -720,7 +720,7 @@ namespace Server { var id = item.ItemData; - if (id.Surface || (id.Flags & TileFlag.Wet) != 0) + if (id.Surface || id.Wet) { var itemZ = item.Z + id.CalcHeight; diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index d4164130c..5e259a849 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -916,8 +916,15 @@ namespace Server else { m_NetState.SendChangeCombatant(m_Combatant.Serial); - Timer.StartTimer(ExpireCombatantDelay, ExpireCombatant, out _expireCombatantTimerToken); - Timer.StartTimer(TimeSpan.FromSeconds(0.01), 0, CheckCombatTime, out _combatTimerToken); + if (!_expireCombatantTimerToken.Running) + { + Timer.StartTimer(ExpireCombatantDelay, ExpireCombatant, out _expireCombatantTimerToken); + } + + if (!_combatTimerToken.Running) + { + Timer.StartTimer(TimeSpan.FromSeconds(0.01), 0, CheckCombatTime, out _combatTimerToken); + } if (CanBeHarmful(m_Combatant, false)) { @@ -1147,9 +1154,6 @@ namespace Server public virtual bool ShouldCheckStatTimers => true; - [CommandProperty(AccessLevel.GameMaster)] - public DateTime CreationTime { get; private set; } - [CommandProperty(AccessLevel.GameMaster)] public int LightLevel { @@ -2472,6 +2476,12 @@ namespace Server AddNameProperties(list); } + [CommandProperty(AccessLevel.GameMaster, readOnly: true)] + public DateTime Created { get; set; } = Core.Now; + + [CommandProperty(AccessLevel.GameMaster)] + DateTime ISerializable.LastSerialized { get; set; } = Core.Now; + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } @@ -2483,7 +2493,7 @@ namespace Server public virtual void Serialize(IGenericWriter writer) { - writer.Write(32); // version + writer.Write(33); // version writer.WriteDeltaTime(LastStrGain); writer.WriteDeltaTime(LastIntGain); @@ -2519,7 +2529,7 @@ namespace Server writer.Write(Corpse); - writer.Write(CreationTime); + // writer.Write(CreationTime); Stabled.Tidy(); writer.Write(Stabled); @@ -6167,12 +6177,21 @@ namespace Server { } + public virtual void BeforeSerialize() + { + } + public virtual void Deserialize(IGenericReader reader) { var version = reader.ReadInt(); switch (version) { + case 33: + { + // Removed created + goto case 32; + } case 32: { // Removed StuckMenu @@ -6232,7 +6251,10 @@ namespace Server } case 23: { - CreationTime = reader.ReadDateTime(); + if (version < 33) + { + Created = reader.ReadDateTime(); + } goto case 22; } @@ -7818,7 +7840,6 @@ namespace Server DamageEntries = new List(); NextSkillTime = Core.TickCount; - CreationTime = Core.Now; } public virtual void Delta(MobileDelta flag) @@ -7848,10 +7869,8 @@ namespace Server } } - public static int ProcessDeltaQueue() + public static void ProcessDeltaQueue() { - int count = 0; - var limit = m_DeltaQueue.Count; while (m_DeltaQueue.Count > 0 && --limit >= 0) @@ -7863,8 +7882,6 @@ namespace Server continue; } - count++; - mob.m_InDeltaQueue = false; try @@ -7885,8 +7902,6 @@ namespace Server Console.WriteLine("Warning: {0} mobiles left in delta queue after processing.", m_DeltaQueue.Count); Utility.PopColor(); } - - return count; } public virtual void OnKillsChange(int oldValue) diff --git a/Projects/Server/Network/ISocket.cs b/Projects/Server/Network/ISocket.cs index b1182c77a..75d3d8475 100644 --- a/Projects/Server/Network/ISocket.cs +++ b/Projects/Server/Network/ISocket.cs @@ -23,6 +23,8 @@ namespace Server.Network { public interface ISocket { + public IntPtr Handle { get; } + public EndPoint LocalEndPoint { get; } public EndPoint RemoteEndPoint { get; } @@ -33,6 +35,8 @@ namespace Server.Network 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/DumpNetStates.cs b/Projects/Server/Network/NetState/DumpNetStates.cs index ad0e3a821..6992254dc 100644 --- a/Projects/Server/Network/NetState/DumpNetStates.cs +++ b/Projects/Server/Network/NetState/DumpNetStates.cs @@ -32,7 +32,7 @@ namespace Server.Network foreach (var ns in TcpServer.Instances) { - file.WriteLine($"{ns}, {ns._recvState}, {ns._sendState}, {ns._protocolState}, {ns._parserState}"); + file.WriteLine($"{ns}, {ns._protocolState}, {ns._parserState}"); } } } diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs old mode 100644 new mode 100755 index 84ce948be..47c6bf381 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -20,8 +20,9 @@ using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; +using System.Network; using System.Runtime.CompilerServices; -using System.Threading; +using System.Runtime.InteropServices; using Server.Accounting; using Server.Diagnostics; using Server.Gumps; @@ -43,12 +44,15 @@ namespace Server.Network private const int RecvPipeSize = 1024 * 64; private const int SendPipeSize = 1024 * 256; - private static int GumpCap = 512; - private static int HuePickerCap = 512; - private static int MenuCap = 512; - private static int PacketPerSecondThreshold = 3000; + private const int GumpCap = 512; + private const int HuePickerCap = 512; + private const int MenuCap = 512; + private const int PacketPerSecondThreshold = 3000; + 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(); public static NetStateCreatedCallback CreatedCallback { get; set; } @@ -56,7 +60,7 @@ namespace Server.Network private readonly string _toString; private ClientVersion _version; private long _nextActivityCheck; - private int _running; + private bool _running = true; private volatile DecodePacket _packetDecoder; private volatile EncodePacket _packetEncoder; private bool _flushQueued; @@ -66,14 +70,12 @@ namespace Server.Network internal int _authId; internal int _seed; - internal ParserState _parserState = ParserState.Uninitialized; - internal ProtocolState _protocolState = ProtocolState.Uninitialized; - internal RecvState _recvState = RecvState.Uninitialized; - internal SendState _sendState = SendState.Uninitialized; + internal ParserState _parserState = ParserState.AwaitingNextPacket; + internal ProtocolState _protocolState = ProtocolState.AwaitingSeed; + internal GCHandle _handle; internal enum ParserState { - Uninitialized, AwaitingNextPacket, AwaitingPartialPacket, ProcessingPacket, @@ -83,7 +85,6 @@ namespace Server.Network internal enum ProtocolState { - Uninitialized, AwaitingSeed, // Based on the way the seed arrives, we know if this is a login server or a game server connection LoginServer_AwaitingLogin, @@ -96,38 +97,12 @@ namespace Server.Network Error } - internal enum RecvState - { - Uninitialized, - AwaitingMemory, - AwaitingRecv, - DataReceived, - Exited, - } - - internal enum SendState - { - Uninitialized, - AwaitingData, - Sending, - SendCompleted, - Exited, - } - - public static void Configure() - { - GumpCap = ServerConfiguration.GetOrUpdateSetting("netstate.gumpCap", GumpCap); - HuePickerCap = ServerConfiguration.GetOrUpdateSetting("netstate.huePickerCap", HuePickerCap); - MenuCap = ServerConfiguration.GetOrUpdateSetting("netstate.menuCap", MenuCap); - PacketPerSecondThreshold = ServerConfiguration.GetOrUpdateSetting("netstate.packetsPerSecondThreshold", PacketPerSecondThreshold); - } - public static void Initialize() { Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); } - public NetState(ISocket connection) + public NetState(Socket connection) { Connection = connection; Seeded = false; @@ -138,6 +113,7 @@ namespace Server.Network RecvPipe = new Pipe(GC.AllocateUninitializedArray(RecvPipeSize)); SendPipe = new Pipe(GC.AllocateUninitializedArray(SendPipeSize)); _nextActivityCheck = Core.TickCount + 30000; + ConnectedOn = Core.Now; try { @@ -151,7 +127,17 @@ namespace Server.Network _toString = "(error)"; } - ConnectedOn = Core.Now; + _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); } @@ -174,25 +160,21 @@ namespace Server.Network 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 Running - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _running == 1; - } - public bool Seeded { get; set; } public Pipe RecvPipe { get; } public Pipe SendPipe { get; } - public ISocket Connection { get; } + public Socket Connection { get; } public bool CompressionEnabled { get; set; } @@ -524,36 +506,21 @@ namespace Server.Network } } - internal void Start() + public void HandleReceive() { - if (Interlocked.CompareExchange(ref _running, 1, 0) == 1 || Connection == null) + if (!_running) { return; } - _parserState = ParserState.AwaitingNextPacket; - _protocolState = ProtocolState.AwaitingSeed; - - ThreadPool.UnsafeQueueUserWorkItem(RecvTask, null); - ThreadPool.UnsafeQueueUserWorkItem(SendTask, null); - } - - // Return true if there was any data to be processed. False otherwise. Used for idle detection. - public bool HandleReceive() - { - if (!Running) - { - return false; - } - - bool active = false; + ReceiveData(); var reader = RecvPipe.Reader; try { // Process as many packets as we can synchronously - while (Running && _parserState != ParserState.Error && _protocolState != ProtocolState.Error) + while (_running && _parserState != ParserState.Error && _protocolState != ProtocolState.Error) { var result = reader.TryRead(); var length = result.Length; @@ -563,9 +530,6 @@ namespace Server.Network break; } - // There was at least some data found, so it's not idle. - active = true; - var packetReader = new CircularBufferReader(result.Buffer); var packetId = packetReader.ReadByte(); int packetLength = length; @@ -580,12 +544,6 @@ namespace Server.Network { switch (_protocolState) { - case ProtocolState.Uninitialized: - { - HandleError(packetId, packetLength); - return true; - } - case ProtocolState.AwaitingSeed: { if (packetId == 0xEF) @@ -604,7 +562,7 @@ namespace Server.Network if (seed == 0) { HandleError(0, 0); - return true; + return; } _seed = seed; @@ -626,7 +584,7 @@ namespace Server.Network { LogInfo("Possible encrypted client detected, disconnecting..."); HandleError(packetId, packetLength); - return true; + return; } _parserState = ParserState.ProcessingPacket; @@ -643,7 +601,7 @@ namespace Server.Network if (packetId != 0xA0) { HandleError(packetId, packetLength); - return true; + return; } _parserState = ParserState.ProcessingPacket; @@ -665,7 +623,7 @@ namespace Server.Network _parserState = ParserState.AwaitingNextPacket; _protocolState = ProtocolState.AwaitingSeed; #endif - return true; + return; } case ProtocolState.GameServer_AwaitingGameServerLogin: @@ -673,7 +631,7 @@ namespace Server.Network if (packetId != 0x91 && packetId != 0x80) { HandleError(packetId, packetLength); - return true; + return; } _parserState = ParserState.ProcessingPacket; @@ -719,8 +677,6 @@ namespace Server.Network TraceException(ex); Disconnect("Exception during HandleReceive"); } - - return active; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -811,83 +767,56 @@ namespace Server.Network return ParserState.AwaitingNextPacket; } - private async void SendTask(object state) + 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 { - while (Running) - { - _sendState = SendState.AwaitingData; - - var result = await reader.Read(); - - if (result.IsClosed || !Running) - { - break; - } - - if (result.Length <= 0) - { - continue; - } - - _sendState = SendState.Sending; - - var bytesWritten = await Connection.SendAsync(result.Buffer, SocketFlags.None); - - _sendState = SendState.SendCompleted; - - if (bytesWritten > 0) - { - _nextActivityCheck = Core.TickCount + 90000; - reader.Advance((uint)bytesWritten); - } - } - - // Grab any remaining data and flush it - var data = reader.TryRead(); - - if (data.Length > 0) - { - _sendState = SendState.Sending; - Connection.Send(data.Buffer, SocketFlags.None); - - reader.Advance((uint)data.Length); - } + bytesWritten = Connection.Send(result.Buffer, SocketFlags.None); } catch (SocketException ex) { - // If the user closes the connection (or the recv side does) - // between the check for m_Running above and the call to SendAsync, - // we can still get a socket exception here. That's ok. + // 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); } - finally - { - try - { - Connection.Shutdown(SocketShutdown.Both); - Connection.Close(); - } - catch (Exception ex) - { - TraceException(ex); - } - Disconnect("Exiting SendTask."); - _sendState = SendState.Exited; + if (bytesWritten > 0) + { + _nextActivityCheck = Core.TickCount + 90000; + reader.Advance((uint)bytesWritten); } + + return bytesWritten == result.Length; } private void DecodePacket(ArraySegment[] buffer, ref int length) @@ -896,47 +825,21 @@ namespace Server.Network _packetDecoder?.Invoke(cBuffer, ref length); } - private async void RecvTask(object state) + private void ReceiveData() { - var socket = Connection; var writer = RecvPipe.Writer; + var result = writer.TryGetMemory(); + + if (result.IsClosed || result.Length == 0) + { + return; + } + + var bytesWritten = 0; try { - while (Running) - { - _recvState = RecvState.AwaitingMemory; - var result = await writer.GetMemory(); - - if (result.IsClosed || !Running) - { - break; - } - - if (result.Length <= 0) - { - continue; - } - - _recvState = RecvState.AwaitingRecv; - - var bytesWritten = await socket.ReceiveAsync(result.Buffer, SocketFlags.None); - if (bytesWritten <= 0) - { - break; - } - - _recvState = RecvState.DataReceived; - - DecodePacket(result.Buffer, ref bytesWritten); - - writer.Advance((uint)bytesWritten); - _nextActivityCheck = Core.TickCount + 90000; - - // No need to flush - } - - Disconnect(string.Empty); + bytesWritten = Connection.Receive(result.Buffer, SocketFlags.None); } catch (SocketException ex) { @@ -953,38 +856,20 @@ namespace Server.Network #if DEBUG Console.WriteLine(ex); #endif - Disconnect("RecvTask exited unexpectedly."); + Disconnect($"Disconnected with error: {ex}"); TraceException(ex); } - finally - { - _recvState = RecvState.Exited; - } - } - public static int HandleAllReceives() - { - int count = 0; - - foreach (var ns in TcpServer.Instances) + if (bytesWritten <= 0) { - if (ns.HandleReceive()) - { - count++; - } + Disconnect(string.Empty); + return; } - return count; - } + DecodePacket(result.Buffer, ref bytesWritten); - public void Flush() - { - if (Connection != null) - { - SendPipe.Writer.Flush(); - } - - _flushQueued = false; + writer.Advance((uint)bytesWritten); + _nextActivityCheck = Core.TickCount + 90000; } public static void FlushAll() @@ -995,22 +880,38 @@ namespace Server.Network } } - public static int Slice() + public static void Slice() { - int count = 0; - while (FlushPending.Count != 0) + int count = _pollGroup.Poll(ref _polledStates); + + if (count > 0) { - FlushPending.Dequeue()?.Flush(); - count++; + 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)) { - TcpServer.Instances.Remove(ns); ns.Dispose(); } - return count; + if (hasDisposes) + { + _pollGroup.Poll(ref _polledStates); + } } public void CheckAlive(long curTicks) @@ -1064,11 +965,13 @@ namespace Server.Network public void Disconnect(string reason) { - if (Interlocked.CompareExchange(ref _running, 0, 1) == 0) + if (!_running) { return; } + _running = false; + try { if (_disconnectReason != string.Empty) @@ -1082,7 +985,6 @@ namespace Server.Network } _disconnectReason = reason; - Disposed.Enqueue(this); } @@ -1114,6 +1016,11 @@ namespace Server.Network { TraceDisconnect(_disconnectReason, _toString); + if (_running) + { + throw new Exception("Disconnected a NetState that is still running."); + } + #if THREADGUARD if (Thread.CurrentThread != Core.Thread) { @@ -1125,8 +1032,18 @@ namespace Server.Network } #endif - RecvPipe.Writer.Close(); - SendPipe.Writer.Close(); + TcpServer.Instances.Remove(this); + try + { + _pollGroup.Remove(Connection); + } + catch (Exception ex) + { + TraceException(ex); + } + + Connection.Close(); + _handle.Free(); var m = Mobile; Mobile = null; diff --git a/Projects/Server/Network/NetworkCompression.cs b/Projects/Server/Network/NetworkCompression.cs index 908d51339..53fe87d2c 100644 --- a/Projects/Server/Network/NetworkCompression.cs +++ b/Projects/Server/Network/NetworkCompression.cs @@ -125,7 +125,6 @@ namespace Server.Network return outputIdx; } - public static int Compress(CircularBuffer input, CircularBuffer output) { if (input.Length > DefiniteOverflow) diff --git a/Projects/Server/Network/NetworkSocket.cs b/Projects/Server/Network/NetworkSocket.cs deleted file mode 100644 index e4e4c3a8a..000000000 --- a/Projects/Server/Network/NetworkSocket.cs +++ /dev/null @@ -1,67 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: NetworkSocket.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.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Server.Network -{ - public class NetworkSocket : ISocket - { - private readonly Socket _connection; - - public Socket Connection - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _connection; - } - - public EndPoint LocalEndPoint - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _connection.LocalEndPoint; - } - - public EndPoint RemoteEndPoint - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _connection.RemoteEndPoint; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NetworkSocket(Socket connection) => _connection = connection; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Task SendAsync(IList> buffers, SocketFlags flags) => - _connection.SendAsync(buffers, flags); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Send(IList> buffers, SocketFlags flags) => _connection.Send(buffers, flags); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Task ReceiveAsync(IList> buffers, SocketFlags flags) => - _connection.ReceiveAsync(buffers, flags); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Shutdown(SocketShutdown how) => _connection.Shutdown(how); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Close() => _connection.Close(); - } -} diff --git a/Projects/Server/Network/Packets/IncomingAccountPackets.cs b/Projects/Server/Network/Packets/IncomingAccountPackets.cs index dd34c9099..4444b95ad 100644 --- a/Projects/Server/Network/Packets/IncomingAccountPackets.cs +++ b/Projects/Server/Network/Packets/IncomingAccountPackets.cs @@ -73,40 +73,20 @@ namespace Server.Network int genderRace = reader.ReadByte(); - int str = reader.ReadByte(); - int dex = reader.ReadByte(); - int intl = reader.ReadByte(); - int is1 = reader.ReadByte(); - int vs1 = reader.ReadByte(); - int is2 = reader.ReadByte(); - int vs2 = reader.ReadByte(); - int is3 = reader.ReadByte(); - int vs3 = reader.ReadByte(); + var stats = new StatNameValue[] + { + new(StatType.Str, reader.ReadByte()), + new(StatType.Dex, reader.ReadByte()), + new(StatType.Int, reader.ReadByte()) + }; - SkillNameValue[] skills; - - // We have protocol changes by now, so this is ok + 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) { - int is4 = reader.ReadByte(); - int vs4 = reader.ReadByte(); - - skills = new[] - { - new SkillNameValue((SkillName)is1, vs1), - new SkillNameValue((SkillName)is2, vs2), - new SkillNameValue((SkillName)is3, vs3), - new SkillNameValue((SkillName)is4, vs4) - }; - } - else - { - skills = new[] - { - new SkillNameValue((SkillName)is1, vs1), - new SkillNameValue((SkillName)is2, vs2), - new SkillNameValue((SkillName)is3, vs3) - }; + skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); } int hue = reader.ReadUInt16(); @@ -171,9 +151,7 @@ namespace Server.Network name, female, hue, - str, - dex, - intl, + stats, info[cityIndex], skills, shirtHue, diff --git a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs index 3e613466f..7f2a0a3f1 100644 --- a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs @@ -110,7 +110,6 @@ namespace Server.Network parentSerial = Serial.Zero; } - var writer = new SpanWriter(stackalloc byte[15]); writer.Write((byte)0x2E); // Packet ID writer.Write(item.Serial); diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs index 7dcd9a962..a9b9f1955 100644 --- a/Projects/Server/Network/TcpServer.cs +++ b/Projects/Server/Network/TcpServer.cs @@ -84,6 +84,14 @@ namespace Server.Network Listeners = listeners.ToArray(); } + public static void Shutdown() + { + foreach (var listener in Listeners) + { + listener.Server.Close(); + } + } + public static IEnumerable GetListeningAddresses(IPEndPoint ipep) => NetworkInterface.GetAllNetworkInterfaces().SelectMany(adapter => adapter.GetIPProperties().UnicastAddresses @@ -93,12 +101,19 @@ namespace Server.Network public static TcpListener CreateListener(IPEndPoint ipep) { - var listener = new TcpListener(ipep); - listener.Server.ExclusiveAddressUse = false; + var listener = new TcpListener(ipep) + { + Server = + { + LingerState = new LingerOption(false, 0), + ExclusiveAddressUse = true, + NoDelay = true + } + }; try { - listener.Start(8); + listener.Start(32); return listener; } catch (SocketException se) @@ -122,7 +137,7 @@ namespace Server.Network return null; } - public static int Slice() + public static void Slice() { int count = 0; @@ -130,10 +145,7 @@ namespace Server.Network { Instances.Add(ns); ns.LogInfo("Connected. [{0} Online]", Instances.Count); - ns.Start(); } - - return count; } private static async void BeginAcceptingSockets(this TcpListener listener) @@ -184,7 +196,7 @@ namespace Server.Network } else { - var ns = new NetState(new NetworkSocket(socket)); + var ns = new NetState(socket); _connectedQueue.Enqueue(ns); } } diff --git a/Projects/Server/Serialization/BinaryFileReader.cs b/Projects/Server/Serialization/BinaryFileReader.cs index 890fc0c49..81064faa3 100644 --- a/Projects/Server/Serialization/BinaryFileReader.cs +++ b/Projects/Server/Serialization/BinaryFileReader.cs @@ -30,6 +30,8 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Close() => _reader.Close(); + public DateTime LastSerialized { get; init; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadString(bool intern = false) { diff --git a/Projects/Server/Serialization/BinaryFileWriter.cs b/Projects/Server/Serialization/BinaryFileWriter.cs index 340e9c66b..9980eb000 100644 --- a/Projects/Server/Serialization/BinaryFileWriter.cs +++ b/Projects/Server/Serialization/BinaryFileWriter.cs @@ -35,7 +35,6 @@ namespace Server public override long Position => _position + Index; - protected override int BufferSize => 81920; public override void Flush() diff --git a/Projects/Server/Serialization/BufferReader.cs b/Projects/Server/Serialization/BufferReader.cs index 5b512215b..e04135db5 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -37,6 +37,8 @@ namespace Server _encoding = encoding ?? TextEncoding.UTF8; } + public BufferReader(byte[] buffer, DateTime LastSerialized) : this(buffer) => LastSerialized = LastSerialized; + public void Reset(byte[] newBuffer, out byte[] oldBuffer) { oldBuffer = _buffer; @@ -45,6 +47,8 @@ namespace Server } // Compatible with BinaryReader.ReadString() + public DateTime LastSerialized { get; init; } = DateTime.MinValue; + public string ReadString(bool intern = false) { if (!ReadBool()) diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs index 2912664b7..eb878303c 100644 --- a/Projects/Server/Serialization/GenericPersistence.cs +++ b/Projects/Server/Serialization/GenericPersistence.cs @@ -41,7 +41,7 @@ namespace Server { var path = Path.Combine(savePath, name); - AssemblyHandler.EnsureDirectory(path); + PathUtility.EnsureDirectory(path); string binPath = Path.Combine(path, $"{name}.bin"); using var bin = new BinaryFileWriter(binPath, true); @@ -54,7 +54,7 @@ namespace Server { var path = Path.Combine(savePath, name); - AssemblyHandler.EnsureDirectory(path); + PathUtility.EnsureDirectory(path); string binPath = Path.Combine(path, $"{name}.bin"); diff --git a/Projects/Server/Serialization/IGenericReader.cs b/Projects/Server/Serialization/IGenericReader.cs index 27194c4eb..67e27764e 100644 --- a/Projects/Server/Serialization/IGenericReader.cs +++ b/Projects/Server/Serialization/IGenericReader.cs @@ -21,6 +21,9 @@ namespace Server { public interface IGenericReader { + // Used to determine valid Entity deserialization + DateTime LastSerialized { get; init; } + string ReadString(bool intern = false); long ReadLong(); ulong ReadULong(); diff --git a/Projects/Server/Serialization/IGenericWriter.cs b/Projects/Server/Serialization/IGenericWriter.cs index c7f8a0281..95aa9d92f 100644 --- a/Projects/Server/Serialization/IGenericWriter.cs +++ b/Projects/Server/Serialization/IGenericWriter.cs @@ -39,14 +39,13 @@ namespace Server void Write(DateTime value) { - var ticks = (value.Kind switch + // If DateTimeKind is Unspecified, we can't assume it needs to be converted. + if (value.Kind == DateTimeKind.Local) { - DateTimeKind.Local => value.ToUniversalTime(), - DateTimeKind.Unspecified => value.ToLocalTime().ToUniversalTime(), - _ => value - }).Ticks; + value = value.ToUniversalTime(); + } - Write(ticks); + Write(value.Ticks); } void WriteDeltaTime(DateTime value) { @@ -59,17 +58,16 @@ namespace Server if (value == DateTime.MaxValue) { Write(long.MaxValue); + return; } - var ticks = (value.Kind switch + if (value.Kind == DateTimeKind.Local) { - DateTimeKind.Local => value.ToUniversalTime(), - DateTimeKind.Unspecified => value.ToLocalTime().ToUniversalTime(), - _ => value - }).Ticks; + value = value.ToUniversalTime(); + } // Technically supports negative deltas for times in the past - Write(ticks - DateTime.UtcNow.Ticks); + Write(value.Ticks - DateTime.UtcNow.Ticks); } void Write(IPAddress value) { diff --git a/Projects/Server/Serialization/ISerializable.cs b/Projects/Server/Serialization/ISerializable.cs index d91366c9b..3488c2c38 100644 --- a/Projects/Server/Serialization/ISerializable.cs +++ b/Projects/Server/Serialization/ISerializable.cs @@ -20,10 +20,20 @@ namespace Server { public interface ISerializable { + // Should be serialized/deserialized with the index so it can be referenced by IGenericReader + DateTime Created { get; set; } + + // Should be serialized/deserialized with the index so it can be referenced by IGenericReader + DateTime LastSerialized { get; protected internal set; } long SavePosition { get; protected internal set; } BufferWriter SaveBuffer { get; protected internal set; } + int TypeRef { get; } Serial Serial { get; } + + // Executed on every entity, before it's serialized. + // For example, this is used to clean up weak references and mark them dirty. + void BeforeSerialize(); void Deserialize(IGenericReader reader); void Serialize(IGenericWriter writer); void Delete(); @@ -48,6 +58,8 @@ namespace Server { SaveBuffer ??= new BufferWriter(true); + BeforeSerialize(); + // Clean, don't bother serializing if (SavePosition > -1) { @@ -55,6 +67,7 @@ namespace Server return; } + LastSerialized = Core.Now; SaveBuffer.Seek(0, SeekOrigin.Begin); Serialize(SaveBuffer); @@ -64,7 +77,7 @@ namespace Server } else { - SavePosition = -1; + this.MarkDirty(); } } } diff --git a/Projects/Server/Serialization/ISerializableExtensions.cs b/Projects/Server/Serialization/ISerializableExtensions.cs index 17ca4b8bf..b98b239ef 100644 --- a/Projects/Server/Serialization/ISerializableExtensions.cs +++ b/Projects/Server/Serialization/ISerializableExtensions.cs @@ -27,6 +27,13 @@ namespace Server entity.SavePosition = -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Delete(this ISerializable entity, IEntity toDelete) + { + toDelete?.Delete(); + entity.MarkDirty(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Add(this ISerializable entity, ICollection list, T value) { diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index 60936169a..cf357a582 100755 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -12,31 +12,30 @@ 0.0.0 - - - - - - - - - - + - - - - + + + + + + + + + + + - + + @@ -47,32 +46,7 @@ all - - - TargetFramework=netstandard2.0 - Analyzer - false - all - - - - - - - true - Generated - - - - - - - - - - - diff --git a/Projects/Server/Skills.cs b/Projects/Server/Skills.cs index 082e548fd..077205507 100644 --- a/Projects/Server/Skills.cs +++ b/Projects/Server/Skills.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using Server.Network; @@ -384,7 +385,8 @@ namespace Server { public SkillInfo( int skillID, string name, double strScale, double dexScale, double intScale, string title, - SkillUseCallback callback, double strGain, double dexGain, double intGain, double gainFactor + SkillUseCallback callback, double strGain, double dexGain, double intGain, double gainFactor, + string professionSkillName = null ) { Name = name; @@ -398,7 +400,7 @@ namespace Server DexGain = dexGain; IntGain = intGain; GainFactor = gainFactor; - + ProfessionSkillName = professionSkillName ?? Name.Replace(" ", ""); StatTotal = strScale + dexScale + intScale; } @@ -426,25 +428,27 @@ namespace Server public double GainFactor { get; set; } + public string ProfessionSkillName { get; set; } + public static SkillInfo[] Table { get; set; } = { new(0, "Alchemy", 0.0, 5.0, 5.0, "Alchemist", null, 0.0, 0.5, 0.5, 1.0), new(1, "Anatomy", 0.0, 0.0, 0.0, "Biologist", null, 0.15, 0.15, 0.7, 1.0), new(2, "Animal Lore", 0.0, 0.0, 0.0, "Naturalist", null, 0.0, 0.0, 1.0, 1.0), - new(3, "Item Identification", 0.0, 0.0, 0.0, "Merchant", null, 0.0, 0.0, 1.0, 1.0), + new(3, "Item Identification", 0.0, 0.0, 0.0, "Merchant", null, 0.0, 0.0, 1.0, 1.0, "ItemID"), new(4, "Arms Lore", 0.0, 0.0, 0.0, "Weapon Master", null, 0.75, 0.15, 0.1, 1.0), new(5, "Parrying", 7.5, 2.5, 0.0, "Duelist", null, 0.75, 0.25, 0.0, 1.0), new(6, "Begging", 0.0, 0.0, 0.0, "Beggar", null, 0.0, 0.0, 0.0, 1.0), - new(7, "Blacksmithy", 10.0, 0.0, 0.0, "Blacksmith", null, 1.0, 0.0, 0.0, 1.0), - new(8, "Bowcraft/Fletching", 6.0, 16.0, 0.0, "Bowyer", null, 0.6, 1.6, 0.0, 1.0), + new(7, "Blacksmithy", 10.0, 0.0, 0.0, "Blacksmith", null, 1.0, 0.0, 0.0, 1.0, "Blacksmith"), + new(8, "Bowcraft/Fletching", 6.0, 16.0, 0.0, "Bowyer", null, 0.6, 1.6, 0.0, 1.0, "Bowcraft"), new(9, "Peacemaking", 0.0, 0.0, 0.0, "Pacifier", null, 0.0, 0.0, 0.0, 1.0), new(10, "Camping", 20.0, 15.0, 15.0, "Explorer", null, 2.0, 1.5, 1.5, 1.0), new(11, "Carpentry", 20.0, 5.0, 0.0, "Carpenter", null, 2.0, 0.5, 0.0, 1.0), new(12, "Cartography", 0.0, 7.5, 7.5, "Cartographer", null, 0.0, 0.75, 0.75, 1.0), new(13, "Cooking", 0.0, 20.0, 30.0, "Chef", null, 0.0, 2.0, 3.0, 1.0), new(14, "Detecting Hidden", 0.0, 0.0, 0.0, "Scout", null, 0.0, 0.4, 0.6, 1.0), - new(15, "Discordance", 0.0, 2.5, 2.5, "Demoralizer", null, 0.0, 0.25, 0.25, 1.0), - new(16, "Evaluating Intelligence", 0.0, 0.0, 0.0, "Scholar", null, 0.0, 0.0, 1.0, 1.0), + new(15, "Discordance", 0.0, 2.5, 2.5, "Demoralizer", null, 0.0, 0.25, 0.25, 1.0, "Enticement"), + new(16, "Evaluating Intelligence", 0.0, 0.0, 0.0, "Scholar", null, 0.0, 0.0, 1.0, 1.0, "EvaluateIntelligence"), new(17, "Healing", 6.0, 6.0, 8.0, "Healer", null, 0.6, 0.6, 0.8, 1.0), new(18, "Fishing", 0.0, 0.0, 0.0, "Fisherman", null, 0.5, 0.5, 0.0, 1.0), new(19, "Forensic Evaluation", 0.0, 0.0, 0.0, "Detective", null, 0.0, 0.2, 0.8, 1.0), @@ -476,7 +480,7 @@ namespace Server new(45, "Mining", 20.0, 0.0, 0.0, "Miner", null, 2.0, 0.0, 0.0, 1.0), new(46, "Meditation", 0.0, 0.0, 0.0, "Stoic", null, 0.0, 0.0, 0.0, 1.0), new(47, "Stealth", 0.0, 0.0, 0.0, "Rogue", null, 0.0, 0.0, 0.0, 1.0), - new(48, "Remove Trap", 0.0, 0.0, 0.0, "Trap Specialist", null, 0.0, 0.0, 0.0, 1.0), + new(48, "Remove Trap", 0.0, 0.0, 0.0, "Trap Specialist", null, 0.0, 0.0, 0.0, 1.0, "Disarm"), new(49, "Necromancy", 0.0, 0.0, 0.0, "Necromancer", null, 0.0, 0.0, 0.0, 1.0), new(50, "Focus", 0.0, 0.0, 0.0, "Driven", null, 0.0, 0.0, 0.0, 1.0), new(51, "Chivalry", 0.0, 0.0, 0.0, "Paladin", null, 0.0, 0.0, 0.0, 1.0), @@ -892,16 +896,16 @@ namespace Server } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SkillsEnumerator GetEnumerator() => new(m_Skills); + public Enumerator GetEnumerator() => new(m_Skills); - public ref struct SkillsEnumerator + public ref struct Enumerator { private readonly Skill[] _skills; private int _index; private Skill _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal SkillsEnumerator(Skill[] skills) + internal Enumerator(Skill[] skills) { _skills = skills; _index = 0; diff --git a/Projects/Server/Text/StringHelpers.cs b/Projects/Server/Text/StringHelpers.cs index 7e05497c2..cf97b6a7c 100644 --- a/Projects/Server/Text/StringHelpers.cs +++ b/Projects/Server/Text/StringHelpers.cs @@ -169,6 +169,28 @@ namespace Server 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) diff --git a/Projects/Server/TileData.cs b/Projects/Server/TileData.cs index 3434d0455..6e2b66aaf 100644 --- a/Projects/Server/TileData.cs +++ b/Projects/Server/TileData.cs @@ -104,11 +104,28 @@ namespace Server } [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - public int CalcHeight => (Flags & TileFlag.Bridge) != 0 ? _height / 2 : _height; + public int CalcHeight => Bridge ? _height / 2 : _height; + + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + public bool Door + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this[TileFlag.Door]; + set => this[TileFlag.Door] = value; + } + + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + public bool Background + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this[TileFlag.Background]; + set => this[TileFlag.Background] = value; + } [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool Bridge { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.Bridge]; set => this[TileFlag.Bridge] = value; } @@ -116,6 +133,7 @@ namespace Server [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool Wall { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.Wall]; set => this[TileFlag.Wall] = value; } @@ -123,13 +141,22 @@ namespace Server [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool Window { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.Window]; set => this[TileFlag.Window] = value; } + public bool ImpassableSurface + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this[TileFlag.Impassable | TileFlag.Surface]; + set => this[TileFlag.Impassable | TileFlag.Surface] = value; + } + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool Impassable { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.Impassable]; set => this[TileFlag.Impassable] = value; } @@ -137,6 +164,7 @@ namespace Server [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool Surface { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.Surface]; set => this[TileFlag.Surface] = value; } @@ -144,6 +172,7 @@ namespace Server [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool Roof { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.Roof]; set => this[TileFlag.Roof] = value; } @@ -151,10 +180,19 @@ namespace Server [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool LightSource { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this[TileFlag.LightSource]; set => this[TileFlag.LightSource] = value; } + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + public bool Wet + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this[TileFlag.Wet]; + set => this[TileFlag.Wet] = value; + } + public bool this[TileFlag flag] { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/TimeZones/TimeZoneHandler.cs b/Projects/Server/TimeZones/TimeZoneHandler.cs new file mode 100644 index 000000000..5e9450f02 --- /dev/null +++ b/Projects/Server/TimeZones/TimeZoneHandler.cs @@ -0,0 +1,80 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TimeZoneHandler.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 Server.Json; + +namespace Server +{ + /** + * TimeZoneHandler provides a cross platform compatible way to handle system timezones. + * + * By default the system timezone is used. + * To manually configure the local timezone add the following setting to modernuo.json: + * "system.localTimeZone": "Pacific Standard Time" + * All valid timezones can be found in the Distribution/Data/timezones.json file. + * + * Notes for timezones.json: + * By default, Windows will use the values in the "timezone" property. + * By default, Unix/MacOS will use the values in the "region" property. + * system.localTimeZone can be either timezone or a region. + * + * Example: + * "system.localTimeZone": "Europe/Lisbon" + */ + public static class TimeZoneHandler + { + private static readonly Dictionary _timeZoneById = new(); + public static TimeZoneInfo SystemTimeZone { get; private set; } + + static TimeZoneHandler() + { + var timezones = JsonConfig.Deserialize(Path.Combine(Core.BaseDirectory, "Data/timezones.json")); + foreach (var tz in timezones) + { + // Get the timezone if we are on Windows + var tzInfo = Core.IsWindows ? TimeZoneInfo.FindSystemTimeZoneById(tz.TimeZone) : null; + + foreach (var region in tz.Regions) + { + // Get the timezone by region if we are on linux + tzInfo ??= TimeZoneInfo.FindSystemTimeZoneById(region); + _timeZoneById[region] = tzInfo; + } + + _timeZoneById[tz.TimeZone] = tzInfo; + } + } + + public static void Configure() + { + var tzId = ServerConfiguration.GetSetting("system.localTimeZone", TimeZoneInfo.Local.Id); + SystemTimeZone = FindTimeZoneById(tzId); + } + + /** + * Cross platform version of TimeZoneInfo.FindSystemTimeZoneById + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeZoneInfo FindTimeZoneById(string id) => _timeZoneById[id]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime ToSystemLocalTime(this DateTime date) => + TimeZoneInfo.ConvertTimeFromUtc(date, SystemTimeZone); + } +} diff --git a/Projects/Server/TimeZones/TimeZoneWithRegions.cs b/Projects/Server/TimeZones/TimeZoneWithRegions.cs new file mode 100644 index 000000000..4c19a0086 --- /dev/null +++ b/Projects/Server/TimeZones/TimeZoneWithRegions.cs @@ -0,0 +1,28 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TimeZoneWithRegions.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 Server +{ + public record TimeZoneWithRegions + { + [JsonPropertyName("timezone")] + public string TimeZone { get; init; } + + [JsonPropertyName("regions")] + public string[] Regions { get; init; } + } +} diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index 11d6b84c6..5d98180bb 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -71,7 +71,7 @@ namespace Server public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) { DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); - t._selfReturn = true; + t._returnOnDetach = true; t.Start(); #if DEBUG_TIMERS @@ -115,7 +115,7 @@ namespace Server public sealed class DelayCallTimer : Timer, INotifyCompletion { - internal bool _selfReturn; + internal bool _returnOnDetach; #if DEBUG_TIMERS internal bool _allowFinalization; #endif @@ -143,57 +143,50 @@ namespace Server _continuation?.Invoke(); } - public override void Stop() - { - base.Stop(); - - if (_selfReturn) - { - Return(); - } - } - - internal void Return() + internal override void OnDetach() { if (Running) { - logger.Error($"Timer is returned while still running! {new StackTrace()}"); + logger.Error($"Timer is returned while still running!\n{new StackTrace()}"); return; } - Version++; // Increment the version so if this is called from OnTick() and another timer is started, we don't have a problem - -#if DEBUG_TIMERS - _stackTraces.Remove(GetHashCode()); -#endif - - if (_poolCount >= _poolCapacity) + if (_returnOnDetach) { -#if DEBUG_TIMERS - logger.Warning($"DelayCallTimer pool reached maximum of {_poolCapacity} timers"); - _allowFinalization = true; -#endif - return; - } + // Increment the version so if this is called from OnTick() and another timer is started, we don't have a problem + // Version++; - _continuation = null; - ReturnToPool(1, this, this); +#if DEBUG_TIMERS + _stackTraces.Remove(GetHashCode()); +#endif + + if (_poolCount >= _poolCapacity) + { +#if DEBUG_TIMERS + logger.Warning($"DelayCallTimer pool reached maximum of {_poolCapacity} timers"); + _allowFinalization = true; +#endif + return; + } + + _continuation = null; + _returnOnDetach = false; + ReturnToPool(1, this, this); + } } public static DelayCallTimer GetTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) { - if (_poolHead != null) + var timer = GetFromPool(); + if (timer != null) { - _poolCount--; #if DEBUG_TIMERS - logger.Information($"Pool count: {_poolCount} / {_poolCapacity}"); + logger.Information($"Getting from pool: ({_poolCount} / {_poolCapacity})"); #endif - var timer = GetFromPool(); - timer.Init(delay, interval, count); timer._continuation = callback; - timer._selfReturn = false; + timer._returnOnDetach = false; #if DEBUG_TIMERS timer._allowFinalization = false; #endif diff --git a/Projects/Server/Timer/Timer.Pool.cs b/Projects/Server/Timer/Timer.Pool.cs index a7db9c273..cb92fc18f 100644 --- a/Projects/Server/Timer/Timer.Pool.cs +++ b/Projects/Server/Timer/Timer.Pool.cs @@ -62,22 +62,29 @@ namespace Server _poolHead = head; _poolCount += amount; #if DEBUG_TIMERS - logger.Information($"Pool count: {_poolCount} / {_poolCapacity}"); + logger.Information($"Returning to pool. ({_poolCount} / {_poolCapacity})"); #endif } private static DelayCallTimer GetFromPool() { + if (_poolHead == null) + { + return null; + } + var timer = _poolHead; _poolHead = _poolHead._nextTimer as DelayCallTimer; timer.Detach(); + _poolCount--; + return timer; } 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."); #endif head = null; diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index e1af8df8e..5e9d66334 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -28,11 +28,12 @@ namespace Server private const int _tickRatePowerOf2 = 3; private const int _tickRate = 1 << _tickRatePowerOf2; // 8ms - private static long _lastTickTurned = -1; - private static readonly Timer[][] _rings = new Timer[_ringLayers][]; private static readonly int[] _ringIndexes = new int[_ringLayers]; + private static long _lastTickTurned = -1; + private static bool _timerWheelExecuting; + public static void Init(long tickCount) { _lastTickTurned = tickCount; @@ -44,46 +45,63 @@ namespace Server } } - public static int Slice(long tickCount) + public static void Slice(long tickCount) { var deltaSinceTurn = tickCount - _lastTickTurned; - var events = 0; while (deltaSinceTurn >= _tickRate) { deltaSinceTurn -= _tickRate; _lastTickTurned += _tickRate; - events += Turn() ? 1 : 0; + Turn(); } - - return events; } - private static bool Turn() + private static void Turn() { - bool events = false; + _timerWheelExecuting = true; + var turnNextWheel = false; + + var _executingRings = new Timer[_ringLayers]; + + // Detach the chain from the timer wheel. This allows adding timers to the same slot during execution. + for (var i = 0; i < _ringLayers; i++) + { + if (i == 0 || turnNextWheel) + { + var ringIndex = ++_ringIndexes[i]; + turnNextWheel = ringIndex >= _ringSize; + + if (turnNextWheel) + { + ringIndex = _ringIndexes[i] = 0; + } + + _executingRings[i] = _rings[i][ringIndex]; + _rings[i][ringIndex] = null; + } + else + { + _executingRings[i] = null; + } + } for (var i = 0; i < _ringLayers; i++) { - // Increment the ring index, then get the timer. - var ringIndex = ++_ringIndexes[i]; - bool turnNextWheel = ringIndex >= _ringSize; - - if (turnNextWheel) + var timer = _executingRings[i]; + if (timer == null) { - ringIndex = _ringIndexes[i] = 0; + continue; } - var timer = _rings[i][ringIndex]; - if (timer != null) + do { - events = true; + var next = timer._nextTimer; - do + timer.Detach(); + + // Check to see if it's running just in case it was stopped by another timer + if (timer.Running) { - var next = _rings[i][ringIndex] = timer._nextTimer; - - timer.Detach(); - if (i > 0 && timer._remaining > 0) { // Promote @@ -93,18 +111,18 @@ namespace Server { Execute(timer); } + } - timer = next; - } while (timer != null); - } + if (!timer.Running) + { + timer.OnDetach(); + } - if (!turnNextWheel) - { - break; - } + timer = next; + } while (timer != null); } - return events; + _timerWheelExecuting = false; } private static void Execute(Timer timer) @@ -136,14 +154,17 @@ namespace Server private static void AddTimer(Timer timer, long delay) { +#if DEBUG_TIMERS + var originalDelay = delay; +#endif delay = Math.Max(0, delay); var resolutionPowerOf2 = _tickRatePowerOf2; for (var i = 0; i < _ringLayers; i++) { - var resolution = 1 << resolutionPowerOf2; + var resolution = 1L << resolutionPowerOf2; var nextResolutionPowerOf2 = resolutionPowerOf2 + _ringSizePowerOf2; - long max = 1 << nextResolutionPowerOf2; + var max = 1L << nextResolutionPowerOf2; if (delay < max) { var remaining = delay & (resolution - 1); @@ -168,13 +189,18 @@ namespace Server _rings[i][slot] = timer; - break; + return; } // The remaining amount until we turn this ring delay -= resolution * (_ringSize - _ringIndexes[i]); resolutionPowerOf2 = nextResolutionPowerOf2; } + + // TODO: Handle timers > 17yrs +#if DEBUG_TIMERS + logger.Error($"Timer is more than max duration. ({originalDelay})"); +#endif } public static void DumpInfo(TextWriter tw) @@ -216,7 +242,7 @@ namespace Server } #if DEBUG_TIMERS - tw.WriteLine("\nStack Traces:"); + tw.WriteLine($"{Environment.NewLine}Stack Traces:"); foreach (var kvp in DelayCallTimer._stackTraces) { tw.WriteLine(kvp.Value); diff --git a/Projects/Server/Timer/Timer.cs b/Projects/Server/Timer/Timer.cs index 7d10bf441..786d1c86c 100644 --- a/Projects/Server/Timer/Timer.cs +++ b/Projects/Server/Timer/Timer.cs @@ -35,9 +35,9 @@ namespace Server private Timer _nextTimer; private Timer _prevTimer; - public Timer(TimeSpan delay) : this(delay, TimeSpan.Zero, 1) - { - } + public Timer(TimeSpan delay) => Init(delay, TimeSpan.Zero, 1); + + public Timer(TimeSpan interval, int count) => Init(interval, interval, count); public Timer(TimeSpan delay, TimeSpan interval, int count = 0) => Init(delay, interval, count); @@ -102,17 +102,22 @@ namespace Server return; } - // We are at the head - if (_rings[_ring][_slot] == this) + // Do not detach if we are in the middle of executing the timer wheel for this ring/slot + if (!_timerWheelExecuting || _ringIndexes[_ring] != _slot) { - _rings[_ring][_slot] = _nextTimer; + // We are at the head + if (_rings[_ring][_slot] == this) + { + _rings[_ring][_slot] = _nextTimer; + } + + Detach(); } - Detach(); Running = false; Version++; - var prof = GetProfile(); + var prof = GetProfile(); if (prof != null) { prof.Stopped++; @@ -147,5 +152,9 @@ namespace Server _nextTimer = null; _prevTimer = null; } + + internal virtual void OnDetach() + { + } } } diff --git a/Projects/Server/Timer/TimerExecutionToken.cs b/Projects/Server/Timer/TimerExecutionToken.cs index 9535e726e..9e9992e3b 100644 --- a/Projects/Server/Timer/TimerExecutionToken.cs +++ b/Projects/Server/Timer/TimerExecutionToken.cs @@ -50,9 +50,12 @@ namespace Server public void Cancel() { - _timer?.Stop(); - _timer?.Return(); - _timer = null; + if (_timer != null) + { + _timer._returnOnDetach = true; + _timer?.Stop(); + _timer = null; + } this = default; } diff --git a/Projects/Server/Utilities/PathUtility.cs b/Projects/Server/Utilities/PathUtility.cs new file mode 100644 index 000000000..d2206143d --- /dev/null +++ b/Projects/Server/Utilities/PathUtility.cs @@ -0,0 +1,67 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PathUtility.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.Text; + +namespace Server +{ + public static class PathUtility + { + 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) + { + 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())); + } + } +} diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 430dd0907..286e488e7 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -484,7 +484,6 @@ namespace Server break; } - num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); byteIndex += 2; @@ -927,14 +926,11 @@ namespace Server return outputList; } - public static bool ToBoolean(string value) - { -#pragma warning disable CA1806 // Do not ignore method results - bool.TryParse(value, out var b); -#pragma warning restore CA1806 // Do not ignore method results - - return b; - } + 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) { @@ -1335,28 +1331,6 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Max(T val, T max) where T : IComparable => val.CompareTo(max) > 0 ? val : max; - 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); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Tidy(this List list) where T : ISerializable { @@ -1484,7 +1458,10 @@ namespace Server } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetTimeStamp() => Core.Now.ToString("yyyy-MM-dd-HH-mm-ss"); + 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) @@ -1639,5 +1616,26 @@ namespace Server 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; + } } } diff --git a/Projects/Server/World/EntityPersistence.cs b/Projects/Server/World/EntityPersistence.cs index 93c597bba..bf2f415df 100644 --- a/Projects/Server/World/EntityPersistence.cs +++ b/Projects/Server/World/EntityPersistence.cs @@ -24,6 +24,8 @@ namespace Server { public static class EntityPersistence { + private const int _idxVersion = 1; + public static void WriteEntities( IIndexInfo indexInfo, Dictionary entities, @@ -38,7 +40,7 @@ namespace Server var path = Path.Combine(savePath, typeName); - AssemblyHandler.EnsureDirectory(path); + PathUtility.EnsureDirectory(path); string idxPath = Path.Combine(path, $"{typeName}.idx"); string tdbPath = Path.Combine(path, $"{typeName}.tdb"); @@ -48,6 +50,7 @@ namespace Server using var tdb = new BinaryFileWriter(tdbPath, false); using var bin = new BinaryFileWriter(binPath, true); + idx.Write(1); // Version idx.Write(entities.Count); foreach (var e in entities.Values) { @@ -55,6 +58,8 @@ namespace Server idx.Write(e.TypeRef); idx.Write(e.Serial); + idx.Write(e.Created.Ticks); + idx.Write(e.LastSerialized.Ticks); idx.Write(start); e.SerializeTo(bin); @@ -110,12 +115,28 @@ namespace Server List> types = ReadTypes(tdbReader); - var count = idxReader.ReadInt32(); + int count; + var version = idxReader.ReadInt32(); + + // Handle non-versioned (version 0). + if (version > _idxVersion || idx.Length - 4 - version * 20 == 0) + { + count = version; + version = 0; + } + else + { + count = idxReader.ReadInt32(); + } + + var now = DateTime.UtcNow; for (int i = 0; i < count; ++i) { var typeID = idxReader.ReadInt32(); - var number = idxReader.ReadUInt32(); + var serial = idxReader.ReadUInt32(); + var created = version == 0 ? now : new DateTime(idxReader.ReadInt64(), DateTimeKind.Utc); + var lastSerialized = version == 0 ? DateTime.MinValue : new DateTime(idxReader.ReadInt64(), DateTimeKind.Utc); var pos = idxReader.ReadInt64(); var length = idxReader.ReadInt32(); @@ -127,12 +148,14 @@ namespace Server } ConstructorInfo ctor = objs.Item1; - I indexer = indexInfo.CreateIndex(number); + I indexer = indexInfo.CreateIndex(serial); ctorArgs[0] = indexer; if (ctor.Invoke(ctorArgs) is T t) { + t.Created = created; + t.LastSerialized = lastSerialized; entities.Add(new EntityIndex(t, typeID, pos, length)); map[indexer] = t; } @@ -180,7 +203,7 @@ namespace Server var buffer = GC.AllocateUninitializedArray(entry.Length); if (br == null) { - br = new BufferReader(buffer); + br = new BufferReader(buffer, t.LastSerialized); } else { diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index 0120d2991..02b3013d5 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -46,7 +46,7 @@ namespace Server private static readonly ConcurrentQueue _decayQueue = new(); private static string _tempSavePath; // Path to the temporary folder for the save - private static string _savePath; // Path to "Saves" folder + private static bool _enableSaveStats; public const bool DirtyTrackingEnabled = false; public const uint ItemOffset = 0x40000000; @@ -129,9 +129,11 @@ namespace Server internal static List MobileTypes { get; } = new(); internal static List GuildTypes { get; } = new(); + public static string SavePath { get; private set; } + public static WorldState WorldState { get; private set; } public static bool Saving => WorldState == WorldState.Saving; - public static bool Running => WorldState != WorldState.Loading && WorldState != WorldState.Initial; + public static bool Running => WorldState is not WorldState.Loading and not WorldState.Initial; public static bool Loading => WorldState == WorldState.Loading; public static Dictionary Mobiles { get; private set; } @@ -140,10 +142,13 @@ namespace Server public static void Configure() { - var tempSavePath = ServerConfiguration.GetOrUpdateSetting("world.tempSavePath", "temp"); - _tempSavePath = Path.Combine(Core.BaseDirectory, tempSavePath); + var tempSavePath = ServerConfiguration.GetSetting("world.tempSavePath", "temp"); + _tempSavePath = PathUtility.GetFullPath(tempSavePath); + var savePath = ServerConfiguration.GetOrUpdateSetting("world.savePath", "Saves"); - _savePath = Path.Combine(Core.BaseDirectory, savePath); + SavePath = PathUtility.GetFullPath(savePath); + + _enableSaveStats = ServerConfiguration.GetOrUpdateSetting("world.enableSaveStats", false); // Mobiles & Items Persistence.Register("Mobiles & Items", SaveEntities, WriteEntities, LoadEntities, 1); @@ -254,7 +259,7 @@ namespace Server logger.Information("Loading world"); var watch = Stopwatch.StartNew(); - Persistence.Load(_savePath); + Persistence.Load(SavePath); EventSink.InvokeWorldLoad(); ProcessSafetyQueues(); @@ -341,7 +346,10 @@ namespace Server int count = 0; var timestamp = Utility.GetTimeStamp(); - using var op = new StreamWriter("Logs/Saves/Save-Stats-{0}.log", true); + var saveStatsPath = Path.Combine(Core.BaseDirectory, $"Logs/Saves/Save-Stats-{timestamp}.log"); + PathUtility.EnsureDirectory(saveStatsPath); + + using var op = new StreamWriter(saveStatsPath, true); for (var i = 0; i < entityTypes.Length; i++) { @@ -373,14 +381,17 @@ namespace Server EntityPersistence.WriteEntities(itemIndexInfo, Items, ItemTypes, basePath, out var itemCounts); EntityPersistence.WriteEntities(guildIndexInfo, Guilds, GuildTypes, basePath, out var guildCounts); - TraceSave(mobileCounts?.ToList(), itemCounts?.ToList(), guildCounts?.ToList()); + if (_enableSaveStats) + { + TraceSave(mobileCounts?.ToList(), itemCounts?.ToList(), guildCounts?.ToList()); + } } public static void WriteFiles(object state) { Exception exception = null; - var tempPath = Path.Combine(_tempSavePath, Utility.GetTimeStamp()); + var tempPath = PathUtility.EnsureRandomPath(_tempSavePath); try { @@ -409,8 +420,8 @@ namespace Server { try { - EventSink.InvokeWorldSavePostSnapshot(_savePath, tempPath); - Directory.Move(tempPath, _savePath); + EventSink.InvokeWorldSavePostSnapshot(SavePath, tempPath); + Directory.Move(tempPath, SavePath); } catch (Exception ex) { @@ -652,13 +663,24 @@ namespace Server Serial serial = reader.ReadSerial(); var typeT = typeof(T); + T entity; + // Add to this list when creating new serializable types if (typeof(BaseGuild).IsAssignableTo(typeT)) { - return FindGuild(serial) as T; + entity = FindGuild(serial) as T; + } + else + { + entity = FindEntity(serial) as T; } - return 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 diff --git a/Projects/UOContent.Tests/Fixtures/ServerFixture.cs b/Projects/UOContent.Tests/Fixtures/ServerFixture.cs index ab391505b..a5cbdca8e 100644 --- a/Projects/UOContent.Tests/Fixtures/ServerFixture.cs +++ b/Projects/UOContent.Tests/Fixtures/ServerFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Server.Misc; namespace Server.Tests @@ -8,8 +9,8 @@ namespace Server.Tests // Global setup static ServerFixture() { + Core.Assembly = Assembly.GetExecutingAssembly(); Core.LoopContext = new EventLoopContext(); - Core.Expansion = Expansion.EJ; // Load Configurations diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index 2fd4f6f7b..baa2a972e 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -3,7 +3,7 @@ false - + diff --git a/Projects/UOContent/Accounting/Account.Migrations.cs b/Projects/UOContent/Accounting/Account.Migrations.cs new file mode 100644 index 000000000..dd9148b01 --- /dev/null +++ b/Projects/UOContent/Accounting/Account.Migrations.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.IO; +using System.Net; +using Server.Accounting.Security; + +namespace Server.Accounting +{ + public partial class Account + { + private void MigrateFrom(V3Content content) + { + _username = content.Username; + _passwordAlgorithm = content.PasswordAlgorithm; + _password = content.Password; + _accessLevel = content.AccessLevel; + _flags = content.Flags; + Created = content.Created; + _lastLogin = content.LastLogin; + _totalGold = content.TotalGold; + _totalPlat = content.TotalPlat; + _mobiles = content.Mobiles; + _comments = content.Comments; + _tags = content.Tags; + _loginIPs = content.LoginIPs; + _ipRestrictions = content.IpRestrictions; + _totalGameTime = content.TotalGameTime; + _email = content.Email; + } + + private void Deserialize(IGenericReader reader, int version) + { + if (version != 2) + { + // Due to a bug where we were not versioning at all, reset so we don't have an issue deserializing + reader.Seek(0, SeekOrigin.Begin); + } + + _username = reader.ReadString(); + _passwordAlgorithm = version < 2 ? (PasswordProtectionAlgorithm)reader.ReadInt() : reader.ReadEnum(); + _password = reader.ReadString(); + _accessLevel = version < 2 ? (AccessLevel)reader.ReadInt() : reader.ReadEnum(); + _flags = reader.ReadInt(); + Created = reader.ReadDateTime(); + _lastLogin = reader.ReadDateTime(); + + _totalGold = reader.ReadInt(); + _totalPlat = reader.ReadInt(); + + var length = reader.ReadInt(); + _mobiles = new Mobile[length]; + for (int i = 0; i < length; i++) + { + _mobiles[i] = reader.ReadEntity(); + } + + length = reader.ReadInt(); + _comments = length > 0 ? new List(length) : null; + for (int i = 0; i < length; i++) + { + _comments!.Add(new AccountComment(reader)); + } + + length = reader.ReadInt(); + _tags = length > 0 ? new List(length) : null; + for (int i = 0; i < length; i++) + { + _tags!.Add(new AccountTag(reader)); + } + + length = reader.ReadInt(); + _loginIPs = new IPAddress[length]; + for (int i = 0; i < length; i++) + { + if (version < 2) + { + if (IPAddress.TryParse(reader.ReadString(), out var address)) + { + _loginIPs[i] = Utility.Intern(address); + } + } + else + { + _loginIPs[i] = reader.ReadIPAddress(); + } + } + + length = reader.ReadInt(); + _ipRestrictions = new string[length]; + for (int i = 0; i < length; i++) + { + _ipRestrictions[i] = reader.ReadString(); + } + + _totalGameTime = reader.ReadTimeSpan(); + + if (version > 1) + _email = reader.ReadString(); + + Timer.StartTimer(AfterDeserialization); + } + } +} diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index 02a3cc2ff..43a1f8fb8 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Net; using System.Xml; using Server.Accounting.Security; @@ -11,7 +10,7 @@ using Server.Network; namespace Server.Accounting { - [Serializable(3)] + [Serializable(4)] public partial class Account : IAccount, IComparable, ISerializable { public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0); @@ -33,10 +32,7 @@ namespace Server.Accounting [SerializableField(4)] private int _flags; - [SerializableField(5, setter: "private")] - private DateTime _created; - - [SerializableField(6)] + [SerializableField(5)] private DateTime _lastLogin; /// @@ -44,7 +40,7 @@ namespace Server.Accounting /// The value does not include the value of Platinum and ranges from /// 0 to 999,999,999 by default. /// - [SerializableField(7, setter: "private")] + [SerializableField(6, setter: "private")] [SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")] public int _totalGold; @@ -54,16 +50,16 @@ namespace Server.Accounting /// 0 to 2,147,483,647 by default. /// One Platinum represents the value of CurrencyThreshold in Gold. /// - [SerializableField(8, setter: "private")] + [SerializableField(7, setter: "private")] [SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")] public int _totalPlat; - [SerializableField(9, "private", "private")] + [SerializableField(8, "private", "private")] private Mobile[] _mobiles; private List _comments; - [SerializableField(10)] + [SerializableField(9)] public List Comments { get => _comments ??= new List(); @@ -76,7 +72,7 @@ namespace Server.Accounting private List _tags; - [SerializableField(11)] + [SerializableField(10)] public List Tags { get => _tags ??= new List(); @@ -87,14 +83,14 @@ namespace Server.Accounting } } - [SerializableField(12)] + [SerializableField(11)] private IPAddress[] _loginIPs; /// /// List of IP addresses for restricted access. '*' wildcard supported. If the array contains zero entries, all IP addresses /// are allowed. /// - [SerializableField(13)] + [SerializableField(12)] private string[] _ipRestrictions; private TimeSpan _totalGameTime; @@ -103,7 +99,7 @@ namespace Server.Accounting /// Gets the total game time of this account, also considering the game time of characters /// that have been deleted. /// - [SerializableField(14)] + [SerializableField(13)] public TimeSpan TotalGameTime { get @@ -125,7 +121,7 @@ namespace Server.Accounting } } - [SerializableField(15)] + [SerializableField(14)] [SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")] private string _email; @@ -139,7 +135,7 @@ namespace Server.Accounting _accessLevel = AccessLevel.Player; - _created = _lastLogin = Core.Now; + _lastLogin = Core.Now; _totalGameTime = TimeSpan.Zero; _mobiles = new Mobile[7]; @@ -184,7 +180,7 @@ namespace Server.Accounting Enum.TryParse(Utility.GetText(node["accessLevel"], "Player"), true, out _accessLevel); _flags = Utility.GetXMLInt32(Utility.GetText(node["flags"], "0"), 0); - _created = Utility.GetXMLDateTime(Utility.GetText(node["created"], null), Core.Now); + Created = Utility.GetXMLDateTime(Utility.GetText(node["created"], null), Core.Now); _lastLogin = Utility.GetXMLDateTime(Utility.GetText(node["lastLogin"], null), Core.Now); _totalGold = Utility.GetXMLInt32(Utility.GetText(node["totalGold"], "0"), 0); @@ -306,10 +302,22 @@ namespace Server.Accounting } } + public TimeSpan AccountAge => Core.Now - Created; + + [CommandProperty(AccessLevel.GameMaster, readOnly: true)] + public DateTime Created { get; set; } = Core.Now; + + [CommandProperty(AccessLevel.GameMaster)] + DateTime ISerializable.LastSerialized { get; set; } = Core.Now; + public int TypeRef { get; private set; } public Serial Serial { get; set; } + public void BeforeSerialize() + { + } + [AfterDeserialization] private void AfterDeserialization() { @@ -348,77 +356,6 @@ namespace Server.Accounting } } - private void Deserialize(IGenericReader reader, int version) - { - if (version != 2) - { - // Due to a bug where we were not versioning at all, reset so we don't have an issue deserializing - reader.Seek(0, SeekOrigin.Begin); - } - - _username = reader.ReadString(); - _passwordAlgorithm = version < 2 ? (PasswordProtectionAlgorithm)reader.ReadInt() : reader.ReadEnum(); - _password = reader.ReadString(); - _accessLevel = version < 2 ? (AccessLevel)reader.ReadInt() : reader.ReadEnum(); - _flags = reader.ReadInt(); - _created = reader.ReadDateTime(); - _lastLogin = reader.ReadDateTime(); - - _totalGold = reader.ReadInt(); - _totalPlat = reader.ReadInt(); - - var length = reader.ReadInt(); - _mobiles = new Mobile[length]; - for (int i = 0; i < length; i++) - { - _mobiles[i] = reader.ReadEntity(); - } - - length = reader.ReadInt(); - _comments = length > 0 ? new List(length) : null; - for (int i = 0; i < length; i++) - { - _comments!.Add(new AccountComment(reader)); - } - - length = reader.ReadInt(); - _tags = length > 0 ? new List(length) : null; - for (int i = 0; i < length; i++) - { - _tags!.Add(new AccountTag(reader)); - } - - length = reader.ReadInt(); - _loginIPs = new IPAddress[length]; - for (int i = 0; i < length; i++) - { - if (version < 2) - { - if (IPAddress.TryParse(reader.ReadString(), out var address)) - { - _loginIPs[i] = Utility.Intern(address); - } - } - else - { - _loginIPs[i] = reader.ReadIPAddress(); - } - } - - length = reader.ReadInt(); - _ipRestrictions = new string[length]; - for (int i = 0; i < length; i++) - { - _ipRestrictions[i] = reader.ReadString(); - } - - _totalGameTime = reader.ReadTimeSpan(); - - _email = reader.ReadString(); - - Timer.StartTimer(AfterDeserialization); - } - /// /// Deletes the account, all characters of the account, and all houses of those characters /// diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs index 91a5a4096..a115d993a 100644 --- a/Projects/UOContent/Accounting/AccountHandler.cs +++ b/Projects/UOContent/Accounting/AccountHandler.cs @@ -241,7 +241,7 @@ namespace Server.Misc { res = DeleteResultType.CharBeingPlayed; } - else if (RestrictDeletion && Core.Now < m.CreationTime + DeleteDelay) + else if (RestrictDeletion && Core.Now < m.Created + DeleteDelay) { res = DeleteResultType.CharTooYoung; } diff --git a/Projects/UOContent/Commands/Batch.cs b/Projects/UOContent/Commands/Batch.cs index fcc5bdab6..84aeca315 100644 --- a/Projects/UOContent/Commands/Batch.cs +++ b/Projects/UOContent/Commands/Batch.cs @@ -76,67 +76,57 @@ namespace Server.Commands CommandLogging.Enabled = false; } - List usedList; + var propertyChains = new Dictionary(); + var usedList = new List(list.Count); - if (Utility.InsensitiveCompare(bc.Object, "Current") == 0) + for (var j = 0; j < list.Count; ++j) { - usedList = list; - } - else - { - var propertyChains = new Dictionary(); + var obj = list[j]; - usedList = new List(list.Count); - - for (var j = 0; j < list.Count; ++j) + if (obj == null) { - var obj = list[j]; + continue; + } - if (obj == null) + var type = obj.GetType(); + var failReason = ""; + + if (!propertyChains.TryGetValue(type, out var chain)) + { + propertyChains[type] = chain = Properties.GetPropertyInfoChain( + e.Mobile, + type, + bc.Object, + PropertyAccess.Read, + out failReason + ); + } + + if (chain == null) + { + continue; + } + + var endProp = Properties.GetPropertyInfo(ref obj, chain, out failReason); + + if (endProp == null) + { + continue; + } + + try + { + obj = endProp.GetValue(obj, null); + + if (obj != null) { - continue; - } - - var type = obj.GetType(); - var failReason = ""; - - if (!propertyChains.TryGetValue(type, out var chain)) - { - propertyChains[type] = chain = Properties.GetPropertyInfoChain( - e.Mobile, - type, - bc.Object, - PropertyAccess.Read, - ref failReason - ); - } - - if (chain == null) - { - continue; - } - - var endProp = Properties.GetPropertyInfo(ref obj, chain, ref failReason); - - if (endProp == null) - { - continue; - } - - try - { - obj = endProp.GetValue(obj, null); - - if (obj != null) - { - usedList.Add(obj); - } - } - catch - { - // ignored + usedList.Add(obj); } } + catch + { + // ignored + } } command.ExecuteList(eventArgs[i], usedList); diff --git a/Projects/UOContent/Commands/Generic/Commands/Interface.cs b/Projects/UOContent/Commands/Generic/Commands/Interface.cs index 6a343f554..b8c4b91e0 100644 --- a/Projects/UOContent/Commands/Generic/Commands/Interface.cs +++ b/Projects/UOContent/Commands/Generic/Commands/Interface.cs @@ -115,13 +115,12 @@ namespace Server.Commands.Generic if (obj != null) { - string failReason = null; var chain = Properties.GetPropertyInfoChain( m_From, obj.GetType(), m_Columns[i], PropertyAccess.Read, - ref failReason + out _ ); if (chain?.Length > 0) @@ -187,14 +186,13 @@ namespace Server.Commands.Generic var src = obj; string value; - var failReason = ""; var chain = Properties.GetPropertyInfoChain( m_From, src.GetType(), m_Columns[j], PropertyAccess.Read, - ref failReason + out _ ); if (chain == null || chain.Length == 0) @@ -203,7 +201,7 @@ namespace Server.Commands.Generic } else { - var p = Properties.GetPropertyInfo(ref src, chain, ref failReason); + var p = Properties.GetPropertyInfo(ref src, chain, out _); if (p == null) { diff --git a/Projects/UOContent/Commands/Handlers.cs b/Projects/UOContent/Commands/Handlers.cs index 03dc060af..867ed68d6 100644 --- a/Projects/UOContent/Commands/Handlers.cs +++ b/Projects/UOContent/Commands/Handlers.cs @@ -11,6 +11,7 @@ using Server.Misc; using Server.Mobiles; using Server.Multis; using Server.Network; +using Server.Saves; using Server.Spells; using Server.Targeting; using Server.Targets; @@ -44,8 +45,6 @@ namespace Server.Commands Register("Help", AccessLevel.Player, Help_OnCommand); - Register("Save", AccessLevel.Administrator, Save_OnCommand); - Register("Move", AccessLevel.GameMaster, Move_OnCommand); Register("Client", AccessLevel.Counselor, Client_OnCommand); @@ -459,12 +458,6 @@ namespace Server.Commands e.Mobile.Target = new PickMoveTarget(); } - [Usage("Save"), Description("Saves the world.")] - private static void Save_OnCommand(CommandEventArgs e) - { - AutoSave.Save(); - } - private static bool FixMap(ref Map map, ref Point3D loc, Item item) => map != null && map != Map.Internal || item.RootParent is Mobile m && FixMap(ref map, ref loc, m); diff --git a/Projects/UOContent/Commands/Object Creation/Add.cs b/Projects/UOContent/Commands/Object Creation/Add.cs index ae422c02e..a54c84e94 100644 --- a/Projects/UOContent/Commands/Object Creation/Add.cs +++ b/Projects/UOContent/Commands/Object Creation/Add.cs @@ -149,46 +149,16 @@ namespace Server.Commands for (var i = 0; i < realProps.Length; ++i) { - PropertyInfo thisProp = null; - var propName = props[i, 0]; + var thisProp = Properties.GetPropertyInfoByName(from, allProps, propName, PropertyAccess.Write, out var failReason); - for (var j = 0; thisProp == null && j < allProps.Length; ++j) + if (failReason == null) { - if (propName.InsensitiveEquals(allProps[j].Name)) - { - thisProp = allProps[j]; - } - } - - if (thisProp == null) - { - from.SendMessage("Property not found: {0}", propName); + realProps[i] = thisProp; } else { - var attr = GetCPA(thisProp); - - if (attr == null) - { - from.SendMessage("Property ({0}) not found.", propName); - } - else if (from.AccessLevel < attr.WriteLevel) - { - from.SendMessage( - "Setting this property ({0}) requires at least {1} access level.", - propName, - Mobile.GetAccessLevelName(attr.WriteLevel) - ); - } - else if (!thisProp.CanWrite || attr.ReadOnly) - { - from.SendMessage("Property ({0}) is read only.", propName); - } - else - { - realProps[i] = thisProp; - } + from.SendMessage(failReason); } } } diff --git a/Projects/UOContent/Commands/Object Creation/Decorate.cs b/Projects/UOContent/Commands/Object Creation/Decorate.cs index ae808f1a0..314bc011b 100644 --- a/Projects/UOContent/Commands/Object Creation/Decorate.cs +++ b/Projects/UOContent/Commands/Object Creation/Decorate.cs @@ -1078,7 +1078,7 @@ namespace Server.Commands } } } - else if ((TileData.ItemTable[itemID & TileData.MaxItemValue].Flags & TileFlag.LightSource) != 0) + else if (TileData.ItemTable[itemID & TileData.MaxItemValue].LightSource) { eable = map.GetItemsInRange(new Point3D(x, y, z), 0); @@ -1100,7 +1100,7 @@ namespace Server.Commands res = true; } } - else if ((item.ItemData.Flags & TileFlag.LightSource) != 0 && item.ItemData.Name == srcName) + else if (item.ItemData.LightSource && item.ItemData.Name == srcName) { m_DeleteQueue.Enqueue(item); } diff --git a/Projects/UOContent/Commands/Object Creation/DecorateMag.cs b/Projects/UOContent/Commands/Object Creation/DecorateMag.cs index 62eeb73c1..a1300cef3 100644 --- a/Projects/UOContent/Commands/Object Creation/DecorateMag.cs +++ b/Projects/UOContent/Commands/Object Creation/DecorateMag.cs @@ -1074,7 +1074,7 @@ namespace Server.Commands } } } - else if ((TileData.ItemTable[itemID & TileData.MaxItemValue].Flags & TileFlag.LightSource) != 0) + else if (TileData.ItemTable[itemID & TileData.MaxItemValue].LightSource) { eable = map.GetItemsInRange(new Point3D(x, y, z), 0); @@ -1096,7 +1096,7 @@ namespace Server.Commands res = true; } } - else if ((item.ItemData.Flags & TileFlag.LightSource) != 0 && item.ItemData.Name == srcName) + else if (item.ItemData.LightSource && item.ItemData.Name == srcName) { m_DeleteQueue.Enqueue(item); } diff --git a/Projects/UOContent/Commands/Properties.cs b/Projects/UOContent/Commands/Properties.cs index 61c6b20fb..29d92e594 100644 --- a/Projects/UOContent/Commands/Properties.cs +++ b/Projects/UOContent/Commands/Properties.cs @@ -55,13 +55,69 @@ namespace Server.Commands } } - private static bool CIEqual(string l, string r) => l.InsensitiveEquals(r); + public static PropertyInfo GetPropertyInfoByName( + Mobile from, PropertyInfo[] props, string propertyName, PropertyAccess access, out string failReason + ) + { + for (var i = 0; i < props.Length; i++) + { + var p = props[i]; + + if (!p.Name.InsensitiveEquals(propertyName)) + { + continue; + } + + var attr = GetCPA(p); + + if (attr == null) + { + failReason = $"Property '${propertyName}' not found."; + return null; + } + + if ((access & PropertyAccess.Read) != 0 && from.AccessLevel < attr.ReadLevel) + { + failReason = + $"You must be at least {Mobile.GetAccessLevelName(attr.ReadLevel)} to get the property '{propertyName}'."; + + return null; + } + + if ((access & PropertyAccess.Write) != 0 && from.AccessLevel < attr.WriteLevel) + { + failReason = + $"You must be at least {Mobile.GetAccessLevelName(attr.WriteLevel)} to set the property '{propertyName}'."; + + return null; + } + + if ((access & PropertyAccess.Read) != 0 && !p.CanRead) + { + failReason = $"Property '{propertyName}' is write only."; + return null; + } + + if ((access & PropertyAccess.Write) != 0 && (!p.CanWrite && !attr.CanModify || attr.ReadOnly)) + { + failReason = $"Property '{propertyName}' is read only."; + return null; + } + + failReason = null; + return p; + } + + failReason = null; + return null; + } public static PropertyInfo[] GetPropertyInfoChain( Mobile from, Type type, string propertyString, - PropertyAccess endAccess, ref string failReason + PropertyAccess access, out string failReason ) { + failReason = null; var split = propertyString.Split('.'); if (split.Length == 0) @@ -74,76 +130,18 @@ namespace Server.Commands for (var i = 0; i < info.Length; ++i) { var propertyName = split[i]; - - if (CIEqual(propertyName, "current")) - { - continue; - } - var props = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); - var isFinal = i == info.Length - 1; + var p = GetPropertyInfoByName(from, props, propertyName, access, out failReason); - var access = endAccess; - - if (!isFinal) + if (p == null) { - access |= PropertyAccess.Read; - } - - for (var j = 0; j < props.Length; ++j) - { - var p = props[j]; - - if (CIEqual(p.Name, propertyName)) - { - var attr = GetCPA(p); - - if (attr == null) - { - failReason = $"Property '{propertyName}' not found."; - return null; - } - - if ((access & PropertyAccess.Read) != 0 && from.AccessLevel < attr.ReadLevel) - { - failReason = - $"You must be at least {Mobile.GetAccessLevelName(attr.ReadLevel)} to get the property '{propertyName}'."; - - return null; - } - - if ((access & PropertyAccess.Write) != 0 && from.AccessLevel < attr.WriteLevel) - { - failReason = - $"You must be at least {Mobile.GetAccessLevelName(attr.WriteLevel)} to set the property '{propertyName}'."; - - return null; - } - - if ((access & PropertyAccess.Read) != 0 && !p.CanRead) - { - failReason = $"Property '{propertyName}' is write only."; - return null; - } - - if ((access & PropertyAccess.Write) != 0 && (!p.CanWrite || attr.ReadOnly) && isFinal) - { - failReason = $"Property '{propertyName}' is read only."; - return null; - } - - info[i] = p; - type = p.PropertyType; - break; - } - } - - if (info[i] == null) - { - failReason = $"Property '{propertyName}' not found."; + failReason ??= $"Property '{propertyName}' not found."; return null; } + + info[i] = p; + type = p.PropertyType; } return info; @@ -151,15 +149,15 @@ namespace Server.Commands public static PropertyInfo GetPropertyInfo( Mobile from, ref object obj, string propertyName, PropertyAccess access, - ref string failReason + out string failReason ) { - var chain = GetPropertyInfoChain(from, obj.GetType(), propertyName, access, ref failReason); + var chain = GetPropertyInfoChain(from, obj.GetType(), propertyName, access, out failReason); - return chain == null ? null : GetPropertyInfo(ref obj, chain, ref failReason); + return chain == null ? null : GetPropertyInfo(ref obj, chain, out failReason); } - public static PropertyInfo GetPropertyInfo(ref object obj, PropertyInfo[] chain, ref string failReason) + public static PropertyInfo GetPropertyInfo(ref object obj, PropertyInfo[] chain, out string failReason) { if (chain == null || chain.Length == 0) { @@ -183,6 +181,7 @@ namespace Server.Commands } } + failReason = null; return chain[^1]; } @@ -190,14 +189,14 @@ namespace Server.Commands { var failReason = ""; - var chain = GetPropertyInfoChain(from, o.GetType(), name, PropertyAccess.Read, ref failReason); + var chain = GetPropertyInfoChain(from, o.GetType(), name, PropertyAccess.Read, out failReason); if (chain == null || chain.Length == 0) { return failReason; } - var p = GetPropertyInfo(ref o, chain, ref failReason); + var p = GetPropertyInfo(ref o, chain, out failReason); return p == null ? failReason : InternalGetValue(o, p, chain); } @@ -246,9 +245,8 @@ namespace Server.Commands return "Zero is not a valid value to offset."; } - string failReason = null; realObjs[i] = o; - realProps[i] = GetPropertyInfo(from, ref realObjs[i], name, PropertyAccess.ReadWrite, ref failReason); + realProps[i] = GetPropertyInfo(from, ref realObjs[i], name, PropertyAccess.ReadWrite, out var failReason); if (failReason != null) { @@ -353,8 +351,7 @@ namespace Server.Commands { var logObject = o; - var failReason = ""; - var p = GetPropertyInfo(from, ref o, name, PropertyAccess.Write, ref failReason); + var p = GetPropertyInfo(from, ref o, name, PropertyAccess.Write, out var failReason); return p == null ? failReason : InternalSetValue(from, logObject, o, p, name, value, true); } @@ -407,7 +404,6 @@ namespace Server.Commands TryParse(p.PropertyType, value, out var toSet) ?? SetDirect(from, logobj, o, p, pname, toSet, shouldLog); - private class PropsTarget : Target { public PropsTarget() : base(-1, true, TargetFlags.None) diff --git a/Projects/UOContent/Compression/TarArchive.cs b/Projects/UOContent/Compression/TarArchive.cs new file mode 100755 index 000000000..0e5889e4a --- /dev/null +++ b/Projects/UOContent/Compression/TarArchive.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net; +using Server.Buffers; + +namespace Server.Compression +{ + public static class TarArchive + { + private const string _libArchiveWindowsUrl = @"https://www.libarchive.org/downloads/libarchive-v3.5.2-win64.zip"; + private static string _pathToTar; + + private static string GetPathToTar() + { + if (!Core.IsWindows || File.Exists(@"C:\Windows\system32\tar.exe")) + { + return "tar"; + } + + var tarPath = Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe"); + return File.Exists(tarPath) ? tarPath : DownloadTarForWindows(); + } + + private static int RunTar(string arguments, string compressionProgramPath) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = _pathToTar, + Arguments = arguments + } + }; + + if (compressionProgramPath != null) + { + var envPaths = $"{process.StartInfo.EnvironmentVariables["PATH"]}:{compressionProgramPath}"; + process.StartInfo.EnvironmentVariables["PATH"] = envPaths; + } + + process.Start(); + process.WaitForExit(); + + return process.ExitCode; + } + + private static string DownloadTarForWindows() + { + var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); + + var libarchiveFile = Path.Combine(tempDir, "libarchive.zip"); + using WebClient wc = new WebClient(); + wc.DownloadFile (new Uri(_libArchiveWindowsUrl), libarchiveFile); + + ZipFile.ExtractToDirectory(libarchiveFile, tempDir); + var libArchivePath = Path.Combine(tempDir, "libarchive"); + Directory.Move(Path.Combine(libArchivePath, "bin"), "bsdtar"); + Directory.Delete(libArchivePath, true); + File.Delete(libarchiveFile); + + return Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe"); + } + + public static bool ExtractToDirectory( + string fileNamePath, + string outputDirectory, + string compressCommand = null, + string compressionProgramPath = null + ) + { + _pathToTar ??= GetPathToTar(); + + var useExternalCompression = compressCommand != null ? $"--use-compress-program \"{compressCommand}\" " : ""; + var arguments = $"{useExternalCompression} -xf \"{fileNamePath}\" -C \"{outputDirectory}\""; + + return RunTar(arguments, compressionProgramPath) == 0; + } + + public static bool CreateFromPaths( + IEnumerable paths, + string destinationArchiveFileName, + string relativeTo, + string compressCommand = null, + string compressionProgramPath = null + ) + { + _pathToTar ??= GetPathToTar(); + + new FileInfo(destinationArchiveFileName).EnsureDirectory(); + + using var builder = new ValueStringBuilder(); + var i = 0; + foreach (var path in paths) + { + builder.Append($"{(i++ > 0 ? " " : "")}\"{Path.GetRelativePath(relativeTo, path)}\""); + } + var pathsToCompress = builder.ToString(); + + var tarFlags = compressCommand == null ? "-acf" : "-cf"; + var useExternalCompression = compressCommand != null ? $"--use-compress-program \"{compressCommand}\" " : ""; + var arguments = $"{useExternalCompression}{tarFlags} \"{destinationArchiveFileName}\" -C \"{relativeTo}\" {pathsToCompress}"; + + return RunTar(arguments, compressionProgramPath) == 0; + } + } +} diff --git a/Projects/UOContent/Compression/ZstdArchive.cs b/Projects/UOContent/Compression/ZstdArchive.cs new file mode 100755 index 000000000..35fd533d5 --- /dev/null +++ b/Projects/UOContent/Compression/ZstdArchive.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Server.Compression +{ + public static class ZstdArchive + { + private static readonly string _pathToZstd = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory?.FullName; + + public static bool ExtractToDirectory(string fileNamePath, string outputDirectory) + { + // bsdtar has a bug and hangs, so we are doing it in two steps. + if (Core.IsWindows) + { + var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); + var tempTarArchive = Path.Combine(tempDir, "temp-file.tar"); + + try + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = Path.Combine(_pathToZstd, "zstd.exe"), + Arguments = $"-q -d \"{fileNamePath}\" -o \"{tempTarArchive}\"" + } + }; + + process.Start(); + process.WaitForExit(); + + return process.ExitCode == 0 && TarArchive.ExtractToDirectory(tempTarArchive, outputDirectory); + } + catch + { + return false; + } + finally + { + File.Delete(tempTarArchive); + } + } + + return TarArchive.ExtractToDirectory(fileNamePath, outputDirectory, "zstd -q -d", _pathToZstd); + } + + public static bool CreateFromPaths( + IEnumerable paths, + string destinationArchiveFileName, + string relativeTo + ) + { + // bsdtar has a bug and hangs, so we are doing it in two steps. + if (Core.IsWindows) + { + var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); + var tempTarArchive = Path.Combine(tempDir, "temp-file.tar"); + + try + { + if (!TarArchive.CreateFromPaths(paths, tempTarArchive, relativeTo)) + { + return false; + } + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = Path.Combine(_pathToZstd, "zstd.exe"), + Arguments = $"-q \"{tempTarArchive}\" -o \"{destinationArchiveFileName}\"" + } + }; + + process.Start(); + process.WaitForExit(); + + return process.ExitCode == 0; + } + catch + { + return false; + } + finally + { + Directory.Delete(tempDir, true); + } + } + + return TarArchive.CreateFromPaths(paths, destinationArchiveFileName, relativeTo, "zstd -q", _pathToZstd); + } + } +} diff --git a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs index 3d1b55177..2103ff656 100644 --- a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs @@ -31,7 +31,7 @@ namespace Server.Engines.BulkOrders [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] private bool _requireExceptional; - [SerializableField(1)] + [SerializableField(2)] [InvalidateProperties] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] private BulkMaterialType _material; diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index ab21f89bc..e4a68b0c5 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -410,7 +410,6 @@ namespace Server.Engines.CannedEvil } } - // Justice reward var pm = (PlayerMobile)killer; for (var j = 0; j < pm.JusticeProtectors.Count; ++j) diff --git a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs index 01ef61048..b470983f2 100644 --- a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs +++ b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs @@ -54,10 +54,8 @@ namespace Server.Engines.Craft { var type = item.GetType(); - var isAnvil = type.IsDefined(typeofAnvil, false) || item.ItemID == 4015 || item.ItemID == 4016 || - item.ItemID == 0x2DD5 || item.ItemID == 0x2DD6; - var isForge = type.IsDefined(typeofForge, false) || item.ItemID == 4017 || - item.ItemID >= 6522 && item.ItemID <= 6569 || item.ItemID == 0x2DD8; + 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) { @@ -88,8 +86,8 @@ namespace Server.Engines.Craft { var id = tiles[i].ID; - var isAnvil = id == 4015 || id == 4016 || id == 0x2DD5 || id == 0x2DD6; - var isForge = id == 4017 || id >= 6522 && id <= 6569 || id == 0x2DD8; + 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) { diff --git a/Projects/UOContent/Engines/Craft/DefCarpentry.cs b/Projects/UOContent/Engines/Craft/DefCarpentry.cs index 62044ee88..23d303a68 100644 --- a/Projects/UOContent/Engines/Craft/DefCarpentry.cs +++ b/Projects/UOContent/Engines/Craft/DefCarpentry.cs @@ -104,26 +104,13 @@ namespace Server.Engines.Craft 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 - ); + 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); @@ -132,17 +119,7 @@ namespace Server.Engines.Craft 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 - ); + 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); } @@ -154,78 +131,35 @@ namespace Server.Engines.Craft AddRes(index, typeof(RunedPrism), 1073465, 1, 1053098); AddRes(index, typeof(JeweledFiligree), 1072894, 1, 1053098); SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(WarriorStatueSouthDeed), - 1044294, - 1072887, - 0.0, - 35.0, - typeof(Log), - 1044041, - 250, - 1044351 - ); + 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 - ); + 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 - ); + 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 - ); + 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(AcidProofRope), - 1044294, - 1074886, - 80, - 130.0, - typeof(GreaterStrengthPotion), - 1073466, - 2, - 1044253 - ); + 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); @@ -252,16 +186,29 @@ namespace Server.Engines.Craft { 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(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); } // Containers @@ -278,64 +225,24 @@ namespace Server.Engines.Craft { 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 - ); + 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 - ); + 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 - ); + 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); } @@ -346,193 +253,77 @@ namespace Server.Engines.Craft if (Core.ML) { - index = AddCraft( - typeof(ArcaneBookshelfSouthDeed), - 1044292, - 1072871, - 94.7, - 119.7, - typeof(Log), - 1044041, - 80, - 1044351 - ); + 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 - ); + index = AddCraft(typeof(ArcaneBookshelfEastDeed), 1044292, 1073371, 94.7, 119.7, typeof(Log), 1044041, 80, 1044351); AddRecipe(index, 307); ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - - /* TODO - 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(ElvenDresserSouthDeed), - 1044292, - 1072864, - 75.0, - 100.0, - typeof(Log), - 1044041, - 45, - 1044351 - ); + 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 - ); + index = AddCraft(typeof(ElvenDresserEastDeed), 1044292, 1073388, 75.0, 100.0, typeof(Log), 1044041, 45, 1044351); AddRecipe(index, 311); ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - - /* TODO - 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(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); } // 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 - ); - AddCraft( - typeof(WoodenShield), - Core.ML ? 1062760 : 1044295, - 1027034, - 52.6, - 77.6, - typeof(Log), - 1044041, - 9, - 1044351 - ); + 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.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 - ); + 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(Bokuto), - Core.ML ? 1044566 : 1044295, - 1030227, - 70.0, - 95.0, - typeof(Log), - 1044041, - 6, - 1044351 - ); + 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 - ); + 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 - ); + 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); @@ -540,51 +331,56 @@ namespace Server.Engines.Craft AddRareRecipe(index, 313); ForceNonExceptional(index); SetNeededExpansion(index, Expansion.ML); - - index = AddCraft( - typeof(ArcanistsWildStaff), - 1044566, - 1073549, - 63.8, - 113.8, - typeof(Log), - 1044041, - 16, - 1044351 - ); + 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 - ); + 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); @@ -592,7 +388,6 @@ namespace Server.Engines.Craft 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); @@ -606,23 +401,18 @@ namespace Server.Engines.Craft 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); @@ -634,44 +424,58 @@ namespace Server.Engines.Craft 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); + } + if (Core.ML) { - index = AddCraft( - typeof(TallElvenBedSouthDeed), - 1044290, - 1072858, - 94.7, - 119.7, - typeof(Log), - 1044041, - 200, - 1044351 - ); + 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 - ); + 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); } - // Misc 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); @@ -694,13 +498,14 @@ namespace Server.Engines.Craft AddSkill(index, SkillName.Magery, 50.0, 55.0); AddRes(index, typeof(IronIngot), 1044036, 40, 1044037); - if (Core.AOS) + // Blacksmithy - This changed to Anvils and Forges (1111809) for SA + if (Core.ML) { - 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); + index = AddCraft(typeof(ElvenForgeDeed), 1044296, 1072875, 94.7, 119.7, typeof(Log), 1044041, 200, 1044351); + ForceNonExceptional(index); + SetNeededExpansion(index, Expansion.ML); } - // Blacksmithy - This changed to Anvils and Forges (1111809) for SA 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); @@ -721,33 +526,13 @@ namespace Server.Engines.Craft 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 - ); + 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 - ); + 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); @@ -755,20 +540,31 @@ namespace Server.Engines.Craft index = AddCraft(typeof(Dressform), 1044298, 1044339, 63.1, 88.1, typeof(Log), 1044041, 25, 1044351); AddSkill(index, SkillName.Tailoring, 65.0, 70.0); AddRes(index, typeof(Cloth), 1044286, 10, 1044287); - index = AddCraft(typeof(SpinningwheelEastDeed), 1044298, 1044341, 73.6, 98.6, typeof(Log), 1044041, 75, 1044351); + + 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 - ); + 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); @@ -777,82 +573,20 @@ namespace Server.Engines.Craft index = AddCraft(typeof(LoomSouthDeed), 1044298, 1044344, 84.2, 109.2, typeof(Log), 1044041, 85, 1044351); AddSkill(index, SkillName.Tailoring, 65.0, 70.0); AddRes(index, typeof(Cloth), 1044286, 25, 1044287); - - // Cooking - index = AddCraft( - typeof(StoneOvenEastDeed), - Core.ML ? 1044298 : 1044299, - 1044345, - 68.4, - 93.4, - typeof(Log), - 1044041, - 85, - 1044351 - ); + 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 - ); + 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 - ); + 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 - ); + 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 - ); + 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; diff --git a/Projects/UOContent/Engines/Craft/DefTinkering.cs b/Projects/UOContent/Engines/Craft/DefTinkering.cs index 19324ef38..dbe6f497a 100644 --- a/Projects/UOContent/Engines/Craft/DefTinkering.cs +++ b/Projects/UOContent/Engines/Craft/DefTinkering.cs @@ -35,7 +35,7 @@ namespace Server.Engines.Craft public override double GetChanceAtMin(CraftItem item) { - if (item.NameNumber == 1044258 || item.NameNumber == 1046445) // potion keg and faction trap removal kit + if (item.NameNumber is 1044258 or 1046445) // potion keg and faction trap removal kit { return 0.5; // 50% } diff --git a/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs b/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs index cad99c5f7..cd3e4cca4 100644 --- a/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs +++ b/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs @@ -24,22 +24,23 @@ namespace Server.PathAlgorithms.FastAStar private const int PlaneHeight = 20; public static PathAlgorithm Instance = new FastAStarAlgorithm(); - private static readonly Direction[] m_Path = new Direction[AreaSize * AreaSize]; - private static readonly PathNode[] m_Nodes = new PathNode[NodeCount]; - private static readonly BitArray m_Touched = new(NodeCount); - private static readonly BitArray m_OnOpen = new(NodeCount); - private static readonly int[] m_Successors = new int[8]; + private static readonly Direction[] _path = new Direction[AreaSize * AreaSize]; + private static readonly PathNode[] _nodes = new PathNode[NodeCount]; + private static readonly BitArray _touched = new(NodeCount); + private static readonly BitArray _onOpen = new(NodeCount); + private static readonly int[] _successors = new int[8]; - private static int m_xOffset, m_yOffset; - private static int m_OpenList; + private static int _xOffset; + private static int _yOffset; + private static int _openList; - private Point3D m_Goal; + private Point3D _goal; public int Heuristic(int x, int y, int z) { - x -= m_Goal.X - m_xOffset; - y -= m_Goal.Y - m_yOffset; - z -= m_Goal.Z; + x -= _goal.X - _xOffset; + y -= _goal.Y - _yOffset; + z -= _goal.Z; x *= 11; y *= 11; @@ -52,59 +53,59 @@ namespace Server.PathAlgorithms.FastAStar private void RemoveFromChain(int node) { - if (node < 0 || node >= NodeCount) + if (node is < 0 or >= NodeCount) { return; } - if (!m_Touched[node] || !m_OnOpen[node]) + if (!_touched[node] || !_onOpen[node]) { return; } - var prev = m_Nodes[node].prev; - var next = m_Nodes[node].next; + var prev = _nodes[node].prev; + var next = _nodes[node].next; - if (m_OpenList == node) + if (_openList == node) { - m_OpenList = next; + _openList = next; } if (prev != -1) { - m_Nodes[prev].next = next; + _nodes[prev].next = next; } if (next != -1) { - m_Nodes[next].prev = prev; + _nodes[next].prev = prev; } - m_Nodes[node].prev = -1; - m_Nodes[node].next = -1; + _nodes[node].prev = -1; + _nodes[node].next = -1; } private void AddToChain(int node) { - if (node < 0 || node >= NodeCount) + if (node is < 0 or >= NodeCount) { return; } RemoveFromChain(node); - if (m_OpenList != -1) + if (_openList != -1) { - m_Nodes[m_OpenList].prev = node; + _nodes[_openList].prev = node; } - m_Nodes[node].next = m_OpenList; - m_Nodes[node].prev = -1; + _nodes[node].next = _openList; + _nodes[node].prev = -1; - m_OpenList = node; + _openList = node; - m_Touched[node] = true; - m_OnOpen[node] = true; + _touched[node] = true; + _onOpen[node] = true; } public override Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal) @@ -114,37 +115,38 @@ namespace Server.PathAlgorithms.FastAStar return null; } - m_Touched.SetAll(false); + _touched.SetAll(false); + _onOpen.SetAll(false); - m_Goal = goal; + _goal = goal; - m_xOffset = (start.X + goal.X - AreaSize) / 2; - m_yOffset = (start.Y + goal.Y - AreaSize) / 2; + _xOffset = (start.X + goal.X - AreaSize) / 2; + _yOffset = (start.Y + goal.Y - AreaSize) / 2; var fromNode = GetIndex(start.X, start.Y, start.Z); var destNode = GetIndex(goal.X, goal.Y, goal.Z); - m_OpenList = fromNode; + _openList = fromNode; - m_Nodes[m_OpenList].cost = 0; - m_Nodes[m_OpenList].total = Heuristic(start.X - m_xOffset, start.Y - m_yOffset, start.Z); - m_Nodes[m_OpenList].parent = -1; - m_Nodes[m_OpenList].next = -1; - m_Nodes[m_OpenList].prev = -1; - m_Nodes[m_OpenList].z = start.Z; + _nodes[_openList].cost = 0; + _nodes[_openList].total = Heuristic(start.X - _xOffset, start.Y - _yOffset, start.Z); + _nodes[_openList].parent = -1; + _nodes[_openList].next = -1; + _nodes[_openList].prev = -1; + _nodes[_openList].z = start.Z; - m_OnOpen[m_OpenList] = true; - m_Touched[m_OpenList] = true; + _onOpen[_openList] = true; + _touched[_openList] = true; var bc = m as BaseCreature; int backtrack = 0, depth = 0; - var path = m_Path; + var path = _path; - while (m_OpenList != -1) + while (_openList != -1) { - var bestNode = FindBest(m_OpenList); + var bestNode = FindBest(_openList); if (++depth > MaxDepth) { @@ -159,7 +161,7 @@ namespace Server.PathAlgorithms.FastAStar MoveImpl.Goal = goal; - var vals = m_Successors; + var vals = _successors; var count = GetSuccessors(bestNode, m, map); MoveImpl.AlwaysIgnoreDoors = false; @@ -175,30 +177,25 @@ namespace Server.PathAlgorithms.FastAStar { var newNode = vals[i]; - var wasTouched = m_Touched[newNode]; + var wasTouched = _touched[newNode]; if (wasTouched) { continue; } - var newCost = m_Nodes[bestNode].cost + 1; + var newCost = _nodes[bestNode].cost + 1; var newTotal = newCost + Heuristic( newNode % AreaSize, newNode / AreaSize % AreaSize, - m_Nodes[newNode].z + _nodes[newNode].z ); - if (m_Nodes[newNode].total <= newTotal) - { - continue; - } + _nodes[newNode].parent = bestNode; + _nodes[newNode].cost = newCost; + _nodes[newNode].total = newTotal; - m_Nodes[newNode].parent = bestNode; - m_Nodes[newNode].cost = newCost; - m_Nodes[newNode].total = newTotal; - - if (m_OnOpen[newNode]) + if (_onOpen[newNode]) { continue; } @@ -211,7 +208,7 @@ namespace Server.PathAlgorithms.FastAStar } var pathCount = 0; - var parent = m_Nodes[newNode].parent; + var parent = _nodes[newNode].parent; while (parent != -1) { @@ -222,7 +219,7 @@ namespace Server.PathAlgorithms.FastAStar newNode / AreaSize % AreaSize ); newNode = parent; - parent = m_Nodes[newNode].parent; + parent = _nodes[newNode].parent; if (newNode == fromNode) { @@ -246,8 +243,8 @@ namespace Server.PathAlgorithms.FastAStar private int GetIndex(int x, int y, int z) { - x -= m_xOffset; - y -= m_yOffset; + x -= _xOffset; + y -= _yOffset; z += PlaneOffset; z /= PlaneHeight; @@ -256,24 +253,24 @@ namespace Server.PathAlgorithms.FastAStar private int FindBest(int node) { - var least = m_Nodes[node].total; + var least = _nodes[node].total; var leastNode = node; while (node != -1) { - if (m_Nodes[node].total < least) + if (_nodes[node].total < least) { - least = m_Nodes[node].total; + least = _nodes[node].total; leastNode = node; } - node = m_Nodes[node].next; + node = _nodes[node].next; } RemoveFromChain(leastNode); - m_Touched[leastNode] = true; - m_OnOpen[leastNode] = false; + _touched[leastNode] = true; + _onOpen[leastNode] = false; return leastNode; } @@ -282,11 +279,11 @@ namespace Server.PathAlgorithms.FastAStar { var px = p % AreaSize; var py = p / AreaSize % AreaSize; - var pz = m_Nodes[p].z; + var pz = _nodes[p].z; - var p3D = new Point3D(px + m_xOffset, py + m_yOffset, pz); + var p3D = new Point3D(px + _xOffset, py + _yOffset, pz); - var vals = m_Successors; + var vals = _successors; var count = 0; for (var i = 0; i < 8; ++i) @@ -332,18 +329,18 @@ namespace Server.PathAlgorithms.FastAStar x += px; y += py; - if (x < 0 || x >= AreaSize || y < 0 || y >= AreaSize) + if (x is < 0 or >= AreaSize || y is < 0 or >= AreaSize) { continue; } if (CalcMoves.CheckMovement(m, map, p3D, (Direction)i, out var z)) { - var idx = GetIndex(x + m_xOffset, y + m_yOffset, z); + var idx = GetIndex(x + _xOffset, y + _yOffset, z); if (idx >= 0 && idx < NodeCount) { - m_Nodes[idx].z = z; + _nodes[idx].z = z; vals[count++] = idx; } } diff --git a/Projects/UOContent/Engines/Pathing/FastMovement.cs b/Projects/UOContent/Engines/Pathing/FastMovement.cs deleted file mode 100644 index 364bc924a..000000000 --- a/Projects/UOContent/Engines/Pathing/FastMovement.cs +++ /dev/null @@ -1,674 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Server.Items; -using Server.Mobiles; - -namespace Server.Movement -{ - public class FastMovementImpl : IMovementImpl - { - private const int PersonHeight = 16; - private const int StepHeight = 2; - - private const TileFlag ImpassableSurface = TileFlag.Impassable | TileFlag.Surface; - public static bool Enabled = false; - - private static IMovementImpl _Successor; - - private FastMovementImpl() - { - } - - public bool CheckMovement(Mobile m, Map map, Point3D loc, Direction d, out int newZ) - { - if (!Enabled && _Successor != null) - { - return _Successor.CheckMovement(m, map, loc, d, out newZ); - } - - if (map == null || map == Map.Internal) - { - newZ = 0; - return false; - } - - var xStart = loc.X; - var yStart = loc.Y; - - int xForward = xStart, yForward = yStart; - int xRight = xStart, yRight = yStart; - int xLeft = xStart, yLeft = yStart; - - var checkDiagonals = ((int)d & 0x1) == 0x1; - - Offset(d, ref xForward, ref yForward); - Offset((Direction)(((int)d - 1) & 0x7), ref xLeft, ref yLeft); - Offset((Direction)(((int)d + 1) & 0x7), ref xRight, ref yRight); - - if (xForward < 0 || yForward < 0 || xForward >= map.Width || yForward >= map.Height) - { - newZ = 0; - return false; - } - - IEnumerable itemsStart, itemsForward, itemsLeft, itemsRight; - - var ignoreMovableImpassables = MovementImpl.IgnoreMovableImpassables; - var reqFlags = ImpassableSurface; - - if (m.CanSwim) - { - reqFlags |= TileFlag.Wet; - } - - if (checkDiagonals) - { - var sStart = map.GetSector(xStart, yStart); - var sForward = map.GetSector(xForward, yForward); - var sLeft = map.GetSector(xLeft, yLeft); - var sRight = map.GetSector(xRight, yRight); - - itemsStart = sStart.Items.Where(i => Verify(i, reqFlags, ignoreMovableImpassables, xStart, yStart)); - itemsForward = sForward.Items.Where(i => Verify(i, reqFlags, ignoreMovableImpassables, xForward, yForward)); - itemsLeft = sLeft.Items.Where(i => Verify(i, reqFlags, ignoreMovableImpassables, xLeft, yLeft)); - itemsRight = sRight.Items.Where(i => Verify(i, reqFlags, ignoreMovableImpassables, xRight, yRight)); - } - else - { - var sStart = map.GetSector(xStart, yStart); - var sForward = map.GetSector(xForward, yForward); - - itemsStart = sStart.Items.Where(i => Verify(i, reqFlags, ignoreMovableImpassables, xStart, yStart)); - itemsForward = sForward.Items.Where(i => Verify(i, reqFlags, ignoreMovableImpassables, xForward, yForward)); - itemsLeft = Enumerable.Empty(); - itemsRight = Enumerable.Empty(); - } - - GetStartZ(m, map, loc, itemsStart, out var startZ, out var startTop); - - List list = null; - - MovementPool.AcquireMoveCache(ref list, itemsForward); - - var moveIsOk = Check(map, m, list, xForward, yForward, startTop, startZ, m.CanSwim, m.CantWalk, out newZ); - - if (moveIsOk && checkDiagonals) - { - if (m.Player && m.AccessLevel < AccessLevel.GameMaster) - { - MovementPool.AcquireMoveCache(ref list, itemsLeft); - - if (!Check(map, m, list, xLeft, yLeft, startTop, startZ, m.CanSwim, m.CantWalk, out _)) - { - moveIsOk = false; - } - else - { - MovementPool.AcquireMoveCache(ref list, itemsRight); - - if (!Check(map, m, list, xRight, yRight, startTop, startZ, m.CanSwim, m.CantWalk, out _)) - { - moveIsOk = false; - } - } - } - else - { - MovementPool.AcquireMoveCache(ref list, itemsLeft); - - if (!Check(map, m, list, xLeft, yLeft, startTop, startZ, m.CanSwim, m.CantWalk, out _)) - { - MovementPool.AcquireMoveCache(ref list, itemsRight); - - if (!Check(map, m, list, xRight, yRight, startTop, startZ, m.CanSwim, m.CantWalk, out _)) - { - moveIsOk = false; - } - } - } - } - - MovementPool.ClearMoveCache(ref list, true); - - if (!moveIsOk) - { - newZ = startZ; - } - - return moveIsOk; - } - - public bool CheckMovement(Mobile m, Direction d, out int newZ) => - !Enabled && _Successor != null - ? _Successor.CheckMovement(m, d, out newZ) - : CheckMovement(m, m.Map, m.Location, d, out newZ); - - public static void Initialize() - { - _Successor = Movement.Impl; - Movement.Impl = new FastMovementImpl(); - } - - private static bool IsOk(StaticTile tile, int ourZ, int ourTop) - { - var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - - return tile.Z + itemData.CalcHeight <= ourZ || ourTop <= tile.Z || (itemData.Flags & ImpassableSurface) == 0; - } - - private static bool IsOk(Item item, int ourZ, int ourTop, bool ignoreDoors, bool ignoreSpellFields) - { - var itemID = item.ItemID & TileData.MaxItemValue; - var itemData = TileData.ItemTable[itemID]; - - if ((itemData.Flags & ImpassableSurface) == 0) - { - return true; - } - - if (((itemData.Flags & TileFlag.Door) != 0 || itemID == 0x692 || itemID == 0x846 || itemID == 0x873 || - itemID >= 0x6F5 && itemID <= 0x6F6) && ignoreDoors) - { - return true; - } - - if ((itemID == 0x82 || itemID == 0x3946 || itemID == 0x3956) && ignoreSpellFields) - { - return true; - } - - return item.Z + itemData.CalcHeight <= ourZ || ourTop <= item.Z; - } - - private static bool IsOk( - bool ignoreDoors, - bool ignoreSpellFields, - int ourZ, - int ourTop, - IEnumerable tiles, - IEnumerable items - ) - { - return tiles.All(t => IsOk(t, ourZ, ourTop)) && - items.All(i => IsOk(i, ourZ, ourTop, ignoreDoors, ignoreSpellFields)); - } - - private static bool Check( - Map map, - Mobile m, - List items, - int x, - int y, - int startTop, - int startZ, - bool canSwim, - bool cantWalk, - out int newZ - ) - { - newZ = 0; - - var tiles = map.Tiles.GetStaticTiles(x, y, true); - var landTile = map.Tiles.GetLandTile(x, y); - var landData = TileData.LandTable[landTile.ID & TileData.MaxLandValue]; - var considerLand = !landTile.Ignored; - var landBlocks = (landData.Flags & TileFlag.Impassable) != 0; - - if (landBlocks && canSwim && (landData.Flags & TileFlag.Wet) != 0) - { - landBlocks = false; - } - else if (cantWalk && (landData.Flags & TileFlag.Wet) == 0) - { - landBlocks = true; - } - - map.GetAverageZ(x, y, out var landZ, out var landCenter, out _); - - var moveIsOk = false; - - var stepTop = startTop + StepHeight; - var checkTop = startZ + PersonHeight; - - var ignoreDoors = MovementImpl.AlwaysIgnoreDoors || !m.Alive || m.IsDeadBondedPet || m.Body.IsGhost || - m.Body.BodyID == 987; - var ignoreSpellFields = m is PlayerMobile && map.MapID != 0; - - int itemZ, itemTop, ourZ, ourTop, testTop; - ItemData itemData; - TileFlag flags; - - foreach (var tile in tiles) - { - itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - - if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) - { - newZ = tile.Z; - return true; - } - - // Stygian Dragon - if (m.Body == 826 && map?.MapID == 5) - { - if (x >= 307 && x <= 354 && y >= 126 && y <= 192) - { - if (tile.Z > newZ) - { - newZ = tile.Z; - } - - moveIsOk = true; - } - else if (x >= 42 && x <= 89) - { - if (y >= 333 && y <= 399 || y >= 531 && y <= 597 || y >= 739 && y <= 805) - { - if (tile.Z > newZ) - { - newZ = tile.Z; - } - - moveIsOk = true; - } - } - } - - flags = itemData.Flags; - - if ((flags & ImpassableSurface) != TileFlag.Surface && (!canSwim || (flags & TileFlag.Wet) == 0)) - { - continue; - } - - if (cantWalk && (flags & TileFlag.Wet) == 0) - { - continue; - } - - itemZ = tile.Z; - itemTop = itemZ; - ourZ = itemZ + itemData.CalcHeight; - ourTop = ourZ + PersonHeight; - testTop = checkTop; - - if (moveIsOk) - { - var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) - { - continue; - } - } - - if (ourTop > testTop) - { - testTop = ourTop; - } - - if (!itemData.Bridge) - { - itemTop += itemData.Height; - } - - if (stepTop < itemTop) - { - continue; - } - - var landCheck = itemZ; - - if (itemData.Height >= StepHeight) - { - landCheck += StepHeight; - } - else - { - landCheck += itemData.Height; - } - - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) - { - continue; - } - - if (!IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) - { - continue; - } - - newZ = ourZ; - moveIsOk = true; - } - - foreach (var item in items) - { - itemData = item.ItemData; - flags = itemData.Flags; - - if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) - { - newZ = item.Z; - return true; - } - - if (item.Movable) - { - continue; - } - - if ((flags & ImpassableSurface) != TileFlag.Surface && (!m.CanSwim || (flags & TileFlag.Wet) == 0)) - { - continue; - } - - if (cantWalk && (flags & TileFlag.Wet) == 0) - { - continue; - } - - itemZ = item.Z; - itemTop = itemZ; - ourZ = itemZ + itemData.CalcHeight; - ourTop = ourZ + PersonHeight; - testTop = checkTop; - - if (moveIsOk) - { - var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) - { - continue; - } - } - - if (ourTop > testTop) - { - testTop = ourTop; - } - - if (!itemData.Bridge) - { - itemTop += itemData.Height; - } - - if (stepTop < itemTop) - { - continue; - } - - var landCheck = itemZ; - - if (itemData.Height >= StepHeight) - { - landCheck += StepHeight; - } - else - { - landCheck += itemData.Height; - } - - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) - { - continue; - } - - if (!IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) - { - continue; - } - - newZ = ourZ; - moveIsOk = true; - } - - if (!considerLand || landBlocks || stepTop < landZ) - { - return moveIsOk; - } - - ourZ = landCenter; - ourTop = ourZ + PersonHeight; - testTop = checkTop; - - if (ourTop > testTop) - { - testTop = ourTop; - } - - var shouldCheck = true; - - if (moveIsOk) - { - var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) - { - shouldCheck = false; - } - } - - if (!shouldCheck || !IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) - { - return moveIsOk; - } - - newZ = ourZ; - moveIsOk = true; - - return moveIsOk; - } - - private static bool Verify(Item item, int x, int y) => item.AtWorldPoint(x, y); - - private static bool Verify(Item item, TileFlag reqFlags, bool ignoreMovableImpassables) => - item != null && (!ignoreMovableImpassables || !item.Movable || !item.ItemData.Impassable) && - (item.ItemData.Flags & reqFlags) != 0 && !(item is BaseMulti) && item.ItemID <= TileData.MaxItemValue; - - private static bool Verify(Item item, TileFlag reqFlags, bool ignoreMovableImpassables, int x, int y) => - Verify(item, reqFlags, ignoreMovableImpassables) && Verify(item, x, y); - - private static void GetStartZ(Mobile m, Map map, Point3D loc, IEnumerable itemList, out int zLow, out int zTop) - { - int xCheck = loc.X, yCheck = loc.Y; - - var landTile = map.Tiles.GetLandTile(xCheck, yCheck); - var landData = TileData.LandTable[landTile.ID & TileData.MaxLandValue]; - var landBlocks = (landData.Flags & TileFlag.Impassable) != 0; - - if (landBlocks && m.CanSwim && (landData.Flags & TileFlag.Wet) != 0) - { - landBlocks = false; - } - else if (m.CantWalk && (landData.Flags & TileFlag.Wet) == 0) - { - landBlocks = true; - } - - map.GetAverageZ(xCheck, yCheck, out var landZ, out var landCenter, out var landTop); - - var considerLand = !landTile.Ignored; - - var zCenter = zLow = zTop = 0; - var isSet = false; - - if (considerLand && !landBlocks && loc.Z >= landCenter) - { - zLow = landZ; - zCenter = landCenter; - zTop = landTop; - isSet = true; - } - - var staticTiles = map.Tiles.GetStaticTiles(xCheck, yCheck, true); - - foreach (var tile in staticTiles) - { - var tileData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var calcTop = tile.Z + tileData.CalcHeight; - - if (isSet && calcTop < zCenter) - { - continue; - } - - if ((tileData.Flags & TileFlag.Surface) == 0 && - (!m.CanSwim || (tileData.Flags & TileFlag.Wet) == 0)) - { - continue; - } - - if (loc.Z < calcTop) - { - continue; - } - - if (m.CantWalk && (tileData.Flags & TileFlag.Wet) == 0) - { - continue; - } - - zLow = tile.Z; - zCenter = calcTop; - - var top = tile.Z + tileData.Height; - - if (!isSet || top > zTop) - { - zTop = top; - } - - isSet = true; - } - - foreach (var item in itemList) - { - var itemData = item.ItemData; - - var calcTop = item.Z + itemData.CalcHeight; - - if (isSet && calcTop < zCenter) - { - continue; - } - - if ((itemData.Flags & TileFlag.Surface) == 0 && - (!m.CanSwim || (itemData.Flags & TileFlag.Wet) == 0)) - { - continue; - } - - if (loc.Z < calcTop) - { - continue; - } - - if (m.CantWalk && (itemData.Flags & TileFlag.Wet) == 0) - { - continue; - } - - zLow = item.Z; - zCenter = calcTop; - - var top = item.Z + itemData.Height; - - if (!isSet || top > zTop) - { - zTop = top; - } - - isSet = true; - } - - if (!isSet) - { - zLow = zTop = loc.Z; - } - else if (loc.Z > zTop) - { - zTop = loc.Z; - } - } - - public void Offset(Direction d, ref int x, ref int y) - { - switch (d & Direction.Mask) - { - case Direction.North: - --y; - break; - case Direction.South: - ++y; - break; - case Direction.West: - --x; - break; - case Direction.East: - ++x; - break; - case Direction.Right: - ++x; - --y; - break; - case Direction.Left: - --x; - ++y; - break; - case Direction.Down: - ++x; - ++y; - break; - case Direction.Up: - --x; - --y; - break; - } - } - - private static class MovementPool - { - private static readonly object _MovePoolLock = new(); - private static readonly Queue> _MoveCachePool = new(0x400); - - public static void AcquireMoveCache(ref List cache, IEnumerable items) - { - if (cache == null) - { - lock (_MovePoolLock) - { - cache = _MoveCachePool.Count > 0 ? _MoveCachePool.Dequeue() : new List(0x10); - } - } - else - { - cache.Clear(); - } - - cache.AddRange(items); - } - - public static void ClearMoveCache(ref List cache, bool free) - { - cache?.Clear(); - - if (!free) - { - return; - } - - lock (_MovePoolLock) - { - if (_MoveCachePool.Count < 0x400) - { - _MoveCachePool.Enqueue(cache); - } - } - - cache = null; - } - } - } -} diff --git a/Projects/UOContent/Engines/Pathing/Movement.cs b/Projects/UOContent/Engines/Pathing/Movement.cs index b7a8ff1c0..1b733bea3 100644 --- a/Projects/UOContent/Engines/Pathing/Movement.cs +++ b/Projects/UOContent/Engines/Pathing/Movement.cs @@ -11,13 +11,18 @@ namespace Server.Movement private const TileFlag ImpassableSurface = TileFlag.Impassable | TileFlag.Surface; - private static Point3D m_Goal; + private static Point3D _goal; - private readonly List[] m_MobPools = { new(), new(), new() }; + public static void Configure() + { + Movement.Impl = new MovementImpl(); + } - private readonly List[] m_Pools = { new(), new(), new(), new() }; + private readonly List[] _mobPools = { new(), new(), new() }; - private readonly List m_Sectors = new(); + private readonly List[] _pools = { new(), new(), new(), new() }; + + private readonly HashSet _sectors = new(); private MovementImpl() { @@ -25,12 +30,11 @@ namespace Server.Movement public static bool AlwaysIgnoreDoors { get; set; } public static bool IgnoreMovableImpassables { get; set; } - public static bool IgnoreSpellFields { get; set; } public static Point3D Goal { - get => m_Goal; - set => m_Goal = value; + get => _goal; + set => _goal = value; } public bool CheckMovement(Mobile m, Map map, Point3D loc, Direction d, out int newZ) @@ -43,6 +47,7 @@ namespace Server.Movement var xStart = loc.X; var yStart = loc.Y; + int xForward = xStart, yForward = yStart; int xRight = xStart, yRight = yStart; int xLeft = xStart, yLeft = yStart; @@ -59,10 +64,10 @@ namespace Server.Movement return false; } - var itemsStart = m_Pools[0]; - var itemsForward = m_Pools[1]; - var itemsLeft = m_Pools[2]; - var itemsRight = m_Pools[3]; + var itemsStart = _pools[0]; + var itemsForward = _pools[1]; + var itemsLeft = _pools[2]; + var itemsRight = _pools[3]; var ignoreMovableImpassables = IgnoreMovableImpassables; var reqFlags = ImpassableSurface; @@ -72,11 +77,11 @@ namespace Server.Movement reqFlags |= TileFlag.Wet; } - var mobsForward = m_MobPools[0]; - var mobsLeft = m_MobPools[1]; - var mobsRight = m_MobPools[2]; + var mobsForward = _mobPools[0]; + var mobsLeft = _mobPools[1]; + var mobsRight = _mobPools[2]; - var checkMobs = (m as BaseCreature)?.Controlled == false && (xForward != m_Goal.X || yForward != m_Goal.Y); + var checkMobs = (m as BaseCreature)?.Controlled == false && (xForward != _goal.X || yForward != _goal.Y); if (checkDiagonals) { @@ -85,40 +90,24 @@ namespace Server.Movement var sectorLeft = map.GetSector(xLeft, yLeft); var sectorRight = map.GetSector(xRight, yRight); - var sectors = m_Sectors; + _sectors.Add(sectorStart); - sectors.Add(sectorStart); + _sectors.Add(sectorForward); + _sectors.Add(sectorLeft); + _sectors.Add(sectorRight); - if (!sectors.Contains(sectorForward)) + foreach (var sector in _sectors) { - sectors.Add(sectorForward); - } - - if (!sectors.Contains(sectorLeft)) - { - sectors.Add(sectorLeft); - } - - if (!sectors.Contains(sectorRight)) - { - sectors.Add(sectorRight); - } - - for (var i = 0; i < sectors.Count; ++i) - { - var sector = sectors[i]; - for (var j = 0; j < sector.Items.Count; ++j) { var item = sector.Items[j]; - if (ignoreMovableImpassables && item.Movable && - (item.ItemData.Flags & ImpassableSurface) != 0) + if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) { continue; } - if ((item.ItemData.Flags & reqFlags) == 0) + if (!item.ItemData[reqFlags]) { continue; } @@ -168,29 +157,26 @@ namespace Server.Movement } } - if (m_Sectors.Count > 0) - { - m_Sectors.Clear(); - } + _sectors.Clear(); } else { var sectorStart = map.GetSector(xStart, yStart); var sectorForward = map.GetSector(xForward, yForward); + var sectorStartIsForward = sectorStart == sectorForward; - if (sectorStart == sectorForward) + if (!sectorStartIsForward) { - for (var i = 0; i < sectorStart.Items.Count; ++i) + for (var i = 0; i < sectorForward.Items.Count; ++i) { - var item = sectorStart.Items[i]; + var item = sectorForward.Items[i]; - if (ignoreMovableImpassables && item.Movable && - (item.ItemData.Flags & ImpassableSurface) != 0) + if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) { continue; } - if ((item.ItemData.Flags & reqFlags) == 0) + if (!item.ItemData[reqFlags]) { continue; } @@ -200,60 +186,39 @@ namespace Server.Movement continue; } - if (item.AtWorldPoint(xStart, yStart)) - { - itemsStart.Add(item); - } - else if (item.AtWorldPoint(xForward, yForward)) + if (item.AtWorldPoint(xForward, yForward)) { itemsForward.Add(item); } } } - else + + for (var i = 0; i < sectorStart.Items.Count; ++i) { - for (var i = 0; i < sectorForward.Items.Count; ++i) + var item = sectorStart.Items[i]; + + if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) { - var item = sectorForward.Items[i]; - - if (ignoreMovableImpassables && item.Movable && - (item.ItemData.Flags & ImpassableSurface) != 0) - { - continue; - } - - if ((item.ItemData.Flags & reqFlags) == 0) - { - continue; - } - - if (item.AtWorldPoint(xForward, yForward) && !(item is BaseMulti) && - item.ItemID <= TileData.MaxItemValue) - { - itemsForward.Add(item); - } + continue; } - for (var i = 0; i < sectorStart.Items.Count; ++i) + if (!item.ItemData[reqFlags]) { - var item = sectorStart.Items[i]; + continue; + } - if (ignoreMovableImpassables && item.Movable && - (item.ItemData.Flags & ImpassableSurface) != 0) - { - continue; - } + if (item is BaseMulti || item.ItemID > TileData.MaxItemValue) + { + continue; + } - if ((item.ItemData.Flags & reqFlags) == 0) - { - continue; - } - - if (item.AtWorldPoint(xStart, yStart) && !(item is BaseMulti) && - item.ItemID <= TileData.MaxItemValue) - { - itemsStart.Add(item); - } + if (item.AtWorldPoint(xStart, yStart)) + { + itemsStart.Add(item); + } + else if (sectorStartIsForward && item.AtWorldPoint(xForward, yForward)) + { + itemsForward.Add(item); } } @@ -273,58 +238,22 @@ namespace Server.Movement GetStartZ(m, map, loc, itemsStart, out var startZ, out var startTop); - var moveIsOk = Check( - map, - m, - itemsForward, - mobsForward, - xForward, - yForward, - startTop, - startZ, - m.CanSwim, - m.CantWalk, - out newZ - ); + var moveIsOk = Check(map, m, itemsForward, mobsForward, xForward, yForward, startTop, startZ, out newZ); if (moveIsOk && checkDiagonals) { if (m.Player && m.AccessLevel < AccessLevel.GameMaster) { - if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, m.CanSwim, m.CantWalk, out _) || - !Check( - map, - m, - itemsRight, - mobsRight, - xRight, - yRight, - startTop, - startZ, - m.CanSwim, - m.CantWalk, - out _ - )) + if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, out _) || + !Check(map, m, itemsRight, mobsRight, xRight, yRight, startTop, startZ, out _)) { moveIsOk = false; } } else { - if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, m.CanSwim, m.CantWalk, out _) && - !Check( - map, - m, - itemsRight, - mobsRight, - xRight, - yRight, - startTop, - startZ, - m.CanSwim, - m.CantWalk, - out _ - )) + if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, out _) && + !Check(map, m, itemsRight, mobsRight, xRight, yRight, startTop, startZ, out _)) { moveIsOk = false; } @@ -333,12 +262,12 @@ namespace Server.Movement for (int i = 0, c = checkDiagonals ? 4 : 2; i < c; ++i) { - m_Pools[i].Clear(); + _pools[i].Clear(); } for (int i = 0, c = checkDiagonals ? 3 : 1; i < c; ++i) { - m_MobPools[i].Clear(); + _mobPools[i].Clear(); } if (!moveIsOk) @@ -351,11 +280,6 @@ namespace Server.Movement public bool CheckMovement(Mobile m, Direction d, out int newZ) => CheckMovement(m, m.Map, m.Location, d, out newZ); - public static void Configure() - { - Movement.Impl = new MovementImpl(); - } - private bool IsOk( bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, StaticTile[] tiles, List items ) @@ -365,7 +289,7 @@ namespace Server.Movement var check = tiles[i]; var itemData = TileData.ItemTable[check.ID & TileData.MaxItemValue]; - if ((itemData.Flags & ImpassableSurface) != 0) // Impassable || Surface + if (itemData.ImpassableSurface) { var checkZ = check.Z; var checkTop = checkZ + itemData.CalcHeight; @@ -382,18 +306,15 @@ namespace Server.Movement var item = items[i]; var itemID = item.ItemID & TileData.MaxItemValue; var itemData = TileData.ItemTable[itemID]; - var flags = itemData.Flags; - if ((flags & ImpassableSurface) != 0) // Impassable || Surface + if (itemData.ImpassableSurface) { - if (ignoreDoors && ((flags & TileFlag.Door) != 0 || itemID == 0x692 || itemID == 0x846 || - itemID == 0x873 || - itemID >= 0x6F5 && itemID <= 0x6F6)) + if (ignoreDoors && (itemData.Door || itemID is 0x692 or 0x846 or 0x873 || itemID >= 0x6F5 && itemID <= 0x6F6)) { continue; } - if (ignoreSpellFields && (itemID == 0x82 || itemID == 0x3946 || itemID == 0x3956)) + if (ignoreSpellFields && itemID is 0x82 or 0x3946 or 0x3956) { continue; } @@ -412,12 +333,21 @@ namespace Server.Movement } private bool Check( - Map map, Mobile m, List items, List mobiles, int x, int y, int startTop, int startZ, - bool canSwim, bool cantWalk, out int newZ + Map map, + Mobile m, + List items, + List mobiles, + int x, + int y, + int startTop, + int startZ, + out int newZ ) { newZ = 0; + var cantWalk = m.CantWalk; + var canSwim = m.CanSwim; var tiles = map.Tiles.GetStaticTiles(x, y, true); var landTile = map.Tiles.GetLandTile(x, y); var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; @@ -438,17 +368,45 @@ namespace Server.Movement var ignoreDoors = AlwaysIgnoreDoors || !m.Alive || m.Body.BodyID == 0x3DB || m.IsDeadBondedPet; var ignoreSpellFields = m is PlayerMobile && map != Map.Felucca; + int testTop; + for (var i = 0; i < tiles.Length; ++i) { var tile = tiles[i]; var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - flags = itemData.Flags; - var notWater = (flags & TileFlag.Wet) == 0; + if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) + { + newZ = tile.Z; + return true; + } - // Surface && !Impassable - if ((flags & ImpassableSurface) != TileFlag.Surface && (!canSwim || notWater) || - cantWalk && notWater) + // Stygian Dragon + if (m.Body == 826 && map == Map.TerMur) + { + if (x is >= 307 and <= 354 && y is >= 126 and <= 192) + { + if (tile.Z > newZ) + { + newZ = tile.Z; + } + + moveIsOk = true; + } + else if (x is >= 42 and <= 89 && y is >= 333 and <= 399 or >= 531 and <= 597 or >= 739 and <= 805) + { + if (tile.Z > newZ) + { + newZ = tile.Z; + } + + moveIsOk = true; + } + } + + var notWater = !itemData.Wet; + + if (!itemData.Surface && itemData.Impassable && (!canSwim || notWater) || cantWalk && notWater) { continue; } @@ -456,8 +414,7 @@ namespace Server.Movement var itemZ = tile.Z; var itemTop = itemZ; var ourZ = itemZ + itemData.CalcHeight; - // int ourTop = ourZ + PersonHeight; - var testTop = checkTop; + testTop = checkTop; if (moveIsOk) { @@ -479,29 +436,31 @@ namespace Server.Movement itemTop += itemData.Height; } - if (stepTop >= itemTop) + if (stepTop < itemTop) { - var landCheck = itemZ; + continue; + } - if (itemData.Height >= StepHeight) - { - landCheck += StepHeight; - } - else - { - landCheck += itemData.Height; - } + var landCheck = itemZ; - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) - { - continue; - } + if (itemData.Height >= StepHeight) + { + landCheck += StepHeight; + } + else + { + landCheck += itemData.Height; + } - if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) - { - newZ = ourZ; - moveIsOk = true; - } + if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) + { + continue; + } + + if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) + { + newZ = ourZ; + moveIsOk = true; } } @@ -509,14 +468,16 @@ namespace Server.Movement { var item = items[i]; var itemData = item.ItemData; - flags = itemData.Flags; - var notWater = (flags & TileFlag.Wet) == 0; + if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) + { + newZ = item.Z; + return true; + } - // Surface && !Impassable && !Movable - if (item.Movable || - (flags & ImpassableSurface) != TileFlag.Surface && (!m.CanSwim || notWater) || - cantWalk && notWater) + var notWater = !itemData.Wet; + + if (item.Movable || !itemData.Surface && itemData.Impassable && (!canSwim || notWater) || cantWalk && notWater) { continue; } @@ -524,8 +485,7 @@ namespace Server.Movement var itemZ = item.Z; var itemTop = itemZ; var ourZ = itemZ + itemData.CalcHeight; - // int ourTop = ourZ + PersonHeight; - var testTop = checkTop; + testTop = checkTop; if (moveIsOk) { @@ -547,62 +507,64 @@ namespace Server.Movement itemTop += itemData.Height; } - if (stepTop >= itemTop) + if (stepTop < itemTop) { - var landCheck = itemZ; - - if (itemData.Height >= StepHeight) - { - landCheck += StepHeight; - } - else - { - landCheck += itemData.Height; - } - - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) - { - continue; - } - - if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) - { - newZ = ourZ; - moveIsOk = true; - } - } - } - - if (considerLand && !landBlocks && stepTop >= landZ) - { - var ourZ = landCenter; - // int ourTop = ourZ + PersonHeight; - var testTop = checkTop; - - if (ourZ + PersonHeight > testTop) - { - testTop = ourZ + PersonHeight; + continue; } - var shouldCheck = true; + var landCheck = itemZ; - if (moveIsOk) + if (itemData.Height >= StepHeight) { - var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) - { - shouldCheck = false; - } + landCheck += StepHeight; + } + else + { + landCheck += itemData.Height; } - if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) + if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) + { + continue; + } + + if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) { newZ = ourZ; moveIsOk = true; } } + if (!considerLand || landBlocks || stepTop < landZ) + { + return moveIsOk; + } + + testTop = checkTop; + + if (landCenter + PersonHeight > testTop) + { + testTop = landCenter + PersonHeight; + } + + var shouldCheck = true; + + if (moveIsOk) + { + var cmp = (landCenter - m.Z).Abs() - (newZ - m.Z).Abs(); + + if (cmp > 0 || cmp == 0 && landCenter > newZ) + { + shouldCheck = false; + } + } + + if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, tiles, items)) + { + newZ = landCenter; + moveIsOk = true; + } + if (moveIsOk) { for (var i = 0; moveIsOk && i < mobiles.Count; ++i) @@ -656,59 +618,58 @@ namespace Server.Movement { var tile = staticTiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var calcTop = tile.Z + id.CalcHeight; - if ((!isSet || calcTop >= zCenter) && - ((id.Flags & TileFlag.Surface) != 0 || m.CanSwim && (id.Flags & TileFlag.Wet) != 0) && loc.Z >= calcTop) + if (isSet && calcTop < zCenter || loc.Z < calcTop || !id.Surface && !(m.CanSwim && id.Wet)) { - if (m.CantWalk && (id.Flags & TileFlag.Wet) == 0) - { - continue; - } - - zLow = tile.Z; - zCenter = calcTop; - - var top = tile.Z + id.Height; - - if (!isSet || top > zTop) - { - zTop = top; - } - - isSet = true; + continue; } + + if (m.CantWalk && !id.Wet) + { + continue; + } + + zLow = tile.Z; + zCenter = calcTop; + + var top = tile.Z + id.Height; + + if (!isSet || top > zTop) + { + zTop = top; + } + + isSet = true; } for (var i = 0; i < itemList.Count; ++i) { var item = itemList[i]; - var id = item.ItemData; - var calcTop = item.Z + id.CalcHeight; - if ((!isSet || calcTop >= zCenter) && - ((id.Flags & TileFlag.Surface) != 0 || m.CanSwim && (id.Flags & TileFlag.Wet) != 0) && loc.Z >= calcTop) + if (isSet && calcTop < zCenter || loc.Z < calcTop || !id.Surface && !(m.CanSwim && id.Wet)) { - if (m.CantWalk && (id.Flags & TileFlag.Wet) == 0) - { - continue; - } - - zLow = item.Z; - zCenter = calcTop; - - var top = item.Z + id.Height; - - if (!isSet || top > zTop) - { - zTop = top; - } - - isSet = true; + continue; } + + if (m.CantWalk && !id.Wet) + { + continue; + } + + zLow = item.Z; + zCenter = calcTop; + + var top = item.Z + id.Height; + + if (!isSet || top > zTop) + { + zTop = top; + } + + isSet = true; } if (!isSet) diff --git a/Projects/UOContent/Engines/Pathing/MovementPath.cs b/Projects/UOContent/Engines/Pathing/MovementPath.cs index 98663a060..438817c09 100644 --- a/Projects/UOContent/Engines/Pathing/MovementPath.cs +++ b/Projects/UOContent/Engines/Pathing/MovementPath.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using Server.Items; using Server.PathAlgorithms; using Server.PathAlgorithms.FastAStar; -using Server.PathAlgorithms.SlowAStar; using Server.Spells; using Server.Targeting; @@ -89,18 +88,21 @@ namespace Server var y = from.Y; var z = from.Z; + WayPoint waypoint = null; + for (var i = 0; i < path.Directions.Length; ++i) { Movement.Movement.Offset(path.Directions[i], ref x, ref y); - new RecallRune().MoveToWorld(new Point3D(x, y, z + zOffset), from.Map); + waypoint = new WayPoint(waypoint); + waypoint.MoveToWorld(new Point3D(x, y, z + zOffset), from.Map); } } } public static void Path_OnTarget(Mobile from, object targeted) { - if (!(targeted is IPoint3D p)) + if (targeted is not IPoint3D p) { return; } @@ -108,7 +110,6 @@ namespace Server SpellHelper.GetSurfaceTop(ref p); Path(from, p, FastAStarAlgorithm.Instance, "Fast", 0); - Path(from, p, SlowAStarAlgorithm.Instance, "Slow", 2); OverrideAlgorithm = null; } } diff --git a/Projects/UOContent/Engines/Pathing/SlowAStarAlgorithm.cs b/Projects/UOContent/Engines/Pathing/SlowAStarAlgorithm.cs deleted file mode 100644 index 6299b1863..000000000 --- a/Projects/UOContent/Engines/Pathing/SlowAStarAlgorithm.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using Server.Mobiles; -using CalcMoves = Server.Movement.Movement; -using MoveImpl = Server.Movement.MovementImpl; - -namespace Server.PathAlgorithms.SlowAStar -{ - public struct PathNode - { - public int x, y, z; - public int g, h; - public int px, py, pz; - public int dir; - } - - public class SlowAStarAlgorithm : PathAlgorithm - { - private const int MaxDepth = 300; - private const int MaxNodes = MaxDepth * 16; - public static PathAlgorithm Instance = new SlowAStarAlgorithm(); - - private static readonly PathNode[] m_Closed = new PathNode[MaxNodes]; - private static readonly PathNode[] m_Open = new PathNode[MaxNodes]; - private static readonly PathNode[] m_Successors = new PathNode[8]; - private static readonly Direction[] m_Path = new Direction[MaxNodes]; - - private Point3D m_Goal; - - public int Heuristic(int x, int y, int z) - { - x -= m_Goal.X; - y -= m_Goal.Y; - z -= m_Goal.Z; - - x *= 11; - y *= 11; - - return x * x + y * y + z * z; - } - - public override bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal) => false; - - public override Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal) - { - m_Goal = goal; - - var bc = m as BaseCreature; - - PathNode curNode; - - var goalNode = new PathNode(); - goalNode.x = goal.X; - goalNode.y = goal.Y; - goalNode.z = goal.Z; - - var startNode = new PathNode(); - startNode.x = start.X; - startNode.y = start.Y; - startNode.z = start.Z; - startNode.h = Heuristic(startNode.x, startNode.y, startNode.z); - - PathNode[] closed = m_Closed, open = m_Open, successors = m_Successors; - var path = m_Path; - - int closedCount = 0, openCount = 0; - var pathCount = 0; - var depth = 0; - - var iBacktrack = 0; - - open[openCount++] = startNode; - - while (openCount > 0) - { - curNode = open[0]; - var curF = curNode.g + curNode.h; - var popIndex = 0; - - for (var i = 1; i < openCount; ++i) - { - if (open[i].g + open[i].h < curF) - { - curNode = open[i]; - curF = curNode.g + curNode.h; - popIndex = i; - } - } - - if (curNode.x == goalNode.x && curNode.y == goalNode.y && (curNode.z - goalNode.z).Abs() < 16) - { - if (closedCount == MaxNodes) - { - break; - } - - closed[closedCount++] = curNode; - - var xBacktrack = curNode.px; - var yBacktrack = curNode.py; - var zBacktrack = curNode.pz; - - path[pathCount++] = (Direction)curNode.dir; - - // if (pathCount == MaxNodes) - // { - // break; - // } - - while (xBacktrack != startNode.x || yBacktrack != startNode.y || zBacktrack != startNode.z) - { - var found = false; - - for (var j = 0; !found && j < closedCount; ++j) - { - if (closed[j].x == xBacktrack && closed[j].y == yBacktrack && closed[j].z == zBacktrack) - { - if (pathCount == MaxNodes) - { - break; - } - - curNode = closed[j]; - path[pathCount++] = (Direction)curNode.dir; - xBacktrack = curNode.px; - yBacktrack = curNode.py; - zBacktrack = curNode.pz; - found = true; - } - } - - if (!found) - { - Console.WriteLine("bugaboo.."); - return null; - } - - if (pathCount == MaxNodes) - { - break; - } - } - - if (pathCount == MaxNodes) - { - break; - } - - var dirs = new Direction[pathCount]; - - while (pathCount > 0) - { - dirs[iBacktrack++] = path[--pathCount]; - } - - return dirs; - } - - --openCount; - - for (var i = popIndex; i < openCount; ++i) - { - open[i] = open[i + 1]; - } - - var sucCount = 0; - - if (bc != null) - { - MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; - MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; - } - - MoveImpl.Goal = goal; - - int x; - int y; - int z; - for (var i = 0; i < 8; ++i) - { - switch (i) - { - default: - x = 0; - y = -1; - break; - case 1: - x = 1; - y = -1; - break; - case 2: - x = 1; - y = 0; - break; - case 3: - x = 1; - y = 1; - break; - case 4: - x = 0; - y = 1; - break; - case 5: - x = -1; - y = 1; - break; - case 6: - x = -1; - y = 0; - break; - case 7: - x = -1; - y = -1; - break; - } - - if (CalcMoves.CheckMovement(m, map, new Point3D(curNode.x, curNode.y, curNode.z), (Direction)i, out z)) - { - successors[sucCount].x = x + curNode.x; - successors[sucCount].y = y + curNode.y; - successors[sucCount++].z = z; - } - } - - MoveImpl.AlwaysIgnoreDoors = false; - MoveImpl.IgnoreMovableImpassables = false; - MoveImpl.Goal = Point3D.Zero; - - if (sucCount == 0 || ++depth > MaxDepth) - { - break; - } - - for (var i = 0; i < sucCount; ++i) - { - x = successors[i].x; - y = successors[i].y; - z = successors[i].z; - - successors[i].g = curNode.g + 1; - - int openIndex = -1, closedIndex = -1; - - for (var j = 0; openIndex == -1 && j < openCount; ++j) - { - if (open[j].x == x && open[j].y == y && open[j].z == z) - { - openIndex = j; - } - } - - if (openIndex >= 0 && open[openIndex].g < successors[i].g) - { - continue; - } - - for (var j = 0; closedIndex == -1 && j < closedCount; ++j) - { - if (closed[j].x == x && closed[j].y == y && closed[j].z == z) - { - closedIndex = j; - } - } - - if (closedIndex >= 0 && closed[closedIndex].g < successors[i].g) - { - continue; - } - - if (openIndex >= 0) - { - --openCount; - - for (var j = openIndex; j < openCount; ++j) - { - open[j] = open[j + 1]; - } - } - - if (closedIndex >= 0) - { - --closedCount; - - for (var j = closedIndex; j < closedCount; ++j) - { - closed[j] = closed[j + 1]; - } - } - - successors[i].px = curNode.x; - successors[i].py = curNode.y; - successors[i].pz = curNode.z; - successors[i].dir = (int)GetDirection(curNode.x, curNode.y, x, y); - successors[i].h = Heuristic(x, y, z); - - if (openCount == MaxNodes) - { - break; - } - - open[openCount++] = successors[i]; - } - - if (openCount == MaxNodes || closedCount == MaxNodes) - { - break; - } - - closed[closedCount++] = curNode; - } - - return null; - } - } -} diff --git a/Projects/UOContent/Engines/Spawners/Commands/ConvertPremiumSpawners.cs b/Projects/UOContent/Engines/Spawners/Commands/ConvertPremiumSpawners.cs index a542550bb..75f79c0e5 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/ConvertPremiumSpawners.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/ConvertPremiumSpawners.cs @@ -77,7 +77,7 @@ namespace Server.Engines.Spawners { var stemDir = stem[..^file.Name.Length]; var fullOutputDir = Path.Combine(outputDir, stemDir); - AssemblyHandler.EnsureDirectory(fullOutputDir); + PathUtility.EnsureDirectory(fullOutputDir); var outputFileName = $"{file.Name[..^file.Extension.Length]}.json"; ParsePremiumSpawnerFile(file.FullName, Path.Combine(fullOutputDir, outputFileName), options); diff --git a/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs index 467ea4fe3..4e24ef087 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs @@ -53,7 +53,7 @@ namespace Server.Engines.Spawners if (!Path.IsPathRooted(path)) { path = Path.Combine(Core.BaseDirectory, path); - AssemblyHandler.EnsureDirectory(directory); + PathUtility.EnsureDirectory(directory); } else if (!Directory.Exists(directory)) { diff --git a/Projects/UOContent/Engines/Spawners/Spawner.cs b/Projects/UOContent/Engines/Spawners/Spawner.cs index dcfa82779..517a7779d 100644 --- a/Projects/UOContent/Engines/Spawners/Spawner.cs +++ b/Projects/UOContent/Engines/Spawners/Spawner.cs @@ -67,8 +67,7 @@ namespace Server.Engines.Spawners { var staticTile = staticTiles[i]; - if (staticTile.Z == z && - (TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet) != 0) + if (staticTile.Z == z && TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Wet) { return true; } diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs index 582410159..e7412891c 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs @@ -783,7 +783,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2811, 0x2812)] + [Furniture] + [Flippable(0x2811, 0x2812)] public class ChestOfHeirlooms : LockableContainer { [Constructible] 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 014f38688..eeb92baa8 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -515,8 +515,7 @@ namespace Server.Mobiles { if (from.Account is Account acct && from.AccessLevel == AccessLevel.Player) { - var time = TimeSpan.FromDays(RewardSystem.RewardInterval.TotalDays * 6) - - (Core.Now - acct.Created); + var time = TimeSpan.FromDays(RewardSystem.RewardInterval.TotalDays * 6) - acct.AccountAge; if (time > TimeSpan.Zero) { diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs index 869b1b3fc..504de1247 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs @@ -59,15 +59,8 @@ namespace Server.Engines.VeteranRewards return false; } - public static bool HasAccess(Mobile mob, RewardEntry entry) - { - if (Core.Expansion < entry.RequiredExpansion) - { - return false; - } - - return HasAccess(mob, entry.List, out var _); - } + public static bool HasAccess(Mobile mob, RewardEntry entry) => + Core.Expansion >= entry.RequiredExpansion && HasAccess(mob, entry.List, out var _); public static bool HasAccess(Mobile mob, RewardList list, out TimeSpan ts) { @@ -77,13 +70,13 @@ namespace Server.Engines.VeteranRewards return false; } - if (!(mob.Account is Account acct)) + if (mob.Account is not Account acct) { ts = TimeSpan.Zero; return false; } - ts = list.Age - (Core.Now - acct.Created); + ts = list.Age - acct.AccountAge; return ts <= TimeSpan.Zero; } @@ -98,12 +91,8 @@ namespace Server.Engines.VeteranRewards return GetRewardLevel(acct); } - public static int GetRewardLevel(Account acct) - { - var totalTime = Core.Now - acct.Created; - - return Math.Max((int)(totalTime.TotalDays / RewardInterval.TotalDays), 0); - } + public static int GetRewardLevel(Account acct) => + Math.Max((int)(acct.AccountAge.TotalDays / RewardInterval.TotalDays), 0); public static bool HasHalfLevel(Mobile mob) { @@ -115,14 +104,7 @@ namespace Server.Engines.VeteranRewards return HasHalfLevel(acct); } - public static bool HasHalfLevel(Account acct) - { - var totalTime = Core.Now - acct.Created; - - var level = totalTime.TotalDays / RewardInterval.TotalDays; - - return level >= 0.5; - } + public static bool HasHalfLevel(Account acct) => acct.AccountAge.TotalDays / RewardInterval.TotalDays >= 0.5; public static bool ConsumeRewardPoint(Mobile mob) { diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index add8aa9ec..25777e491 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -2696,9 +2696,9 @@ namespace Server.Gumps { case 0: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_Information, 0, null, @@ -2710,9 +2710,9 @@ namespace Server.Gumps } case 1: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_Characters, 0, null, @@ -2724,9 +2724,9 @@ namespace Server.Gumps } case 2: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_Comments, 0, null, @@ -2738,23 +2738,23 @@ namespace Server.Gumps } case 3: { - @from.SendGump( - new AdminGump(@from, AdminGumpPage.AccountDetails_Tags, 0, null, null, m_State) + from.SendGump( + new AdminGump(from, AdminGumpPage.AccountDetails_Tags, 0, null, null, m_State) ); break; } case 13: { - @from.SendGump( - new AdminGump(@from, AdminGumpPage.AccountDetails_Access, 0, null, null, m_State) + from.SendGump( + new AdminGump(from, AdminGumpPage.AccountDetails_Access, 0, null, null, m_State) ); break; } case 14: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, @@ -2766,9 +2766,9 @@ namespace Server.Gumps } case 15: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_Access_Restrictions, 0, null, @@ -2780,14 +2780,14 @@ namespace Server.Gumps } case 4: { - @from.Prompt = new AddCommentPrompt(m_State as Account); - @from.SendMessage("Enter the new account comment."); + from.Prompt = new AddCommentPrompt(m_State as Account); + from.SendMessage("Enter the new account comment."); break; } case 5: { - @from.Prompt = new AddTagNamePrompt(m_State as Account); - @from.SendMessage("Enter the new tag name."); + from.Prompt = new AddTagNamePrompt(m_State as Account); + from.SendMessage("Enter the new tag name."); break; } case 6: @@ -2897,9 +2897,9 @@ namespace Server.Gumps } case 8: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_ChangePassword, 0, null, @@ -2911,9 +2911,9 @@ namespace Server.Gumps } case 9: { - @from.SendGump( + from.SendGump( new AdminGump( - @from, + from, AdminGumpPage.AccountDetails_ChangeAccess, 0, null, diff --git a/Projects/UOContent/Gumps/Props/PropsGump.cs b/Projects/UOContent/Gumps/Props/PropsGump.cs index 6d1840b7f..b0be1bdfb 100644 --- a/Projects/UOContent/Gumps/Props/PropsGump.cs +++ b/Projects/UOContent/Gumps/Props/PropsGump.cs @@ -402,7 +402,7 @@ namespace Server.Gumps : new PropertiesGump(from, m_Object, m_Stack, m_List, m_Page) ); } - else if (IsParsable(type)) + else if (IsType(type, OfString) || IsParsable(type)) { from.SendGump(new SetGump(prop, from, m_Object, this)); } diff --git a/Projects/UOContent/Gumps/Props/SetGump.cs b/Projects/UOContent/Gumps/Props/SetGump.cs index eb69e92b1..e543c3a4a 100644 --- a/Projects/UOContent/Gumps/Props/SetGump.cs +++ b/Projects/UOContent/Gumps/Props/SetGump.cs @@ -117,7 +117,6 @@ namespace Server.Gumps } } - public override void OnResponse(NetState sender, RelayInfo info) { object toSet = null; diff --git a/Projects/UOContent/Items/Addons/AddonComponent.cs b/Projects/UOContent/Items/Addons/AddonComponent.cs index e7b58f8d6..f32576ff7 100644 --- a/Projects/UOContent/Items/Addons/AddonComponent.cs +++ b/Projects/UOContent/Items/Addons/AddonComponent.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using Server.ContextMenus; using Server.Engines.Craft; namespace Server.Items @@ -141,10 +143,7 @@ namespace Server.Items } } - public override void OnDoubleClick(Mobile from) - { - _addon?.OnComponentUsed(this, from); - } + public override void OnDoubleClick(Mobile from) => _addon?.OnComponentUsed(this, from); public override void OnLocationChange(Point3D old) { @@ -161,7 +160,12 @@ namespace Server.Items _addon.Map = Map; } } +/* + public override void GetProperties(ObjectPropertyList list) => _addon?.GetProperties(list); + public override void GetContextMenuEntries(Mobile from, List list) => + _addon?.GetContextMenuEntries(from, list); +*/ public override void OnAfterDelete() { base.OnAfterDelete(); @@ -186,7 +190,7 @@ namespace Server.Items public static void ApplyLightTo(Item item) { - if ((item.ItemData.Flags & TileFlag.LightSource) == 0) + if (!item.ItemData.LightSource) { return; // not a light source } diff --git a/Projects/UOContent/Items/Addons/AddonContainerComponent.cs b/Projects/UOContent/Items/Addons/AddonContainerComponent.cs index 7baf0bb64..509d430ea 100644 --- a/Projects/UOContent/Items/Addons/AddonContainerComponent.cs +++ b/Projects/UOContent/Items/Addons/AddonContainerComponent.cs @@ -1,5 +1,8 @@ +using System; +using System.Buffers.Binary; using System.Collections.Generic; using Server.ContextMenus; +using Server.Network; namespace Server.Items { @@ -19,11 +22,11 @@ namespace Server.Items [SerializableField(0)] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] - public BaseAddonContainer _addon; + private BaseAddonContainer _addon; [SerializableField(1)] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] - public Point3D _offset; + private Point3D _offset; [Hue] [CommandProperty(AccessLevel.GameMaster)] @@ -65,9 +68,6 @@ namespace Server.Items } } - public override void GetContextMenuEntries(Mobile from, List list) => - _addon?.GetContextMenuEntries(from, list); - public override void OnMapChange() { if (_addon != null) @@ -76,6 +76,11 @@ namespace Server.Items } } + public override void GetProperties(ObjectPropertyList list) => _addon?.GetProperties(list); + + public override void GetContextMenuEntries(Mobile from, List list) => + _addon?.GetContextMenuEntries(from, list); + public override void OnAfterDelete() { base.OnAfterDelete(); @@ -83,6 +88,17 @@ namespace Server.Items _addon?.Delete(); } + public override void SendWorldPacketTo(NetState ns, ReadOnlySpan world = default) + { + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + var length = OutgoingItemPackets.CreateWorldItem(buffer, this); + // Use an itemid of a real container + BinaryPrimitives.WriteUInt16BigEndian(buffer[7..9], (ushort)(_addon?.ItemID ?? 0x9AB)); + ns.Send(buffer[..length]); + + base.SendWorldPacketTo(ns, world); + } + [AfterDeserialization] private void AfterDeserialization() { diff --git a/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs b/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs index 02e1c448b..ba197ed84 100644 --- a/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcaneBookshelfEastAddon.cs @@ -1,27 +1,29 @@ namespace Server.Items { [Serializable(0)] - public partial class ArcaneBookshelfEastAddon : BaseAddon + public partial class ArcaneBookshelfEastAddon : BaseAddonContainer { [Constructible] - public ArcaneBookshelfEastAddon() + public ArcaneBookshelfEastAddon() : base(0x3084) { - AddComponent(new AddonComponent(0x3084), 0, 0, 0); - AddComponent(new AddonComponent(0x3085), -1, 0, 0); + AddComponent(new AddonContainerComponent(0x3085), -1, 0, 0); } - public override BaseAddonDeed Deed => new ArcaneBookshelfEastDeed(); + public override BaseAddonContainerDeed Deed => new ArcaneBookshelfEastDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x107; + public override int DefaultDropSound => 0x42; } [Serializable(0)] - public partial class ArcaneBookshelfEastDeed : BaseAddonDeed + public partial class ArcaneBookshelfEastDeed : BaseAddonContainerDeed { [Constructible] public ArcaneBookshelfEastDeed() { } - public override BaseAddon Addon => new ArcaneBookshelfEastAddon(); + public override BaseAddonContainer Addon => new ArcaneBookshelfEastAddon(); public override int LabelNumber => 1073371; // arcane bookshelf (east) } } diff --git a/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs b/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs index 744a23c58..4a9569f0d 100644 --- a/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ArcaneBookshelfSouthAddon.cs @@ -1,27 +1,30 @@ namespace Server.Items { [Serializable(0)] - public partial class ArcaneBookshelfSouthAddon : BaseAddon + public partial class ArcaneBookshelfSouthAddon : BaseAddonContainer { [Constructible] - public ArcaneBookshelfSouthAddon() + public ArcaneBookshelfSouthAddon() : base(0x2DEF) { - AddComponent(new AddonComponent(0x3087), 0, 0, 0); - AddComponent(new AddonComponent(0x3086), 0, 1, 0); + AddComponent(new LocalizedContainerComponent(0x21A4, 1031760), -1, 0, 0); } - public override BaseAddonDeed Deed => new ArcaneBookshelfSouthDeed(); + public override BaseAddonContainerDeed Deed => new ArcaneBookshelfSouthDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x107; + public override int DefaultDropSound => 0x42; + public override int LabelNumber => 1032420; // arcane bookshelf } [Serializable(0)] - public partial class ArcaneBookshelfSouthDeed : BaseAddonDeed + public partial class ArcaneBookshelfSouthDeed : BaseAddonContainerDeed { [Constructible] public ArcaneBookshelfSouthDeed() { } - public override BaseAddon Addon => new ArcaneBookshelfSouthAddon(); + public override BaseAddonContainer Addon => new ArcaneBookshelfSouthAddon(); public override int LabelNumber => 1072871; // arcane bookshelf (south) } } diff --git a/Projects/UOContent/Items/Addons/BaseAddon.cs b/Projects/UOContent/Items/Addons/BaseAddon.cs index 9720deff9..a0507e8cb 100644 --- a/Projects/UOContent/Items/Addons/BaseAddon.cs +++ b/Projects/UOContent/Items/Addons/BaseAddon.cs @@ -23,7 +23,8 @@ namespace Server.Items [Serializable(3, false)] public abstract partial class BaseAddon : Item, IChoppable, IAddon { - private CraftResource _resource; + [SerializableField(1, "private", "private")] + private CraftResource _rawResource; public BaseAddon() : base(1) { @@ -65,16 +66,15 @@ namespace Server.Items } [CommandProperty(AccessLevel.GameMaster)] - [SerializableField(1)] public CraftResource Resource { - get => _resource; + get => _rawResource; set { - if (_resource != value) + if (_rawResource != value) { - _resource = value; - Hue = CraftResources.GetHue(_resource); + RawResource = value; + Hue = CraftResources.GetHue(_rawResource); InvalidateProperties(); this.MarkDirty(); @@ -284,7 +284,7 @@ namespace Server.Items if (version == 2) { - _resource = (CraftResource)reader.ReadEncodedInt(); + _rawResource = (CraftResource)reader.ReadEncodedInt(); } } } diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs index c812ac3c5..976a6fc00 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs @@ -9,7 +9,8 @@ namespace Server.Items [SerializableField(0, setter: "private")] private List _components; - private CraftResource _resource; + [SerializableField(1, "private", "private")] + private CraftResource _rawResource; public BaseAddonContainer(int itemID) : base(itemID) { @@ -42,17 +43,16 @@ namespace Server.Items } } - [SerializableField(1)] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { - get => _resource; + get => _rawResource; set { - if (_resource != value) + if (_rawResource != value) { - _resource = value; - Hue = CraftResources.GetHue(_resource); + RawResource = value; + Hue = CraftResources.GetHue(_rawResource); InvalidateProperties(); this.MarkDirty(); @@ -171,9 +171,9 @@ namespace Server.Items { base.GetProperties(list); - if (!CraftResources.IsStandard(_resource)) + if (!CraftResources.IsStandard(_rawResource)) { - list.Add(CraftResources.GetLocalizationNumber(_resource)); + list.Add(CraftResources.GetLocalizationNumber(_rawResource)); } } @@ -187,11 +187,24 @@ namespace Server.Items } } + public override void InvalidateProperties() + { + base.InvalidateProperties(); + + if (_components != null) + { + foreach (var component in _components) + { + component.InvalidateProperties(); + } + } + } + // Handles v0 with old Enum -> Int casting private void Deserialize(IGenericReader reader, int version) { _components = reader.ReadEntityList(); - _resource = version == 1 ? reader.ReadEnum() : (CraftResource)reader.ReadInt(); + _rawResource = version == 1 ? reader.ReadEnum() : (CraftResource)reader.ReadInt(); } [AfterDeserialization] @@ -332,6 +345,10 @@ namespace Server.Items public virtual void OnComponentUsed(AddonContainerComponent c, Mobile from) { + if (!Deleted) + { + OnDoubleClick(from); + } } } } diff --git a/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs index d5b1cb99a..1bf3bebd2 100644 --- a/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenDresserEastAddon.cs @@ -1,27 +1,29 @@ namespace Server.Items { [Serializable(0)] - public partial class ElvenDresserEastAddon : BaseAddon + public partial class ElvenDresserEastAddon : BaseAddonContainer { [Constructible] - public ElvenDresserEastAddon() + public ElvenDresserEastAddon() : base(0x30E4) { - AddComponent(new AddonComponent(0x30E4), 0, 0, 0); - AddComponent(new AddonComponent(0x30E3), 0, -1, 0); + AddComponent(new AddonContainerComponent(0x30E3), 0, -1, 0); } - public override BaseAddonDeed Deed => new ElvenDresserEastDeed(); + public override BaseAddonContainerDeed Deed => new ElvenDresserEastDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x51; + public override int DefaultDropSound => 0x42; } [Serializable(0)] - public partial class ElvenDresserEastDeed : BaseAddonDeed + public partial class ElvenDresserEastDeed : BaseAddonContainerDeed { [Constructible] public ElvenDresserEastDeed() { } - public override BaseAddon Addon => new ElvenDresserEastAddon(); + public override BaseAddonContainer Addon => new ElvenDresserEastAddon(); public override int LabelNumber => 1073388; // elven dresser (east) } } diff --git a/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs index e00e8f6f6..c49be3673 100644 --- a/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenDresserSouthAddon.cs @@ -1,27 +1,29 @@ namespace Server.Items { [Serializable(0)] - public partial class ElvenDresserSouthAddon : BaseAddon + public partial class ElvenDresserSouthAddon : BaseAddonContainer { [Constructible] - public ElvenDresserSouthAddon() + public ElvenDresserSouthAddon() : base(0x30E6) { - AddComponent(new AddonComponent(0x30E5), 0, 0, 0); - AddComponent(new AddonComponent(0x30E6), 1, 0, 0); + AddComponent(new AddonContainerComponent(0x30E5), -1, 0, 0); } - public override BaseAddonDeed Deed => new ElvenDresserSouthDeed(); + public override BaseAddonContainerDeed Deed => new ElvenDresserSouthDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x51; + public override int DefaultDropSound => 0x42; } [Serializable(0)] - public partial class ElvenDresserSouthDeed : BaseAddonDeed + public partial class ElvenDresserSouthDeed : BaseAddonContainerDeed { [Constructible] public ElvenDresserSouthDeed() { } - public override BaseAddon Addon => new ElvenDresserSouthAddon(); + public override BaseAddonContainer Addon => new ElvenDresserSouthAddon(); public override int LabelNumber => 1072864; // elven dresser (south) } } diff --git a/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs b/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs index a8826de9a..52dd5add9 100644 --- a/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenForgeAddon.cs @@ -9,6 +9,7 @@ namespace Server.Items AddComponent(new AddonComponent(0x2DD8), 0, 0, 0); } + public override int LabelNumber => 1072875; // elven forge public override BaseAddonDeed Deed => new ElvenForgeDeed(); } @@ -21,6 +22,6 @@ namespace Server.Items } public override BaseAddon Addon => new ElvenForgeAddon(); - public override int LabelNumber => 1072875; // squirrel statue (east) + public override int LabelNumber => 1072875; // elven forge } } diff --git a/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs index d8738daae..cbd01379c 100644 --- a/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenLoveseatEastAddon.cs @@ -6,8 +6,8 @@ namespace Server.Items [Constructible] public ElvenLoveseatEastAddon() { - AddComponent(new AddonComponent(0x3089), 0, 0, 0); - AddComponent(new AddonComponent(0x3088), 1, 0, 0); + AddComponent(new AddonComponent(0x308A), 0, 0, 0); + AddComponent(new AddonComponent(0x308B), 0, -1, 0); } public override BaseAddonDeed Deed => new ElvenLoveseatEastDeed(); diff --git a/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs index 9c7c6e127..879ca82ce 100644 --- a/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenLoveseatSouthAddon.cs @@ -6,8 +6,8 @@ namespace Server.Items [Constructible] public ElvenLoveseatSouthAddon() { - AddComponent(new AddonComponent(0x308A), 0, 0, 0); - AddComponent(new AddonComponent(0x308B), 0, -1, 0); + AddComponent(new AddonComponent(0x3088), 0, 0, 0); + AddComponent(new AddonComponent(0x3089), -1, 0, 0); } public override BaseAddonDeed Deed => new ElvenLoveseatSouthDeed(); diff --git a/Projects/UOContent/Items/Addons/ElvenSpinningwheelEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs similarity index 62% rename from Projects/UOContent/Items/Addons/ElvenSpinningwheelEastAddon.cs rename to Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs index 4136f5ed7..fe27635e8 100644 --- a/Projects/UOContent/Items/Addons/ElvenSpinningwheelEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenSpinningWheelEastAddon.cs @@ -2,18 +2,28 @@ using System; namespace Server.Items { + public delegate void SpinCallback(ISpinningWheel sender, Mobile from, int hue); + + public interface ISpinningWheel + { + bool Spinning { get; } + void BeginSpin(SpinCallback callback, Mobile from, int hue); + } + [Serializable(0)] - public partial class ElvenSpinningwheelEastAddon : BaseAddon, ISpinningWheel + [TypeAlias("Server.Items.ElvenSpinningwheelEastAddon")] + public partial class ElvenSpinningWheelEastAddon : BaseAddon, ISpinningWheel { private Timer m_Timer; [Constructible] - public ElvenSpinningwheelEastAddon() + public ElvenSpinningWheelEastAddon() { - AddComponent(new AddonComponent(0x2DD9), 0, 0, 0); + AddComponent(new AddonComponent(0x2E3D), 0, 0, 0); } - public override BaseAddonDeed Deed => new ElvenSpinningwheelEastDeed(); + public override int LabelNumber => 1031737; // elven spinning wheel + public override BaseAddonDeed Deed => new ElvenSpinningWheelEastDeed(); public bool Spinning => m_Timer != null; @@ -26,10 +36,9 @@ namespace Server.Items { switch (c.ItemID) { - case 0x2DD9: - case 0x101C: - case 0x10A4: - ++c.ItemID; + case 0x2E3D: + case 0x2E3F: + --c.ItemID; break; } } @@ -39,10 +48,9 @@ namespace Server.Items { switch (c.ItemID) { - case 0x2E3D: - case 0x101D: - case 0x10A5: - --c.ItemID; + case 0x2E3C: + case 0x2E3E: + ++c.ItemID; break; } } @@ -57,11 +65,9 @@ namespace Server.Items { switch (c.ItemID) { - case 0x1016: - case 0x101A: - case 0x101D: - case 0x10A5: - --c.ItemID; + case 0x2E3C: + case 0x2E3E: + ++c.ItemID; break; } } @@ -74,9 +80,9 @@ namespace Server.Items private readonly SpinCallback m_Callback; private readonly Mobile m_From; private readonly int m_Hue; - private readonly ElvenSpinningwheelEastAddon m_Wheel; + private readonly ElvenSpinningWheelEastAddon m_Wheel; - public SpinTimer(ElvenSpinningwheelEastAddon wheel, SpinCallback callback, Mobile from, int hue) : base( + public SpinTimer(ElvenSpinningWheelEastAddon wheel, SpinCallback callback, Mobile from, int hue) : base( TimeSpan.FromSeconds(3.0) ) { @@ -94,14 +100,16 @@ namespace Server.Items } [Serializable(0)] - public partial class ElvenSpinningwheelEastDeed : BaseAddonDeed + [TypeAlias("Server.Items.ElvenSpinningwheelEastDeed")] + public partial class ElvenSpinningWheelEastDeed : BaseAddonDeed { [Constructible] - public ElvenSpinningwheelEastDeed() + public ElvenSpinningWheelEastDeed() { } - public override BaseAddon Addon => new ElvenSpinningwheelEastAddon(); + public override BaseAddon Addon => new ElvenSpinningWheelEastAddon(); public override int LabelNumber => 1073393; // elven spinning wheel (east) } } + diff --git a/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs index 41b1722c1..e737c3d0f 100644 --- a/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenSpinningwheelSouthAddon.cs @@ -3,17 +3,19 @@ using System; namespace Server.Items { [Serializable(0)] - public partial class ElvenSpinningwheelSouthAddon : BaseAddon, ISpinningWheel + [TypeAlias("Server.Items.ElvenSpinningwheelSouthAddon")] + public partial class ElvenSpinningWheelSouthAddon : BaseAddon, ISpinningWheel { private Timer m_Timer; [Constructible] - public ElvenSpinningwheelSouthAddon() + public ElvenSpinningWheelSouthAddon() { - AddComponent(new AddonComponent(0x2DDA), 0, 0, 0); + AddComponent(new AddonComponent(0x2E3F), 0, 0, 0); } - public override BaseAddonDeed Deed => new ElvenSpinningwheelSouthDeed(); + public override int LabelNumber => 1031737; // elven spinning wheel + public override BaseAddonDeed Deed => new ElvenSpinningWheelSouthDeed(); public bool Spinning => m_Timer != null; @@ -26,11 +28,9 @@ namespace Server.Items { switch (c.ItemID) { - case 0x1015: - case 0x1019: - case 0x101C: - case 0x10A4: - ++c.ItemID; + case 0x2E3D: + case 0x2E3F: + --c.ItemID; break; } } @@ -40,11 +40,9 @@ namespace Server.Items { switch (c.ItemID) { - case 0x1016: - case 0x101A: - case 0x101D: - case 0x10A5: - --c.ItemID; + case 0x2E3C: + case 0x2E3E: + ++c.ItemID; break; } } @@ -52,18 +50,15 @@ namespace Server.Items public void EndSpin(SpinCallback callback, Mobile from, int hue) { m_Timer?.Stop(); - m_Timer = null; foreach (var c in Components) { switch (c.ItemID) { - case 0x1016: - case 0x101A: - case 0x101D: - case 0x10A5: - --c.ItemID; + case 0x2E3C: + case 0x2E3E: + ++c.ItemID; break; } } @@ -76,9 +71,9 @@ namespace Server.Items private readonly SpinCallback m_Callback; private readonly Mobile m_From; private readonly int m_Hue; - private readonly ElvenSpinningwheelSouthAddon m_Wheel; + private readonly ElvenSpinningWheelSouthAddon m_Wheel; - public SpinTimer(ElvenSpinningwheelSouthAddon wheel, SpinCallback callback, Mobile from, int hue) : base( + public SpinTimer(ElvenSpinningWheelSouthAddon wheel, SpinCallback callback, Mobile from, int hue) : base( TimeSpan.FromSeconds(3.0) ) { @@ -96,14 +91,15 @@ namespace Server.Items } [Serializable(0)] - public partial class ElvenSpinningwheelSouthDeed : BaseAddonDeed + [TypeAlias("Server.Items.ElvenSpinningwheelSouthDeed")] + public partial class ElvenSpinningWheelSouthDeed : BaseAddonDeed { [Constructible] - public ElvenSpinningwheelSouthDeed() + public ElvenSpinningWheelSouthDeed() { } - public override BaseAddon Addon => new ElvenSpinningwheelSouthAddon(); - public override int LabelNumber => 1072878; // spinning wheel (south) + public override BaseAddon Addon => new ElvenSpinningWheelSouthAddon(); + public override int LabelNumber => 1072878; // elven spinning wheel (south) } } diff --git a/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs b/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs index cac9d9ff5..a47e6dbbb 100644 --- a/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenWashbasinEastAddon.cs @@ -1,27 +1,29 @@ namespace Server.Items { [Serializable(0)] - public partial class ElvenWashBasinEastAddon : BaseAddon + public partial class ElvenWashBasinEastAddon : BaseAddonContainer { [Constructible] - public ElvenWashBasinEastAddon() + public ElvenWashBasinEastAddon() : base(0x30E0) { - AddComponent(new AddonComponent(0x30DF), 0, 0, 0); - AddComponent(new AddonComponent(0x30E0), 0, 1, 0); + AddComponent(new AddonContainerComponent(0x30DF), 0, -1, 0); } - public override BaseAddonDeed Deed => new ElvenWashBasinEastDeed(); + public override BaseAddonContainerDeed Deed => new ElvenWashBasinEastDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x0104; + public override int DefaultDropSound => 0x0042; } [Serializable(0)] - public partial class ElvenWashBasinEastDeed : BaseAddonDeed + public partial class ElvenWashBasinEastDeed : BaseAddonContainerDeed { [Constructible] public ElvenWashBasinEastDeed() { } - public override BaseAddon Addon => new ElvenWashBasinEastAddon(); + public override BaseAddonContainer Addon => new ElvenWashBasinEastAddon(); public override int LabelNumber => 1073387; // elven wash basin (east) } } diff --git a/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs b/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs index 2387bc2e8..a46b37fcc 100644 --- a/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/ElvenWashbasinSouthAddon.cs @@ -1,27 +1,29 @@ namespace Server.Items { [Serializable(0)] - public partial class ElvenWashBasinSouthAddon : BaseAddon + public partial class ElvenWashBasinSouthAddon : BaseAddonContainer { [Constructible] - public ElvenWashBasinSouthAddon() + public ElvenWashBasinSouthAddon() : base(0x30E2) { - AddComponent(new AddonComponent(0x30E1), 0, 0, 0); - AddComponent(new AddonComponent(0x30E2), 1, 0, 0); + AddComponent(new AddonContainerComponent(0x30E1), -1, 0, 0); } - public override BaseAddonDeed Deed => new ElvenWashBasinSouthDeed(); + public override BaseAddonContainerDeed Deed => new ElvenWashBasinSouthDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x0104; + public override int DefaultDropSound => 0x0042; } [Serializable(0)] - public partial class ElvenWashBasinSouthDeed : BaseAddonDeed + public partial class ElvenWashBasinSouthDeed : BaseAddonContainerDeed { [Constructible] public ElvenWashBasinSouthDeed() { } - public override BaseAddon Addon => new ElvenWashBasinSouthAddon(); + public override BaseAddonContainer Addon => new ElvenWashBasinSouthAddon(); public override int LabelNumber => 1072865; // elven wash basin (south) } } diff --git a/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs b/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs new file mode 100644 index 000000000..f936d0da7 --- /dev/null +++ b/Projects/UOContent/Items/Addons/OrnateElvenChestEastAddon.cs @@ -0,0 +1,29 @@ +namespace Server.Items +{ + [Serializable(0)] + public partial class OrnateElvenChestEastAddon : BaseAddonContainer + { + [Constructible] + public OrnateElvenChestEastAddon() : base(0x309A) + { + AddComponent(new AddonContainerComponent(0x309B), 0, -1, 0); + } + + public override BaseAddonContainerDeed Deed => new OrnateElvenChestEastDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x10C; + public override int DefaultDropSound => 0x42; + } + + [Serializable(0)] + public partial class OrnateElvenChestEastDeed : BaseAddonContainerDeed + { + [Constructible] + public OrnateElvenChestEastDeed() + { + } + + public override BaseAddonContainer Addon => new OrnateElvenChestEastAddon(); + public override int LabelNumber => 1073383; // ornate elven chest (east) + } +} diff --git a/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs b/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs new file mode 100644 index 000000000..109433111 --- /dev/null +++ b/Projects/UOContent/Items/Addons/OrnateElvenChestSouthAddon.cs @@ -0,0 +1,29 @@ +namespace Server.Items +{ + [Serializable(0)] + public partial class OrnateElvenChestSouthAddon : BaseAddonContainer + { + [Constructible] + public OrnateElvenChestSouthAddon() : base(0x3098) + { + AddComponent(new AddonContainerComponent(0x3099), -1, 0, 0); + } + + public override BaseAddonContainerDeed Deed => new OrnateElvenChestSouthDeed(); + public override bool RetainDeedHue => true; + public override int DefaultGumpID => 0x10C; + public override int DefaultDropSound => 0x42; + } + + [Serializable(0)] + public partial class OrnateElvenChestSouthDeed : BaseAddonContainerDeed + { + [Constructible] + public OrnateElvenChestSouthDeed() + { + } + + public override BaseAddonContainer Addon => new OrnateElvenChestSouthAddon(); + public override int LabelNumber => 1072862; // ornate elven chest (south) + } +} diff --git a/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs b/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs index d616c7d58..7f3803ae1 100644 --- a/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs +++ b/Projects/UOContent/Items/Addons/SpinningwheelEastAddon.cs @@ -2,26 +2,20 @@ using System; namespace Server.Items { - public delegate void SpinCallback(ISpinningWheel sender, Mobile from, int hue); - - public interface ISpinningWheel - { - bool Spinning { get; } - void BeginSpin(SpinCallback callback, Mobile from, int hue); - } - [Serializable(0, false)] - public partial class SpinningwheelEastAddon : BaseAddon, ISpinningWheel + [TypeAlias("Server.Items.SpinningwheelEastAddon")] + public partial class SpinningWheelEastAddon : BaseAddon, ISpinningWheel { private Timer m_Timer; [Constructible] - public SpinningwheelEastAddon() + public SpinningWheelEastAddon() { AddComponent(new AddonComponent(0x1019), 0, 0, 0); } - public override BaseAddonDeed Deed => new SpinningwheelEastDeed(); + public override int LabelNumber => 1024117; // spinning wheel + public override BaseAddonDeed Deed => new SpinningWheelEastDeed(); public bool Spinning => m_Timer != null; @@ -84,9 +78,9 @@ namespace Server.Items private readonly SpinCallback m_Callback; private readonly Mobile m_From; private readonly int m_Hue; - private readonly SpinningwheelEastAddon m_Wheel; + private readonly SpinningWheelEastAddon m_Wheel; - public SpinTimer(SpinningwheelEastAddon wheel, SpinCallback callback, Mobile from, int hue) : base( + public SpinTimer(SpinningWheelEastAddon wheel, SpinCallback callback, Mobile from, int hue) : base( TimeSpan.FromSeconds(3.0) ) { @@ -104,14 +98,15 @@ namespace Server.Items } [Serializable(0, false)] - public partial class SpinningwheelEastDeed : BaseAddonDeed + [TypeAlias("Server.Items.SpinningwheelEastDeed")] + public partial class SpinningWheelEastDeed : BaseAddonDeed { [Constructible] - public SpinningwheelEastDeed() + public SpinningWheelEastDeed() { } - public override BaseAddon Addon => new SpinningwheelEastAddon(); - public override int LabelNumber => 1044341; // spining wheel (east) + public override BaseAddon Addon => new SpinningWheelEastAddon(); + public override int LabelNumber => 1044341; // spinning wheel (east) } } diff --git a/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs b/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs index 67410b6fa..062e7eccf 100644 --- a/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/SpinningwheelSouthAddon.cs @@ -3,17 +3,19 @@ using System; namespace Server.Items { [Serializable(0, false)] - public partial class SpinningwheelSouthAddon : BaseAddon, ISpinningWheel + [TypeAlias("Server.Items.SpinningwheelSouthAddon")] + public partial class SpinningWheelSouthAddon : BaseAddon, ISpinningWheel { private Timer m_Timer; [Constructible] - public SpinningwheelSouthAddon() + public SpinningWheelSouthAddon() { AddComponent(new AddonComponent(0x1015), 0, 0, 0); } - public override BaseAddonDeed Deed => new SpinningwheelSouthDeed(); + public override int LabelNumber => 1024117; // spinning wheel + public override BaseAddonDeed Deed => new SpinningWheelSouthDeed(); public bool Spinning => m_Timer != null; @@ -76,9 +78,9 @@ namespace Server.Items private readonly SpinCallback m_Callback; private readonly Mobile m_From; private readonly int m_Hue; - private readonly SpinningwheelSouthAddon m_Wheel; + private readonly SpinningWheelSouthAddon m_Wheel; - public SpinTimer(SpinningwheelSouthAddon wheel, SpinCallback callback, Mobile from, int hue) : base( + public SpinTimer(SpinningWheelSouthAddon wheel, SpinCallback callback, Mobile from, int hue) : base( TimeSpan.FromSeconds(3.0) ) { @@ -96,14 +98,15 @@ namespace Server.Items } [Serializable(0, false)] - public partial class SpinningwheelSouthDeed : BaseAddonDeed + [TypeAlias("Server.Items.SpinningwheelSouthDeed")] + public partial class SpinningWheelSouthDeed : BaseAddonDeed { [Constructible] - public SpinningwheelSouthDeed() + public SpinningWheelSouthDeed() { } - public override BaseAddon Addon => new SpinningwheelSouthAddon(); - public override int LabelNumber => 1044342; // spining wheel (south) + public override BaseAddon Addon => new SpinningWheelSouthAddon(); + public override int LabelNumber => 1044342; // spinning wheel (south) } } diff --git a/Projects/UOContent/Items/Armor/BaseArmor.cs b/Projects/UOContent/Items/Armor/BaseArmor.cs index c0683fc20..55bbe8a65 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.cs @@ -114,7 +114,11 @@ namespace Server.Items private ArmorProtectionLevel _protection = ArmorProtectionLevel.Regular; // Field 14 - private CraftResource _resource; + [SerializableField(14, "private", "private")] + private CraftResource _rawResource; + + [SerializableFieldSaveFlag(14)] + private bool ShouldSerializeResource() => _rawResource != DefaultResource; // Field 15 private int _armorBase = -1; @@ -163,8 +167,8 @@ namespace Server.Items { _crafter = null; - _resource = DefaultResource; - Hue = CraftResources.GetHue(_resource); + _rawResource = DefaultResource; + Hue = CraftResources.GetHue(_rawResource); _hitPoints = _maxHitPoints = Utility.RandomMinMax(InitMinHits, InitMaxHits); @@ -268,41 +272,31 @@ namespace Server.Items [SerializableFieldSaveFlag(13)] private bool ShouldSerializeProtectionLevel() => _protection != ArmorProtectionLevel.Regular; - [SerializableField(14)] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { - get => _resource; + get => _rawResource; set { - if (_resource != value) + if (_rawResource != value) { - if (!World.Loading) - { - UnscaleDurability(); - } + UnscaleDurability(); - - _resource = value; + RawResource = value; if (CraftItem.RetainsColor(GetType())) { - Hue = CraftResources.GetHue(_resource); + Hue = CraftResources.GetHue(_rawResource); } Invalidate(); (Parent as Mobile)?.UpdateResistances(); - if (!World.Loading) - { - ScaleDurability(); - } + + ScaleDurability(); } } } - [SerializableFieldSaveFlag(14)] - private bool ShouldSerializeResource() => _resource != DefaultResource; - [SerializableFieldDefault(14)] private CraftResource ResourceDefaultValue() => DefaultResource; @@ -339,7 +333,7 @@ namespace Server.Items ar += 10 + 5 * (int)_protection; } - ar += _resource switch + ar += _rawResource switch { CraftResource.DullCopper => 2, CraftResource.ShadowIron => 4, @@ -557,7 +551,6 @@ namespace Server.Items public virtual CraftResource DefaultResource => CraftResource.Iron; - [Hue] [CommandProperty(AccessLevel.GameMaster)] public override int Hue @@ -681,7 +674,7 @@ namespace Server.Items { try { - var res = CraftResources.GetInfo(_resource).ResourceTypes[0].CreateInstance(); + var res = CraftResources.GetInfo(_rawResource).ResourceTypes[0].CreateInstance(); ScissorHelper(from, res, PlayerConstructed ? item.Resources[0].Amount / 2 : 1); return true; @@ -887,7 +880,7 @@ namespace Server.Items } public CraftAttributeInfo GetResourceAttrs() => - CraftResources.GetInfo(_resource)?.AttributeInfo ?? CraftAttributeInfo.Blank; + CraftResources.GetInfo(_rawResource)?.AttributeInfo ?? CraftAttributeInfo.Blank; public int GetProtOffset() { @@ -919,7 +912,7 @@ namespace Server.Items { bonus += ArmorAttributes.DurabilityBonus; - var resInfo = CraftResources.GetInfo(_resource); + var resInfo = CraftResources.GetInfo(_rawResource); CraftAttributeInfo attrInfo = null; if (resInfo != null) @@ -992,7 +985,7 @@ namespace Server.Items var v = ArmorAttributes.LowerStatReq; - var info = CraftResources.GetInfo(_resource); + var info = CraftResources.GetInfo(_rawResource); var attrInfo = info?.AttributeInfo; @@ -1051,9 +1044,9 @@ namespace Server.Items SkillBonuses.AddTo(m); } - if (_resource == CraftResource.None) + if (_rawResource == CraftResource.None) { - _resource = DefaultResource; + _rawResource = DefaultResource; } var strBonus = ComputeStatBonus(StatType.Str); @@ -1164,7 +1157,7 @@ namespace Server.Items if (GetSaveFlag(flags, OldSaveFlag.Resource)) { - _resource = (CraftResource)reader.ReadEncodedInt(); + _rawResource = (CraftResource)reader.ReadEncodedInt(); } if (GetSaveFlag(flags, OldSaveFlag.BaseArmor)) @@ -1370,7 +1363,7 @@ namespace Server.Items public override void AddNameProperty(ObjectPropertyList list) { - var oreType = _resource switch + var oreType = _rawResource switch { CraftResource.DullCopper => 1053108, CraftResource.ShadowIron => 1053107, @@ -1430,7 +1423,7 @@ namespace Server.Items return Attributes.SpellChanneling != 0; } - public virtual int GetLuckBonus() => CraftResources.GetInfo(_resource)?.AttributeInfo?.ArmorLuck ?? 0; + public virtual int GetLuckBonus() => CraftResources.GetInfo(_rawResource)?.AttributeInfo?.ArmorLuck ?? 0; public override void GetProperties(ObjectPropertyList list) { diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs new file mode 100644 index 000000000..819d4e44e --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.GargishClothArms", "Server.Items.GargishClothArmsArmor")] + public partial class GargishClothArmsType1 : BaseArmor + { + [Constructible] + public GargishClothArmsType1() : base(0x404) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 20; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs new file mode 100644 index 000000000..c46c5ebf0 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.FemaleGargishClothArms", "Server.Items.FemaleGargishClothArmsArmor")] + public partial class GargishClothArmsType2 : BaseArmor + { + [Constructible] + public GargishClothArmsType2() : base(0x403) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 20; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs new file mode 100644 index 000000000..4785b91bb --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.GargishClothChest", "Server.Items.GargishClothChestArmor")] + public partial class GargishClothChestType1 : BaseArmor + { + [Constructible] + public GargishClothChestType1() : base(0x406) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 25; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs new file mode 100644 index 000000000..a1ba296a2 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.FemaleGargishClothChest", "Server.Items.FemaleGargishClothChestArmor")] + public partial class GargishClothChestType2 : BaseArmor + { + [Constructible] + public GargishClothChestType2() : base(0x405) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 25; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs new file mode 100644 index 000000000..b3b8c1093 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.GargishClothKilt", "Server.Items.GargishClothKiltArmor")] + public partial class GargishClothKiltType1 : BaseArmor + { + [Constructible] + public GargishClothKiltType1() : base(0x408) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 20; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs new file mode 100644 index 000000000..e18718dc1 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.GargishClothKilt", "Server.Items.GargishClothKiltArmor")] + public partial class GargishClothKiltType2 : BaseArmor + { + [Constructible] + public GargishClothKiltType2() : base(0x407) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 20; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs new file mode 100644 index 000000000..45084fbc3 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.GargishClothLegs", "Server.Items.GargishClothLegsArmor")] + public partial class GargishClothLegsType1 : BaseArmor + { + [Constructible] + public GargishClothLegsType1() : base(0x40A) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 20; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs new file mode 100644 index 000000000..bad26d399 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Serializable(0)] + [TypeAlias("Server.Items.FemaleGargishClothLegs", "Server.Items.FemaleGargishClothLegsArmor")] + public partial class GargishClothLegsType2 : BaseArmor + { + [Constructible] + public GargishClothLegsType2() : base(0x409) => Weight = 2.0; + + public override int BasePhysicalResistance => 5; + public override int BaseFireResistance => 7; + public override int BaseColdResistance => 6; + public override int BasePoisonResistance => 6; + public override int BaseEnergyResistance => 6; + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + public override int AosStrReq => 20; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Cloth; + public override CraftResource DefaultResource => CraftResource.None; + public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs new file mode 100644 index 000000000..e9223b58f --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.GargishStoneArms")] + public partial class GargishStoneArmsType1 : BaseArmor + { + [Constructible] + public GargishStoneArmsType1() : base(0x284) => Weight = 10.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs new file mode 100644 index 000000000..ec9a5cefc --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.FemaleGargishStoneArms")] + public partial class GargishStoneArmsType2 : BaseArmor + { + [Constructible] + public GargishStoneArmsType2() : base(0x283) => Weight = 10.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs new file mode 100644 index 000000000..8aafab468 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.GargishStoneChest")] + public partial class GargishStoneChestType1 : BaseArmor + { + [Constructible] + public GargishStoneChestType1() : base(0x286) => Weight = 15.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs new file mode 100644 index 000000000..78fa08650 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs @@ -0,0 +1,27 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.FemaleGargishStoneChest")] + public partial class GargishStoneChestType2 : BaseArmor + { + [Constructible] + public GargishStoneChestType2() : base(0x285) => Weight = 15.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs new file mode 100644 index 000000000..cf826661f --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.GargishStoneKilt")] + public partial class GargishStoneKiltType1 : BaseArmor + { + [Constructible] + public GargishStoneKiltType1() : base(0x288) => Weight = 10.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs new file mode 100644 index 000000000..7ca6f2363 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs @@ -0,0 +1,27 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.FemaleGargishStoneKilt")] + public partial class GargishStoneKiltType2 : BaseArmor + { + [Constructible] + public GargishStoneKiltType2() : base(0x287) => Weight = 10.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs new file mode 100644 index 000000000..3974d969a --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.GargishStoneLegs")] + public partial class GargishStoneLegsType1 : BaseArmor + { + [Constructible] + public GargishStoneLegsType1() : base(0x28A) => Weight = 15.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs new file mode 100644 index 000000000..90707fc02 --- /dev/null +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs @@ -0,0 +1,27 @@ +namespace Server.Items +{ + [Serializable(0, false)] + [TypeAlias("Server.Items.FemaleGargishStoneLegs")] + public partial class GargishStoneLegsType2 : BaseArmor + { + [Constructible] + public GargishStoneLegsType2() : base(0x289) => Weight = 15.0; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 4; + public override int BasePoisonResistance => 8; + public override int BaseEnergyResistance => 6; + + public override int InitMinHits => 40; + public override int InitMaxHits => 50; + + public override int AosStrReq => 40; + public override int OldStrReq => 40; + + public override int ArmorBase => 40; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Stone; + } +} diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.cs b/Projects/UOContent/Items/Clothing/BaseClothing.cs index 341a60e77..251d86ad0 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.cs @@ -25,8 +25,11 @@ namespace Server.Items [Serializable(6, false)] public abstract partial class BaseClothing : Item, IDyable, IScissorable, IFactionItem, ICraftable, IWearableDurability { - // Field 0 - private CraftResource _resource; + [SerializableField(0, "private", "private")] + private CraftResource _rawResource; + + [SerializableFieldSaveFlag(0)] + private bool ShouldSerializeResource() => _rawResource != DefaultResource; [SerializableField(1, setter: "private")] [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster, canModify: true)]")] @@ -113,7 +116,7 @@ namespace Server.Items Layer = layer; Hue = hue; - _resource = DefaultResource; + _rawResource = DefaultResource; _hitPoints = _maxHitPoints = Utility.RandomMinMax(InitMinHits, InitMaxHits); @@ -123,23 +126,19 @@ namespace Server.Items Resistances = new AosElementAttributes(this); } - [SerializableField(0)] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { - get => _resource; + get => _rawResource; set { - _resource = value; - Hue = CraftResources.GetHue(_resource); + RawResource = value; + Hue = CraftResources.GetHue(_rawResource); InvalidateProperties(); this.MarkDirty(); } } - [SerializableFieldSaveFlag(0)] - private bool ShouldSerializeResource() => _resource != DefaultResource; - [SerializableField(10)] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement @@ -275,7 +274,7 @@ namespace Server.Items { try { - var info = CraftResources.GetInfo(_resource); + var info = CraftResources.GetInfo(_rawResource); var resourceType = info.ResourceTypes?[0] ?? item.Resources[0].ItemType; @@ -625,7 +624,7 @@ namespace Server.Items public override void OnAfterDuped(Item newItem) { - if (!(newItem is BaseClothing clothing)) + if (newItem is not BaseClothing clothing) { return; } @@ -658,7 +657,7 @@ namespace Server.Items public override void AddNameProperty(ObjectPropertyList list) { - var oreType = _resource switch + var oreType = _rawResource switch { CraftResource.DullCopper => 1053108, CraftResource.ShadowIron => 1053107, @@ -971,11 +970,11 @@ namespace Server.Items if (GetSaveFlag(flags, OldSaveFlag.Resource)) { - _resource = (CraftResource)reader.ReadEncodedInt(); + _rawResource = (CraftResource)reader.ReadEncodedInt(); } else { - _resource = DefaultResource; + _rawResource = DefaultResource; } Attributes = new AosAttributes(this); diff --git a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs index 8e6bb6411..a29606366 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs @@ -1,27 +1,11 @@ namespace Server.Items { - [Furniture, Flippable(0xB2D, 0xB2C)] - public class WoodenBench : Item + [Furniture] + [Serializable(0, false)] + [Flippable(0xB2D, 0xB2C)] + public partial class WoodenBench : Item { [Constructible] public WoodenBench() : base(0xB2D) => Weight = 6; - - public WoodenBench(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(); - } } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs index 6a1060800..67dfd1878 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs @@ -1,226 +1,78 @@ namespace Server.Items { - [Furniture, Flippable(0xB4F, 0xB4E, 0xB50, 0xB51)] - public class FancyWoodenChairCushion : Item + [Furniture] + [Flippable(0xB4F, 0xB4E, 0xB50, 0xB51)] + [Serializable(0, false)] + public partial class FancyWoodenChairCushion : Item { [Constructible] public FancyWoodenChairCushion() : base(0xB4F) => Weight = 20.0; - - public FancyWoodenChairCushion(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(); - - if (Weight == 6.0) - { - Weight = 20.0; - } - } } - [Furniture, Flippable(0xB53, 0xB52, 0xB54, 0xB55)] - public class WoodenChairCushion : Item + [Furniture] + [Flippable(0xB53, 0xB52, 0xB54, 0xB55)] + [Serializable(0, false)] + public partial class WoodenChairCushion : Item { [Constructible] public WoodenChairCushion() : base(0xB53) => Weight = 20.0; - - public WoodenChairCushion(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(); - - if (Weight == 6.0) - { - Weight = 20.0; - } - } } - [Furniture, Flippable(0xB57, 0xB56, 0xB59, 0xB58)] - public class WoodenChair : Item + [Furniture] + [Flippable(0xB57, 0xB56, 0xB59, 0xB58)] + [Serializable(0, false)] + public partial class WoodenChair : Item { [Constructible] public WoodenChair() : base(0xB57) => Weight = 20.0; - - public WoodenChair(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(); - - if (Weight == 6.0) - { - Weight = 20.0; - } - } } - [Furniture, Flippable(0xB5B, 0xB5A, 0xB5C, 0xB5D)] - public class BambooChair : Item + [Furniture] + [Flippable(0xB5B, 0xB5A, 0xB5C, 0xB5D)] + [Serializable(0, false)] + public partial class BambooChair : Item { [Constructible] public BambooChair() : base(0xB5B) => Weight = 20.0; - - public BambooChair(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(); - - if (Weight == 6.0) - { - Weight = 20.0; - } - } } - [DynamicFliping, Flippable(0x1218, 0x1219, 0x121A, 0x121B)] - public class StoneChair : Item + [DynamicFlipping] + [Flippable(0x1218, 0x1219, 0x121A, 0x121B)] + [Serializable(0, false)] + public partial class StoneChair : Item { [Constructible] public StoneChair() : base(0x1218) => Weight = 20; - - public StoneChair(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(); - } } - [DynamicFliping, Flippable(0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6)] - public class OrnateElvenChair : Item + [DynamicFlipping] + [Flippable(0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6)] + [Serializable(0)] + public partial class OrnateElvenChair : Item { [Constructible] public OrnateElvenChair() : base(0x2DE3) => Weight = 1.0; - - public OrnateElvenChair(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(); - } } - [DynamicFliping, Flippable(0x2DEB, 0x2DEC, 0x2DED, 0x2DEE)] - public class BigElvenChair : Item + [DynamicFlipping] + [Flippable(0x2DEB, 0x2DEC, 0x2DED, 0x2DEE)] + [Serializable(0)] + public partial class BigElvenChair : Item { [Constructible] public BigElvenChair() : base(0x2DEB) { } - - public BigElvenChair(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(); - } } - [DynamicFliping, Flippable(0x2DF5, 0x2DF6)] - public class ElvenReadingChair : Item + [DynamicFlipping] + [Flippable(0x2DF5, 0x2DF6)] + [Serializable(0)] + public partial class ElvenReadingChair : Item { [Constructible] public ElvenReadingChair() : base(0x2DF5) { } - - public ElvenReadingChair(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(); - } } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Stools.cs b/Projects/UOContent/Items/Construction/Chairs/Stools.cs index b52ed63ab..a9e76afc6 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Stools.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Stools.cs @@ -1,62 +1,18 @@ namespace Server.Items { [Furniture] - public class Stool : Item + [Serializable(0, false)] + public partial class Stool : Item { [Constructible] public Stool() : base(0xA2A) => Weight = 10.0; - - public Stool(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(); - - if (Weight == 6.0) - { - Weight = 10.0; - } - } } [Furniture] - public class FootStool : Item + [Serializable(0, false)] + public partial class FootStool : Item { [Constructible] public FootStool() : base(0xB5E) => Weight = 6.0; - - public FootStool(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(); - - if (Weight == 6.0) - { - Weight = 10.0; - } - } } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs index af6498720..2478489ab 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs @@ -1,62 +1,20 @@ namespace Server.Items { - [Furniture, Flippable(0xB32, 0xB33)] - public class Throne : Item + [Furniture] + [Flippable(0xB32, 0xB33)] + [Serializable(0, false)] + public partial class Throne : Item { [Constructible] public Throne() : base(0xB33) => Weight = 1.0; - - public Throne(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(); - - if (Weight == 6.0) - { - Weight = 1.0; - } - } } - [Furniture, Flippable(0xB2E, 0xB2F, 0xB31, 0xB30)] - public class WoodenThrone : Item + [Furniture] + [Flippable(0xB2E, 0xB2F, 0xB31, 0xB30)] + [Serializable(0, false)] + public partial class WoodenThrone : Item { [Constructible] public WoodenThrone() : base(0xB2E) => Weight = 15.0; - - public WoodenThrone(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(); - - if (Weight == 6.0) - { - Weight = 15.0; - } - } } } diff --git a/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs b/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs index 17da20500..286bd0a98 100644 --- a/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs +++ b/Projects/UOContent/Items/Construction/Decorative/DecorativeShield.cs @@ -1,377 +1,122 @@ namespace Server.Items { [Flippable(0x156C, 0x156D)] - public class DecorativeShield1 : Item + [Serializable(0, false)] + public partial class DecorativeShield1 : Item { [Constructible] public DecorativeShield1() : base(0x156C) => Movable = false; - - public DecorativeShield1(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(0x156E, 0x156F)] - public class DecorativeShield2 : Item + [Serializable(0, false)] + public partial class DecorativeShield2 : Item { [Constructible] public DecorativeShield2() : base(0x156E) => Movable = false; - - public DecorativeShield2(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(0x1570, 0x1571)] - public class DecorativeShield3 : Item + [Serializable(0, false)] + public partial class DecorativeShield3 : Item { [Constructible] public DecorativeShield3() : base(0x1570) => Movable = false; - - public DecorativeShield3(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(0x1572, 0x1573)] - public class DecorativeShield4 : Item + [Serializable(0, false)] + public partial class DecorativeShield4 : Item { [Constructible] public DecorativeShield4() : base(0x1572) => Movable = false; - - public DecorativeShield4(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(0x1574, 0x1575)] - public class DecorativeShield5 : Item + [Serializable(0, false)] + public partial class DecorativeShield5 : Item { [Constructible] public DecorativeShield5() : base(0x1574) => Movable = false; - - public DecorativeShield5(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(0x1576, 0x1577)] - public class DecorativeShield6 : Item + [Serializable(0, false)] + public partial class DecorativeShield6 : Item { [Constructible] public DecorativeShield6() : base(0x1576) => Movable = false; - - public DecorativeShield6(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(0x1578, 0x1579)] - public class DecorativeShield7 : Item + [Serializable(0, false)] + public partial class DecorativeShield7 : Item { [Constructible] public DecorativeShield7() : base(0x1578) => Movable = false; - - public DecorativeShield7(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(0x157A, 0x157B)] - public class DecorativeShield8 : Item + [Serializable(0, false)] + public partial class DecorativeShield8 : Item { [Constructible] public DecorativeShield8() : base(0x157A) => Movable = false; - - public DecorativeShield8(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(0x157C, 0x157D)] - public class DecorativeShield9 : Item + [Serializable(0, false)] + public partial class DecorativeShield9 : Item { [Constructible] public DecorativeShield9() : base(0x157C) => Movable = false; - - public DecorativeShield9(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(0x157E, 0x157F)] - public class DecorativeShield10 : Item + [Serializable(0, false)] + public partial class DecorativeShield10 : Item { [Constructible] public DecorativeShield10() : base(0x157E) => Movable = false; - - public DecorativeShield10(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(0x1580, 0x1581)] - public class DecorativeShield11 : Item + [Serializable(0, false)] + public partial class DecorativeShield11 : Item { [Constructible] public DecorativeShield11() : base(0x1580) => Movable = false; - - public DecorativeShield11(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(0x1582, 0x1583, 0x1634, 0x1635)] - public class DecorativeShieldSword1North : Item + [Serializable(0, false)] + public partial class DecorativeShieldSword1North : Item { [Constructible] public DecorativeShieldSword1North() : base(Utility.Random(0x1582, 2)) => Movable = false; - - public DecorativeShieldSword1North(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(0x1634, 0x1635, 0x1582, 0x1583)] - public class DecorativeShieldSword1West : Item + [Serializable(0, false)] + public partial class DecorativeShieldSword1West : Item { [Constructible] public DecorativeShieldSword1West() : base(Utility.Random(0x1634, 2)) => Movable = false; - - public DecorativeShieldSword1West(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(0x1584, 0x1585, 0x1636, 0x1637)] - public class DecorativeShieldSword2North : Item + [Serializable(0, false)] + public partial class DecorativeShieldSword2North : Item { [Constructible] public DecorativeShieldSword2North() : base(Utility.Random(0x1584, 2)) => Movable = false; - - public DecorativeShieldSword2North(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(0x1636, 0x1637, 0x1584, 0x1585)] - public class DecorativeShieldSword2West : Item + [Serializable(0, false)] + public partial class DecorativeShieldSword2West : Item { [Constructible] public DecorativeShieldSword2West() : base(Utility.Random(0x1636, 2)) => Movable = false; - - public DecorativeShieldSword2West(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/Construction/Decorative/DecorativeWeapon.cs b/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs index 16814d64a..4b3d9f087 100644 --- a/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs +++ b/Projects/UOContent/Items/Construction/Decorative/DecorativeWeapon.cs @@ -1,134 +1,63 @@ namespace Server.Items { [Flippable(0x155E, 0x155F, 0x155C, 0x155D)] - public class DecorativeBowWest : Item + [Serializable(0, false)] + public partial class DecorativeBowWest : Item { [Constructible] public DecorativeBowWest() : base(Utility.Random(0x155E, 2)) => Movable = false; - - public DecorativeBowWest(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(0x155C, 0x155D, 0x155E, 0x155F)] - public class DecorativeBowNorth : Item + [Serializable(0, false)] + public partial class DecorativeBowNorth : Item { [Constructible] public DecorativeBowNorth() : base(Utility.Random(0x155C, 2)) => Movable = false; - - public DecorativeBowNorth(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(0x1560, 0x1561, 0x1562, 0x1563)] - public class DecorativeAxeNorth : Item + [Serializable(0, false)] + public partial class DecorativeAxeNorth : Item { [Constructible] public DecorativeAxeNorth() : base(Utility.Random(0x1560, 2)) => Movable = false; - - public DecorativeAxeNorth(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(0x1562, 0x1563, 0x1560, 0x1561)] - public class DecorativeAxeWest : Item + [Serializable(0, false)] + public partial class DecorativeAxeWest : Item { [Constructible] public DecorativeAxeWest() : base(Utility.Random(0x1562, 2)) => Movable = false; - - public DecorativeAxeWest(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 DecorativeSwordNorth : Item + [Serializable(0, false)] + public partial class DecorativeSwordNorth : Item { - private InternalItem m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private InternalItem _item; [Constructible] public DecorativeSwordNorth() : base(0x1565) { Movable = false; - - m_Item = new InternalItem(this); - } - - public DecorativeSwordNorth(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -136,55 +65,35 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private DecorativeSwordNorth m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private DecorativeSwordNorth _item; public InternalItem(DecorativeSwordNorth item) : base(0x1564) { Movable = true; - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -192,58 +101,37 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class DecorativeSwordWest : Item + [Serializable(0)] + public partial class DecorativeSwordWest : Item { - private InternalItem m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private InternalItem _item; [Constructible] public DecorativeSwordWest() : base(0x1566) { Movable = false; - - m_Item = new InternalItem(this); - } - - public DecorativeSwordWest(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 1, Z); + _item.Location = new Point3D(X, Y - 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -251,55 +139,34 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private DecorativeSwordWest m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private DecorativeSwordWest _item; public InternalItem(DecorativeSwordWest item) : base(0x1567) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 1, Z); + _item.Location = new Point3D(X, Y + 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -307,58 +174,37 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class DecorativeDAxeNorth : Item + [Serializable(0)] + public partial class DecorativeDAxeNorth : Item { - private InternalItem m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private InternalItem _item; [Constructible] public DecorativeDAxeNorth() : base(0x1569) { Movable = false; - - m_Item = new InternalItem(this); - } - - public DecorativeDAxeNorth(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -366,55 +212,35 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private DecorativeDAxeNorth m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private DecorativeDAxeNorth _item; public InternalItem(DecorativeDAxeNorth item) : base(0x1568) { Movable = true; - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -422,58 +248,38 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class DecorativeDAxeWest : Item + [Serializable(0)] + public partial class DecorativeDAxeWest : Item { - private InternalItem m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private InternalItem _item; [Constructible] public DecorativeDAxeWest() : base(0x156A) { Movable = false; - m_Item = new InternalItem(this); - } - - public DecorativeDAxeWest(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 1, Z); + _item.Location = new Point3D(X, Y - 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -481,55 +287,35 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private DecorativeDAxeWest m_Item; + [SerializableField(0, getter: "private", setter: "private")] + private DecorativeDAxeWest _item; public InternalItem(DecorativeDAxeWest item) : base(0x156B) { Movable = true; - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 1, Z); + _item.Location = new Point3D(X, Y + 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } @@ -537,25 +323,7 @@ namespace Server.Items { base.OnAfterDelete(); - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } diff --git a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs new file mode 100644 index 000000000..e522d12ed --- /dev/null +++ b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs @@ -0,0 +1,12 @@ +namespace Server.Items +{ + [Furniture] + [Serializable(0)] + public partial class GiantReplicaAcorn : Item + { + [Constructible] + public GiantReplicaAcorn() : base(0x2D4A) => Weight = 1.0; + + public override int LabelNumber => 1072889; // giant replica acorn + } +} diff --git a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs new file mode 100644 index 000000000..d73518208 --- /dev/null +++ b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs @@ -0,0 +1,12 @@ +namespace Server.Items +{ + [Flippable(0x3158, 0x3159)] + [Serializable(0)] + public partial class MountedDreadHorn : Item + { + [Constructible] + public MountedDreadHorn() : base(0x3158) => Weight = 1.0; + + public override int LabelNumber => 1074464; // mounted Dread Horn + } +} diff --git a/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs b/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs index 688bcd52b..6695b94f5 100644 --- a/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs +++ b/Projects/UOContent/Items/Construction/Decorative/PaintingPortraits.cs @@ -1,176 +1,57 @@ namespace Server.Items { - public class LargePainting : Item + [Serializable(0, false)] + public partial class LargePainting : Item { [Constructible] public LargePainting() : base(0x0EA0) => Movable = false; - - public LargePainting(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(); - } } + [Serializable(0, false)] [Flippable(0x0E9F, 0x0EC8)] - public class WomanPortrait1 : Item + public partial class WomanPortrait1 : Item { [Constructible] public WomanPortrait1() : base(0x0E9F) => Movable = false; - - public WomanPortrait1(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(); - } } + [Serializable(0, false)] [Flippable(0x0EE7, 0x0EC9)] - public class WomanPortrait2 : Item + public partial class WomanPortrait2 : Item { [Constructible] public WomanPortrait2() : base(0x0EE7) => Movable = false; - - public WomanPortrait2(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(); - } } + [Serializable(0, false)] [Flippable(0x0EA2, 0x0EA1)] - public class ManPortrait1 : Item + public partial class ManPortrait1 : Item { [Constructible] public ManPortrait1() : base(0x0EA2) => Movable = false; - - public ManPortrait1(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(); - } } + [Serializable(0, false)] [Flippable(0x0EA3, 0x0EA4)] - public class ManPortrait2 : Item + public partial class ManPortrait2 : Item { [Constructible] public ManPortrait2() : base(0x0EA3) => Movable = false; - - public ManPortrait2(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(); - } } + [Serializable(0, false)] [Flippable(0x0EA6, 0x0EA5)] - public class LadyPortrait1 : Item + public partial class LadyPortrait1 : Item { [Constructible] public LadyPortrait1() : base(0x0EA6) => Movable = false; - - public LadyPortrait1(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(); - } } + [Serializable(0, false)] [Flippable(0x0EA7, 0x0EA8)] - public class LadyPortrait2 : Item + public partial class LadyPortrait2 : Item { [Constructible] public LadyPortrait2() : base(0x0EA7) => Movable = false; - - public LadyPortrait2(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/Construction/Decorative/Tapestry.cs b/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs index d9d474220..1a5868096 100644 --- a/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs +++ b/Projects/UOContent/Items/Construction/Decorative/Tapestry.cs @@ -1,1266 +1,782 @@ namespace Server.Items { - public class Tapestry1N : Item + [Serializable(0, false)] + public partial class Tapestry1N : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry1N() : base(0xEAA) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry1N(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry1N m_Item; + [SerializableField(0, "private", "private")] + private Tapestry1N _item; public InternalItem(Tapestry1N item) : base(0xEAB) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry2N : Item + [Serializable(0, false)] + public partial class Tapestry2N : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry2N() : base(0xEAC) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry2N(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry2N m_Item; + [SerializableField(0, "private", "private")] + private Tapestry2N _item; public InternalItem(Tapestry2N item) : base(0xEAD) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry2W : Item + [Serializable(0, false)] + public partial class Tapestry2W : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry2W() : base(0xEAE) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry2W(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 1, Z); + _item.Location = new Point3D(X, Y - 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry2W m_Item; + [SerializableField(0, "private", "private")] + private Tapestry2W _item; public InternalItem(Tapestry2W item) : base(0xEAF) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 1, Z); + _item.Location = new Point3D(X, Y + 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry3N : Item + [Serializable(0, false)] + public partial class Tapestry3N : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry3N() : base(0xFD6) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry3N(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 2, Y, Z); + _item.Location = new Point3D(X - 2, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry3N m_Item; + [SerializableField(0, "private", "private")] + private Tapestry3N _item; public InternalItem(Tapestry3N item) : base(0xFD5) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 2, Y, Z); + _item.Location = new Point3D(X + 2, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry3W : Item + [Serializable(0, false)] + public partial class Tapestry3W : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry3W() : base(0xFD7) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry3W(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 2, Z); + _item.Location = new Point3D(X, Y - 2, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry3W m_Item; + [SerializableField(0, "private", "private")] + private Tapestry3W _item; public InternalItem(Tapestry3W item) : base(0xFD8) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 2, Z); + _item.Location = new Point3D(X, Y + 2, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry4N : Item + [Serializable(0, false)] + public partial class Tapestry4N : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry4N() : base(0xFDA) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry4N(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry4N m_Item; + [SerializableField(0, "private", "private")] + private Tapestry4N _item; public InternalItem(Tapestry4N item) : base(0xFD9) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry4W : Item + [Serializable(0, false)] + public partial class Tapestry4W : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry4W() : base(0xFDB) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry4W(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 1, Z); + _item.Location = new Point3D(X, Y - 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry4W m_Item; + [SerializableField(0, "private", "private")] + private Tapestry4W _item; public InternalItem(Tapestry4W item) : base(0xFDC) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 1, Z); + _item.Location = new Point3D(X, Y + 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry5N : Item + [Serializable(0, false)] + public partial class Tapestry5N : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry5N() : base(0xFDE) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry5N(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry5N m_Item; + [SerializableField(0, "private", "private")] + private Tapestry5N _item; public InternalItem(Tapestry5N item) : base(0xFDD) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry5W : Item + [Serializable(0, false)] + public partial class Tapestry5W : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry5W() : base(0xFDF) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry5W(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 1, Z); + _item.Location = new Point3D(X, Y - 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry5W m_Item; + [SerializableField(0, "private", "private")] + private Tapestry5W _item; public InternalItem(Tapestry5W item) : base(0xFE0) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 1, Z); + _item.Location = new Point3D(X, Y + 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry6N : Item + [Serializable(0, false)] + public partial class Tapestry6N : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry6N() : base(0xFE2) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry6N(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X - 1, Y, Z); + _item.Location = new Point3D(X - 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry6N m_Item; + [SerializableField(0, "private", "private")] + private Tapestry6N _item; public InternalItem(Tapestry6N item) : base(0xFE1) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X + 1, Y, Z); + _item.Location = new Point3D(X + 1, Y, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } - public class Tapestry6W : Item + [Serializable(0, false)] + public partial class Tapestry6W : Item { - private InternalItem m_Item; + [SerializableField(0, "private", "private")] + private InternalItem _item; [Constructible] public Tapestry6W() : base(0xFE3) { Movable = false; - - m_Item = new InternalItem(this); - } - - public Tapestry6W(Serial serial) : base(serial) - { + _item = new InternalItem(this); } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y - 1, Z); + _item.Location = new Point3D(X, Y - 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); + _item?.Delete(); } - public override void Serialize(IGenericWriter writer) + [Serializable(0, false)] + private partial class InternalItem : Item { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); - } - - private class InternalItem : Item - { - private Tapestry6W m_Item; + [SerializableField(0, "private", "private")] + private Tapestry6W _item; public InternalItem(Tapestry6W item) : base(0xFE4) { Movable = true; - - m_Item = item; - } - - public InternalItem(Serial serial) : base(serial) - { + _item = item; } public override void OnLocationChange(Point3D oldLocation) { - if (m_Item != null) + if (_item != null) { - m_Item.Location = new Point3D(X, Y + 1, Z); + _item.Location = new Point3D(X, Y + 1, Z); } } public override void OnMapChange() { - if (m_Item != null) + if (_item != null) { - m_Item.Map = Map; + _item.Map = Map; } } public override void OnAfterDelete() { base.OnAfterDelete(); - - m_Item?.Delete(); - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - - writer.Write(m_Item); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - m_Item = reader.ReadEntity(); + _item?.Delete(); } } } diff --git a/Projects/UOContent/Items/Construction/Misc/MusicStand.cs b/Projects/UOContent/Items/Construction/Misc/MusicStand.cs index a45042e65..af0cbda0a 100644 --- a/Projects/UOContent/Items/Construction/Misc/MusicStand.cs +++ b/Projects/UOContent/Items/Construction/Misc/MusicStand.cs @@ -1,6 +1,7 @@ namespace Server.Items { - [Furniture, Flippable(0xEBB, 0xEBC)] + [Furniture] + [Flippable(0xEBB, 0xEBC)] public class TallMusicStand : Item { [Constructible] @@ -30,7 +31,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xEB6, 0xEB8)] + [Furniture] + [Flippable(0xEB6, 0xEB8)] public class ShortMusicStand : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Misc/Screens.cs b/Projects/UOContent/Items/Construction/Misc/Screens.cs index f0a827085..3f6f0a506 100644 --- a/Projects/UOContent/Items/Construction/Misc/Screens.cs +++ b/Projects/UOContent/Items/Construction/Misc/Screens.cs @@ -1,6 +1,7 @@ namespace Server.Items { - [Furniture, Flippable(0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4)] + [Furniture] + [Flippable(0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4)] public class BambooScreen : Item { [Constructible] @@ -25,7 +26,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x24CB, 0x24CC, 0x24CD, 0x24CE, 0x24CF)] + [Furniture] + [Flippable(0x24CB, 0x24CC, 0x24CD, 0x24CE, 0x24CF)] public class ShojiScreen : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs new file mode 100644 index 000000000..df9620907 --- /dev/null +++ b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs @@ -0,0 +1,11 @@ +namespace Server.Items +{ + [Furniture] + [Serializable(0)] + [Flippable(0x2DDD, 0x2DDE)] + public partial class ElvenPodium : Item + { + [Constructible] + public ElvenPodium() : base(0x2DDD) => Weight = 2.0; + } +} diff --git a/Projects/UOContent/Items/Construction/Tables/Tables.cs b/Projects/UOContent/Items/Construction/Tables/Tables.cs index ae94aeb69..eef852237 100644 --- a/Projects/UOContent/Items/Construction/Tables/Tables.cs +++ b/Projects/UOContent/Items/Construction/Tables/Tables.cs @@ -50,7 +50,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xB90, 0xB7D)] + [Furniture] + [Flippable(0xB90, 0xB7D)] public class LargeTable : Item { [Constructible] @@ -80,7 +81,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xB35, 0xB34)] + [Furniture] + [Flippable(0xB35, 0xB34)] public class Nightstand : Item { [Constructible] @@ -110,7 +112,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xB8F, 0xB7C)] + [Furniture] + [Flippable(0xB8F, 0xB7C)] public class YewWoodTable : Item { [Constructible] diff --git a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs index c202fcf32..a711392b7 100644 --- a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs +++ b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs @@ -1,6 +1,7 @@ namespace Server.Items { - [Furniture, Flippable(0xB4A, 0xB49, 0xB4B, 0xB4C)] + [Furniture] + [Flippable(0xB4A, 0xB49, 0xB4B, 0xB4C)] public class WritingTable : Item { [Constructible] diff --git a/Projects/UOContent/Items/Containers/Container.cs b/Projects/UOContent/Items/Containers/Container.cs index d2d57ef38..f9c92bbcc 100644 --- a/Projects/UOContent/Items/Containers/Container.cs +++ b/Projects/UOContent/Items/Containers/Container.cs @@ -16,28 +16,9 @@ namespace Server.Items { } - public override int DefaultMaxWeight - { - get - { - if (IsSecure) - { - return 0; - } + public override int DefaultMaxWeight => IsSecure ? 0 : base.DefaultMaxWeight; - return base.DefaultMaxWeight; - } - } - - public override bool IsAccessibleTo(Mobile m) - { - if (!BaseHouse.CheckAccessible(m, this)) - { - return false; - } - - return base.IsAccessibleTo(m); - } + 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) { @@ -597,7 +578,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x9AA, 0xE7D)] + [Furniture] + [Flippable(0x9AA, 0xE7D)] public class WoodenBox : LockableContainer { [Constructible] @@ -622,7 +604,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x9A9, 0xE7E)] + [Furniture] + [Flippable(0x9A9, 0xE7E)] public class SmallCrate : LockableContainer { [Constructible] @@ -652,7 +635,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xE3F, 0xE3E)] + [Furniture] + [Flippable(0xE3F, 0xE3E)] public class MediumCrate : LockableContainer { [Constructible] @@ -682,7 +666,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xE3D, 0xE3C)] + [Furniture] + [Flippable(0xE3D, 0xE3C)] public class LargeCrate : LockableContainer { [Constructible] @@ -712,7 +697,7 @@ namespace Server.Items } } - [DynamicFliping, Flippable(0x9A8, 0xE80)] + [DynamicFlipping, Flippable(0x9A8, 0xE80)] public class MetalBox : LockableContainer { [Constructible] @@ -744,7 +729,7 @@ namespace Server.Items } } - [DynamicFliping, Flippable(0x9AB, 0xE7C)] + [DynamicFlipping, Flippable(0x9AB, 0xE7C)] public class MetalChest : LockableContainer { [Constructible] @@ -776,7 +761,7 @@ namespace Server.Items } } - [DynamicFliping, Flippable(0xE41, 0xE40)] + [DynamicFlipping, Flippable(0xE41, 0xE40)] public class MetalGoldenChest : LockableContainer { [Constructible] @@ -808,7 +793,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xe43, 0xe42)] + [Furniture] + [Flippable(0xe43, 0xe42)] public class WoodenChest : LockableContainer { [Constructible] @@ -838,7 +824,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x280B, 0x280C)] + [Furniture] + [Flippable(0x280B, 0x280C)] public class PlainWoodenChest : LockableContainer { [Constructible] @@ -870,7 +857,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x280D, 0x280E)] + [Furniture] + [Flippable(0x280D, 0x280E)] public class OrnateWoodenChest : LockableContainer { [Constructible] @@ -902,7 +890,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x280F, 0x2810)] + [Furniture] + [Flippable(0x280F, 0x2810)] public class GildedWoodenChest : LockableContainer { [Constructible] @@ -934,7 +923,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2811, 0x2812)] + [Furniture] + [Flippable(0x2811, 0x2812)] public class WoodenFootLocker : LockableContainer { [Constructible] @@ -969,7 +959,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2813, 0x2814)] + [Furniture] + [Flippable(0x2813, 0x2814)] public class FinishedWoodenChest : LockableContainer { [Constructible] @@ -1000,4 +991,26 @@ namespace Server.Items } } } + + [Furniture] + [Serializable(0)] + [Flippable(0x2DF1, 0x2DF2)] + public partial class RarewoodChest : LockableContainer + { + [Constructible] + public RarewoodChest() : base(0x2DF1) + { + } + } + + [Furniture] + [Serializable(0)] + [Flippable(0x2DF3, 0x2DF4)] + public partial class DecorativeBox : LockableContainer + { + [Constructible] + public DecorativeBox() : base(0x2DF4) + { + } + } } diff --git a/Projects/UOContent/Items/Containers/FurnitureContainer.cs b/Projects/UOContent/Items/Containers/FurnitureContainer.cs index 406d738cd..ea41f73d3 100644 --- a/Projects/UOContent/Items/Containers/FurnitureContainer.cs +++ b/Projects/UOContent/Items/Containers/FurnitureContainer.cs @@ -3,7 +3,8 @@ using System.Collections.Generic; namespace Server.Items { - [Furniture, Flippable(0x2815, 0x2816)] + [Furniture] + [Flippable(0x2815, 0x2816)] public class TallCabinet : BaseContainer { [Constructible] @@ -26,7 +27,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2817, 0x2818)] + [Furniture] + [Flippable(0x2817, 0x2818)] public class ShortCabinet : BaseContainer { [Constructible] @@ -49,7 +51,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2857, 0x2858)] + [Furniture] + [Flippable(0x2857, 0x2858)] public class RedArmoire : BaseContainer { [Constructible] @@ -72,7 +75,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x285D, 0x285E)] + [Furniture] + [Flippable(0x285D, 0x285E)] public class CherryArmoire : BaseContainer { [Constructible] @@ -95,7 +99,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x285B, 0x285C)] + [Furniture] + [Flippable(0x285B, 0x285C)] public class MapleArmoire : BaseContainer { [Constructible] @@ -118,7 +123,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2859, 0x285A)] + [Furniture] + [Flippable(0x2859, 0x285A)] public class ElegantArmoire : BaseContainer { [Constructible] @@ -141,7 +147,30 @@ namespace Server.Items } } - [Furniture, Flippable(0xa97, 0xa99, 0xa98, 0xa9a, 0xa9b, 0xa9c)] + [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] @@ -164,7 +193,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xa9d, 0xa9e)] + [Furniture] + [Flippable(0xa9d, 0xa9e)] public class EmptyBookcase : BaseContainer { [Constructible] @@ -195,7 +225,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xa2c, 0xa34)] + [Furniture] + [Flippable(0xa2c, 0xa34)] public class Drawer : BaseContainer { [Constructible] @@ -218,7 +249,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xa30, 0xa38)] + [Furniture] + [Flippable(0xa30, 0xa38)] public class FancyDrawer : BaseContainer { [Constructible] @@ -241,7 +273,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xa4f, 0xa53)] + [Furniture] + [Flippable(0xa4f, 0xa53)] public class Armoire : BaseContainer { [Constructible] @@ -274,7 +307,8 @@ namespace Server.Items } } - [Furniture, Flippable(0xa4d, 0xa51)] + [Furniture] + [Flippable(0xa4d, 0xa51)] public class FancyArmoire : BaseContainer { [Constructible] diff --git a/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs b/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs new file mode 100644 index 000000000..186e919e9 --- /dev/null +++ b/Projects/UOContent/Items/Decoration Artifacts/AcademicBooksArtifacts.cs @@ -0,0 +1,12 @@ +namespace Server.Items +{ + [Serializable(0)] + public partial class AcademicBooksArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity => 8; + public override int LabelNumber => 1071202; // academic books + + [Constructible] + public AcademicBooksArtifact() : base(0x1E25) => Hue = 2413; + } +} diff --git a/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs b/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs index 8865c48c8..d7e34a96e 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs @@ -1,11 +1,14 @@ 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; @@ -342,17 +345,25 @@ namespace Server.Items public Item CreateInstance() { - var item = Type.CreateInstance(); - - if (Hue > 0) + try { - item.Hue = Hue; + 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; } - - item.Movable = false; - item.MoveToWorld(Location, Map); - - return item; } } diff --git a/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfElements.cs b/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfElements.cs index cd72fa5f6..abc834acc 100644 --- a/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfElements.cs +++ b/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfElements.cs @@ -16,8 +16,8 @@ namespace Server.Items public override int LabelNumber => 1075040; // Quiver of the Elements public override void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, + ref int chaos, ref int direct ) { phys = fire = cold = pois = nrgy = direct = 0; diff --git a/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfRage.cs b/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfRage.cs index c0abb2706..1e654961c 100644 --- a/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfRage.cs +++ b/Projects/UOContent/Items/Minor Artifacts/ML/QuiverOfRage.cs @@ -18,8 +18,8 @@ namespace Server.Items public override int LabelNumber => 1075038; // Quiver of Rage public override void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, + ref int chaos, ref int direct ) { chaos = direct = 0; diff --git a/Projects/UOContent/Items/Misc/FlippableAttribute.cs b/Projects/UOContent/Items/Misc/FlippableAttribute.cs index 6da0ecfc7..8dea21cce 100644 --- a/Projects/UOContent/Items/Misc/FlippableAttribute.cs +++ b/Projects/UOContent/Items/Misc/FlippableAttribute.cs @@ -51,7 +51,7 @@ namespace Server.Items } [AttributeUsage(AttributeTargets.Class)] - public class DynamicFlipingAttribute : Attribute + public class DynamicFlippingAttribute : Attribute { } diff --git a/Projects/UOContent/Items/Misc/Waypoint.cs b/Projects/UOContent/Items/Misc/WayPoint.cs similarity index 99% rename from Projects/UOContent/Items/Misc/Waypoint.cs rename to Projects/UOContent/Items/Misc/WayPoint.cs index a7a222fb2..1836fb29b 100644 --- a/Projects/UOContent/Items/Misc/Waypoint.cs +++ b/Projects/UOContent/Items/Misc/WayPoint.cs @@ -13,6 +13,7 @@ namespace Server.Items Hue = 0x498; Visible = false; // this.Movable = false; + if (prev != null) { prev.NextPoint = this; diff --git a/Projects/UOContent/Items/Quivers/BaseQuiver.cs b/Projects/UOContent/Items/Quivers/BaseQuiver.cs index e961f39e1..02a832003 100644 --- a/Projects/UOContent/Items/Quivers/BaseQuiver.cs +++ b/Projects/UOContent/Items/Quivers/BaseQuiver.cs @@ -181,19 +181,29 @@ namespace Server.Items public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) { - if (CheckType(item)) + if (!CheckType(item)) { - return Items.Count >= DefaultMaxItems && !checkItems && Ammo?.Deleted == false && - Ammo.Amount + item.Amount <= m_Capacity || item.Amount <= m_Capacity && - base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); + if (message) + { + m.SendLocalizedMessage( 1074836 ); // The container can not hold that type of object. + } + + return false; } - if (message) + if (Items.Count < DefaultMaxItems) { - m.SendLocalizedMessage(1074836); // The container can not hold that type of object. + return item.Amount <= m_Capacity && base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); } - return false; + if (checkItems) + { + return false; + } + + Item ammo = Ammo; + + return ammo?.Deleted == false && ammo.Amount + item.Amount <= m_Capacity; } public override void AddItem(Item dropped) @@ -265,15 +275,9 @@ namespace Server.Items list.Add(1074762, prop.ToString()); // Damage modifier: ~1_PERCENT~% } - AlterBowDamage( - out var phys, - out var fire, - out var cold, - out var pois, - out var nrgy, - out var chaos, - out var direct - ); + int phys = 0, fire = 0, cold = 0, pois = 0, nrgy = 0, chaos = 0, direct = 0; + + AlterBowDamage(ref phys, ref fire, ref cold, ref pois, ref nrgy, ref chaos, ref direct); if (phys != 0) { @@ -550,11 +554,9 @@ namespace Server.Items } public virtual void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, ref int chaos, ref int direct ) { - phys = fire = cold = pois = nrgy = chaos = direct = 0; } public void InvalidateWeight() diff --git a/Projects/UOContent/Items/Quivers/QuiverOfBlight.cs b/Projects/UOContent/Items/Quivers/QuiverOfBlight.cs index 6378d52fb..c0ed1bd11 100644 --- a/Projects/UOContent/Items/Quivers/QuiverOfBlight.cs +++ b/Projects/UOContent/Items/Quivers/QuiverOfBlight.cs @@ -12,8 +12,8 @@ namespace Server.Items public override int LabelNumber => 1073111; // Quiver of Blight public override void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, + ref int chaos, ref int direct ) { phys = fire = nrgy = chaos = direct = 0; diff --git a/Projects/UOContent/Items/Quivers/QuiverOfFire.cs b/Projects/UOContent/Items/Quivers/QuiverOfFire.cs index 135d89434..8caf6aa2a 100644 --- a/Projects/UOContent/Items/Quivers/QuiverOfFire.cs +++ b/Projects/UOContent/Items/Quivers/QuiverOfFire.cs @@ -12,8 +12,8 @@ namespace Server.Items public override int LabelNumber => 1073109; // quiver of fire public override void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, + ref int chaos, ref int direct ) { cold = pois = nrgy = chaos = direct = 0; diff --git a/Projects/UOContent/Items/Quivers/QuiverOfIce.cs b/Projects/UOContent/Items/Quivers/QuiverOfIce.cs index faf35ff1c..281519a7a 100644 --- a/Projects/UOContent/Items/Quivers/QuiverOfIce.cs +++ b/Projects/UOContent/Items/Quivers/QuiverOfIce.cs @@ -12,8 +12,8 @@ namespace Server.Items public override int LabelNumber => 1073110; // quiver of ice public override void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, + ref int chaos, ref int direct ) { fire = pois = nrgy = chaos = direct = 0; diff --git a/Projects/UOContent/Items/Quivers/QuiverOfLightning.cs b/Projects/UOContent/Items/Quivers/QuiverOfLightning.cs index ced6fcfea..d00563ab0 100644 --- a/Projects/UOContent/Items/Quivers/QuiverOfLightning.cs +++ b/Projects/UOContent/Items/Quivers/QuiverOfLightning.cs @@ -12,8 +12,8 @@ namespace Server.Items public override int LabelNumber => 1073112; // Quiver of Lightning public override void AlterBowDamage( - out int phys, out int fire, out int cold, out int pois, out int nrgy, - out int chaos, out int direct + ref int phys, ref int fire, ref int cold, ref int pois, ref int nrgy, + ref int chaos, ref int direct ) { fire = cold = pois = chaos = direct = 0; diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs index 8b0849113..70d8f484e 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs @@ -25,7 +25,7 @@ namespace Server.Items { get { - if (_resource >= CraftResource.DullCopper && _resource <= CraftResource.Valorite) + if (_resource is >= CraftResource.DullCopper and <= CraftResource.Valorite) { return 1042845 + (_resource - CraftResource.DullCopper); } @@ -161,7 +161,7 @@ namespace Server.Items _ => 0 }; - return itemID == 4017 || itemID >= 6522 && itemID <= 6569; + return itemID is 4017 or >= 6522 and <= 6569 or 11736; } protected override void OnTarget(Mobile from, object targeted) diff --git a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs new file mode 100644 index 000000000..a64473eb5 --- /dev/null +++ b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs @@ -0,0 +1,21 @@ +namespace Server.Items +{ + [Flippable(0x4200, 0x4207)] + [Serializable(0)] + public partial class GargishWoodenShield : BaseShield + { + [Constructible] + public GargishWoodenShield() : base(0x4200) => Weight = 5.0; + + public override int BasePhysicalResistance => 0; + public override int BaseFireResistance => 0; + public override int BaseColdResistance => 0; + public override int BasePoisonResistance => 0; + public override int BaseEnergyResistance => 1; + public override int InitMinHits => 20; + public override int InitMaxHits => 25; + public override int AosStrReq => 20; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs index 9b67bffe1..424199d15 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs @@ -29,7 +29,6 @@ namespace Server.Items m_Entries = new List(); - Created = Core.Now; Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick, out _timerToken); } @@ -37,9 +36,6 @@ namespace Server.Items { } - [CommandProperty(AccessLevel.GameMaster)] - public DateTime Created { get; } - [CommandProperty(AccessLevel.GameMaster)] public CampfireStatus Status { diff --git a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs index 4a9b35add..872bc099a 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs @@ -3,7 +3,8 @@ using Server.Engines.Craft; namespace Server.Items { [Flippable(0x1EB8, 0x1EB9)] - public class TinkerTools : BaseTool + [Serializable(0, false)] + public partial class TinkerTools : BaseTool { [Constructible] public TinkerTools() : base(0x1EB8) => Weight = 1.0; @@ -11,58 +12,18 @@ namespace Server.Items [Constructible] public TinkerTools(int uses) : base(uses, 0x1EB8) => Weight = 1.0; - public TinkerTools(Serial serial) : base(serial) - { - } - public override CraftSystem CraftSystem => DefTinkering.CraftSystem; - - 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 TinkersTools : BaseTool + [Serializable(0, false)] + public partial class TinkersTools : BaseTool { [Constructible] - public TinkersTools() - : base(0x1EBC) => - Weight = 1.0; + public TinkersTools() : base(0x1EBC) => Weight = 1.0; [Constructible] - public TinkersTools(int uses) - : base(uses, 0x1EBC) => - Weight = 1.0; - - public TinkersTools(Serial serial) - : base(serial) - { - } + public TinkersTools(int uses) : base(uses, 0x1EBC) => Weight = 1.0; public override CraftSystem CraftSystem => DefTinkering.CraftSystem; - - 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/Special/Holiday/GiftBox.cs b/Projects/UOContent/Items/Special/Holiday/GiftBox.cs index 90cc54275..c790ee3d4 100644 --- a/Projects/UOContent/Items/Special/Holiday/GiftBox.cs +++ b/Projects/UOContent/Items/Special/Holiday/GiftBox.cs @@ -1,6 +1,7 @@ namespace Server.Items { - [Furniture, Flippable(0x232A, 0x232B)] + [Furniture] + [Flippable(0x232A, 0x232B)] public class GiftBox : BaseContainer { [Constructible] diff --git a/Projects/UOContent/Items/Special/Holiday/Stockings.cs b/Projects/UOContent/Items/Special/Holiday/Stockings.cs index 820c43781..eed708dac 100644 --- a/Projects/UOContent/Items/Special/Holiday/Stockings.cs +++ b/Projects/UOContent/Items/Special/Holiday/Stockings.cs @@ -1,6 +1,7 @@ namespace Server.Items { - [Furniture, Flippable(0x2bd9, 0x2bda)] + [Furniture] + [Flippable(0x2bd9, 0x2bda)] public class GreenStocking : BaseContainer { [Constructible] @@ -28,7 +29,8 @@ namespace Server.Items } } - [Furniture, Flippable(0x2bdb, 0x2bdc)] + [Furniture] + [Flippable(0x2bdb, 0x2bdc)] public class RedStocking : BaseContainer { [Constructible] diff --git a/Projects/UOContent/Items/Talismans/BaseTalisman.cs b/Projects/UOContent/Items/Talismans/BaseTalisman.cs index 920005091..84f159dc1 100644 --- a/Projects/UOContent/Items/Talismans/BaseTalisman.cs +++ b/Projects/UOContent/Items/Talismans/BaseTalisman.cs @@ -975,7 +975,6 @@ namespace Server.Items m_Blessed = GetSaveFlag(flags, SaveFlag.Blessed); - if (Parent is Mobile m) { Attributes.AddStatBonuses(m); diff --git a/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs b/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs index 448de8b64..1d3cb2507 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs @@ -10,18 +10,7 @@ namespace Server.Items public override double DamageScalar => 1.5; public override bool RequiresSE => true; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs b/Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs new file mode 100644 index 000000000..48e69f012 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/Bladeweave.cs @@ -0,0 +1,106 @@ +using Server.Mobiles; +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class Bladeweave : WeaponAbility + { + private class BladeWeaveRedirect + { + public readonly WeaponAbility NewAbility; + public readonly int ClilocEntry; + + public BladeWeaveRedirect(WeaponAbility ability, int cliloc) + { + NewAbility = ability; + ClilocEntry = cliloc; + } + } + + private static readonly Dictionary _newAttack = new(); + + public static bool BladeWeaving(Mobile attacker, out WeaponAbility a) + { + if (_newAttack.TryGetValue(attacker, out var bwr)) + { + a = bwr.NewAbility; + return true; + } + + a = null; + return false; + } + + public override int BaseMana => 30; + + public override bool OnBeforeSwing(Mobile attacker, Mobile defender) + { + if (!Validate(attacker) || !CheckMana(attacker, false)) + { + return false; + } + + // Bladeweave is only from 90 fighting and tactics skill, so you need only to check bushido/ninjitsu value over 50 to perform block and feint. + var requiredBushido = Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value); + + var ran = Utility.Random(requiredBushido >= 50 ? 9 : 7); + + var newAttack = _newAttack[attacker] = GetBladeWeaveRedirect(ran); + + return newAttack.NewAbility.OnBeforeSwing(attacker, defender); + } + + private static BladeWeaveRedirect GetBladeWeaveRedirect(int number) => + number switch + { + 0 => new BladeWeaveRedirect(ArmorIgnore, 1028838), + 1 => new BladeWeaveRedirect(BleedAttack, 1028839), + 2 => new BladeWeaveRedirect(ConcussionBlow, 1028840), + 3 => new BladeWeaveRedirect(CrushingBlow, 1028841), + 4 => new BladeWeaveRedirect(DoubleStrike, 1028844), + 5 => new BladeWeaveRedirect(MortalStrike, 1028846), + 6 => new BladeWeaveRedirect(ParalyzingBlow, 1028848), + 7 => new BladeWeaveRedirect(Block, 1028853), + _ => new BladeWeaveRedirect(Feint, 1028857) //8 + }; + + public override bool OnBeforeDamage(Mobile attacker, Mobile defender) => + _newAttack.TryGetValue(attacker, out var bwr) + ? bwr.NewAbility.OnBeforeDamage(attacker, defender) + : base.OnBeforeDamage(attacker, defender); + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (CheckMana(attacker, false)) + { + if (_newAttack.TryGetValue(attacker, out var bwr)) + { + attacker.SendLocalizedMessage(1072841, $"#{bwr.ClilocEntry}"); + bwr.NewAbility.OnHit(attacker, defender, damage); + } + else + { + base.OnHit(attacker, defender, damage); + } + + _newAttack.Remove(attacker); + ClearCurrentAbility(attacker); + } + } + + public override void OnMiss(Mobile attacker, Mobile defender) + { + if (_newAttack.TryGetValue(attacker, out var bwr)) + { + bwr.NewAbility.OnMiss(attacker, defender); + } + else + { + base.OnMiss(attacker, defender); + } + + _newAttack.Remove(attacker); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/Block.cs b/Projects/UOContent/Items/Weapons/Abilities/Block.cs index 811d71dd6..8ace4d3bd 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Block.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Block.cs @@ -11,18 +11,7 @@ namespace Server.Items private static readonly Dictionary _table = new(); public override int BaseMana => 30; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs b/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs index a172b82ee..0dc5d9a60 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/CrushingBlow.cs @@ -8,6 +8,24 @@ namespace Server.Items public override int BaseMana => 25; public override double DamageScalar => 1.5; + public virtual double GetRequiredTactics(Mobile from) + { + if (from.Weapon is BaseWeapon weapon) + { + if (weapon.PrimaryAbility == this) + { + return 30.0; + } + + if (weapon.SecondaryAbility == this) + { + return 60.0; + } + } + + return 200.0; + } + public override void OnHit(Mobile attacker, Mobile defender, int damage) { if (!Validate(attacker) || !CheckMana(attacker, true)) diff --git a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs index 174951364..ee6ce5792 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs @@ -12,18 +12,7 @@ namespace Server.Items private static readonly Dictionary _table = new(); public override int BaseMana => 30; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -40,8 +29,7 @@ namespace Server.Items var modifier = (int)(30.0 * - ((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) - - 50.0) / 70.0)); + ((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) - 50.0) / 70.0)); if (_table.TryGetValue(attacker, out var info)) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs index e8a6c9c1f..f3ede2c1d 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs @@ -12,33 +12,25 @@ namespace Server.Items public override int BaseMana => 20; - // No longer active in pub21: - /*public override bool CheckSkills( Mobile from ) + public override bool RequiresTactics(Mobile from) => from.Weapon is not BaseWeapon { Skill: SkillName.Wrestling }; + + // Disarm is special. Doesnt need tactics when wresling and tactics need is lower than fighting skill. + public virtual double GetRequiredTactics(Mobile from) { - if (!base.CheckSkills( from )) - return false; - - if (!(from.Weapon is Fists)) - return true; - - Skill skill = from.Skills.ArmsLore; - - if (skill?.Base >= 80.0) - return true; - - from.SendLocalizedMessage( 1061812 ); // You lack the required skill in armslore to perform that attack! - - return false; - }*/ - - public override bool RequiresTactics(Mobile from) - { - if (!(from.Weapon is BaseWeapon weapon)) + if (from.Weapon is BaseWeapon weapon) { - return false; + if (weapon.PrimaryAbility == this) + { + return 30.0; + } + + if (weapon.SecondaryAbility == this) + { + return 60.0; + } } - return weapon.Skill != SkillName.Wrestling; + return 200.0; } public override void OnHit(Mobile attacker, Mobile defender, int damage) @@ -52,7 +44,7 @@ namespace Server.Items var toDisarm = defender.FindItemOnLayer(Layer.OneHanded); - if (toDisarm?.Movable == false) + if (toDisarm?.Movable != false) { toDisarm = defender.FindItemOnLayer(Layer.TwoHanded); } @@ -63,7 +55,7 @@ namespace Server.Items { attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. } - else if (!Core.ML && toDisarm == null || toDisarm is BaseShield || toDisarm is Spellbook) + else if (!Core.ML && toDisarm == null || toDisarm is BaseShield or Spellbook) { attacker.SendLocalizedMessage(1060849); // Your target is already unarmed! } @@ -77,6 +69,8 @@ namespace Server.Items pack.DropItem(toDisarm); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.NoRearm, 1075637, BlockEquipDuration, defender)); + BaseWeapon.BlockEquip(defender, BlockEquipDuration); } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs index 8512bf0db..903803d19 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs @@ -44,8 +44,8 @@ namespace Server.Items return; } - if (attacker.Mounted && (!(attacker.Weapon is Lance) || !(defender.Weapon is Lance)) - ) // TODO: Should there be a message here? + // TODO: Should there be a message here? + if (attacker.Mounted && (attacker.Weapon is not Lance || defender.Weapon is not Lance)) { return; } @@ -101,12 +101,9 @@ namespace Server.Items { playerMobile.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, true); } - else if (Core.ML && attacker is BaseCreature bc) + else if (Core.ML && attacker is BaseCreature { ControlMaster: PlayerMobile pm }) { - if (bc.ControlMaster is PlayerMobile pm) - { - pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false); - } + pm.SetMountBlock(BlockMountType.DismountRecovery, RemountDelay, false); } if (!attacker.Mounted) diff --git a/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs b/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs index a97ad27f0..8f01dea14 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs @@ -1,3 +1,5 @@ +using System; + namespace Server.Items { /// @@ -7,17 +9,8 @@ namespace Server.Items { public override int BaseMana => 30; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresTactics(Mobile from) => false; + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs b/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs index 76d2597ff..c8ea1b1af 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs @@ -12,17 +12,8 @@ namespace Server.Items public override int BaseMana => 30; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063352, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Ninjitsu; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -44,8 +35,8 @@ namespace Server.Items timer = new DualWieldTimer( attacker, - (int)(20.0 + 3.0 * (attacker.Skills.Ninjitsu.Value - 50.0) / 7.0) - ); // 20-50 % increase + (int)(20.0 + 3.0 * (attacker.Skills.Ninjitsu.Value - 50.0) / 7.0) // 20-50 % increase + ); timer.Start(); Registry.Add(attacker, timer); @@ -55,8 +46,7 @@ namespace Server.Items { private readonly Mobile m_Owner; - public DualWieldTimer(Mobile owner, int bonusSwingSpeed) - : base(TimeSpan.FromSeconds(6.0)) + public DualWieldTimer(Mobile owner, int bonusSwingSpeed) : base(TimeSpan.FromSeconds(6.0)) { m_Owner = owner; BonusSwingSpeed = bonusSwingSpeed; diff --git a/Projects/UOContent/Items/Weapons/Abilities/Feint.cs b/Projects/UOContent/Items/Weapons/Abilities/Feint.cs index 70f32fc43..20c738a77 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Feint.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Feint.cs @@ -11,18 +11,7 @@ namespace Server.Items public static Dictionary Registry { get; } = new(); public override int BaseMana => 30; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -46,10 +35,8 @@ namespace Server.Items timer = new FeintTimer( defender, - (int)(20.0 + 3.0 * (Math.Max( - attacker.Skills.Ninjitsu.Value, - attacker.Skills.Bushido.Value - ) - 50.0) / 7.0) + (int)(20.0 + 3.0 * + (Math.Max(attacker.Skills.Ninjitsu.Value, attacker.Skills.Bushido.Value) - 50.0) / 7.0) ); // 20-50 % decrease timer.Start(); diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs new file mode 100644 index 000000000..d8b5c9add --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs @@ -0,0 +1,164 @@ +using Server.Spells; +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class ForceArrow : WeaponAbility + { + public override int BaseMana => 20; + + public override bool RequiresTactics(Mobile from) => false; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1074381); // You fire an arrow of pure force. + defender.SendLocalizedMessage(1074382); // You are struck by a force arrow! + + if (0.4 > Utility.RandomDouble()) + { + defender.Combatant = null; + defender.Warmode = false; + } + + ForceArrowInfo info = GetInfo(attacker, defender); + + if (info == null) + { + BeginForceArrow(attacker, defender); + } + else if (info.Timer.Running) + { + info.Timer.IncreaseExpiration(); + + BuffInfo.RemoveBuff(defender, BuffIcon.ForceArrow); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString())); + } + + if (defender.Spell is Spell spell && spell.IsCasting) + { + spell.Disturb(DisturbType.Hurt, false, true); + } + } + + private static readonly Dictionary> _table = new Dictionary>(); + + public static void BeginForceArrow(Mobile attacker, Mobile defender) + { + ForceArrowInfo info = new ForceArrowInfo(attacker, defender); + info.Timer = new ForceArrowTimer(info); + + if (_table.TryGetValue(attacker, out var list)) + { + list.Add(info); + } + else + { + _table.Add(attacker, new List() { info }); + } + + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString())); + } + + public static void EndForceArrow(ForceArrowInfo info) + { + if (info == null) + { + return; + } + + Mobile attacker = info.Attacker; + + if (_table.TryGetValue(attacker, out var list) && list.Remove(info) && list.Count == 0) + { + _table.Remove(attacker); + } + + BuffInfo.RemoveBuff(info.Defender, BuffIcon.ForceArrow); + } + + public static bool HasForceArrow(Mobile attacker, Mobile defender) + { + if (_table.TryGetValue(attacker, out var list)) + { + foreach (ForceArrowInfo info in list) + { + if (info.Defender == defender) + { + return true; + } + } + } + + return false; + } + + public static ForceArrowInfo GetInfo(Mobile attacker, Mobile defender) + { + if (_table.TryGetValue(attacker,out var list)) + { + foreach (ForceArrowInfo info in list) + { + if (info.Defender == defender) + { + return info; + } + } + } + + return null; + } + + public class ForceArrowInfo + { + public Mobile Attacker { get; } + public Mobile Defender { get; } + public ForceArrowTimer Timer { get; set; } + public int DefenseChanceMalus { get; set; } + + public ForceArrowInfo(Mobile attacker, Mobile defender) + { + Attacker = attacker; + Defender = defender; + DefenseChanceMalus = 10; + } + } + + public class ForceArrowTimer : Timer + { + private readonly ForceArrowInfo _info; + private DateTime _expires; + + public ForceArrowTimer(ForceArrowInfo info) + : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1)) + { + _info = info; + _expires = Core.Now + TimeSpan.FromSeconds(10); + + Start(); + } + + protected override void OnTick() + { + if (_expires < Core.Now) + { + Stop(); + EndForceArrow(_info); + } + } + + public void IncreaseExpiration() + { + _expires += TimeSpan.FromSeconds(2); + _info.DefenseChanceMalus += 5; + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs new file mode 100644 index 000000000..17d4ffc8a --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class ForceOfNature : WeaponAbility + { + public override int BaseMana => 35; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1074374); // You attack your enemy with the force of nature! + defender.SendLocalizedMessage(1074375); // You are assaulted with great force! + + defender.PlaySound(0x22F); + defender.FixedParticles(0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head); + defender.FixedParticles(0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255); + + Remove(attacker); + + ForceOfNatureTimer t = new ForceOfNatureTimer(attacker, defender); + t.Start(); + + _table[attacker] = t; + } + + private static readonly Dictionary _table = new Dictionary(); + + public static void Remove(Mobile m) + { + if (_table.Remove(m, out var timer)) + { + timer.Stop(); + } + } + + public static void OnHit(Mobile from, Mobile target) + { + if (_table.TryGetValue(from,out var t)) + { + t.Hits++; + t.LastHit = Core.Now; + + if (t.Hits % 12 == 0) + { + int duration = target.Skills[SkillName.MagicResist].Value >= 90.0 ? 1 : 2; + target.Paralyze(TimeSpan.FromSeconds(duration)); + + target.FixedEffect(0x376A, 9, 32); + target.PlaySound(0x204); + + t.Hits = 0; + + from.SendLocalizedMessage(1004013); // You successfully stun your opponent! + target.SendLocalizedMessage(1004014); // You have been stunned! + } + } + } + + public static double GetDamageScalar(Mobile from, Mobile target) + { + if (_table.TryGetValue(from, out var t) && t.Target == target) + { + var bonus = Math.Min(100, Math.Max(50, from.Str - 50)); + return (100.0 + bonus) / 100.0; + } + + return 1.0; + } + + private class ForceOfNatureTimer : Timer + { + public Mobile Target { get; } + public Mobile From { get; } + public int Hits { get; set; } + public DateTime LastHit { get; set; } + + public ForceOfNatureTimer(Mobile from, Mobile target) + : base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)) + { + Target = target; + From = from; + Hits = 1; + LastHit = Core.Now; + } + + protected override void OnTick() + { + if (!From.Alive || !Target.Alive || Target.Map != From.Map || Target.GetDistanceToSqrt(From.Location) > 10 || LastHit + TimeSpan.FromSeconds(20) < Core.Now || Index > 36) + { + Remove(From); + return; + } + + if (Index == 1) + { + int damage = Utility.RandomMinMax(15, 35); + + AOS.Damage(From, From, damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false); + } + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs index a523b7a1b..f6b2c6167 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs @@ -12,18 +12,8 @@ namespace Server.Items public override int BaseMana => 30; public static Dictionary Registry { get; } = new(); - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063347, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresTactics(Mobile from) => false; + public override bool RequiresSecondarySkill(Mobile from) => true; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs index ce155ce09..05b0e9969 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs @@ -30,7 +30,7 @@ namespace Server.Items ClearCurrentAbility(attacker); - if (!(attacker.Weapon is BaseWeapon weapon)) + if (attacker.Weapon is not BaseWeapon weapon) { return; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs new file mode 100644 index 000000000..750b0e4cf --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs @@ -0,0 +1,58 @@ +using Server.Spells; +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class LightningArrow : WeaponAbility + { + public override int BaseMana => 20; + + //TODO - add ConsumeAmmo on weaponabilities + // public override bool ConsumeAmmo => false; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker)) + { + return; + } + + ClearCurrentAbility(attacker); + + Map map = attacker.Map; + + if (map == null || attacker.Weapon is not BaseWeapon weapon || !CheckMana(attacker, true)) + { + return; + } + + List targets = new List(); + IPooledEnumerable eable = defender.GetMobilesInRange(5); + + foreach (Mobile m in eable) + { + if (m != defender && m != attacker && SpellHelper.ValidIndirectTarget(attacker, m) && m?.Deleted == false && + m.Map == attacker.Map && m.Alive && attacker.CanSee(m) && attacker.CanBeHarmful(m) && + attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m)) + { + targets.Add(m); + } + } + + eable.Free(); + defender.BoltEffect(0); + + var mobilesLeft = Math.Min(targets.Count, 2); + while (mobilesLeft-- > 0) + { + var index = Utility.Random(targets.Count); + var m = targets[index]; + targets.RemoveAt(index); + + m.BoltEffect(0); + AOS.Damage(m, attacker, Utility.RandomMinMax(29, 40), 0, 0, 0, 0, 100); + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs index cead5302b..11eb81352 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs @@ -9,17 +9,8 @@ namespace Server.Items { public override int BaseMana => 30; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! - from.SendLocalizedMessage(1070768, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Bushido; public override bool OnBeforeSwing(Mobile attacker, Mobile defender) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs index 9c8184ebd..43f985df1 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs @@ -17,25 +17,7 @@ namespace Server.Items public override int BaseMana => 30; - // No longer active in pub21: - /*public override bool CheckSkills( Mobile from ) - { - if (!base.CheckSkills( from )) - return false; - - if (!(from.Weapon is Fists)) - return true; - - Skill skill = from.Skills.Anatomy; - - if (skill?.Base >= 80.0) - return true; - - from.SendLocalizedMessage( 1061811 ); // You lack the required anatomy skill to perform that attack! - - return false; - }*/ - + // When using Wrestling, tactics isnt needed. public override bool RequiresTactics(Mobile from) => !(from.Weapon is BaseWeapon weapon && weapon.Skill == SkillName.Wrestling); diff --git a/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs new file mode 100644 index 000000000..c38d387dd --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; + +namespace Server.Items +{ + public class PsychicAttack : WeaponAbility + { + public static Dictionary Registry { get; } = new(); + public override int BaseMana => 30; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + attacker.SendLocalizedMessage(1074383); // Your shot sends forth a wave of psychic energy. + defender.SendLocalizedMessage(1074384); // Your mind is attacked by psychic force! + + defender.FixedParticles(0x3789, 10, 25, 5032, EffectLayer.Head); + defender.PlaySound(0x1F8); + + if (Registry.TryGetValue(defender, out var timer)) + { + if (!timer.DoneIncrease) + { + timer.SpellDamageMalus *= 2; + timer.ManaCostMalus *= 2; + } + } + else + { + timer = new PsychicAttackTimer(defender); + timer.Start(); + Registry.Add(defender, timer); + } + + BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack); + + string args = $"{timer.SpellDamageMalus}\t{timer.ManaCostMalus}"; + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.PsychicAttack, 1151296, 1151297, args)); + } + + public static void RemoveEffects(Mobile defender) + { + if (defender == null) + { + return; + } + + BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack); + + Registry.Remove(defender); + + defender.SendLocalizedMessage(1150292); // You recover from the effects of the psychic attack. + } + + public class PsychicAttackTimer : Timer + { + private readonly Mobile _defender; + private int _spellDamageMalus; + private int _manaCostMalus; + + public int SpellDamageMalus + { + get => _spellDamageMalus; + set { _spellDamageMalus = value; DoneIncrease = true; } + } + public int ManaCostMalus + { + get => _manaCostMalus; + set { _manaCostMalus = value; DoneIncrease = true; } + } + public bool DoneIncrease { get; private set; } + + public PsychicAttackTimer(Mobile defender) : base(TimeSpan.FromSeconds(10)) + { + _defender = defender; + _spellDamageMalus = 15; + _manaCostMalus = 15; + DoneIncrease = false; + } + + protected override void OnTick() + { + RemoveEffects(_defender); + } + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs b/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs index bd880c6b9..cf2be4a65 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs @@ -13,18 +13,8 @@ namespace Server.Items public override int BaseMana => 30; public override bool RequiresSE => true; - - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Bushido) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! - from.SendLocalizedMessage(1070768, "50"); - return false; - } - - return base.CheckSkills(from); - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Bushido; public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs new file mode 100644 index 000000000..138dc25bf --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Abilities/SerpentArrow.cs @@ -0,0 +1,46 @@ +namespace Server.Items +{ + public class SerpentArrow : WeaponAbility + { + public override int BaseMana => 25; + + // Serpent arrow is used only as secondary ability on "Elven composite longbow" so needed skill is always 60, but for sure we will use GetRequiredSecondarySkill + public override bool RequiresSecondarySkill(Mobile from) => true; + public override double GetRequiredSecondarySkill(Mobile from) => 60; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Poisoning; + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if (!Validate(attacker) || !CheckMana(attacker, true)) + { + return; + } + + ClearCurrentAbility(attacker); + + defender.SendLocalizedMessage(1112369); // You have been poisoned by a lethal arrow! + + int level; + + if (attacker.InRange(defender, 2)) + { + level = attacker.Skills.Poisoning.Fixed / 2 switch + { + >= 1000 => 3, + > 850 => 2, + > 650 => 1, + _ => 0 + }; + } + else + { + level = 0; + } + + defender.ApplyPoison(attacker, Poison.GetPoison(level)); + + defender.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist); + defender.PlaySound(0x474); + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs index a43bf94f2..29d6e5eaa 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ShadowStrike.cs @@ -12,6 +12,10 @@ namespace Server.Items public override bool RequiresTactics(Mobile from) => false; + public override bool RequiresSecondarySkill(Mobile from) => true; + public override double GetRequiredSecondarySkill(Mobile from) => 80; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Stealth; + public override bool CheckSkills(Mobile from) { if (!base.CheckSkills(from)) diff --git a/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs index 9741fdcb6..13963d11d 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs @@ -13,17 +13,9 @@ namespace Server.Items public override int BaseMana => 30; public override double DamageScalar => 1.2; - public override bool CheckSkills(Mobile from) - { - if (GetSkill(from, SkillName.Ninjitsu) < 50.0) - { - // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! - from.SendLocalizedMessage(1063352, "50"); - return false; - } + public override bool RequiresSecondarySkill(Mobile from) => true; + public override SkillName GetSecondarySkillName(Mobile from) => SkillName.Ninjitsu; - return base.CheckSkills(from); - } public override void OnHit(Mobile attacker, Mobile defender, int damage) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index dff1d95c6..9ad7e996f 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -38,12 +38,12 @@ namespace Server.Items new DualWield(), new DoubleShot(), new ArmorPierce(), - null, - null, - null, - null, - null, - null, + new Bladeweave(), + new ForceArrow(), + new LightningArrow(), + new PsychicAttack(), + new SerpentArrow(), + new ForceOfNature(), new Disrobe() }; @@ -109,16 +109,60 @@ namespace Server.Items public virtual bool RequiresTactics(Mobile from) => true; + public virtual bool RequiresSecondarySkill(Mobile from) => false; + + /// + /// Return primary skill for ability. Default 70/90 + /// + /// + /// public virtual double GetRequiredSkill(Mobile from) { if (from.Weapon is BaseWeapon weapon) { - if (weapon.PrimaryAbility == this) + if (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave) { return 70.0; } - if (weapon.SecondaryAbility == this) + if (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave) + { + return 90.0; + } + } + + return 200.0; + } + + /// + /// Returns secondary skill for Ability. Default 50. + /// + /// + /// + public virtual double GetRequiredSecondarySkill(Mobile from) => 50.0; + + /// + /// Return secondary skillName. Default bushido/ninjitsu + /// + /// + /// + public virtual SkillName GetSecondarySkillName(Mobile from) => from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido; + + /// + /// Returns tactics needed for Ability. Default 70/90. + /// + /// + /// + public virtual double GetRequiredTactics(Mobile from) + { + if (from.Weapon is BaseWeapon weapon) + { + if (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave) + { + return 70.0; + } + + if (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave) { return 90.0; } @@ -184,13 +228,32 @@ namespace Server.Items var reqSkill = GetRequiredSkill(from); var reqTactics = Core.ML && RequiresTactics(from); - if (Core.ML && reqTactics && from.Skills.Tactics.Base < reqSkill) + if (reqTactics && from.Skills.Tactics.Base < GetRequiredTactics(from)) { // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack from.SendLocalizedMessage(1079308, reqSkill.ToString()); return false; } + var reqSecondarySkill = GetRequiredSecondarySkill(from); + SkillName secondarySkill = GetSecondarySkillName(from); + + if (RequiresSecondarySkill(from) && from.Skills[secondarySkill].Base < reqSecondarySkill) + { + int loc = GetSkillLocalization(secondarySkill); + + if (loc == 1060184) + { + from.SendLocalizedMessage(loc); + } + else + { + from.SendLocalizedMessage(loc, reqSecondarySkill.ToString()); + } + + return false; + } + if (skill?.Base >= reqSkill) { return true; @@ -207,19 +270,22 @@ namespace Server.Items } /* */ - if (reqTactics) - { - // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack - from.SendLocalizedMessage(1079308, reqSkill.ToString()); - } - else - { - // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack - from.SendLocalizedMessage(1060182, reqSkill.ToString()); - } + // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack + from.SendLocalizedMessage(1060182, reqSkill.ToString()); return false; } + private int GetSkillLocalization(SkillName skill) + { + return skill switch + { + SkillName.Bushido => 1063347, // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + SkillName.Ninjitsu => 1063347, // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + SkillName.Poisoning => 1060184, // You lack the required poisoning to perform that attack + _ => 1157351 // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack + }; + } + public virtual bool CheckSkills(Mobile from) => CheckWeaponSkill(from); @@ -420,10 +486,7 @@ namespace Server.Items { private readonly Mobile m_Mobile; - public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) - { - m_Mobile = from; - } + public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0)) => m_Mobile = from; protected override void OnTick() { diff --git a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs index 316ae4c53..32496a2a7 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs @@ -28,7 +28,7 @@ namespace Server.Items return; } - if (!(attacker.Weapon is BaseWeapon weapon)) + if (attacker.Weapon is not BaseWeapon weapon) { return; } diff --git a/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs b/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs new file mode 100644 index 000000000..d3dfb4d18 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Axes/DualShortAxes.cs @@ -0,0 +1,24 @@ +namespace Server.Items +{ + [Flippable(0x8FD, 0x4068)] + [Serializable(0)] + public partial class DualShortAxes : BaseAxe + { + [Constructible] + public DualShortAxes() : base(0x8FD) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; + public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; + public override int AosStrengthReq => 35; + public override int AosMinDamage => 14; + public override int AosMaxDamage => 17; + public override float MlSpeed => 3.00f; + + public override int InitMinHits => 31; + public override int InitMaxHits => 110; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 1d7fe5db1..b50b97f32 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -1024,6 +1024,7 @@ namespace Server.Items } ImmolatingWeaponSpell.StopImmolating(this); + ForceOfNature.Remove(m); m.CheckStatTimers(); @@ -1157,6 +1158,13 @@ namespace Server.Items bonus = AosAttributes.GetValue(defender, AosAttribute.DefendChance); + var info = ForceArrow.GetInfo(attacker, defender); + + if (info != null && info.Defender == defender) + { + bonus -= info.DefenseChanceMalus; + } + if (DivineFurySpell.UnderEffect(defender)) { bonus -= 20; // defender loses 20% bonus when they're under divine fury @@ -1662,6 +1670,8 @@ namespace Server.Items percentageBonus += (int)(move.GetDamageScalar(attacker, defender) * 100) - 100; } + percentageBonus += (int)(ForceOfNature.GetDamageScalar(attacker, defender) * 100) - 100; + percentageBonus += (int)(damageBonus * 100) - 100; var cs = CheckSlayers(attacker, defender); @@ -1786,7 +1796,7 @@ namespace Server.Items if (Core.ML && this is BaseRanged && attacker.FindItemOnLayer(Layer.Cloak) is BaseQuiver quiver) { - quiver.AlterBowDamage(out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct); + quiver.AlterBowDamage(ref phys, ref fire, ref cold, ref pois, ref nrgy, ref chaos, ref direct); } if (Consecrated) @@ -1864,7 +1874,10 @@ namespace Server.Items move = null; } - var ignoreArmor = a is ArmorIgnore || move?.IgnoreArmor(attacker) == true; + var ignoreArmor = a is ArmorIgnore || + move?.IgnoreArmor(attacker) == true || + Bladeweave.BladeWeaving(attacker, out var bladeweavingAbi) && + bladeweavingAbi is ArmorIgnore; var damageGiven = AOS.Damage( defender, @@ -1981,8 +1994,8 @@ namespace Server.Items mobile.LocalOverheadMessage( MessageType.Regular, 0x3B2, - 1061121 - ); // Your equipment is severely damaged. + 1061121 // Your equipment is severely damaged. + ); } } else @@ -2101,6 +2114,8 @@ namespace Server.Items a?.OnHit(attacker, defender, damage); move?.OnHit(attacker, defender, damage); + ForceOfNature.OnHit(attacker, defender); + if (defender is IHonorTarget it) { it.ReceivedHonorContext?.OnTargetHit(attacker); @@ -2142,6 +2157,11 @@ namespace Server.Items // SDI bonus damageBonus += AosAttributes.GetValue(attacker, AosAttribute.SpellDamage); + if(PsychicAttack.Registry.TryGetValue(attacker,out var timer)) + { + damageBonus -= timer.SpellDamageMalus; + } + var context = TransformationSpellHelper.GetContext(attacker); if (context?.Spell is ReaperFormSpell spell) diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs new file mode 100644 index 000000000..77d76c981 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/FaerieFire.cs @@ -0,0 +1,29 @@ +namespace Server.Items +{ + [Serializable(0)] + public partial class FaerieFire : ElvenCompositeLongbow + { + [Constructible] + public FaerieFire() + { + Hue = 0x489; + WeaponAttributes.HitFireball = 25; + Balanced = true; + Attributes.BonusDex = 3; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 60; + } + + public override int LabelNumber => 1072908; // Faerie Fire + + public override void GetDamageTypes( + Mobile wielder, out int phys, out int fire, out int cold, out int pois, + out int nrgy, out int chaos, out int direct + ) + { + fire = 100; + + phys = cold = pois = nrgy = chaos = direct = 0; + } + } +} diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs new file mode 100644 index 000000000..cc1c5354e --- /dev/null +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Artifacts/MischiefMaker.cs @@ -0,0 +1,27 @@ +namespace Server.Items +{ + [Serializable(0)] + public partial class MischiefMaker : MagicalShortbow + { + [Constructible] + public MischiefMaker() + { + Hue = 0x8AB; + Slayer = SlayerName.Silver; + Balanced = true; + Attributes.WeaponSpeed = 35; + Attributes.WeaponDamage = 45; + } + + public override int LabelNumber => 1072910; // Mischief Maker + + public override void GetDamageTypes( + Mobile wielder, out int phys, out int fire, out int cold, out int pois, + out int nrgy, out int chaos, out int direct + ) + { + phys = fire = pois = nrgy = chaos = direct = 0; + cold = 100; + } + } +} diff --git a/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs b/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs new file mode 100644 index 000000000..7152cc377 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Maces/DiscMace.cs @@ -0,0 +1,24 @@ +namespace Server.Items +{ + [Flippable(0x903, 0x406E)] + [Serializable(0)] + public partial class DiscMace : BaseBashing + { + [Constructible] + public DiscMace() : base(0x903) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; + public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; + public override int AosStrengthReq => 45; + 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 => 110; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs new file mode 100644 index 000000000..2aee19229 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/DualPointedSpear.cs @@ -0,0 +1,24 @@ +namespace Server.Items +{ + [Flippable(0x904, 0x406D)] + [Serializable(0)] + public partial class DualPointedSpear : BaseSpear + { + [Constructible] + public DualPointedSpear() : base(0x904) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; + public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; + public override int AosStrengthReq => 50; + public override int AosMinDamage => 11; + public override int AosMaxDamage => 14; + public override float MlSpeed => 2.25f; + + public override int InitMinHits => 31; + public override int InitMaxHits => 80; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs new file mode 100644 index 000000000..03d3b0bbc --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs @@ -0,0 +1,23 @@ +namespace Server.Items +{ + [Flippable(0x905, 0x4070)] + [Serializable(0)] + public partial class GlassStaff : BaseStaff + { + [Constructible] + public GlassStaff() : base(0x905) => + Weight = 4.0; + + public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; + public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; + public override int AosStrengthReq => 20; + public override int AosMinDamage => 11; + public override int AosMaxDamage => 14; + public override float MlSpeed => 2.25f; + + public override int InitMinHits => 31; + public override int InitMaxHits => 70; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs index aaa4d6e19..8ca5f06a6 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs @@ -8,7 +8,7 @@ namespace Server.Items public GnarledStaff() : base(0x13F8) => Weight = 3.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; - public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; + public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature; public override int AosStrengthReq => 20; public override int AosMinDamage => 15; diff --git a/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs b/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs new file mode 100644 index 000000000..a75b82ae4 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Staves/SerpentstoneStaff.cs @@ -0,0 +1,25 @@ +namespace Server.Items +{ + [Flippable(0x906, 0x406F)] + [TypeAlias("Server.Items.SerpentStoneStaff")] + [Serializable(0)] + public partial class SerpentstoneStaff : BaseStaff + { + [Constructible] + public SerpentstoneStaff() : base(0x906) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; + public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; + public override int AosStrengthReq => 35; + public override int AosMinDamage => 16; + public override int AosMaxDamage => 19; + public override float MlSpeed => 3.50f; + + public override int InitMinHits => 31; + public override int InitMaxHits => 50; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs index 12278e291..054c53c07 100644 --- a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs +++ b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs @@ -53,33 +53,31 @@ namespace Server.Items protected override void OnTarget(Mobile from, object targ) { - if (targ is BaseCreature bc) + if (targ is not BaseCreature bc) { - if (IsHerdable(bc)) - { - if (bc.Controlled) - { - bc.PrivateOverheadMessage( - MessageType.Regular, - 0x3B2, - 502467, - from.NetState - ); // That animal looks tame already. - } - else - { - from.SendLocalizedMessage(502475); // Click where you wish the animal to go. - from.Target = new InternalTarget(bc); - } - } - else - { - from.SendLocalizedMessage(502468); // That is not a herdable animal. - } + from.SendLocalizedMessage(502472); // You don't seem to be able to persuade that to move. + return; + } + + if (!IsHerdable(bc)) + { + from.SendLocalizedMessage(502468); // That is not a herdable animal. + return; + } + + if (bc.Controlled) + { + bc.PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + 502467, // That animal looks tame already. + from.NetState + ); } else { - from.SendLocalizedMessage(502472); // You don't seem to be able to persuade that to move. + from.SendLocalizedMessage(502475); // Click where you wish the animal to go. + from.Target = new InternalTarget(bc); } } @@ -136,9 +134,9 @@ namespace Server.Items m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, - 502471, + 502471, // That wasn't even challenging. from.NetState - ); // That wasn't even challenging. + ); } if (from.CheckTargetSkill(SkillName.Herding, m_Creature, min, max)) diff --git a/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs b/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs new file mode 100644 index 000000000..4bcf21831 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Swords/BloodBlade.cs @@ -0,0 +1,29 @@ +namespace Server.Items +{ + [Flippable(0x8FE, 0x4072)] + [Serializable(0)] + public partial class BloodBlade : BaseSword + { + [Constructible] + public BloodBlade() : base(0x8FE) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; + public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; + public override int AosStrengthReq => 10; + public override int AosMinDamage => 10; + public override int AosMaxDamage => 12; + public override float MlSpeed => 2.00f; + + public override int DefHitSound => 0x23C; + public override int DefMissSound => 0x238; + public override int InitMinHits => 31; + public override int InitMaxHits => 90; + public override SkillName DefSkill => SkillName.Fencing; + public override WeaponType DefType => WeaponType.Piercing; + public override WeaponAnimation DefAnimation => WeaponAnimation.Pierce1H; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs b/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs new file mode 100644 index 000000000..9f2df661f --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Swords/DreadSword.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Flippable(0x90B, 0x4074)] + [Serializable(0)] + public partial class DreadSword : BaseSword + { + [Constructible] + public DreadSword() : base(0x90B) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; + public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; + public override int AosStrengthReq => 35; + public override int AosMinDamage => 14; + public override int AosMaxDamage => 18; + public override float MlSpeed => 3.50f; + + public override int DefHitSound => 0x237; + public override int DefMissSound => 0x23A; + public override int InitMinHits => 31; + public override int InitMaxHits => 110; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs b/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs new file mode 100644 index 000000000..713e684b6 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Swords/GargishTalwar.cs @@ -0,0 +1,26 @@ +namespace Server.Items +{ + [Flippable(0x908, 0x4075)] + [Serializable(0)] + public partial class GargishTalwar : BaseSword + { + [Constructible] + public GargishTalwar() : base(0x908) + { + } + + public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; + public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; + public override int AosStrengthReq => 40; + public override int AosMinDamage => 16; + public override int AosMaxDamage => 19; + public override float MlSpeed => 3.50f; + + public override int DefHitSound => 0x237; + public override int DefMissSound => 0x238; + public override int InitMinHits => 31; + public override int InitMaxHits => 80; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs new file mode 100644 index 000000000..1bb290332 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs @@ -0,0 +1,24 @@ +namespace Server.Items +{ + [Flippable(0x90C, 0x4073)] + [Serializable(0)] + public partial class GlassSword : BaseSword + { + [Constructible] + public GlassSword() : base(0x90C) => Weight = 6.0; + + public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; + public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; + public override int AosStrengthReq => 20; + public override int AosMinDamage => 11; + public override int AosMaxDamage => 15; + public override float MlSpeed => 2.75f; + + public override int DefHitSound => 0x23B; + public override int DefMissSound => 0x23A; + public override int InitMinHits => 31; + public override int InitMaxHits => 90; + + public override int RequiredRaces => Race.AllowGargoylesOnly; + } +} diff --git a/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json b/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json new file mode 100644 index 000000000..07d1336c7 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Accounting.Account.v4.json @@ -0,0 +1,127 @@ +{ + "version": 4, + "type": "Server.Accounting.Account", + "properties": [ + { + "name": "Username", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "PasswordAlgorithm", + "type": "Server.Accounting.Security.PasswordProtectionAlgorithm", + "rule": "EnumMigrationRule" + }, + { + "name": "Password", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "AccessLevel", + "type": "Server.AccessLevel", + "rule": "EnumMigrationRule" + }, + { + "name": "Flags", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LastLogin", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TotalGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TotalPlat", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Mobiles", + "type": "Server.Mobile[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Comments", + "type": "System.Collections.Generic.List\u003CServer.Accounting.AccountComment\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "", + "Server.Accounting.AccountComment", + "SerializationMethodSignatureMigrationRule", + "" + ] + }, + { + "name": "Tags", + "type": "System.Collections.Generic.List\u003CServer.Accounting.AccountTag\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "", + "Server.Accounting.AccountTag", + "SerializationMethodSignatureMigrationRule", + "" + ] + }, + { + "name": "LoginIPs", + "type": "System.Net.IPAddress[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "System.Net.IPAddress", + "PrimitiveTypeMigrationRule" + ] + }, + { + "name": "IpRestrictions", + "type": "string[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "string", + "PrimitiveTypeMigrationRule", + "" + ] + }, + { + "name": "TotalGameTime", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "Email", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.BulkOrders.BaseBOD.v1.json b/Projects/UOContent/Migrations/Server.Engines.BulkOrders.BaseBOD.v1.json index 7ffed30e7..e3d720211 100644 --- a/Projects/UOContent/Migrations/Server.Engines.BulkOrders.BaseBOD.v1.json +++ b/Projects/UOContent/Migrations/Server.Engines.BulkOrders.BaseBOD.v1.json @@ -17,6 +17,11 @@ "ruleArguments": [ "" ] + }, + { + "name": "Material", + "type": "Server.Engines.BulkOrders.BulkMaterialType", + "rule": "EnumMigrationRule" } ] } \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.AcademicBooksArtifact.v0.json b/Projects/UOContent/Migrations/Server.Items.AcademicBooksArtifact.v0.json new file mode 100644 index 000000000..fe2c46c6e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.AcademicBooksArtifact.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.AcademicBooksArtifact" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BambooChair.v0.json b/Projects/UOContent/Migrations/Server.Items.BambooChair.v0.json new file mode 100644 index 000000000..c6fad062a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BambooChair.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BambooChair" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseAddon.v3.json b/Projects/UOContent/Migrations/Server.Items.BaseAddon.v3.json index edb8610a0..a80bc2afc 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseAddon.v3.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseAddon.v3.json @@ -13,7 +13,7 @@ ] }, { - "name": "Resource", + "name": "RawResource", "type": "Server.Items.CraftResource", "rule": "EnumMigrationRule" } diff --git a/Projects/UOContent/Migrations/Server.Items.BaseAddonContainer.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseAddonContainer.v2.json index 9a9c284b3..76a123257 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseAddonContainer.v2.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseAddonContainer.v2.json @@ -13,7 +13,7 @@ ] }, { - "name": "Resource", + "name": "RawResource", "type": "Server.Items.CraftResource", "rule": "EnumMigrationRule" } diff --git a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json index 89c438b07..c81b88b8f 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v8.json @@ -117,7 +117,7 @@ "rule": "EnumMigrationRule" }, { - "name": "Resource", + "name": "RawResource", "type": "Server.Items.CraftResource", "usesSaveFlag": true, "rule": "EnumMigrationRule" diff --git a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json index 0a921750b..c59f0615f 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v6.json @@ -3,7 +3,7 @@ "type": "Server.Items.BaseClothing", "properties": [ { - "name": "Resource", + "name": "RawResource", "type": "Server.Items.CraftResource", "usesSaveFlag": true, "rule": "EnumMigrationRule" diff --git a/Projects/UOContent/Migrations/Server.Items.BigElvenChair.v0.json b/Projects/UOContent/Migrations/Server.Items.BigElvenChair.v0.json new file mode 100644 index 000000000..7f0083d3e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BigElvenChair.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BigElvenChair" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BloodBlade.v0.json b/Projects/UOContent/Migrations/Server.Items.BloodBlade.v0.json new file mode 100644 index 000000000..56d90713f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BloodBlade.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BloodBlade" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeAxeNorth.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeAxeNorth.v0.json new file mode 100644 index 000000000..b32c67ab0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeAxeNorth.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeAxeNorth" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeAxeWest.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeAxeWest.v0.json new file mode 100644 index 000000000..b3e55ad42 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeAxeWest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeAxeWest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeBowNorth.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeBowNorth.v0.json new file mode 100644 index 000000000..cab0df582 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeBowNorth.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeBowNorth" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeBowWest.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeBowWest.v0.json new file mode 100644 index 000000000..625326ac1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeBowWest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeBowWest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeBox.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeBox.v0.json new file mode 100644 index 000000000..55c6f81a6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeNorth.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeNorth.InternalItem.v0.json new file mode 100644 index 000000000..8466ac839 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeNorth.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeDAxeNorth.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeDAxeNorth", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeNorth.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeNorth.v0.json new file mode 100644 index 000000000..f6221286b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeNorth.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeDAxeNorth", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeDAxeNorth.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeWest.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeWest.InternalItem.v0.json new file mode 100644 index 000000000..9cae98fff --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeWest.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeDAxeWest.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeDAxeWest", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeWest.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeWest.v0.json new file mode 100644 index 000000000..3d886b6b5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeDAxeWest.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeDAxeWest", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeDAxeWest.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield1.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield1.v0.json new file mode 100644 index 000000000..e3aeaac09 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield10.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield10.v0.json new file mode 100644 index 000000000..e29f89c67 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield10.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield10" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield11.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield11.v0.json new file mode 100644 index 000000000..cb5c86744 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield11.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield11" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield2.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield2.v0.json new file mode 100644 index 000000000..379e5b926 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield3.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield3.v0.json new file mode 100644 index 000000000..5fd054a64 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield3.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield3" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield4.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield4.v0.json new file mode 100644 index 000000000..f0f831887 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield4.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield4" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield5.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield5.v0.json new file mode 100644 index 000000000..164fd3d0a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield5.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield5" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield6.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield6.v0.json new file mode 100644 index 000000000..fdf674b90 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield6.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield6" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield7.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield7.v0.json new file mode 100644 index 000000000..a9029b96a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield7.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield7" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield8.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield8.v0.json new file mode 100644 index 000000000..f6ce37663 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield8.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield8" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShield9.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShield9.v0.json new file mode 100644 index 000000000..5d53c0b65 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShield9.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShield9" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword1North.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword1North.v0.json new file mode 100644 index 000000000..7e8485eba --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword1North.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShieldSword1North" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword1West.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword1West.v0.json new file mode 100644 index 000000000..9263634a0 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword1West.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShieldSword1West" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword2North.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword2North.v0.json new file mode 100644 index 000000000..87e1dfa07 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword2North.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShieldSword2North" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword2West.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword2West.v0.json new file mode 100644 index 000000000..ab806d5fe --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeShieldSword2West.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeShieldSword2West" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeSwordNorth.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordNorth.InternalItem.v0.json new file mode 100644 index 000000000..552c3e4f2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordNorth.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeSwordNorth.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeSwordNorth", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeSwordNorth.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordNorth.v0.json new file mode 100644 index 000000000..c38b36b6a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordNorth.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeSwordNorth", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeSwordNorth.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeSwordWest.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordWest.InternalItem.v0.json new file mode 100644 index 000000000..a72162685 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordWest.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeSwordWest.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeSwordWest", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecorativeSwordWest.v0.json b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordWest.v0.json new file mode 100644 index 000000000..ac9bbb81e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecorativeSwordWest.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.DecorativeSwordWest", + "properties": [ + { + "name": "Item", + "type": "Server.Items.DecorativeSwordWest.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DiscMace.v0.json b/Projects/UOContent/Migrations/Server.Items.DiscMace.v0.json new file mode 100644 index 000000000..98a23931e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DiscMace.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DiscMace" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DreadSword.v0.json b/Projects/UOContent/Migrations/Server.Items.DreadSword.v0.json new file mode 100644 index 000000000..a57fdb8e9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DreadSword.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DreadSword" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DualPointedSpear.v0.json b/Projects/UOContent/Migrations/Server.Items.DualPointedSpear.v0.json new file mode 100644 index 000000000..713fdcf83 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DualPointedSpear.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DualPointedSpear" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DualShortAxes.v0.json b/Projects/UOContent/Migrations/Server.Items.DualShortAxes.v0.json new file mode 100644 index 000000000..4b442af12 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DualShortAxes.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.DualShortAxes" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenPodium.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenPodium.v0.json new file mode 100644 index 000000000..9b0604d54 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElvenPodium.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElvenPodium" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenReadingChair.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenReadingChair.v0.json new file mode 100644 index 000000000..739aad5e8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElvenReadingChair.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElvenReadingChair" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelEastAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelEastAddon.v0.json new file mode 100644 index 000000000..63b2de7aa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelEastAddon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElvenSpinningWheelEastAddon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelEastDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelEastDeed.v0.json new file mode 100644 index 000000000..47c1ea02c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelEastDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElvenSpinningWheelEastDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelSouthAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelSouthAddon.v0.json new file mode 100644 index 000000000..7226e3efa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelSouthAddon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElvenSpinningWheelSouthAddon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelSouthDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelSouthDeed.v0.json new file mode 100644 index 000000000..f2c5f90bd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningWheelSouthDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ElvenSpinningWheelSouthDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelEastAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelEastAddon.v0.json deleted file mode 100644 index dc71c0d6b..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelEastAddon.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ElvenSpinningwheelEastAddon" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelEastDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelEastDeed.v0.json deleted file mode 100644 index 0148d9b4d..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelEastDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ElvenSpinningwheelEastDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelSouthAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelSouthAddon.v0.json deleted file mode 100644 index 1f33e596e..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelSouthAddon.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ElvenSpinningwheelSouthAddon" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelSouthDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelSouthDeed.v0.json deleted file mode 100644 index 318061260..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ElvenSpinningwheelSouthDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ElvenSpinningwheelSouthDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FaerieFire.v0.json b/Projects/UOContent/Migrations/Server.Items.FaerieFire.v0.json new file mode 100644 index 000000000..963042e69 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FaerieFire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FaerieFire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FancyElvenArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.FancyElvenArmoire.v0.json new file mode 100644 index 000000000..a0edeb010 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FancyElvenArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FancyElvenArmoire" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FancyWoodenChairCushion.v0.json b/Projects/UOContent/Migrations/Server.Items.FancyWoodenChairCushion.v0.json new file mode 100644 index 000000000..6849b59ad --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FancyWoodenChairCushion.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FancyWoodenChairCushion" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FootStool.v0.json b/Projects/UOContent/Migrations/Server.Items.FootStool.v0.json new file mode 100644 index 000000000..d504f882c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FootStool.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.FootStool" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothArmsType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothArmsType1.v0.json new file mode 100644 index 000000000..2c8a60038 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothArmsType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothArmsType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothArmsType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothArmsType2.v0.json new file mode 100644 index 000000000..ad10e7655 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothArmsType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothArmsType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothChestType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothChestType1.v0.json new file mode 100644 index 000000000..7bbf21fcf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothChestType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothChestType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothChestType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothChestType2.v0.json new file mode 100644 index 000000000..97322169b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothChestType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothChestType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothKiltType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothKiltType1.v0.json new file mode 100644 index 000000000..bd434b331 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothKiltType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothKiltType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothKiltType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothKiltType2.v0.json new file mode 100644 index 000000000..313a481bd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothKiltType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothKiltType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothLegsType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothLegsType1.v0.json new file mode 100644 index 000000000..00b41e840 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothLegsType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothLegsType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishClothLegsType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishClothLegsType2.v0.json new file mode 100644 index 000000000..20ae6f861 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishClothLegsType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishClothLegsType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneArmsType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneArmsType1.v0.json new file mode 100644 index 000000000..b5b79efab --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneArmsType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneArmsType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneArmsType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneArmsType2.v0.json new file mode 100644 index 000000000..5d4a5e397 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneArmsType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneArmsType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneChestType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneChestType1.v0.json new file mode 100644 index 000000000..7eaf66017 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneChestType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneChestType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneChestType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneChestType2.v0.json new file mode 100644 index 000000000..fb4c260d2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneChestType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneChestType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneKiltType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneKiltType1.v0.json new file mode 100644 index 000000000..a73fe1eb3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneKiltType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneKiltType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneKiltType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneKiltType2.v0.json new file mode 100644 index 000000000..51600018c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneKiltType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneKiltType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneLegsType1.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneLegsType1.v0.json new file mode 100644 index 000000000..8bb36ca4c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneLegsType1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneLegsType1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishStoneLegsType2.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishStoneLegsType2.v0.json new file mode 100644 index 000000000..fbb0a499c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishStoneLegsType2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishStoneLegsType2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishTalwar.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishTalwar.v0.json new file mode 100644 index 000000000..eea41e567 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishTalwar.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishTalwar" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GargishWoodenShield.v0.json b/Projects/UOContent/Migrations/Server.Items.GargishWoodenShield.v0.json new file mode 100644 index 000000000..ae0ebdc87 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GargishWoodenShield.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GargishWoodenShield" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GiantReplicaAcorn.v0.json b/Projects/UOContent/Migrations/Server.Items.GiantReplicaAcorn.v0.json new file mode 100644 index 000000000..6d5849575 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GiantReplicaAcorn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GiantReplicaAcorn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GlassStaff.v0.json b/Projects/UOContent/Migrations/Server.Items.GlassStaff.v0.json new file mode 100644 index 000000000..3b6407a0a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GlassStaff.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GlassStaff" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GlassSword.v0.json b/Projects/UOContent/Migrations/Server.Items.GlassSword.v0.json new file mode 100644 index 000000000..7778923e5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GlassSword.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GlassSword" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LadyPortrait1.v0.json b/Projects/UOContent/Migrations/Server.Items.LadyPortrait1.v0.json new file mode 100644 index 000000000..6f6cf26dd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LadyPortrait1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LadyPortrait1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LadyPortrait2.v0.json b/Projects/UOContent/Migrations/Server.Items.LadyPortrait2.v0.json new file mode 100644 index 000000000..2152daace --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LadyPortrait2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LadyPortrait2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LargePainting.v0.json b/Projects/UOContent/Migrations/Server.Items.LargePainting.v0.json new file mode 100644 index 000000000..f7fe7d846 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.LargePainting.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.LargePainting" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ManPortrait1.v0.json b/Projects/UOContent/Migrations/Server.Items.ManPortrait1.v0.json new file mode 100644 index 000000000..7acb73c52 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ManPortrait1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ManPortrait1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ManPortrait2.v0.json b/Projects/UOContent/Migrations/Server.Items.ManPortrait2.v0.json new file mode 100644 index 000000000..6237dec35 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ManPortrait2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ManPortrait2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MischiefMaker.v0.json b/Projects/UOContent/Migrations/Server.Items.MischiefMaker.v0.json new file mode 100644 index 000000000..b07c2f836 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MischiefMaker.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MischiefMaker" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MountedDreadHorn.v0.json b/Projects/UOContent/Migrations/Server.Items.MountedDreadHorn.v0.json new file mode 100644 index 000000000..035552462 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MountedDreadHorn.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MountedDreadHorn" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnateElvenChair.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChair.v0.json new file mode 100644 index 000000000..418f474ea --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChair.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnateElvenChair" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestEastAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestEastAddon.v0.json new file mode 100644 index 000000000..baf8af5bb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestEastAddon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnateElvenChestEastAddon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestEastDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestEastDeed.v0.json new file mode 100644 index 000000000..9b5ca6b86 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestEastDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnateElvenChestEastDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestSouthAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestSouthAddon.v0.json new file mode 100644 index 000000000..e49da620d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestSouthAddon.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnateElvenChestSouthAddon" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestSouthDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestSouthDeed.v0.json new file mode 100644 index 000000000..85457a74c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.OrnateElvenChestSouthDeed.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.OrnateElvenChestSouthDeed" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RarewoodChest.v0.json b/Projects/UOContent/Migrations/Server.Items.RarewoodChest.v0.json new file mode 100644 index 000000000..8d08c9eb3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RarewoodChest.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RarewoodChest" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SerpentstoneStaff.v0.json b/Projects/UOContent/Migrations/Server.Items.SerpentstoneStaff.v0.json new file mode 100644 index 000000000..27adaf3bd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SerpentstoneStaff.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SerpentstoneStaff" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SimpleElvenArmoire.v0.json b/Projects/UOContent/Migrations/Server.Items.SimpleElvenArmoire.v0.json new file mode 100644 index 000000000..ee051d7c3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SimpleElvenArmoire.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SimpleElvenArmoire" +} \ 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 index c4d9a4e84..1dea32936 100644 --- a/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastAddon.v0.json +++ b/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastAddon.v0.json @@ -1,4 +1,4 @@ { "version": 0, - "type": "Server.Items.SpinningwheelEastAddon" + "type": "Server.Items.SpinningWheelEastAddon" } \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastDeed.v0.json index 3e42d0770..1ea90202c 100644 --- a/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastDeed.v0.json +++ b/Projects/UOContent/Migrations/Server.Items.SpinningwheelEastDeed.v0.json @@ -1,4 +1,4 @@ { "version": 0, - "type": "Server.Items.SpinningwheelEastDeed" + "type": "Server.Items.SpinningWheelEastDeed" } \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthAddon.v0.json index d4021129a..f36648b42 100644 --- a/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthAddon.v0.json +++ b/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthAddon.v0.json @@ -1,4 +1,4 @@ { "version": 0, - "type": "Server.Items.SpinningwheelSouthAddon" + "type": "Server.Items.SpinningWheelSouthAddon" } \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthDeed.v0.json index 9c99435eb..c897c743d 100644 --- a/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthDeed.v0.json +++ b/Projects/UOContent/Migrations/Server.Items.SpinningwheelSouthDeed.v0.json @@ -1,4 +1,4 @@ { "version": 0, - "type": "Server.Items.SpinningwheelSouthDeed" + "type": "Server.Items.SpinningWheelSouthDeed" } \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StoneChair.v0.json b/Projects/UOContent/Migrations/Server.Items.StoneChair.v0.json new file mode 100644 index 000000000..c9c602579 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StoneChair.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.StoneChair" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Stool.v0.json b/Projects/UOContent/Migrations/Server.Items.Stool.v0.json new file mode 100644 index 000000000..f72bb072b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Stool.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Stool" +} \ 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 new file mode 100644 index 000000000..d69b5f76b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Talwar.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Talwar" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry1N.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry1N.InternalItem.v0.json new file mode 100644 index 000000000..2fb1aaec8 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry1N.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry1N.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry1N", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry1N.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry1N.v0.json new file mode 100644 index 000000000..577c9cd64 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry1N.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry1N", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry1N.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry2N.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry2N.InternalItem.v0.json new file mode 100644 index 000000000..7fcb1f326 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry2N.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry2N.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry2N", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry2N.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry2N.v0.json new file mode 100644 index 000000000..e8a098ee3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry2N.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry2N", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry2N.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry2W.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry2W.InternalItem.v0.json new file mode 100644 index 000000000..1441ac840 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry2W.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry2W.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry2W", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry2W.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry2W.v0.json new file mode 100644 index 000000000..2b3f8c6d6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry2W.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry2W", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry2W.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry3N.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry3N.InternalItem.v0.json new file mode 100644 index 000000000..4ec5ebbd3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry3N.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry3N.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry3N", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry3N.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry3N.v0.json new file mode 100644 index 000000000..e82ac3811 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry3N.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry3N", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry3N.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry3W.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry3W.InternalItem.v0.json new file mode 100644 index 000000000..5c4a46669 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry3W.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry3W.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry3W", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry3W.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry3W.v0.json new file mode 100644 index 000000000..462010260 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry3W.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry3W", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry3W.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry4N.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry4N.InternalItem.v0.json new file mode 100644 index 000000000..7bd842cbf --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry4N.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry4N.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry4N", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry4N.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry4N.v0.json new file mode 100644 index 000000000..0fff93718 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry4N.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry4N", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry4N.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry4W.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry4W.InternalItem.v0.json new file mode 100644 index 000000000..88993f352 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry4W.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry4W.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry4W", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry4W.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry4W.v0.json new file mode 100644 index 000000000..6e53c0322 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry4W.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry4W", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry4W.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry5N.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry5N.InternalItem.v0.json new file mode 100644 index 000000000..cec061580 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry5N.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry5N.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry5N", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry5N.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry5N.v0.json new file mode 100644 index 000000000..a4b4790d2 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry5N.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry5N", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry5N.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry5W.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry5W.InternalItem.v0.json new file mode 100644 index 000000000..8b100ca0e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry5W.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry5W.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry5W", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry5W.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry5W.v0.json new file mode 100644 index 000000000..4918fc403 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry5W.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry5W", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry5W.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry6N.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry6N.InternalItem.v0.json new file mode 100644 index 000000000..72bcfcc5e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry6N.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry6N.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry6N", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry6N.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry6N.v0.json new file mode 100644 index 000000000..e03a8239d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry6N.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry6N", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry6N.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry6W.InternalItem.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry6W.InternalItem.v0.json new file mode 100644 index 000000000..b947d843e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry6W.InternalItem.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry6W.InternalItem", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry6W", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Tapestry6W.v0.json b/Projects/UOContent/Migrations/Server.Items.Tapestry6W.v0.json new file mode 100644 index 000000000..952d88021 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Tapestry6W.v0.json @@ -0,0 +1,11 @@ +{ + "version": 0, + "type": "Server.Items.Tapestry6W", + "properties": [ + { + "name": "Item", + "type": "Server.Items.Tapestry6W.InternalItem", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Throne.v0.json b/Projects/UOContent/Migrations/Server.Items.Throne.v0.json new file mode 100644 index 000000000..a5a0f6f72 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Throne.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Throne" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TinkerTools.v0.json b/Projects/UOContent/Migrations/Server.Items.TinkerTools.v0.json new file mode 100644 index 000000000..0a058eb5d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TinkerTools.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TinkerTools" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TinkersTools.v0.json b/Projects/UOContent/Migrations/Server.Items.TinkersTools.v0.json new file mode 100644 index 000000000..4b6531b80 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TinkersTools.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.TinkersTools" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WomanPortrait1.v0.json b/Projects/UOContent/Migrations/Server.Items.WomanPortrait1.v0.json new file mode 100644 index 000000000..9dc75a9a1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WomanPortrait1.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WomanPortrait1" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WomanPortrait2.v0.json b/Projects/UOContent/Migrations/Server.Items.WomanPortrait2.v0.json new file mode 100644 index 000000000..b3a6cd827 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WomanPortrait2.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WomanPortrait2" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenBench.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenBench.v0.json new file mode 100644 index 000000000..bf8f469ea --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenBench.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenBench" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenChair.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenChair.v0.json new file mode 100644 index 000000000..8f4559959 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenChair.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenChair" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenChairCushion.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenChairCushion.v0.json new file mode 100644 index 000000000..5b913d564 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenChairCushion.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenChairCushion" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WoodenThrone.v0.json b/Projects/UOContent/Migrations/Server.Items.WoodenThrone.v0.json new file mode 100644 index 000000000..120b862d1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WoodenThrone.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WoodenThrone" +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/AutoRestart.cs b/Projects/UOContent/Misc/AutoRestart.cs index db881e4b7..b281b9349 100644 --- a/Projects/UOContent/Misc/AutoRestart.cs +++ b/Projects/UOContent/Misc/AutoRestart.cs @@ -1,4 +1,5 @@ using System; +using Server.Saves; namespace Server.Misc { @@ -12,15 +13,15 @@ namespace Server.Misc get => _enabled; set { + _enabled = value; + if (value) { - _enabled = true; _autoRestart ??= new AutoRestart(); _autoRestart.Start(); } else { - _enabled = false; _autoRestart?.Stop(); _autoRestart = null; } @@ -29,21 +30,20 @@ namespace Server.Misc private static readonly TimeSpan RestartTime = TimeSpan.FromHours(2.0); // time of day at which to restart - private static readonly TimeSpan - RestartDelay = - TimeSpan.Zero; // how long the server should remain active before restart (period of 'server wars') + // how long the server should remain active before restart (period of 'server wars') + private static readonly TimeSpan RestartDelay = TimeSpan.Zero; - private static readonly TimeSpan - WarningDelay = TimeSpan.FromMinutes(1.0); // at what interval should the shutdown message be displayed? + // at what interval should the shutdown message be displayed? + private static readonly TimeSpan WarningDelay = TimeSpan.FromMinutes(1.0); private static DateTime m_RestartTime; public AutoRestart() : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) { - m_RestartTime = DateTime.UtcNow.Date + RestartTime; + m_RestartTime = Core.Now.Date + RestartTime; - if (m_RestartTime < DateTime.UtcNow) + if (m_RestartTime < Core.Now) { m_RestartTime += TimeSpan.FromDays(1.0); } @@ -71,7 +71,7 @@ namespace Server.Misc { e.Mobile.SendMessage("You have initiated server shutdown."); Enabled = true; - m_RestartTime = DateTime.UtcNow; + m_RestartTime = Core.Now; } } @@ -92,7 +92,7 @@ namespace Server.Misc return; } - if (DateTime.UtcNow < m_RestartTime) + if (Core.Now < m_RestartTime) { return; } diff --git a/Projects/UOContent/Misc/AutoSave.cs b/Projects/UOContent/Misc/AutoSave.cs deleted file mode 100644 index b35774716..000000000 --- a/Projects/UOContent/Misc/AutoSave.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.IO; -using Server.Logging; - -namespace Server.Misc -{ - public class AutoSave : Timer - { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(Persistence)); - - public static TimeSpan Delay { get; private set; } - public static TimeSpan Warning { get; private set; } - public static string BackupPath { get; private set; } - - public AutoSave() : base(Delay - Warning, Delay) - { - } - - public static bool SavesEnabled { get; set; } = true; - - public static void Configure() - { - BackupPath = ServerConfiguration.GetOrUpdateSetting("autosave.backupPath", "Backups/Automatic"); - Delay = ServerConfiguration.GetOrUpdateSetting("autosave.saveDelay", TimeSpan.FromMinutes(5.0)); - Warning = ServerConfiguration.GetOrUpdateSetting("autosave.warningDelay", TimeSpan.Zero); - } - - public static void Initialize() - { - new AutoSave().Start(); - CommandSystem.Register("SetSaves", AccessLevel.Administrator, SetSaves_OnCommand); - - EventSink.WorldSavePostSnapshot += Backup; - } - - [Usage("SetSaves "), Description("Enables or disables automatic shard saving.")] - public static void SetSaves_OnCommand(CommandEventArgs e) - { - if (e.Length == 1) - { - SavesEnabled = e.GetBoolean(0); - e.Mobile.SendMessage("Saves have been {0}.", SavesEnabled ? "enabled" : "disabled"); - } - else - { - e.Mobile.SendMessage("Format: SetSaves "); - } - } - - protected override void OnTick() - { - if (!SavesEnabled || AutoRestart.Restarting) - { - return; - } - - if (Warning == TimeSpan.Zero) - { - Save(); - } - else - { - var s = (int)Warning.TotalSeconds; - var m = s / 60; - s %= 60; - - if (m > 0 && s > 0) - { - World.Broadcast( - 0x35, - true, - "The world will save in {0} minute{1} and {2} second{3}.", - m, - m != 1 ? "s" : "", - s, - s != 1 ? "s" : "" - ); - } - else if (m > 0) - { - World.Broadcast(0x35, true, "The world will save in {0} minute{1}.", m, m != 1 ? "s" : ""); - } - else - { - World.Broadcast(0x35, true, "The world will save in {0} second{1}.", s, s != 1 ? "s" : ""); - } - - StartTimer(Warning, Save); - } - } - - public static void Save() - { - if (!AutoRestart.Restarting && World.WorldState == WorldState.Running) - { - World.Save(); - } - } - - private static void Backup(WorldSavePostSnapshotEventArgs args) - { - if (!Directory.Exists(args.OldSavePath)) - { - return; - } - - var backupPath = Path.Combine(BackupPath, Utility.GetTimeStamp()); - AssemblyHandler.EnsureDirectory(BackupPath); - Directory.Move(args.OldSavePath, backupPath); - - logger.Information($"Created backup at {backupPath}"); - } - } -} diff --git a/Projects/UOContent/Misc/BuffIcons.cs b/Projects/UOContent/Misc/BuffIcons.cs index 7008ed1cd..0c9e48340 100644 --- a/Projects/UOContent/Misc/BuffIcons.cs +++ b/Projects/UOContent/Misc/BuffIcons.cs @@ -228,57 +228,190 @@ namespace Server { DismountPrevention = 0x3E9, NoRearm = 0x3EA, - - // Currently, no 0x3EB or 0x3EC - NightSight = 0x3ED, // * + //Currently, no 0x3EB or 0x3EC + NightSight = 0x3ED, //* DeathStrike, EvilOmen, - UnknownStandingSwirl, // Which is healing throttle & Stamina throttle? - UnknownKneelingSword, - DivineFury, // * - EnemyOfOne, // * - HidingAndOrStealth, // * - ActiveMeditation, // * - BloodOathCaster, // * - BloodOathCurse, // * - CorpseSkin, // * - Mindrot, // * - PainSpike, // * + HonoredDebuff, + AchievePerfection, + DivineFury, //* + EnemyOfOne, //* + HidingAndOrStealth, //* + ActiveMeditation, //* + BloodOathCaster, //* + BloodOathCurse, //* + CorpseSkin, //* + Mindrot, //* + PainSpike, //* Strangle, - GiftOfRenewal, // * - AttuneWeapon, // * - Thunderstorm, // * - EssenceOfWind, // * - EtherealVoyage, // * - GiftOfLife, // * - ArcaneEmpowerment, // * + GiftOfRenewal, //* + AttuneWeapon, //* + Thunderstorm, //* + EssenceOfWind, //* + EtherealVoyage, //* + GiftOfLife, //* + ArcaneEmpowerment, //* MortalStrike, - ReactiveArmor, // * - Protection, // * + ReactiveArmor, //* + Protection, //* ArchProtection, - MagicReflection, // * - Incognito, // * + MagicReflection, //* + Incognito, //* Disguised, AnimalForm, Polymorph, - Invisibility, // * - Paralyze, // * + Invisibility, //* + Paralyze, //* Poison, Bleed, - Clumsy, // * - FeebleMind, // * - Weaken, // * - Curse, // * + Clumsy, //* + FeebleMind, //* + Weaken, //* + Curse, //* MassCurse, - Agility, // * - Cunning, // * - Strength, // * - Bless, // * + Agility, //* + Cunning, //* + Strength, //* + Bless, //* Sleep, StoneForm, SpellPlague, - SpellTrigger, - NetherBolt, - Fly + Berserk, + MassSleep, + Fly, + Inspire, + Invigorate, + Resilience, + Perseverance, + TribulationTarget, + DespairTarget, + FishPie = 0x426, + HitLowerAttack, + HitLowerDefense, + DualWield, + Block, + DefenseMastery, + DespairCaster, + Healing, + SpellFocusingBuff, + SpellFocusingDebuff, + RageFocusingDebuff, + RageFocusingBuff, + Warding, + TribulationCaster, + ForceArrow, + Disarm, + Surge, + Feint, + TalonStrike, + PsychicAttack, + ConsecrateWeapon, + GrapesOfWrath, + EnemyOfOneDebuff, + HorrificBeast, + LichForm, + VampiricEmbrace, + CurseWeapon, + ReaperForm, + ImmolatingWeapon, + Enchant, + HonorableExecution, + Confidence, + Evasion, + CounterAttack, + LightningStrike, + MomentumStrike, + OrangePetals, + RoseOfTrinsic, + PoisonImmunity, + Veterinary, + Perfection, + Honored, + ManaPhase, + FanDancerFanFire, + Rage, + Webbing, + MedusaStone, + TrueFear, + AuraOfNausea, + HowlOfCacophony, + GazeDespair, + HiryuPhysicalResistance, + RuneBeetleCorruption, + BloodwormAnemia, + RotwormBloodDisease, + SkillUseDelay, + FactionStatLoss, + HeatOfBattleStatus, + CriminalStatus, + ArmorPierce, + SplinteringEffect, + SwingSpeedDebuff, + WraithForm, + CityTradeDeal = 0x466, + HumilityDebuff = 0x467, + Spirituality, + Humility, + // Skill Masteries + Rampage, + Stagger, // Debuff + Toughness, + Thrust, + Pierce, // Debuff + PlayingTheOdds, + FocusedEye, + Onslaught, // Debuff + ElementalFury, + ElementalFuryDebuff, // Debuff + CalledShot, + Knockout, + SavingThrow, + Conduit, + EtherealBurst, + MysticWeapon, + ManaShield, + AnticipateHit, + Warcry, + Shadow, + WhiteTigerForm, + Bodyguard, + HeightenedSenses, + Tolerance, + DeathRay, + DeathRayDebuff, + Intuition, + EnchantedSummoning, + ShieldBash, + Whispering, + CombatTraining, + InjectedStrikeDebuff, + InjectedStrike, + UnknownTomato, + PlayingTheOddsDebuff, + DragonTurtleDebuff, + Boarding, + Potency, + ThrustDebuff, + FistsOfFury, // 1169 + BarrabHemolymphConcentrate, + JukariBurnPoiltice, + KurakAmbushersEssence, + BarakoDraftOfMight, + UraliTranceTonic, + SakkhraProphylaxis, // 1175 + Sparks, + Swarm, + BoneBreaker, + Unknown2, + SwarmImmune, + BoneBreakerImmune, + UnknownGoblin, + UnknownRedDrop, + UnknownStar, + FeintDebuff, + CaddelliteInfused, + PotionGloriousFortune, + MysticalPolymorphTotem, + UnknownDebuff, } } diff --git a/Projects/UOContent/Misc/CharacterCreation.cs b/Projects/UOContent/Misc/CharacterCreation.cs index b88ba1da7..0b58f83a8 100644 --- a/Projects/UOContent/Misc/CharacterCreation.cs +++ b/Projects/UOContent/Misc/CharacterCreation.cs @@ -1,6 +1,7 @@ using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Accounting; -using Server.Factions; using Server.Items; using Server.Logging; using Server.Mobiles; @@ -12,20 +13,78 @@ namespace Server.Misc { private static readonly ILogger logger = LogFactory.GetLogger(typeof(CharacterCreation)); + // Allowed skills that are not race or era specific + private static readonly HashSet _allowedStartingSkills = new() + { + 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 + }; + private static readonly TimeSpan BadStartMessageDelay = TimeSpan.FromSeconds(3.5); - private static readonly CityInfo m_NewHavenInfo = + private static readonly CityInfo _newHavenInfo = new("New Haven", "The Bountiful Harvest Inn", 3503, 2574, 14, Map.Trammel); - private static Mobile m_Mobile; - public static void Initialize() { // Register our event handler EventSink.CharacterCreated += EventSink_CharacterCreated; } - private static void AddBackpack(Mobile m) + private static void AddBackpack(this Mobile m) { var pack = m.Backpack; @@ -37,564 +96,10 @@ namespace Server.Misc m.AddItem(pack); } - PackItem(new RedBook("a book", m.Name, 20, true)); - PackItem(new Gold(1000)); // Starting gold can be customized here - PackItem(new Dagger()); - PackItem(new Candle()); - } - - private static Item MakeNewbie(Item item) - { - if (!Core.AOS) - { - item.LootType = LootType.Newbied; - } - - return item; - } - - private static void PlaceItemIn(Container parent, int x, int y, Item item) - { - parent.AddItem(item); - item.Location = new Point3D(x, y, 0); - } - - private static Item MakePotionKeg(PotionEffect type, int hue) - { - var keg = new PotionKeg(); - - keg.Held = 100; - keg.Type = type; - keg.Hue = hue; - - return MakeNewbie(keg); - } - - private static void FillBankAOS(Mobile m) - { - var bank = m.BankBox; - - // The new AOS bankboxes don't have powerscrolls, they are automatically 'applied': - - for (var i = 0; i < PowerScroll.Skills.Count; ++i) - { - m.Skills[PowerScroll.Skills[i]].Cap = 120.0; - } - - m.StatCap = 250; - - Container cont; - - // Begin box of money - cont = new WoodenBox { ItemID = 0xE7D, Hue = 0x489 }; - - PlaceItemIn(cont, 16, 51, new BankCheck(500000)); - PlaceItemIn(cont, 28, 51, new BankCheck(250000)); - PlaceItemIn(cont, 40, 51, new BankCheck(100000)); - PlaceItemIn(cont, 52, 51, new BankCheck(100000)); - PlaceItemIn(cont, 64, 51, new BankCheck(50000)); - - PlaceItemIn(cont, 16, 115, new Silver(9000)); - PlaceItemIn(cont, 34, 115, new Gold(60000)); - - PlaceItemIn(bank, 18, 169, cont); - // End box of money - - // Begin bag of potion kegs - cont = new Backpack { Name = "Various Potion Kegs" }; - - PlaceItemIn(cont, 45, 149, MakePotionKeg(PotionEffect.CureGreater, 0x2D)); - PlaceItemIn(cont, 69, 149, MakePotionKeg(PotionEffect.HealGreater, 0x499)); - PlaceItemIn(cont, 93, 149, MakePotionKeg(PotionEffect.PoisonDeadly, 0x46)); - PlaceItemIn(cont, 117, 149, MakePotionKeg(PotionEffect.RefreshTotal, 0x21)); - PlaceItemIn(cont, 141, 149, MakePotionKeg(PotionEffect.ExplosionGreater, 0x74)); - - PlaceItemIn(cont, 93, 82, new Bottle(1000)); - - PlaceItemIn(bank, 53, 169, cont); - // End bag of potion kegs - - // Begin bag of tools - cont = new Bag { Name = "Tool Bag" }; - - PlaceItemIn(cont, 30, 35, new TinkerTools(1000)); - PlaceItemIn(cont, 60, 35, new HousePlacementTool()); - PlaceItemIn(cont, 90, 35, new DovetailSaw(1000)); - PlaceItemIn(cont, 30, 68, new Scissors()); - PlaceItemIn(cont, 45, 68, new MortarPestle(1000)); - PlaceItemIn(cont, 75, 68, new ScribesPen(1000)); - PlaceItemIn(cont, 90, 68, new SmithHammer(1000)); - PlaceItemIn(cont, 30, 118, new TwoHandedAxe()); - PlaceItemIn(cont, 60, 118, new FletcherTools(1000)); - PlaceItemIn(cont, 90, 118, new SewingKit(1000)); - - PlaceItemIn(cont, 36, 51, new RunicHammer(CraftResource.DullCopper, 1000)); - PlaceItemIn(cont, 42, 51, new RunicHammer(CraftResource.ShadowIron, 1000)); - PlaceItemIn(cont, 48, 51, new RunicHammer(CraftResource.Copper, 1000)); - PlaceItemIn(cont, 54, 51, new RunicHammer(CraftResource.Bronze, 1000)); - PlaceItemIn(cont, 61, 51, new RunicHammer(CraftResource.Gold, 1000)); - PlaceItemIn(cont, 67, 51, new RunicHammer(CraftResource.Agapite, 1000)); - PlaceItemIn(cont, 73, 51, new RunicHammer(CraftResource.Verite, 1000)); - PlaceItemIn(cont, 79, 51, new RunicHammer(CraftResource.Valorite, 1000)); - - PlaceItemIn(cont, 36, 55, new RunicSewingKit(CraftResource.SpinedLeather, 1000)); - PlaceItemIn(cont, 42, 55, new RunicSewingKit(CraftResource.HornedLeather, 1000)); - PlaceItemIn(cont, 48, 55, new RunicSewingKit(CraftResource.BarbedLeather, 1000)); - - PlaceItemIn(bank, 118, 169, cont); - // End bag of tools - - // Begin bag of archery ammo - cont = new Bag { Name = "Bag Of Archery Ammo" }; - - PlaceItemIn(cont, 48, 76, new Arrow(5000)); - PlaceItemIn(cont, 72, 76, new Bolt(5000)); - - PlaceItemIn(bank, 118, 124, cont); - // End bag of archery ammo - - // Begin bag of treasure maps - cont = new Bag { Name = "Bag Of Treasure Maps" }; - - PlaceItemIn(cont, 30, 35, new TreasureMap(1, Map.Trammel)); - PlaceItemIn(cont, 45, 35, new TreasureMap(2, Map.Trammel)); - PlaceItemIn(cont, 60, 35, new TreasureMap(3, Map.Trammel)); - PlaceItemIn(cont, 75, 35, new TreasureMap(4, Map.Trammel)); - PlaceItemIn(cont, 90, 35, new TreasureMap(5, Map.Trammel)); - PlaceItemIn(cont, 90, 35, new TreasureMap(6, Map.Trammel)); - - PlaceItemIn(cont, 30, 50, new TreasureMap(1, Map.Trammel)); - PlaceItemIn(cont, 45, 50, new TreasureMap(2, Map.Trammel)); - PlaceItemIn(cont, 60, 50, new TreasureMap(3, Map.Trammel)); - PlaceItemIn(cont, 75, 50, new TreasureMap(4, Map.Trammel)); - PlaceItemIn(cont, 90, 50, new TreasureMap(5, Map.Trammel)); - PlaceItemIn(cont, 90, 50, new TreasureMap(6, Map.Trammel)); - - PlaceItemIn(cont, 55, 100, new Lockpick(30)); - PlaceItemIn(cont, 60, 100, new Pickaxe()); - - PlaceItemIn(bank, 98, 124, cont); - // End bag of treasure maps - - // Begin bag of raw materials - cont = new Bag { Hue = 0x835, Name = "Raw Materials Bag" }; - - PlaceItemIn(cont, 92, 60, new BarbedLeather(5000)); - PlaceItemIn(cont, 92, 68, new HornedLeather(5000)); - PlaceItemIn(cont, 92, 76, new SpinedLeather(5000)); - PlaceItemIn(cont, 92, 84, new Leather(5000)); - - PlaceItemIn(cont, 30, 118, new Cloth(5000)); - PlaceItemIn(cont, 30, 84, new Board(5000)); - PlaceItemIn(cont, 57, 80, new BlankScroll(500)); - - PlaceItemIn(cont, 30, 35, new DullCopperIngot(5000)); - PlaceItemIn(cont, 37, 35, new ShadowIronIngot(5000)); - PlaceItemIn(cont, 44, 35, new CopperIngot(5000)); - PlaceItemIn(cont, 51, 35, new BronzeIngot(5000)); - PlaceItemIn(cont, 58, 35, new GoldIngot(5000)); - PlaceItemIn(cont, 65, 35, new AgapiteIngot(5000)); - PlaceItemIn(cont, 72, 35, new VeriteIngot(5000)); - PlaceItemIn(cont, 79, 35, new ValoriteIngot(5000)); - PlaceItemIn(cont, 86, 35, new IronIngot(5000)); - - PlaceItemIn(cont, 30, 59, new RedScales(5000)); - PlaceItemIn(cont, 36, 59, new YellowScales(5000)); - PlaceItemIn(cont, 42, 59, new BlackScales(5000)); - PlaceItemIn(cont, 48, 59, new GreenScales(5000)); - PlaceItemIn(cont, 54, 59, new WhiteScales(5000)); - PlaceItemIn(cont, 60, 59, new BlueScales(5000)); - - PlaceItemIn(bank, 98, 169, cont); - // End bag of raw materials - - // Begin bag of spell casting stuff - cont = new Backpack { Hue = 0x480, Name = "Spell Casting Stuff" }; - - PlaceItemIn(cont, 45, 105, new Spellbook(ulong.MaxValue)); - PlaceItemIn(cont, 65, 105, new NecromancerSpellbook(0xFFFFUL)); - PlaceItemIn(cont, 85, 105, new BookOfChivalry()); - PlaceItemIn(cont, 105, 105, new BookOfBushido()); // Default ctor = full - PlaceItemIn(cont, 125, 105, new BookOfNinjitsu()); // Default ctor = full - - var runebook = new Runebook(10); - runebook.CurCharges = runebook.MaxCharges; - PlaceItemIn(cont, 145, 105, runebook); - - Item toHue = new BagOfReagents(150) { Hue = 0x2D }; - PlaceItemIn(cont, 45, 150, toHue); - - toHue = new BagOfNecroReagents(150) { Hue = 0x488 }; - PlaceItemIn(cont, 65, 150, toHue); - - PlaceItemIn(cont, 140, 150, new BagOfAllReagents(500)); - - for (var i = 0; i < 9; ++i) - { - PlaceItemIn(cont, 45 + i * 10, 75, new RecallRune()); - } - - PlaceItemIn(cont, 141, 74, new FireHorn()); - - PlaceItemIn(bank, 78, 169, cont); - // End bag of spell casting stuff - - // Begin bag of ethereals - cont = new Backpack { Hue = 0x490, Name = "Bag Of Ethy's!" }; - - PlaceItemIn(cont, 45, 66, new EtherealHorse()); - PlaceItemIn(cont, 69, 82, new EtherealOstard()); - PlaceItemIn(cont, 93, 99, new EtherealLlama()); - PlaceItemIn(cont, 117, 115, new EtherealKirin()); - PlaceItemIn(cont, 45, 132, new EtherealUnicorn()); - PlaceItemIn(cont, 69, 66, new EtherealRidgeback()); - PlaceItemIn(cont, 93, 82, new EtherealSwampDragon()); - PlaceItemIn(cont, 117, 99, new EtherealBeetle()); - - PlaceItemIn(bank, 38, 124, cont); - // End bag of ethereals - - // Begin first bag of artifacts - cont = new Backpack { Hue = 0x48F, Name = "Bag of Artifacts" }; - - PlaceItemIn(cont, 45, 66, new TitansHammer()); - PlaceItemIn(cont, 69, 82, new InquisitorsResolution()); - PlaceItemIn(cont, 93, 99, new BladeOfTheRighteous()); - PlaceItemIn(cont, 117, 115, new ZyronicClaw()); - - PlaceItemIn(bank, 58, 124, cont); - // End first bag of artifacts - - // Begin second bag of artifacts - cont = new Backpack { Hue = 0x48F, Name = "Bag of Artifacts" }; - - PlaceItemIn(cont, 45, 66, new GauntletsOfNobility()); - PlaceItemIn(cont, 69, 82, new MidnightBracers()); - PlaceItemIn(cont, 93, 99, new VoiceOfTheFallenKing()); - PlaceItemIn(cont, 117, 115, new OrnateCrownOfTheHarrower()); - PlaceItemIn(cont, 45, 132, new HelmOfInsight()); - PlaceItemIn(cont, 69, 66, new HolyKnightsBreastplate()); - PlaceItemIn(cont, 93, 82, new ArmorOfFortune()); - PlaceItemIn(cont, 117, 99, new TunicOfFire()); - PlaceItemIn(cont, 45, 115, new LeggingsOfBane()); - PlaceItemIn(cont, 69, 132, new ArcaneShield()); - PlaceItemIn(cont, 93, 66, new Aegis()); - PlaceItemIn(cont, 117, 82, new RingOfTheVile()); - PlaceItemIn(cont, 45, 99, new BraceletOfHealth()); - PlaceItemIn(cont, 69, 115, new RingOfTheElements()); - PlaceItemIn(cont, 93, 132, new OrnamentOfTheMagician()); - PlaceItemIn(cont, 117, 66, new DivineCountenance()); - PlaceItemIn(cont, 45, 82, new JackalsCollar()); - PlaceItemIn(cont, 69, 99, new HuntersHeaddress()); - PlaceItemIn(cont, 93, 115, new HatOfTheMagi()); - PlaceItemIn(cont, 117, 132, new ShadowDancerLeggings()); - PlaceItemIn(cont, 45, 66, new SpiritOfTheTotem()); - PlaceItemIn(cont, 69, 82, new BladeOfInsanity()); - PlaceItemIn(cont, 93, 99, new AxeOfTheHeavens()); - PlaceItemIn(cont, 117, 115, new TheBeserkersMaul()); - PlaceItemIn(cont, 45, 132, new Frostbringer()); - PlaceItemIn(cont, 69, 66, new BreathOfTheDead()); - PlaceItemIn(cont, 93, 82, new TheDragonSlayer()); - PlaceItemIn(cont, 117, 99, new BoneCrusher()); - PlaceItemIn(cont, 45, 115, new StaffOfTheMagi()); - PlaceItemIn(cont, 69, 132, new SerpentsFang()); - PlaceItemIn(cont, 93, 66, new LegacyOfTheDreadLord()); - PlaceItemIn(cont, 117, 82, new TheTaskmaster()); - PlaceItemIn(cont, 45, 99, new TheDryadBow()); - - PlaceItemIn(bank, 78, 124, cont); - // End second bag of artifacts - - // Begin bag of minor artifacts - cont = new Backpack { Hue = 0x48F, Name = "Bag of Minor Artifacts" }; - - PlaceItemIn(cont, 45, 66, new LunaLance()); - PlaceItemIn(cont, 69, 82, new VioletCourage()); - PlaceItemIn(cont, 93, 99, new CavortingClub()); - PlaceItemIn(cont, 117, 115, new CaptainQuacklebushsCutlass()); - PlaceItemIn(cont, 45, 132, new NightsKiss()); - PlaceItemIn(cont, 69, 66, new ShipModelOfTheHMSCape()); - PlaceItemIn(cont, 93, 82, new AdmiralsHeartyRum()); - PlaceItemIn(cont, 117, 99, new CandelabraOfSouls()); - PlaceItemIn(cont, 45, 115, new IolosLute()); - PlaceItemIn(cont, 69, 132, new GwennosHarp()); - PlaceItemIn(cont, 93, 66, new ArcticDeathDealer()); - PlaceItemIn(cont, 117, 82, new EnchantedTitanLegBone()); - PlaceItemIn(cont, 45, 99, new NoxRangersHeavyCrossbow()); - PlaceItemIn(cont, 69, 115, new BlazeOfDeath()); - PlaceItemIn(cont, 93, 132, new DreadPirateHat()); - PlaceItemIn(cont, 117, 66, new BurglarsBandana()); - PlaceItemIn(cont, 45, 82, new GoldBricks()); - PlaceItemIn(cont, 69, 99, new AlchemistsBauble()); - PlaceItemIn(cont, 93, 115, new PhillipsWoodenSteed()); - PlaceItemIn(cont, 117, 132, new PolarBearMask()); - PlaceItemIn(cont, 45, 66, new BowOfTheJukaKing()); - PlaceItemIn(cont, 69, 82, new GlovesOfThePugilist()); - PlaceItemIn(cont, 93, 99, new OrcishVisage()); - PlaceItemIn(cont, 117, 115, new StaffOfPower()); - PlaceItemIn(cont, 45, 132, new ShieldOfInvulnerability()); - PlaceItemIn(cont, 69, 66, new HeartOfTheLion()); - PlaceItemIn(cont, 93, 82, new ColdBlood()); - PlaceItemIn(cont, 117, 99, new GhostShipAnchor()); - PlaceItemIn(cont, 45, 115, new SeahorseStatuette()); - PlaceItemIn(cont, 69, 132, new WrathOfTheDryad()); - PlaceItemIn(cont, 93, 66, new PixieSwatter()); - - for (var i = 0; i < 10; i++) - { - PlaceItemIn(cont, 117, 128, new MessageInABottle(Utility.RandomBool() ? Map.Trammel : Map.Felucca, 4)); - } - - PlaceItemIn(bank, 18, 124, cont); - - if (Core.SE) - { - cont = new Bag { Hue = 0x501, Name = "Tokuno Minor Artifacts" }; - - PlaceItemIn(cont, 42, 70, new Exiler()); - PlaceItemIn(cont, 38, 53, new HanzosBow()); - PlaceItemIn(cont, 45, 40, new TheDestroyer()); - PlaceItemIn(cont, 92, 80, new DragonNunchaku()); - PlaceItemIn(cont, 42, 56, new PeasantsBokuto()); - PlaceItemIn(cont, 44, 71, new TomeOfEnlightenment()); - PlaceItemIn(cont, 35, 35, new ChestOfHeirlooms()); - PlaceItemIn(cont, 29, 0, new HonorableSwords()); - PlaceItemIn(cont, 49, 85, new AncientUrn()); - PlaceItemIn(cont, 51, 58, new FluteOfRenewal()); - PlaceItemIn(cont, 70, 51, new PigmentsOfTokuno()); - PlaceItemIn(cont, 40, 79, new AncientSamuraiDo()); - PlaceItemIn(cont, 51, 61, new LegsOfStability()); - PlaceItemIn(cont, 88, 78, new GlovesOfTheSun()); - PlaceItemIn(cont, 55, 62, new AncientFarmersKasa()); - PlaceItemIn(cont, 55, 83, new ArmsOfTacticalExcellence()); - PlaceItemIn(cont, 50, 85, new DaimyosHelm()); - PlaceItemIn(cont, 52, 78, new BlackLotusHood()); - PlaceItemIn(cont, 52, 79, new DemonForks()); - PlaceItemIn(cont, 33, 49, new PilferedDancerFans()); - - PlaceItemIn(bank, 58, 124, cont); - } - - if (Core.SE) // This bag came only after SE. - { - cont = new Bag { Name = "Bag of Bows" }; - - PlaceItemIn(cont, 31, 84, new Bow()); - PlaceItemIn(cont, 78, 74, new CompositeBow()); - PlaceItemIn(cont, 53, 71, new Crossbow()); - PlaceItemIn(cont, 56, 39, new HeavyCrossbow()); - PlaceItemIn(cont, 82, 72, new RepeatingCrossbow()); - PlaceItemIn(cont, 49, 45, new Yumi()); - - for (var i = 0; i < cont.Items.Count; i++) - { - if (cont.Items[i] is BaseRanged bow) - { - bow.Attributes.WeaponSpeed = 35; - bow.Attributes.WeaponDamage = 35; - } - } - - PlaceItemIn(bank, 108, 135, cont); - } - } - - private static void FillBankbox(Mobile m) - { - if (Core.AOS) - { - FillBankAOS(m); - return; - } - - var bank = m.BankBox; - - bank.DropItem(new BankCheck(1000000)); - - // Full spellbook - var book = new Spellbook { Content = ulong.MaxValue }; - - bank.DropItem(book); - - var bag = new Bag(); - - for (var i = 0; i < 5; ++i) - { - bag.DropItem(new Moonstone(MoonstoneType.Felucca)); - } - - // Felucca moonstones - bank.DropItem(bag); - - bag = new Bag(); - - for (var i = 0; i < 5; ++i) - { - bag.DropItem(new Moonstone(MoonstoneType.Trammel)); - } - - // Trammel moonstones - bank.DropItem(bag); - - // Treasure maps - bank.DropItem(new TreasureMap(1, Map.Trammel)); - bank.DropItem(new TreasureMap(2, Map.Trammel)); - bank.DropItem(new TreasureMap(3, Map.Trammel)); - bank.DropItem(new TreasureMap(4, Map.Trammel)); - bank.DropItem(new TreasureMap(5, Map.Trammel)); - - // Bag containing 50 of each reagent - bank.DropItem(new BagOfReagents()); - - // Craft tools - bank.DropItem(MakeNewbie(new Scissors())); - bank.DropItem(MakeNewbie(new SewingKit(1000))); - bank.DropItem(MakeNewbie(new SmithHammer(1000))); - bank.DropItem(MakeNewbie(new FletcherTools(1000))); - bank.DropItem(MakeNewbie(new DovetailSaw(1000))); - bank.DropItem(MakeNewbie(new MortarPestle(1000))); - bank.DropItem(MakeNewbie(new ScribesPen(1000))); - bank.DropItem(MakeNewbie(new TinkerTools(1000))); - - // A few dye tubs - bank.DropItem(new Dyes()); - bank.DropItem(new DyeTub()); - bank.DropItem(new DyeTub()); - bank.DropItem(new BlackDyeTub()); - - var darkRedTub = new DyeTub { DyedHue = 0x485, Redyable = false }; - bank.DropItem(darkRedTub); - - // Some food - bank.DropItem(MakeNewbie(new Apple(1000))); - - // Resources - bank.DropItem(MakeNewbie(new Feather(1000))); - bank.DropItem(MakeNewbie(new BoltOfCloth(1000))); - bank.DropItem(MakeNewbie(new BlankScroll(1000))); - bank.DropItem(MakeNewbie(new Hides(1000))); - bank.DropItem(MakeNewbie(new Bandage(1000))); - bank.DropItem(MakeNewbie(new Bottle(1000))); - bank.DropItem(MakeNewbie(new Log(1000))); - - bank.DropItem(MakeNewbie(new IronIngot(5000))); - bank.DropItem(MakeNewbie(new DullCopperIngot(5000))); - bank.DropItem(MakeNewbie(new ShadowIronIngot(5000))); - bank.DropItem(MakeNewbie(new CopperIngot(5000))); - bank.DropItem(MakeNewbie(new BronzeIngot(5000))); - bank.DropItem(MakeNewbie(new GoldIngot(5000))); - bank.DropItem(MakeNewbie(new AgapiteIngot(5000))); - bank.DropItem(MakeNewbie(new VeriteIngot(5000))); - bank.DropItem(MakeNewbie(new ValoriteIngot(5000))); - - // Reagents - bank.DropItem(MakeNewbie(new BlackPearl(1000))); - bank.DropItem(MakeNewbie(new Bloodmoss(1000))); - bank.DropItem(MakeNewbie(new Garlic(1000))); - bank.DropItem(MakeNewbie(new Ginseng(1000))); - bank.DropItem(MakeNewbie(new MandrakeRoot(1000))); - bank.DropItem(MakeNewbie(new Nightshade(1000))); - bank.DropItem(MakeNewbie(new SulfurousAsh(1000))); - bank.DropItem(MakeNewbie(new SpidersSilk(1000))); - - // Some extra starting gold - bank.DropItem(MakeNewbie(new Gold(9000))); - - // 5 blank recall runes - for (var i = 0; i < 5; ++i) - { - bank.DropItem(MakeNewbie(new RecallRune())); - } - - AddPowerScrolls(bank); - } - - private static void AddPowerScrolls(BankBox bank) - { - var bag = new Bag(); - - for (var i = 0; i < PowerScroll.Skills.Count; ++i) - { - bag.DropItem(new PowerScroll(PowerScroll.Skills[i], 120.0)); - } - - bag.DropItem(new StatCapScroll(250)); - - bank.DropItem(bag); - } - - private static void AddShirt(Mobile m, int shirtHue) - { - var hue = Utility.ClipDyedHue(shirtHue & 0x3FFF); - - if (m.Race == Race.Elf) - { - EquipItem(new ElvenShirt(hue), true); - } - else - { - switch (Utility.Random(3)) - { - case 0: - EquipItem(new Shirt(hue), true); - break; - case 1: - EquipItem(new FancyShirt(hue), true); - break; - case 2: - EquipItem(new Doublet(hue), true); - break; - } - } - } - - private static void AddPants(Mobile m, int pantsHue) - { - var hue = Utility.ClipDyedHue(pantsHue & 0x3FFF); - - if (m.Race == Race.Elf) - { - EquipItem(new ElvenPants(hue), true); - } - else - { - if (m.Female) - { - switch (Utility.Random(2)) - { - case 0: - EquipItem(new Skirt(hue), true); - break; - case 1: - EquipItem(new Kilt(hue), true); - break; - } - } - else - { - switch (Utility.Random(2)) - { - case 0: - EquipItem(new LongPants(hue), true); - break; - case 1: - EquipItem(new ShortPants(hue), true); - break; - } - } - } - } - - private static void AddShoes(Mobile m) - { - if (m.Race == Race.Elf) - { - EquipItem(new ElvenBoots(), true); - } - else - { - EquipItem(new Shoes(Utility.RandomYellowHue()), true); - } + 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) @@ -638,7 +143,6 @@ namespace Server.Misc } args.Mobile = newChar; - m_Mobile = newChar; newChar.Player = true; newChar.AccessLevel = args.Account.AccessLevel; @@ -661,10 +165,10 @@ namespace Server.Misc SetName(newChar, args.Name); - AddBackpack(newChar); + newChar.AddBackpack(); - SetStats(newChar, state, args.Str, args.Dex, args.Int); - SetSkills(newChar, args.Skills, args.Profession); + SetStats(newChar, state, args.Stats, args.Profession); + SetSkills(newChar, args.Skills, args.Profession, args.ShirtHue, args.PantsHue); var race = newChar.Race; @@ -680,16 +184,9 @@ namespace Server.Misc newChar.FacialHairHue = race.ClipHairHue(args.BeardHue & 0x3FFF); } - if (args.Profession <= 3) - { - AddShirt(newChar, args.ShirtHue); - AddPants(newChar, args.PantsHue); - AddShoes(newChar); - } - if (TestCenter.Enabled) { - FillBankbox(newChar); + TestCenter.FillBankbox(newChar); } if (young) @@ -717,14 +214,13 @@ namespace Server.Misc } public static bool VerifyProfession(int profession) => - profession >= 0 && (profession < 4 || Core.AOS && profession < 6 || Core.SE && profession < 8); - + profession >= 0 && profession < ProfessionInfo.Professions.Length; private static CityInfo GetStartLocation(CharacterCreatedEventArgs args, bool isYoung) { if (Core.ML) { - return m_NewHavenInfo; // We don't get the client Version until AFTER Character creation + return _newHavenInfo; // We don't get the client Version until AFTER Character creation } var useHaven = isYoung; @@ -732,9 +228,11 @@ namespace Server.Misc var flags = args.State?.Flags ?? ClientFlags.None; var m = args.Mobile; - switch (args.Profession) + var profession = ProfessionInfo.Professions[args.Profession]; + + switch (profession?.Name.ToLowerInvariant()) { - case 4: // Necro + case "necromancer": { if ((flags & ClientFlags.Malas) != 0) { @@ -754,11 +252,11 @@ namespace Server.Misc break; } - case 5: // Paladin + case "paladin": { - return m_NewHavenInfo; + return _newHavenInfo; } - case 6: // Samurai + case "samurai": { if ((flags & ClientFlags.Tokuno) != 0) { @@ -778,7 +276,7 @@ namespace Server.Misc break; } - case 7: // Ninja + case "ninja": { if ((flags & ClientFlags.Tokuno) != 0) { @@ -800,51 +298,34 @@ namespace Server.Misc } } - return useHaven ? m_NewHavenInfo : args.City; + return useHaven ? _newHavenInfo : args.City; } - private static void FixStats(ref int str, ref int dex, ref int intel, int max) + private static void SetStats(Mobile m, NetState state, StatNameValue[] stats, int prof) { - var vMax = max - 30; + var maxStats = state.NewCharacterCreation ? 90 : 80; - var vStr = Math.Max(str - 10, 0); - var vDex = Math.Max(dex - 10, 0); - var vInt = Math.Max(intel - 10, 0); + var str = 0; + var dex = 0; + var intel = 0; - var total = vStr + vDex + vInt; - - if (total == 0 || total == vMax) + if (prof > 0) { - return; + stats = ProfessionInfo.Professions[prof]?.Stats ?? stats; } - var scalar = vMax / (double)total; + 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; + } + } - vStr = (int)(vStr * scalar); - vDex = (int)(vDex * scalar); - vInt = (int)(vInt * scalar); - - FixStat(ref vStr, vStr + vDex + vInt - vMax, vMax); - FixStat(ref vDex, vStr + vDex + vInt - vMax, vMax); - FixStat(ref vInt, vStr + vDex + vInt - vMax, vMax); - - str = vStr + 10; - dex = vDex + 10; - intel = vInt + 10; - } - - private static void FixStat(ref int stat, int diff, int max) - { - stat = Math.Clamp(stat + diff, 0, max); - } - - private static void SetStats(Mobile m, NetState state, int str, int dex, int intel) - { - var max = state.NewCharacterCreation ? 90 : 80; - - FixStats(ref str, ref dex, ref intel, max); - - if (str < 10 || str > 60 || dex < 10 || dex > 60 || intel < 10 || intel > 60 || str + dex + intel != max) + if (str is < 10 or > 60 || dex is < 10 or > 60 || intel is < 10 or > 60 || str + dex + intel != maxStats) { str = 10; dex = 10; @@ -866,170 +347,63 @@ namespace Server.Misc m.Name = name; } - private static bool ValidSkills(SkillNameValue[] skills) + private static bool ValidateSkills(int raceFlag, SkillNameValue[] skills) { var total = 0; for (var i = 0; i < skills.Length; ++i) { - if (skills[i].Value < 0 || skills[i].Value > 50) + 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) { - return false; + skills[i] = default; + continue; } - total += skills[i].Value; + total += value; for (var j = i + 1; j < skills.Length; ++j) { - if (skills[j].Value > 0 && skills[j].Name == skills[i].Name) + var (nameCheck, valueCheck) = skills[j]; + if (valueCheck > 0 && nameCheck == name) { return false; } } } - return total == 100 || total == 120; + return total is 100 or 120; } - private static void SetSkills(Mobile m, SkillNameValue[] skills, int prof) + private static void SetSkills(Mobile m, SkillNameValue[] skills, int prof, int shirtHue, int pantsHue) { - switch (prof) + ProfessionInfo profession = null; + if (prof > 0) { - case 1: // Warrior - { - skills = new[] - { - new SkillNameValue(SkillName.Anatomy, 30), - new SkillNameValue(SkillName.Healing, 45), - new SkillNameValue(SkillName.Swords, 35), - new SkillNameValue(SkillName.Tactics, 50) - }; - - break; - } - case 2: // Magician - { - skills = new[] - { - new SkillNameValue(SkillName.EvalInt, 30), - new SkillNameValue(SkillName.Wrestling, 30), - new SkillNameValue(SkillName.Magery, 50), - new SkillNameValue(SkillName.Meditation, 50) - }; - - break; - } - case 3: // Blacksmith - { - skills = new[] - { - new SkillNameValue(SkillName.Mining, 30), - new SkillNameValue(SkillName.ArmsLore, 30), - new SkillNameValue(SkillName.Blacksmith, 50), - new SkillNameValue(SkillName.Tinkering, 50) - }; - - break; - } - case 4: // Necromancer - { - skills = new[] - { - new SkillNameValue(SkillName.Necromancy, 50), - new SkillNameValue(SkillName.Focus, 30), - new SkillNameValue(SkillName.SpiritSpeak, 30), - new SkillNameValue(SkillName.Swords, 30), - new SkillNameValue(SkillName.Tactics, 20) - }; - - break; - } - case 5: // Paladin - { - skills = new[] - { - new SkillNameValue(SkillName.Chivalry, 51), - new SkillNameValue(SkillName.Swords, 49), - new SkillNameValue(SkillName.Focus, 30), - new SkillNameValue(SkillName.Tactics, 30) - }; - - break; - } - case 6: // Samurai - { - skills = new[] - { - new SkillNameValue(SkillName.Bushido, 50), - new SkillNameValue(SkillName.Swords, 50), - new SkillNameValue(SkillName.Anatomy, 30), - new SkillNameValue(SkillName.Healing, 30) - }; - break; - } - case 7: // Ninja - { - skills = new[] - { - new SkillNameValue(SkillName.Ninjitsu, 50), - new SkillNameValue(SkillName.Hiding, 50), - new SkillNameValue(SkillName.Fencing, 30), - new SkillNameValue(SkillName.Stealth, 30) - }; - break; - } - default: - { - if (!ValidSkills(skills)) - { - return; - } - - break; - } + 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; } var addSkillItems = true; var elf = m.Race == Race.Elf; var gargoyle = m.Race == Race.Gargoyle; - switch (prof) + switch (profession?.Name.ToLowerInvariant()) { - case 1: // Warrior + case "necromancer": { - if (elf) - { - EquipItem(new Circlet()); - EquipItem(new HideGorget()); - EquipItem(new HideChest()); - EquipItem(new HidePauldrons()); - EquipItem(new HideGloves()); - EquipItem(new HidePants()); - EquipItem(new ElvenBoots()); - } - else if (gargoyle) - { - //EquipItem(new DreadSword()); //NOT IMPLEMENTED - EquipItem(new GargishLeatherChestType1()); - EquipItem(new GargishLeatherArmsType1()); - EquipItem(new GargishLeatherKiltType1()); - EquipItem(new GargishLeatherLegsType1()); - } - else - { - EquipItem(new Bascinet()); - EquipItem(new StuddedGorget()); - EquipItem(new StuddedChest()); - EquipItem(new StuddedArms()); - EquipItem(new StuddedGloves()); - EquipItem(new StuddedLegs()); - EquipItem(new ThighBoots()); - } - break; - } - case 4: // Necromancer - { - Container regs = new BagOfNecroReagents(); + Container regs = new BagOfNecroReagents { LootType = LootType.Regular }; if (!Core.AOS) { @@ -1039,221 +413,250 @@ namespace Server.Misc } } - PackItem(regs); + m.PackItem(regs); - regs.LootType = LootType.Regular; - - EquipItem(new BoneHelm()); + EquipItem(m, new BoneHelm()); if (elf) { - EquipItem(new ElvenMachete()); - EquipItem(NecroHue(new LeafChest())); - EquipItem(NecroHue(new LeafArms())); - EquipItem(NecroHue(new LeafGloves())); - EquipItem(NecroHue(new LeafGorget())); - EquipItem(NecroHue(new LeafLegs())); - EquipItem(new ElvenBoots()); + 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(new GlassSword()); //NOT IMPLEMENTED - EquipItem(new GargishLeatherChestType1()); - EquipItem(new GargishLeatherArmsType1()); - EquipItem(new GargishLeatherKiltType1()); - EquipItem(new GargishLeatherLegsType1()); + 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(new BoneHarvester()); - EquipItem(NecroHue(new LeatherChest())); - EquipItem(NecroHue(new LeatherArms())); - EquipItem(NecroHue(new LeatherGloves())); - EquipItem(NecroHue(new LeatherGorget())); - EquipItem(NecroHue(new LeatherLegs())); - EquipItem(NecroHue(new Skirt())); - EquipItem(new Sandals(0x8FD)); + 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)); } - Spellbook - book = new NecromancerSpellbook( - (ulong)0x8981 - ); // animate dead, evil omen, pain spike, summon familiar, wraith form - - PackItem(book); - - book.LootType = LootType.Blessed; + // animate dead, evil omen, pain spike, summon familiar, wraith form + m.PackItem(new NecromancerSpellbook(0x8981ul) { LootType = LootType.Blessed }); addSkillItems = false; break; } - case 5: // Paladin + case "paladin": { if (elf) { - EquipItem(new ElvenMachete()); - EquipItem(new WingedHelm()); - EquipItem(new LeafGorget()); - EquipItem(new LeafArms()); - EquipItem(new LeafChest()); - EquipItem(new LeafLegs()); - EquipItem(new LeafGloves()); - EquipItem(new ElvenBoots()); // Verify hue + 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(new GlassSword()); // IMPLEMENTION NEEDED - //EquipItem(new GargishStoneChestType1()); - //EquipItem(new GargishStoneArmsType1()); - //EquipItem(new GargishStoneKiltType1()); - //EquipItem(new GargishStoneLegsType1()); + 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(new Broadsword()); - EquipItem(new Helmet()); - EquipItem(new PlateGorget()); - EquipItem(new RingmailArms()); - EquipItem(new RingmailChest()); - EquipItem(new RingmailLegs()); - EquipItem(new RingmailGloves()); - EquipItem(new ThighBoots(0x748)); - EquipItem(new Cloak(0xCF)); - EquipItem(new BodySash(0xCF)); + 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)); } - Spellbook book = new BookOfChivalry(); - - PackItem(book); - - book.LootType = LootType.Blessed; + m.PackItem(new BookOfChivalry { LootType = LootType.Blessed }); addSkillItems = false; break; } - - case 6: // Samurai + case "samurai": { addSkillItems = false; if (elf) { - EquipItem(new RavenHelm()); - EquipItem(new HakamaShita(0x2C3)); - EquipItem(new Hakama(0x2C3)); - EquipItem(new SamuraiTabi(0x2C3)); - EquipItem(new TattsukeHakama(0x22D)); - EquipItem(new Bokuto()); + 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(new GargishTalwar()); // IMPLEMENTION NEEDED - EquipItem(new GargishLeatherChestType1()); - EquipItem(new GargishLeatherArmsType1()); - EquipItem(new GargishLeatherKiltType1()); - EquipItem(new GargishLeatherLegsType1()); + 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(new LeatherJingasa()); - EquipItem(new HakamaShita(0x2C3)); - EquipItem(new Hakama(0x2C3)); - EquipItem(new SamuraiTabi(0x2C3)); - EquipItem(new TattsukeHakama(0x22D)); - EquipItem(new Bokuto()); + 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()); } - PackItem(new Scissors()); - PackItem(new Bandage(50)); - - Spellbook book = new BookOfBushido(); - PackItem(book); + m.PackItem(new Scissors()); + m.PackItem(new Bandage(50)); + m.PackItem(new BookOfBushido()); break; } - case 7: // Ninja + 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(new AssassinSpike()); - EquipItem(new TattsukeHakama(hues.RandomElement())); - EquipItem(new HakamaShita(0x2C3)); - EquipItem(new NinjaTabi(0x2C3)); - EquipItem(new Kasa()); + 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(new DualPointedSpear()); //IMPLEMENTION NEEDED - EquipItem(new GargishLeatherChestType1()); - EquipItem(new GargishLeatherArmsType1()); - EquipItem(new GargishLeatherKiltType1()); - EquipItem(new GargishLeatherLegsType1()); + 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(new Tekagi()); - EquipItem(new TattsukeHakama(hues.RandomElement())); - EquipItem(new HakamaShita(0x2C3)); - EquipItem(new NinjaTabi(0x2C3)); - EquipItem(new Kasa()); + EquipItem(m, new Tekagi()); + EquipItem(m, new TattsukeHakama(hues.RandomElement())); + EquipItem(m, new HakamaShita(0x2C3)); + EquipItem(m, new NinjaTabi(0x2C3)); + EquipItem(m, new Kasa()); } - PackItem(new SmokeBomb()); - PackItem(new SmokeBomb()); - PackItem(new SmokeBomb()); - PackItem(new SmokeBomb()); - PackItem(new SmokeBomb()); - - Spellbook book = new BookOfNinjitsu(); - PackItem(book); - + 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) + { + EquipItem(m, new WildStaff()); + } + } + for (var i = 0; i < skills.Length; ++i) { - var snv = skills[i]; - - if (snv.Value > 0 && (snv.Name != SkillName.Stealth || prof == 7) && snv.Name != SkillName.RemoveTrap && - snv.Name != SkillName.Spellweaving) + var (name, value) = skills[i]; + if (value <= 0) { - var skill = m.Skills[snv.Name]; + continue; + } - if (skill != null) - { - skill.BaseFixedPoint = snv.Value * 10; + var skill = m.Skills[name]; - if (addSkillItems) - { - AddSkillItems(snv.Name, m); - } - } + if (skill != null) + { + skill.BaseFixedPoint = value * 10; + m.AddSkillItems(name); } } } - private static void EquipItem(Item item, bool mustEquip = false) + private static void EquipItem(Mobile m, Item item, bool mustEquip = false) { + if (item == null) + { + return; + } + if (!Core.AOS) { item.LootType = LootType.Newbied; } - if (m_Mobile?.EquipItem(item) == true) + if (m?.EquipItem(item) == true) { return; } - var pack = m_Mobile?.Backpack; + var pack = m?.Backpack; if (!mustEquip && pack != null) { @@ -1265,14 +668,14 @@ namespace Server.Misc } } - private static void PackItem(Item item) + private static void PackItem(this Mobile m, Item item) { if (!Core.AOS) { item.LootType = LootType.Newbied; } - var pack = m_Mobile.Backpack; + var pack = m.Backpack; if (pack != null) { @@ -1284,108 +687,123 @@ namespace Server.Misc } } - private static void PackInstrument() + private static void AddShirt(this Mobile m, int shirtHue) { - switch (Utility.Random(6)) + var hue = Utility.ClipDyedHue(shirtHue & 0x3FFF); + var raceFlag = m.Race.RaceFlag; + + var shirt = raceFlag switch { - case 0: - PackItem(new Drums()); - break; - case 1: - PackItem(new Harp()); - break; - case 2: - PackItem(new LapHarp()); - break; - case 3: - PackItem(new Lute()); - break; - case 4: - PackItem(new Tambourine()); - break; - case 5: - PackItem(new TambourineTassel()); - break; + 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 PackScroll(int circle) + private static void PackInstrument(this Mobile m) { - switch (Utility.Random(8) * (circle + 1)) + Item instrument = Utility.Random(6) switch { - case 0: - PackItem(new ClumsyScroll()); - break; - case 1: - PackItem(new CreateFoodScroll()); - break; - case 2: - PackItem(new FeeblemindScroll()); - break; - case 3: - PackItem(new HealScroll()); - break; - case 4: - PackItem(new MagicArrowScroll()); - break; - case 5: - PackItem(new NightSightScroll()); - break; - case 6: - PackItem(new ReactiveArmorScroll()); - break; - case 7: - PackItem(new WeakenScroll()); - break; - case 8: - PackItem(new AgilityScroll()); - break; - case 9: - PackItem(new CunningScroll()); - break; - case 10: - PackItem(new CureScroll()); - break; - case 11: - PackItem(new HarmScroll()); - break; - case 12: - PackItem(new MagicTrapScroll()); - break; - case 13: - PackItem(new MagicUnTrapScroll()); - break; - case 14: - PackItem(new ProtectionScroll()); - break; - case 15: - PackItem(new StrengthScroll()); - break; - case 16: - PackItem(new BlessScroll()); - break; - case 17: - PackItem(new FireballScroll()); - break; - case 18: - PackItem(new MagicLockScroll()); - break; - case 19: - PackItem(new PoisonScroll()); - break; - case 20: - PackItem(new TelekinesisScroll()); - break; - case 21: - PackItem(new TeleportScroll()); - break; - case 22: - PackItem(new UnlockScroll()); - break; - case 23: - PackItem(new WallOfStoneScroll()); - break; - } + 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) @@ -1395,324 +813,296 @@ namespace Server.Misc return item; } - private static void AddSkillItems(SkillName skill, Mobile m) + [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 GnarledStaff() + }; + + 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: { - PackItem(new Bottle(4)); - PackItem(new MortarPestle()); - - var hue = Utility.RandomPinkHue(); - - if (elf) - { - if (m.Female) - { - EquipItem(new FemaleElvenRobe(hue)); - } - else - { - EquipItem(new MaleElvenRobe(hue)); - } - } - else - { - EquipItem(new Robe(Utility.RandomPinkHue())); - } + m.PackItem(new Bottle(4)); + m.PackItem(new MortarPestle()); + EquipItem(m, Robe(raceFlag, female, Utility.RandomPinkHue())); break; } case SkillName.Anatomy: { - PackItem(new Bandage(3)); - - var hue = Utility.RandomYellowHue(); - - if (elf) - { - if (m.Female) - { - EquipItem(new FemaleElvenRobe(hue)); - } - else - { - EquipItem(new MaleElvenRobe(hue)); - } - } - else - { - EquipItem(new Robe(hue)); - } + m.PackItem(new Bandage(3)); + EquipItem(m, Robe(raceFlag, female, Utility.RandomYellowHue())); break; } case SkillName.AnimalLore: { - var hue = Utility.RandomBlueHue(); - if (elf) { - EquipItem(new WildStaff()); - - if (m.Female) - { - EquipItem(new FemaleElvenRobe(hue)); - } - else - { - EquipItem(new MaleElvenRobe(hue)); - } + EquipItem(m, new WildStaff()); } else { - EquipItem(new ShepherdsCrook()); - EquipItem(new Robe(hue)); + 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: { - PackItem(new Arrow(25)); + m.PackItem(new Arrow(25)); - if (elf) - { - EquipItem(new ElvenCompositeLongbow()); - } - else - { - EquipItem(new Bow()); - } + EquipItem(m, RangedWeapon(raceFlag)); break; } case SkillName.ArmsLore: { - if (elf) + Item item = Utility.Random(3) switch { - switch (Utility.Random(3)) - { - case 0: - EquipItem(new Leafblade()); - break; - case 1: - EquipItem(new RuneBlade()); - break; - case 2: - EquipItem(new DiamondMace()); - break; - } - } - else - { - switch (Utility.Random(3)) - { - case 0: - EquipItem(new Kryss()); - break; - case 1: - EquipItem(new Katana()); - break; - case 2: - EquipItem(new Club()); - break; - } - } + 0 => FencingWeapon(raceFlag), + 1 => MacingWeapon(raceFlag), + _ => SwordsWeapon(raceFlag) + }; + EquipItem(m, item); break; } case SkillName.Begging: { - if (elf) + Item staff = raceFlag switch { - EquipItem(new WildStaff()); - } - else - { - EquipItem(new GnarledStaff()); - } + Race.AllowElvesOnly => new WildStaff(), + Race.AllowGargoylesOnly => new GlassStaff(), + _ => new GnarledStaff() + }; + EquipItem(m, staff); break; } case SkillName.Blacksmith: { - PackItem(new Tongs()); - PackItem(new Pickaxe()); - PackItem(new Pickaxe()); - PackItem(new IronIngot(50)); - EquipItem(new HalfApron(Utility.RandomYellowHue())); + 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: { - EquipItem(new Hakama()); - EquipItem(new Kasa()); - EquipItem(new BookOfBushido()); + 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: { - PackItem(new Board(14)); - PackItem(new Feather(5)); - PackItem(new Shaft(5)); + m.PackItem(new Board(14)); + m.PackItem(new Feather(5)); + m.PackItem(new Shaft(5)); break; } case SkillName.Camping: { - PackItem(new Bedroll()); - PackItem(new Kindling(5)); + m.PackItem(new Bedroll()); + m.PackItem(new Kindling(5)); break; } case SkillName.Carpentry: { - PackItem(new Board(10)); - PackItem(new Saw()); - EquipItem(new HalfApron(Utility.RandomYellowHue())); + m.PackItem(new Board(10)); + m.PackItem(new Saw()); + + if (elfOrHuman) + { + EquipItem(m, new HalfApron(Utility.RandomYellowHue())); + } + break; } case SkillName.Cartography: { - PackItem(new BlankMap()); - PackItem(new BlankMap()); - PackItem(new BlankMap()); - PackItem(new BlankMap()); - PackItem(new Sextant()); + m.PackItem(new BlankMap()); + m.PackItem(new BlankMap()); + m.PackItem(new BlankMap()); + m.PackItem(new BlankMap()); + m.PackItem(new Sextant()); break; } case SkillName.Cooking: { - PackItem(new Kindling(2)); - PackItem(new RawLambLeg()); - PackItem(new RawChickenLeg()); - PackItem(new RawFishSteak()); - PackItem(new SackFlour()); - PackItem(new Pitcher(BeverageType.Water)); + 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: { - if (Core.ML) - { - PackItem(new BookOfChivalry()); - } - + m.PackItem(new BookOfChivalry()); break; } case SkillName.DetectHidden: { - EquipItem(new Cloak(0x455)); + if (elfOrHuman) + { + EquipItem(m, new Cloak(0x455)); + } + break; } case SkillName.Discordance: { - PackInstrument(); + m.PackInstrument(); break; } case SkillName.Fencing: { - if (elf) - { - EquipItem(new Leafblade()); - } - else - { - EquipItem(new Kryss()); - } - + EquipItem(m, FencingWeapon(raceFlag)); break; } case SkillName.Fishing: { - EquipItem(new FishingPole()); + EquipItem(m, new FishingPole()); var hue = Utility.RandomYellowHue(); - if (elf) { - Item i = new Circlet(); - i.Hue = hue; - EquipItem(i); + EquipItem(m, new Circlet { Hue = hue }); } - else + else if (human) { - EquipItem(new FloppyHat(hue)); + EquipItem(m, new FloppyHat(hue)); } break; } case SkillName.Healing: { - PackItem(new Bandage(50)); - PackItem(new Scissors()); + m.PackItem(new Bandage(50)); + m.PackItem(new Scissors()); break; } case SkillName.Herding: { - if (elf) - { - EquipItem(new WildStaff()); - } - else - { - EquipItem(new ShepherdsCrook()); - } - + EquipItem(m, new ShepherdsCrook()); break; } case SkillName.Hiding: { - EquipItem(new Cloak(0x455)); + if (elfOrHuman) + { + EquipItem(m, new Cloak(0x455)); + } + break; } case SkillName.Inscribe: { - PackItem(new BlankScroll(2)); - PackItem(new BlueBook()); + m.PackItem(new BlankScroll(2)); + m.PackItem(new BlueBook()); break; } case SkillName.ItemID: { - if (elf) + Item staff = raceFlag switch { - EquipItem(new WildStaff()); - } - else - { - EquipItem(new GnarledStaff()); - } + Race.AllowElvesOnly => new WildStaff(), + Race.AllowGargoylesOnly => new SerpentstoneStaff(), + _ => new GnarledStaff() + }; + EquipItem(m, staff); break; } case SkillName.Lockpicking: { - PackItem(new Lockpick(20)); + m.PackItem(new Lockpick(20)); break; } case SkillName.Lumberjacking: { - EquipItem(new Hatchet()); + EquipItem(m, elfOrHuman ? new Hatchet() : new DualShortAxes()); break; } case SkillName.Macing: { - if (elf) - { - EquipItem(new DiamondMace()); - } - else - { - EquipItem(new Club()); - } - + EquipItem(m, MacingWeapon(raceFlag)); break; } case SkillName.Magery: { - var regs = new BagOfReagents(30); + var regs = new BagOfReagents(30) { LootType = LootType.Regular }; if (!Core.AOS) { @@ -1722,180 +1112,164 @@ namespace Server.Misc } } - PackItem(regs); + m.PackItem(regs); + m.PackScroll(0); + m.PackScroll(1); + m.PackScroll(2); - regs.LootType = LootType.Regular; - - PackScroll(0); - PackScroll(1); - PackScroll(2); - - var book = new Spellbook((ulong)0x382A8C38); - - EquipItem(book); - - book.LootType = LootType.Blessed; + EquipItem(m, new Spellbook(0x382A8C38ul) { LootType = LootType.Blessed }); + EquipItem(m, Robe(raceFlag, female, Utility.RandomBlueHue())); if (elf) { - EquipItem(new Circlet()); - - if (m.Female) - { - EquipItem(new FemaleElvenRobe(Utility.RandomBlueHue())); - } - else - { - EquipItem(new MaleElvenRobe(Utility.RandomBlueHue())); - } + EquipItem(m, new Circlet()); } - else + else if (human) { - EquipItem(new WizardsHat()); - EquipItem(new Robe(Utility.RandomBlueHue())); + EquipItem(m, new WizardsHat()); } break; } case SkillName.Mining: { - PackItem(new Pickaxe()); + m.PackItem(new Pickaxe()); break; } case SkillName.Musicianship: { - PackInstrument(); + m.PackInstrument(); break; } case SkillName.Necromancy: { if (Core.ML) { - Container regs = new BagOfNecroReagents(); - - PackItem(regs); - - regs.LootType = LootType.Regular; + m.PackItem(new BagOfNecroReagents { LootType = LootType.Regular }); } break; } case SkillName.Ninjitsu: { - EquipItem(new Hakama(0x2C3)); // Only ninjas get the hued one. - EquipItem(new Kasa()); - EquipItem(new BookOfNinjitsu()); + 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: { - EquipItem(new WoodenShield()); + Item shield = raceFlag switch + { + Race.AllowGargoylesOnly => new GargishWoodenShield(), + _ => new WoodenShield() + }; + EquipItem(m, shield); + break; } case SkillName.Peacemaking: { - PackInstrument(); + m.PackInstrument(); break; } case SkillName.Poisoning: { - PackItem(new LesserPoisonPotion()); - PackItem(new LesserPoisonPotion()); + m.PackItem(new LesserPoisonPotion()); + m.PackItem(new LesserPoisonPotion()); break; } case SkillName.Provocation: { - PackInstrument(); + m.PackInstrument(); break; } + case SkillName.Stealing: case SkillName.Snooping: { - PackItem(new Lockpick(20)); + m.PackItem(new Lockpick(20)); break; } case SkillName.SpiritSpeak: { - EquipItem(new Cloak(0x455)); - break; - } - case SkillName.Stealing: - { - PackItem(new Lockpick(20)); - break; - } - case SkillName.Swords: - { - if (elf) - { - EquipItem(new RuneBlade()); - } - //else if (gargoyle) - //{ - //EquipItem(new DreadSword()); IMPLEMENTION NEEDED - //} - else - { - EquipItem(new Katana()); - } - + EquipItem(m, new Cloak(0x455)); break; } case SkillName.Tactics: + case SkillName.Swords: { - if (elf) - { - EquipItem(new RuneBlade()); - } - else - { - EquipItem(new Katana()); - } + EquipItem(m, SwordsWeapon(raceFlag)); break; } case SkillName.Tailoring: { - PackItem(new BoltOfCloth()); - PackItem(new SewingKit()); + 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: { - var shoes = m_Mobile?.FindItemOnLayer(Layer.Shoes); - - shoes?.Delete(); - - var hue = Utility.RandomYellowHue(); - - if (elf) + if (elfOrHuman) { - EquipItem(new ElvenBoots(hue)); - } - else - { - EquipItem(new Boots(hue)); + // Delete shoes + m?.FindItemOnLayer(Layer.Shoes)?.Delete(); + + var hue = Utility.RandomYellowHue(); + EquipItem(m, elf ? new ElvenBoots(hue) : new Boots(hue)); } - EquipItem(new SkinningKnife()); + EquipItem(m, new SkinningKnife()); break; } case SkillName.Veterinary: { - PackItem(new Bandage(5)); - PackItem(new Scissors()); + m.PackItem(new Bandage(5)); + m.PackItem(new Scissors()); break; } case SkillName.Wrestling: { - if (elf) + Item item = raceFlag switch { - EquipItem(new LeafGloves()); - } - else + 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(new LeatherGloves()); + // EquipItem(m, new Boomerang()); } + break; + } + case SkillName.Mysticism: + { + // PackItem(new MysticBook(0xAB)); break; } } diff --git a/Projects/UOContent/Misc/ClientVerification.cs b/Projects/UOContent/Misc/ClientVerification.cs index cf3c92e72..23764174a 100644 --- a/Projects/UOContent/Misc/ClientVerification.cs +++ b/Projects/UOContent/Misc/ClientVerification.cs @@ -87,7 +87,7 @@ namespace Server.Misc if (Required != null && version < Required && (m_OldClientResponse == OldClientResponse.Kick || m_OldClientResponse == OldClientResponse.LenientKick && - Core.Now - state.Mobile.CreationTime > m_AgeLeniency && + Core.Now - state.Mobile.Created > m_AgeLeniency && state.Mobile is PlayerMobile mobile && mobile.GameTime > m_GameTimeLeniency)) { diff --git a/Projects/UOContent/Misc/CrashGuard.cs b/Projects/UOContent/Misc/CrashGuard.cs index 7b029b35c..c41b213fa 100644 --- a/Projects/UOContent/Misc/CrashGuard.cs +++ b/Projects/UOContent/Misc/CrashGuard.cs @@ -4,6 +4,7 @@ using System.IO; using Server.Accounting; using Server.Logging; using Server.Network; +using Server.Saves; namespace Server.Misc { @@ -76,24 +77,23 @@ namespace Server.Misc } } - private static void CopyFile(string rootOrigin, string rootBackup, string path) + private static void DirectoryCopy(string sourceDirName, string destDirName) { - var originPath = Path.Combine(rootOrigin, path); - if (!File.Exists(originPath)) + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + DirectoryInfo[] dirs = dir.GetDirectories(); + + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) { - return; + string tempPath = Path.Combine(destDirName, file.Name); + file.CopyTo(tempPath, false); } - var backupPath = Path.Combine(rootBackup, path); - Directory.CreateDirectory(Path.GetDirectoryName(backupPath)); - - try + foreach (DirectoryInfo subdir in dirs) { - File.Copy(originPath, backupPath); - } - catch - { - // ignored + string tempPath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, tempPath); } } @@ -105,26 +105,12 @@ namespace Server.Misc { var timeStamp = Utility.GetTimeStamp(); - var root = Core.BaseDirectory; - var rootBackup = Path.Combine(root, $"Backups/Crashed/{timeStamp}/"); - var rootOrigin = Path.Combine(root, "Saves/"); - - // Copy files - CopyFile(rootOrigin, rootBackup, "Accounts/Accounts.xml"); - - CopyFile(rootOrigin, rootBackup, "Items/Items.bin"); - CopyFile(rootOrigin, rootBackup, "Items/Items.idx"); - CopyFile(rootOrigin, rootBackup, "Items/Items.tdb"); - - CopyFile(rootOrigin, rootBackup, "Mobiles/Mobiles.bin"); - CopyFile(rootOrigin, rootBackup, "Mobiles/Mobiles.idx"); - CopyFile(rootOrigin, rootBackup, "Mobiles/Mobiles.tdb"); - - CopyFile(rootOrigin, rootBackup, "Guilds/Guilds.bin"); - CopyFile(rootOrigin, rootBackup, "Guilds/Guilds.idx"); - - CopyFile(rootOrigin, rootBackup, "Regions/Regions.bin"); - CopyFile(rootOrigin, rootBackup, "Regions/Regions.idx"); + var backupPath = PathUtility.EnsureDirectory(Path.Combine(AutoArchive.BackupPath, "Crashed", timeStamp)); + var savePath = World.SavePath; + if (Directory.Exists(savePath)) + { + DirectoryCopy(savePath, backupPath); + } logger.Information("Backup done"); } @@ -160,7 +146,7 @@ namespace Server.Misc try { - op.WriteLine("Mobiles: {0}", World.Mobiles.Count); + op.WriteLine($"Accounts: {Accounts.Count}"); } catch { @@ -169,15 +155,23 @@ namespace Server.Misc try { - op.WriteLine("Items: {0}", World.Items.Count); + op.WriteLine($"Mobiles: {World.Mobiles.Count}"); } catch { // ignored } - op.WriteLine("Exception:"); - op.WriteLine(e.Exception); + try + { + op.WriteLine($"Items: {World.Items.Count}"); + } + catch + { + // ignored + } + + op.WriteLine($"Exception: {e.Exception}"); op.WriteLine(); op.WriteLine("Clients:"); @@ -186,22 +180,22 @@ namespace Server.Misc { var states = TcpServer.Instances; - op.WriteLine("- Count: {0}", states.Count); + op.WriteLine($"- Count: {states.Count}"); foreach (var ns in TcpServer.Instances) { - op.Write("+ {0}:", ns); + op.Write($"+ {ns}:"); if (ns.Account is Account a) { - op.Write(" (account = {0})", a.Username); + op.Write($" (account = {a.Username})"); } var m = ns.Mobile; if (m != null) { - op.Write(" (mobile = 0x{0:X} '{1}')", m.Serial.Value, m.Name); + op.Write($" (mobile = 0x{m.Serial.Value:X} '{m.Name}')"); } op.WriteLine(); diff --git a/Projects/UOContent/Misc/Guild.cs b/Projects/UOContent/Misc/Guild.cs index 854b7680e..dd891a8b1 100644 --- a/Projects/UOContent/Misc/Guild.cs +++ b/Projects/UOContent/Misc/Guild.cs @@ -1156,6 +1156,10 @@ namespace Server.Guilds list.TrimExcess(); } + public override void BeforeSerialize() + { + } + public override void Serialize(IGenericWriter writer) { if (LastFealty + TimeSpan.FromDays(1.0) < Core.Now) diff --git a/Projects/UOContent/Misc/PacketThrottles.cs b/Projects/UOContent/Misc/PacketThrottles.cs index 40293221b..b0459647b 100644 --- a/Projects/UOContent/Misc/PacketThrottles.cs +++ b/Projects/UOContent/Misc/PacketThrottles.cs @@ -72,7 +72,6 @@ namespace Server.Network return; } - e.Mobile.SendMessage("Packet 0x{0:X} throttle is currently {1}ms.", packetID, Delays[packetID]); } @@ -95,7 +94,6 @@ namespace Server.Network return; } - if (delay > 5000) { e.Mobile.SendMessage("Invalid Command Format. Delay cannot exceed 5000 milliseconds."); diff --git a/Projects/UOContent/Misc/ProfessionInfo.cs b/Projects/UOContent/Misc/ProfessionInfo.cs new file mode 100644 index 000000000..9eda06bd9 --- /dev/null +++ b/Projects/UOContent/Misc/ProfessionInfo.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Server +{ + public class ProfessionInfo + { + public static ProfessionInfo[] Professions { get; } + + private static bool TryGetSkillName(string name, out SkillName skillName) + { + if (Enum.TryParse(name, out skillName)) + { + return true; + } + + var lowerName = name.ToLowerInvariant().Replace(" ", ""); + + foreach (var so in SkillInfo.Table) + { + if (lowerName == so.ProfessionSkillName.ToLowerInvariant()) + { + skillName = (SkillName)so.SkillID; + return true; + } + } + + return false; + + } + + static ProfessionInfo() + { + var profs = new List + { + new() + { + ID = 0, // Custom + Name = "Advanced", + TopLevel = false, + GumpID = 5571 + } + }; + + var file = Core.FindDataFile("prof.txt"); + if (!File.Exists(file)) + { + var parent = Path.Combine(Core.BaseDirectory, "Data/Professions"); + file = Path.Combine(ExpansionInfo.GetEraFolder(parent), "prof.txt"); + } + + var maxProf = 0; + + if (File.Exists(file)) + { + using var s = File.OpenText(file); + + while (!s.EndOfStream) + { + var line = s.ReadLine(); + + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + line = line.Trim(); + + if (!line.InsensitiveStartsWith("Begin")) + { + continue; + } + + var prof = new ProfessionInfo(); + + int stats; + int valid; + var skills = stats = valid = 0; + + while (!s.EndOfStream) + { + line = s.ReadLine(); + + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + line = line.Trim(); + + if (line.InsensitiveStartsWith("End")) + { + if (valid >= 4) + { + profs.Add(prof); + } + + break; + } + + var cols = line.Split('\t', StringSplitOptions.RemoveEmptyEntries); + var key = cols[0].ToLowerInvariant(); + var value = cols[1].Trim('"'); + + if (key == "type" && value != "profession") + { + break; + } + + switch (key) + { + case "truename": + { + prof.Name = value; + ++valid; + } + break; + case "nameid": + prof.NameID = Utility.ToInt32(value); + break; + case "descid": + prof.DescID = Utility.ToInt32(value); + break; + case "desc": + { + prof.ID = Utility.ToInt32(value); + if (prof.ID > maxProf) + { + maxProf = prof.ID; + } + + ++valid; + } + break; + case "toplevel": + prof.TopLevel = Utility.ToBoolean(value); + break; + case "gump": + prof.GumpID = Utility.ToInt32(value); + break; + case "skill": + { + if (!TryGetSkillName(value, out var skillName)) + { + break; + } + + prof.Skills[skills++] = new SkillNameValue(skillName, Utility.ToInt32(cols[2])); + + if (skills == prof.Skills.Length) + { + ++valid; + } + } + break; + case "stat": + { + if (!Enum.TryParse(value, out StatType stat)) + { + break; + } + + prof.Stats[stats++] = new StatNameValue(stat, Utility.ToInt32(cols[2])); + + if (stats == prof.Stats.Length) + { + ++valid; + } + } + break; + } + } + } + } + + Professions = new ProfessionInfo[1 + maxProf]; + + foreach (var p in profs) + { + Professions[p.ID] = p; + } + + profs.Clear(); + profs.TrimExcess(); + } + + private ProfessionInfo() + { + Name = string.Empty; + + Skills = new SkillNameValue[4]; + Stats = new StatNameValue[3]; + } + + public int ID { get; private set; } + public string Name { get; private set; } + public int NameID { get; private set; } + public int DescID { get; private set; } + public bool TopLevel { get; private set; } + public int GumpID { get; private set; } + public SkillNameValue[] Skills { get; } + public StatNameValue[] Stats { get; } + } +} diff --git a/Projects/UOContent/Misc/Profile.cs b/Projects/UOContent/Misc/Profile.cs index 085396208..04ceddea5 100644 --- a/Projects/UOContent/Misc/Profile.cs +++ b/Projects/UOContent/Misc/Profile.cs @@ -69,24 +69,24 @@ namespace Server.Misc return ""; } - var ts = Core.Now - a.Created; + var age = a.AccountAge; - if (Format(ts.TotalDays, "This account is {0} day{1} old.", out var v)) + if (Format(age.TotalDays, "This account is {0} day{1} old.", out var v)) { return v; } - if (Format(ts.TotalHours, "This account is {0} hour{1} old.", out v)) + if (Format(age.TotalHours, "This account is {0} hour{1} old.", out v)) { return v; } - if (Format(ts.TotalMinutes, "This account is {0} minute{1} old.", out v)) + if (Format(age.TotalMinutes, "This account is {0} minute{1} old.", out v)) { return v; } - if (Format(ts.TotalSeconds, "This account is {0} second{1} old.", out v)) + if (Format(age.TotalSeconds, "This account is {0} second{1} old.", out v)) { return v; } diff --git a/Projects/UOContent/Misc/ServerList.cs b/Projects/UOContent/Misc/ServerList.cs index a600dd3f4..031f44bc8 100644 --- a/Projects/UOContent/Misc/ServerList.cs +++ b/Projects/UOContent/Misc/ServerList.cs @@ -1,6 +1,4 @@ using System; -using System.IO; -using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; @@ -8,39 +6,33 @@ using Server.Logging; namespace Server.Misc { + /* + * The default settings are configured to automatically detect your external IP address. + * + * If your public IP address cannot be determined then set your IP address in modernuo.json, for example: + * "serverListing.address": "1.2.3.4" + * + * If you do not plan on allowing clients outside of your LAN to connect, then set the following in modernuo.json + * "serverListing.address": null, + * "serverListing.autoDetect": false + * + * If you want players outside your LAN to be able to connect to your server and you are behind a router, you must also + * forward TCP port 2593 to your private IP address. The procedure for doing this varies by manufacturer but generally + * involves configuration of the router through your web browser. + * + * ServerList will direct connecting clients depending on both the address they are connecting from and the address and + * port they are connecting to. If it is determined that both ends of a connection are private IP addresses, ServerList + * will direct the client to the local private IP address. If a client is connecting to a local public IP address, they + * will be directed to whichever address and port they initially connected to. This allows multi-homed servers to function + * properly and fully supports listening on multiple ports. If a client with a public IP address is connecting to a + * locally private address, the server will direct the client to either the automatically detected IP address or the + * manually entered IP address or hostname, whichever is applicable. Loopback clients will be directed to loopback. + */ public static class ServerList { private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerList)); - private static IPAddress m_PublicAddress; - /* - * The default setting for Address, a value of 'null', will use your local IP address. If all of your local IP addresses - * are private network addresses and AutoDetect is 'true' then RunUO will attempt to discover your public IP address - * for you automatically. - * - * If you do not plan on allowing clients outside of your LAN to connect, you can set AutoDetect to 'false' and leave - * Address set to 'null'. - * - * If your public IP address cannot be determined, you must change the value of Address to your public IP address - * manually to allow clients outside of your LAN to connect to your server. Address can be either an IP address or - * a hostname that will be resolved when RunUO starts. - * - * If you want players outside your LAN to be able to connect to your server and you are behind a router, you must also - * forward TCP port 2593 to your private IP address. The procedure for doing this varies by manufacturer but generally - * involves configuration of the router through your web browser. - * - * ServerList will direct connecting clients depending on both the address they are connecting from and the address and - * port they are connecting to. If it is determined that both ends of a connection are private IP addresses, ServerList - * will direct the client to the local private IP address. If a client is connecting to a local public IP address, they - * will be directed to whichever address and port they initially connected to. This allows multihomed servers to function - * properly and fully supports listening on multiple ports. If a client with a public IP address is connecting to a - * locally private address, the server will direct the client to either the AutoDetected IP address or the manually entered - * IP address or hostname, whichever is applicable. Loopback clients will be directed to loopback. - * - * If you would like to listen on additional ports (i.e. 22, 23, 80, for clients behind highly restrictive egress - * firewalls) or specific IP addresses you can do so by modifying the file SocketOptions.cs found in this directory. - */ - + private static IPAddress _publicAddress; public static string Address { get; private set; } public static string ServerName { get; private set; } @@ -64,7 +56,7 @@ namespace Server.Misc } else { - Resolve(Address, out m_PublicAddress); + Resolve(Address, out _publicAddress); } EventSink.ServerList += EventSink_ServerList; @@ -76,7 +68,11 @@ namespace Server.Misc { var ns = e.State; - var ipep = (IPEndPoint)ns.Connection.LocalEndPoint; + var ipep = (IPEndPoint)ns.Connection?.LocalEndPoint; + if (ipep == null) + { + return; + } var localAddress = ipep.Address; var localPort = ipep.Port; @@ -84,9 +80,9 @@ namespace Server.Misc if (IsPrivateNetwork(localAddress)) { ipep = (IPEndPoint)ns.Connection.RemoteEndPoint; - if (!IsPrivateNetwork(ipep.Address) && m_PublicAddress != null) + if (ipep == null || !IsPrivateNetwork(ipep.Address) && _publicAddress != null) { - localAddress = m_PublicAddress; + localAddress = _publicAddress; } } @@ -103,11 +99,11 @@ namespace Server.Misc { if (!HasPublicIPAddress()) { - m_PublicAddress = FindPublicAddress(); + _publicAddress = FindPublicAddress(); - if (m_PublicAddress != null) + if (_publicAddress != null) { - logger.Information("Auto-detected public IP address ({0})", m_PublicAddress); + logger.Information("Auto-detected public IP address ({0})", _publicAddress); } else { @@ -138,16 +134,23 @@ namespace Server.Misc } } - private static bool HasPublicIPAddress() => - NetworkInterface.GetAllNetworkInterfaces() - .Select(adapter => adapter.GetIPProperties()) - .Any( - properties => properties.UnicastAddresses.Select(unicast => unicast.Address) - .Any( - ip => !IPAddress.IsLoopback(ip) && ip.AddressFamily != AddressFamily.InterNetworkV6 && - !IsPrivateNetwork(ip) - ) - ); + private static bool HasPublicIPAddress() + { + foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces()) + { + foreach (var unicast in adapter.GetIPProperties().UnicastAddresses) + { + var ip = unicast.Address; + if (!IPAddress.IsLoopback(ip) && ip.AddressFamily != AddressFamily.InterNetworkV6 && + !IsPrivateNetwork(ip)) + { + return true; + } + } + } + + return false; + } // 10.0.0.0/8 // 172.16.0.0/12 @@ -166,23 +169,8 @@ namespace Server.Misc { try { - var req = WebRequest.Create("https://api.ipify.org"); - - req.Timeout = 15000; - - var res = req.GetResponse(); - - var s = res.GetResponseStream(); - - var sr = new StreamReader(s); - - var ip = IPAddress.Parse(sr.ReadLine() ?? ""); - - sr.Close(); - s.Close(); - res.Close(); - - return ip; + using WebClient wc = new WebClient(); + return IPAddress.Parse(wc.DownloadString("https://api.ipify.org")); } catch { diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index 8631bfba0..abead7460 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -405,7 +405,8 @@ namespace Server.Mobiles if (spell == null) { - if (!m_Mobile.DisallowAllMoves && Utility.Random((int)m_Mobile.GetDistanceToSqrt(toDispel)) == 0) + var distance = (int)m_Mobile.GetDistanceToSqrt(toDispel); + if (!m_Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) { spell = new TeleportSpell(m_Mobile); } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs index b0059787e..4b6df14d4 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/InterredGrizzle .cs @@ -119,7 +119,8 @@ namespace Server.Mobiles p = GetSpawnPosition(2); var eable = Map.GetItemsInRange(p, 0); - bool atLocation = eable.GetEnumerator().MoveNext(); + using var enumerator = eable.GetEnumerator(); + bool atLocation = enumerator.MoveNext(); eable.Free(); if (!atLocation) { diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs index 801c03733..073c4729c 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs @@ -252,7 +252,8 @@ namespace Server.Mobiles p = GetSpawnPosition(2); var eable = Map.GetItemsInRange(p, 0); - bool atLocation = eable.GetEnumerator().MoveNext(); + using var enumerator = eable.GetEnumerator(); + bool atLocation = enumerator.MoveNext(); eable.Free(); if (!atLocation) { diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 46d16848d..17e7da183 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -767,7 +767,6 @@ namespace Server.Mobiles } } - public void ClearQuestArrow() => m_QuestArrow = null; public override void ToggleFlying() @@ -804,14 +803,21 @@ namespace Server.Mobiles switch (type) { case BlockMountType.Dazed: - SendLocalizedMessage(1112457); - break; // You are still too dazed to fly. + { + SendLocalizedMessage(1112457); // You are still too dazed to fly. + break; + } case BlockMountType.BolaRecovery: - SendLocalizedMessage(1112455); - break; // You cannot fly while recovering from a bola throw. + { + SendLocalizedMessage(1112455); // You cannot fly while recovering from a bola throw. + break; + } case BlockMountType.DismountRecovery: - SendLocalizedMessage(1112456); - break; // You cannot fly while recovering from a dismount maneuver. + { + // You cannot fly while recovering from a dismount maneuver. + SendLocalizedMessage(1112456); + break; + } } } else if (Hits < 25) // TODO confirm @@ -901,7 +907,7 @@ namespace Server.Mobiles var type = item.GetType(); if (type.IsDefined(typeof(FurnitureAttribute), true) || - type.IsDefined(typeof(DynamicFlipingAttribute), true)) + type.IsDefined(typeof(DynamicFlippingAttribute), true)) { var objs = type.GetCustomAttributes(typeof(FlippableAttribute), true); @@ -1751,14 +1757,12 @@ namespace Server.Mobiles var speed = ComputeMovementSpeed(d); - bool res; - if (!Alive) { MovementImpl.IgnoreMovableImpassables = true; } - res = base.Move(d); + var res = base.Move(d); MovementImpl.IgnoreMovableImpassables = false; @@ -3207,7 +3211,6 @@ namespace Server.Mobiles RecentlyReported ??= new List(); - // Professions weren't verified on 1.0 RC0 if (!CharacterCreation.VerifyProfession(Profession)) { Profession = 0; diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs b/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs index 73056c170..4a8666ec0 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs @@ -94,7 +94,7 @@ namespace Server.Mobiles { SayTo(pm, 501046); // Thou must resign from thy other guild first. } - else if (pm.GameTime < JoinGameAge || pm.CreationTime + JoinAge > Core.Now) + else if (pm.GameTime < JoinGameAge || pm.Created + JoinAge > Core.Now) { SayTo(pm, 501048); // You are too young to join my guild... } @@ -141,7 +141,7 @@ namespace Server.Mobiles { SayTo(pm, 501046); // Thou must resign from thy other guild first. } - else if (pm.GameTime < JoinGameAge || pm.CreationTime + JoinAge > Core.Now) + else if (pm.GameTime < JoinGameAge || pm.Created + JoinAge > Core.Now) { SayTo(pm, 501048); // You are too young to join my guild... } diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index 9eba0fca3..eef3a3b15 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -1471,7 +1471,7 @@ namespace Server.Multis var data = TileData.ItemTable[id]; // Slanted roofs do not count; they overhang blocking south and east sides of the multi - if ((data.Flags & TileFlag.Roof) != 0) + if (data.Roof) { continue; } diff --git a/Projects/UOContent/Multis/Houses/ContestHouses.cs b/Projects/UOContent/Multis/Houses/ContestHouses.cs index a624c663b..18df0537e 100644 --- a/Projects/UOContent/Multis/Houses/ContestHouses.cs +++ b/Projects/UOContent/Multis/Houses/ContestHouses.cs @@ -185,13 +185,13 @@ namespace Server.Multis var data = TileData.ItemTable[entry.ItemId & TileData.MaxItemValue]; // door - if ((data.Flags & TileFlag.Door) != 0) + if (data.Door) { AddDoor(entry.ItemId, entry.OffsetX, entry.OffsetY, entry.OffsetZ); } else { - Item st = new Static((int)entry.ItemId); + Item st = new Static(entry.ItemId); st.MoveToWorld(new Point3D(X + entry.OffsetX, Y + entry.OffsetY, entry.OffsetZ), Map); AddFixture(st); diff --git a/Projects/UOContent/Multis/Houses/HouseFoundation.cs b/Projects/UOContent/Multis/Houses/HouseFoundation.cs index 92c91f63f..45a1a5925 100644 --- a/Projects/UOContent/Multis/Houses/HouseFoundation.cs +++ b/Projects/UOContent/Multis/Houses/HouseFoundation.cs @@ -1305,7 +1305,7 @@ namespace Server.Multis public static bool ValidPiece(int itemID, bool roof = false) { itemID &= TileData.MaxItemValue; - return roof != ((TileData.ItemTable[itemID].Flags & TileFlag.Roof) == 0) && Verification.IsItemValid(itemID); + return roof == TileData.ItemTable[itemID].Roof && Verification.IsItemValid(itemID); } public static bool IsStairBlock(int id) @@ -1479,12 +1479,12 @@ namespace Server.Multis } /* Client chose to delete a component - * - Read data detailing which component to delete - * - Verify component is deletable - * - Remove the component - * - If needed, replace removed component with a dirt tile - * - Update revision - */ + * - Read data detailing which component to delete + * - Verify component is deletable + * - Remove the component + * - If needed, replace removed component with a dirt tile + * - Update revision + */ // Read data detailing which component to delete var itemID = reader.ReadInt32(); @@ -1502,9 +1502,9 @@ namespace Server.Multis if (z == 0 && ax >= 0 && ax < mcl.Width && ay >= 0 && ay < mcl.Height - 1) { /* Component is not deletable - * - Resend design state - * - Return without further processing - */ + * - Resend design state + * - Return without further processing + */ design.SendDetailedInfoTo(state); return; @@ -1644,10 +1644,10 @@ namespace Server.Multis } /* Client chose to add a component - * - Read data detailing component graphic and location - * - Add component - * - Update revision - */ + * - Read data detailing component graphic and location + * - Add component + * - Update revision + */ // Read data detailing component graphic and location var itemID = reader.ReadInt32(); @@ -1690,13 +1690,13 @@ namespace Server.Multis } /* Client closed his house design window - * - Remove design context - * - Notify the client that customization has ended - * - Refresh client with current visible design state - * - If a signpost is needed, add it - * - Eject all from house - * - Restore relocated entities - */ + * - Remove design context + * - Notify the client that customization has ended + * - Refresh client with current visible design state + * - If a signpost is needed, add it + * - Eject all from house + * - Restore relocated entities + */ // Remove design context DesignContext.Remove(from); @@ -1739,13 +1739,13 @@ namespace Server.Multis } /* Client is moving to a new floor level - * - Read data detailing the target level - * - Validate target level - * - Update design context with new level - * - Teleport mobile to new level - * - Update client - * - */ + * - Read data detailing the target level + * - Validate target level + * - Update design context with new level + * - Teleport mobile to new level + * - Update client + * + */ // Read data detailing the target level var newLevel = reader.ReadInt32(); @@ -1823,7 +1823,7 @@ namespace Server.Multis if (mte.OffsetX == x && mte.OffsetY == y && GetZLevel(mte.OffsetZ, context.Foundation) == context.Level && - (TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Flags & TileFlag.Roof) != 0) + TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Roof) { mcl.Remove(mte.ItemId, x, y, mte.OffsetZ); } @@ -1856,7 +1856,7 @@ namespace Server.Multis var design = context.Foundation.DesignState; var mcl = design.Components; - if ((TileData.ItemTable[itemID & TileData.MaxItemValue].Flags & TileFlag.Roof) == 0) + if (!TileData.ItemTable[itemID & TileData.MaxItemValue].Roof) { design.SendDetailedInfoTo(state); return; diff --git a/Projects/UOContent/Multis/Houses/HousePlacement.cs b/Projects/UOContent/Multis/Houses/HousePlacement.cs index 563ef319d..3f452ad67 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacement.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacement.cs @@ -177,9 +177,9 @@ namespace Server.Multis continue; } - var addTileFlags = TileData.ItemTable[addTile.ID & TileData.MaxItemValue].Flags; + var addTileData = TileData.ItemTable[addTile.ID & TileData.MaxItemValue]; - var isFoundation = addTile.Z == 0 && (addTileFlags & TileFlag.Wall) != 0; + var isFoundation = addTile.Z == 0 && addTileData.Wall; var hasSurface = false; if (isFoundation) @@ -190,7 +190,7 @@ namespace Server.Multis var addTileZ = center.Z + addTile.Z; var addTileTop = addTileZ + addTile.Height; - if ((addTileFlags & TileFlag.Surface) != 0) + if (addTileData.Surface) { addTileTop += 16; } @@ -212,7 +212,7 @@ namespace Server.Multis var oldTile = oldTiles[j]; var id = TileData.ItemTable[oldTile.ID & TileData.MaxItemValue]; - if ((id.Impassable || id.Surface && (id.Flags & TileFlag.Background) == 0) && + if ((id.Impassable || id.Surface && !id.Background) && addTileTop > oldTile.Z && oldTile.Z + id.CalcHeight > addTileZ) { return HousePlacementResult.BadStatic; // Broke rule #2 @@ -233,7 +233,7 @@ namespace Server.Multis { toMove.Add(item); } - else if (id.Impassable || id.Surface && (id.Flags & TileFlag.Background) == 0) + else if (id.Impassable || id.Surface && !id.Background) { return HousePlacementResult.BadItem; // Broke rule #2 } @@ -359,8 +359,7 @@ namespace Server.Multis var tile = tiles[j]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - if (id.Impassable || id.Surface && (id.Flags & TileFlag.Background) == 0 && - tile.Z + id.CalcHeight > center.Z + 2) + if (id.Impassable || id.Surface && !id.Background && tile.Z + id.CalcHeight > center.Z + 2) { return HousePlacementResult.BadStatic; // Broke rule #1 } @@ -380,8 +379,7 @@ namespace Server.Multis var id = item.ItemData; - if (id.Impassable || id.Surface && (id.Flags & TileFlag.Background) == 0 && - item.Z + id.CalcHeight > center.Z + 2) + if (id.Impassable || id.Surface && !id.Background && item.Z + id.CalcHeight > center.Z + 2) { return HousePlacementResult.BadItem; // Broke rule #1 } diff --git a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs index 2d9a5f61a..0006e8413 100644 --- a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs +++ b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs @@ -37,7 +37,6 @@ namespace Server.Network writer.Write((byte)0xBA); // Packet ID writer.Write(command); - if (ns.HighSeas) { writer.Write((short)x); diff --git a/Projects/UOContent/Special Systems/Engines/TestCenter.cs b/Projects/UOContent/Special Systems/Engines/TestCenter.cs index 706045e35..2c84bc177 100644 --- a/Projects/UOContent/Special Systems/Engines/TestCenter.cs +++ b/Projects/UOContent/Special Systems/Engines/TestCenter.cs @@ -2,7 +2,10 @@ using System; using System.Text; using Microsoft.Toolkit.HighPerformance; using Server.Commands; +using Server.Factions; using Server.Gumps; +using Server.Items; +using Server.Mobiles; using Server.Network; namespace Server.Misc @@ -84,7 +87,7 @@ namespace Server.Misc private static void ChangeStrength(Mobile from, int value) { - if (value < 10 || value > 125) + if (value is < 10 or > 125) { from.SendLocalizedMessage(1005628); // Stats range between 10 and 125. } @@ -106,7 +109,7 @@ namespace Server.Misc private static void ChangeDexterity(Mobile from, int value) { - if (value < 10 || value > 125) + if (value is < 10 or > 125) { from.SendLocalizedMessage(1005628); // Stats range between 10 and 125. } @@ -128,7 +131,7 @@ namespace Server.Misc private static void ChangeIntelligence(Mobile from, int value) { - if (value < 10 || value > 125) + if (value is < 10 or > 125) { from.SendLocalizedMessage(1005628); // Stats range between 10 and 125. } @@ -186,6 +189,485 @@ namespace Server.Misc } } + private static Item MakeNewbie(Item item) + { + if (!Core.AOS) + { + item.LootType = LootType.Newbied; + } + + return item; + } + + private static void PlaceItemIn(Container parent, int x, int y, Item item) + { + parent.AddItem(item); + item.Location = new Point3D(x, y, 0); + } + + private static Item MakePotionKeg(PotionEffect type, int hue) + { + var keg = new PotionKeg + { + Held = 100, + Type = type, + Hue = hue + }; + + return MakeNewbie(keg); + } + + private static void FillBankAOS(Mobile m) + { + var bank = m.BankBox; + + // The new AOS bankboxes don't have powerscrolls, they are automatically 'applied': + + for (var i = 0; i < PowerScroll.Skills.Count; ++i) + { + m.Skills[PowerScroll.Skills[i]].Cap = 120.0; + } + + m.StatCap = 250; + + Container cont; + + // Begin box of money + cont = new WoodenBox { ItemID = 0xE7D, Hue = 0x489 }; + + PlaceItemIn(cont, 16, 51, new BankCheck(500000)); + PlaceItemIn(cont, 28, 51, new BankCheck(250000)); + PlaceItemIn(cont, 40, 51, new BankCheck(100000)); + PlaceItemIn(cont, 52, 51, new BankCheck(100000)); + PlaceItemIn(cont, 64, 51, new BankCheck(50000)); + + PlaceItemIn(cont, 16, 115, new Silver(9000)); + PlaceItemIn(cont, 34, 115, new Gold(60000)); + + PlaceItemIn(bank, 18, 169, cont); + // End box of money + + // Begin bag of potion kegs + cont = new Backpack { Name = "Various Potion Kegs" }; + + PlaceItemIn(cont, 45, 149, MakePotionKeg(PotionEffect.CureGreater, 0x2D)); + PlaceItemIn(cont, 69, 149, MakePotionKeg(PotionEffect.HealGreater, 0x499)); + PlaceItemIn(cont, 93, 149, MakePotionKeg(PotionEffect.PoisonDeadly, 0x46)); + PlaceItemIn(cont, 117, 149, MakePotionKeg(PotionEffect.RefreshTotal, 0x21)); + PlaceItemIn(cont, 141, 149, MakePotionKeg(PotionEffect.ExplosionGreater, 0x74)); + + PlaceItemIn(cont, 93, 82, new Bottle(1000)); + + PlaceItemIn(bank, 53, 169, cont); + // End bag of potion kegs + + // Begin bag of tools + cont = new Bag { Name = "Tool Bag" }; + + PlaceItemIn(cont, 30, 35, new TinkerTools(1000)); + PlaceItemIn(cont, 60, 35, new HousePlacementTool()); + PlaceItemIn(cont, 90, 35, new DovetailSaw(1000)); + PlaceItemIn(cont, 30, 68, new Scissors()); + PlaceItemIn(cont, 45, 68, new MortarPestle(1000)); + PlaceItemIn(cont, 75, 68, new ScribesPen(1000)); + PlaceItemIn(cont, 90, 68, new SmithHammer(1000)); + PlaceItemIn(cont, 30, 118, new TwoHandedAxe()); + PlaceItemIn(cont, 60, 118, new FletcherTools(1000)); + PlaceItemIn(cont, 90, 118, new SewingKit(1000)); + + PlaceItemIn(cont, 36, 51, new RunicHammer(CraftResource.DullCopper, 1000)); + PlaceItemIn(cont, 42, 51, new RunicHammer(CraftResource.ShadowIron, 1000)); + PlaceItemIn(cont, 48, 51, new RunicHammer(CraftResource.Copper, 1000)); + PlaceItemIn(cont, 54, 51, new RunicHammer(CraftResource.Bronze, 1000)); + PlaceItemIn(cont, 61, 51, new RunicHammer(CraftResource.Gold, 1000)); + PlaceItemIn(cont, 67, 51, new RunicHammer(CraftResource.Agapite, 1000)); + PlaceItemIn(cont, 73, 51, new RunicHammer(CraftResource.Verite, 1000)); + PlaceItemIn(cont, 79, 51, new RunicHammer(CraftResource.Valorite, 1000)); + + PlaceItemIn(cont, 36, 55, new RunicSewingKit(CraftResource.SpinedLeather, 1000)); + PlaceItemIn(cont, 42, 55, new RunicSewingKit(CraftResource.HornedLeather, 1000)); + PlaceItemIn(cont, 48, 55, new RunicSewingKit(CraftResource.BarbedLeather, 1000)); + + PlaceItemIn(bank, 118, 169, cont); + // End bag of tools + + // Begin bag of archery ammo + cont = new Bag { Name = "Bag Of Archery Ammo" }; + + PlaceItemIn(cont, 48, 76, new Arrow(5000)); + PlaceItemIn(cont, 72, 76, new Bolt(5000)); + + PlaceItemIn(bank, 118, 124, cont); + // End bag of archery ammo + + // Begin bag of treasure maps + cont = new Bag { Name = "Bag Of Treasure Maps" }; + + PlaceItemIn(cont, 30, 35, new TreasureMap(1, Map.Trammel)); + PlaceItemIn(cont, 45, 35, new TreasureMap(2, Map.Trammel)); + PlaceItemIn(cont, 60, 35, new TreasureMap(3, Map.Trammel)); + PlaceItemIn(cont, 75, 35, new TreasureMap(4, Map.Trammel)); + PlaceItemIn(cont, 90, 35, new TreasureMap(5, Map.Trammel)); + PlaceItemIn(cont, 90, 35, new TreasureMap(6, Map.Trammel)); + + PlaceItemIn(cont, 30, 50, new TreasureMap(1, Map.Trammel)); + PlaceItemIn(cont, 45, 50, new TreasureMap(2, Map.Trammel)); + PlaceItemIn(cont, 60, 50, new TreasureMap(3, Map.Trammel)); + PlaceItemIn(cont, 75, 50, new TreasureMap(4, Map.Trammel)); + PlaceItemIn(cont, 90, 50, new TreasureMap(5, Map.Trammel)); + PlaceItemIn(cont, 90, 50, new TreasureMap(6, Map.Trammel)); + + PlaceItemIn(cont, 55, 100, new Lockpick(30)); + PlaceItemIn(cont, 60, 100, new Pickaxe()); + + PlaceItemIn(bank, 98, 124, cont); + // End bag of treasure maps + + // Begin bag of raw materials + cont = new Bag { Hue = 0x835, Name = "Raw Materials Bag" }; + + PlaceItemIn(cont, 92, 60, new BarbedLeather(5000)); + PlaceItemIn(cont, 92, 68, new HornedLeather(5000)); + PlaceItemIn(cont, 92, 76, new SpinedLeather(5000)); + PlaceItemIn(cont, 92, 84, new Leather(5000)); + + PlaceItemIn(cont, 30, 118, new Cloth(5000)); + PlaceItemIn(cont, 30, 84, new Board(5000)); + PlaceItemIn(cont, 57, 80, new BlankScroll(500)); + + PlaceItemIn(cont, 30, 35, new DullCopperIngot(5000)); + PlaceItemIn(cont, 37, 35, new ShadowIronIngot(5000)); + PlaceItemIn(cont, 44, 35, new CopperIngot(5000)); + PlaceItemIn(cont, 51, 35, new BronzeIngot(5000)); + PlaceItemIn(cont, 58, 35, new GoldIngot(5000)); + PlaceItemIn(cont, 65, 35, new AgapiteIngot(5000)); + PlaceItemIn(cont, 72, 35, new VeriteIngot(5000)); + PlaceItemIn(cont, 79, 35, new ValoriteIngot(5000)); + PlaceItemIn(cont, 86, 35, new IronIngot(5000)); + + PlaceItemIn(cont, 30, 59, new RedScales(5000)); + PlaceItemIn(cont, 36, 59, new YellowScales(5000)); + PlaceItemIn(cont, 42, 59, new BlackScales(5000)); + PlaceItemIn(cont, 48, 59, new GreenScales(5000)); + PlaceItemIn(cont, 54, 59, new WhiteScales(5000)); + PlaceItemIn(cont, 60, 59, new BlueScales(5000)); + + PlaceItemIn(bank, 98, 169, cont); + // End bag of raw materials + + // Begin bag of spell casting stuff + cont = new Backpack { Hue = 0x480, Name = "Spell Casting Stuff" }; + + PlaceItemIn(cont, 45, 105, new Spellbook(ulong.MaxValue)); + PlaceItemIn(cont, 65, 105, new NecromancerSpellbook(0xFFFFUL)); + PlaceItemIn(cont, 85, 105, new BookOfChivalry()); + PlaceItemIn(cont, 105, 105, new BookOfBushido()); // Default ctor = full + PlaceItemIn(cont, 125, 105, new BookOfNinjitsu()); // Default ctor = full + + var runebook = new Runebook(10); + runebook.CurCharges = runebook.MaxCharges; + PlaceItemIn(cont, 145, 105, runebook); + + Item toHue = new BagOfReagents(150) { Hue = 0x2D }; + PlaceItemIn(cont, 45, 150, toHue); + + toHue = new BagOfNecroReagents(150) { Hue = 0x488 }; + PlaceItemIn(cont, 65, 150, toHue); + + PlaceItemIn(cont, 140, 150, new BagOfAllReagents(500)); + + for (var i = 0; i < 9; ++i) + { + PlaceItemIn(cont, 45 + i * 10, 75, new RecallRune()); + } + + PlaceItemIn(cont, 141, 74, new FireHorn()); + + PlaceItemIn(bank, 78, 169, cont); + // End bag of spell casting stuff + + // Begin bag of ethereals + cont = new Backpack { Hue = 0x490, Name = "Bag Of Ethy's!" }; + + PlaceItemIn(cont, 45, 66, new EtherealHorse()); + PlaceItemIn(cont, 69, 82, new EtherealOstard()); + PlaceItemIn(cont, 93, 99, new EtherealLlama()); + PlaceItemIn(cont, 117, 115, new EtherealKirin()); + PlaceItemIn(cont, 45, 132, new EtherealUnicorn()); + PlaceItemIn(cont, 69, 66, new EtherealRidgeback()); + PlaceItemIn(cont, 93, 82, new EtherealSwampDragon()); + PlaceItemIn(cont, 117, 99, new EtherealBeetle()); + + PlaceItemIn(bank, 38, 124, cont); + // End bag of ethereals + + // Begin first bag of artifacts + cont = new Backpack { Hue = 0x48F, Name = "Bag of Artifacts" }; + + PlaceItemIn(cont, 45, 66, new TitansHammer()); + PlaceItemIn(cont, 69, 82, new InquisitorsResolution()); + PlaceItemIn(cont, 93, 99, new BladeOfTheRighteous()); + PlaceItemIn(cont, 117, 115, new ZyronicClaw()); + + PlaceItemIn(bank, 58, 124, cont); + // End first bag of artifacts + + // Begin second bag of artifacts + cont = new Backpack { Hue = 0x48F, Name = "Bag of Artifacts" }; + + PlaceItemIn(cont, 45, 66, new GauntletsOfNobility()); + PlaceItemIn(cont, 69, 82, new MidnightBracers()); + PlaceItemIn(cont, 93, 99, new VoiceOfTheFallenKing()); + PlaceItemIn(cont, 117, 115, new OrnateCrownOfTheHarrower()); + PlaceItemIn(cont, 45, 132, new HelmOfInsight()); + PlaceItemIn(cont, 69, 66, new HolyKnightsBreastplate()); + PlaceItemIn(cont, 93, 82, new ArmorOfFortune()); + PlaceItemIn(cont, 117, 99, new TunicOfFire()); + PlaceItemIn(cont, 45, 115, new LeggingsOfBane()); + PlaceItemIn(cont, 69, 132, new ArcaneShield()); + PlaceItemIn(cont, 93, 66, new Aegis()); + PlaceItemIn(cont, 117, 82, new RingOfTheVile()); + PlaceItemIn(cont, 45, 99, new BraceletOfHealth()); + PlaceItemIn(cont, 69, 115, new RingOfTheElements()); + PlaceItemIn(cont, 93, 132, new OrnamentOfTheMagician()); + PlaceItemIn(cont, 117, 66, new DivineCountenance()); + PlaceItemIn(cont, 45, 82, new JackalsCollar()); + PlaceItemIn(cont, 69, 99, new HuntersHeaddress()); + PlaceItemIn(cont, 93, 115, new HatOfTheMagi()); + PlaceItemIn(cont, 117, 132, new ShadowDancerLeggings()); + PlaceItemIn(cont, 45, 66, new SpiritOfTheTotem()); + PlaceItemIn(cont, 69, 82, new BladeOfInsanity()); + PlaceItemIn(cont, 93, 99, new AxeOfTheHeavens()); + PlaceItemIn(cont, 117, 115, new TheBeserkersMaul()); + PlaceItemIn(cont, 45, 132, new Frostbringer()); + PlaceItemIn(cont, 69, 66, new BreathOfTheDead()); + PlaceItemIn(cont, 93, 82, new TheDragonSlayer()); + PlaceItemIn(cont, 117, 99, new BoneCrusher()); + PlaceItemIn(cont, 45, 115, new StaffOfTheMagi()); + PlaceItemIn(cont, 69, 132, new SerpentsFang()); + PlaceItemIn(cont, 93, 66, new LegacyOfTheDreadLord()); + PlaceItemIn(cont, 117, 82, new TheTaskmaster()); + PlaceItemIn(cont, 45, 99, new TheDryadBow()); + + PlaceItemIn(bank, 78, 124, cont); + // End second bag of artifacts + + // Begin bag of minor artifacts + cont = new Backpack { Hue = 0x48F, Name = "Bag of Minor Artifacts" }; + + PlaceItemIn(cont, 45, 66, new LunaLance()); + PlaceItemIn(cont, 69, 82, new VioletCourage()); + PlaceItemIn(cont, 93, 99, new CavortingClub()); + PlaceItemIn(cont, 117, 115, new CaptainQuacklebushsCutlass()); + PlaceItemIn(cont, 45, 132, new NightsKiss()); + PlaceItemIn(cont, 69, 66, new ShipModelOfTheHMSCape()); + PlaceItemIn(cont, 93, 82, new AdmiralsHeartyRum()); + PlaceItemIn(cont, 117, 99, new CandelabraOfSouls()); + PlaceItemIn(cont, 45, 115, new IolosLute()); + PlaceItemIn(cont, 69, 132, new GwennosHarp()); + PlaceItemIn(cont, 93, 66, new ArcticDeathDealer()); + PlaceItemIn(cont, 117, 82, new EnchantedTitanLegBone()); + PlaceItemIn(cont, 45, 99, new NoxRangersHeavyCrossbow()); + PlaceItemIn(cont, 69, 115, new BlazeOfDeath()); + PlaceItemIn(cont, 93, 132, new DreadPirateHat()); + PlaceItemIn(cont, 117, 66, new BurglarsBandana()); + PlaceItemIn(cont, 45, 82, new GoldBricks()); + PlaceItemIn(cont, 69, 99, new AlchemistsBauble()); + PlaceItemIn(cont, 93, 115, new PhillipsWoodenSteed()); + PlaceItemIn(cont, 117, 132, new PolarBearMask()); + PlaceItemIn(cont, 45, 66, new BowOfTheJukaKing()); + PlaceItemIn(cont, 69, 82, new GlovesOfThePugilist()); + PlaceItemIn(cont, 93, 99, new OrcishVisage()); + PlaceItemIn(cont, 117, 115, new StaffOfPower()); + PlaceItemIn(cont, 45, 132, new ShieldOfInvulnerability()); + PlaceItemIn(cont, 69, 66, new HeartOfTheLion()); + PlaceItemIn(cont, 93, 82, new ColdBlood()); + PlaceItemIn(cont, 117, 99, new GhostShipAnchor()); + PlaceItemIn(cont, 45, 115, new SeahorseStatuette()); + PlaceItemIn(cont, 69, 132, new WrathOfTheDryad()); + PlaceItemIn(cont, 93, 66, new PixieSwatter()); + + for (var i = 0; i < 10; i++) + { + PlaceItemIn(cont, 117, 128, new MessageInABottle(Utility.RandomBool() ? Map.Trammel : Map.Felucca, 4)); + } + + PlaceItemIn(bank, 18, 124, cont); + + if (Core.SE) + { + cont = new Bag { Hue = 0x501, Name = "Tokuno Minor Artifacts" }; + + PlaceItemIn(cont, 42, 70, new Exiler()); + PlaceItemIn(cont, 38, 53, new HanzosBow()); + PlaceItemIn(cont, 45, 40, new TheDestroyer()); + PlaceItemIn(cont, 92, 80, new DragonNunchaku()); + PlaceItemIn(cont, 42, 56, new PeasantsBokuto()); + PlaceItemIn(cont, 44, 71, new TomeOfEnlightenment()); + PlaceItemIn(cont, 35, 35, new ChestOfHeirlooms()); + PlaceItemIn(cont, 29, 0, new HonorableSwords()); + PlaceItemIn(cont, 49, 85, new AncientUrn()); + PlaceItemIn(cont, 51, 58, new FluteOfRenewal()); + PlaceItemIn(cont, 70, 51, new PigmentsOfTokuno()); + PlaceItemIn(cont, 40, 79, new AncientSamuraiDo()); + PlaceItemIn(cont, 51, 61, new LegsOfStability()); + PlaceItemIn(cont, 88, 78, new GlovesOfTheSun()); + PlaceItemIn(cont, 55, 62, new AncientFarmersKasa()); + PlaceItemIn(cont, 55, 83, new ArmsOfTacticalExcellence()); + PlaceItemIn(cont, 50, 85, new DaimyosHelm()); + PlaceItemIn(cont, 52, 78, new BlackLotusHood()); + PlaceItemIn(cont, 52, 79, new DemonForks()); + PlaceItemIn(cont, 33, 49, new PilferedDancerFans()); + + PlaceItemIn(bank, 58, 124, cont); + } + + if (Core.SE) // This bag came only after SE. + { + cont = new Bag { Name = "Bag of Bows" }; + + PlaceItemIn(cont, 31, 84, new Bow()); + PlaceItemIn(cont, 78, 74, new CompositeBow()); + PlaceItemIn(cont, 53, 71, new Crossbow()); + PlaceItemIn(cont, 56, 39, new HeavyCrossbow()); + PlaceItemIn(cont, 82, 72, new RepeatingCrossbow()); + PlaceItemIn(cont, 49, 45, new Yumi()); + + for (var i = 0; i < cont.Items.Count; i++) + { + if (cont.Items[i] is BaseRanged bow) + { + bow.Attributes.WeaponSpeed = 35; + bow.Attributes.WeaponDamage = 35; + } + } + + PlaceItemIn(bank, 108, 135, cont); + } + } + + public static void FillBankbox(Mobile m) + { + if (Core.AOS) + { + FillBankAOS(m); + return; + } + + var bank = m.BankBox; + + bank.DropItem(new BankCheck(1000000)); + + // Full spellbook + var book = new Spellbook { Content = ulong.MaxValue }; + + bank.DropItem(book); + + var bag = new Bag(); + + for (var i = 0; i < 5; ++i) + { + bag.DropItem(new Moonstone(MoonstoneType.Felucca)); + } + + // Felucca moonstones + bank.DropItem(bag); + + bag = new Bag(); + + for (var i = 0; i < 5; ++i) + { + bag.DropItem(new Moonstone(MoonstoneType.Trammel)); + } + + // Trammel moonstones + bank.DropItem(bag); + + // Treasure maps + bank.DropItem(new TreasureMap(1, Map.Trammel)); + bank.DropItem(new TreasureMap(2, Map.Trammel)); + bank.DropItem(new TreasureMap(3, Map.Trammel)); + bank.DropItem(new TreasureMap(4, Map.Trammel)); + bank.DropItem(new TreasureMap(5, Map.Trammel)); + + // Bag containing 50 of each reagent + bank.DropItem(new BagOfReagents()); + + // Craft tools + bank.DropItem(MakeNewbie(new Scissors())); + bank.DropItem(MakeNewbie(new SewingKit(1000))); + bank.DropItem(MakeNewbie(new SmithHammer(1000))); + bank.DropItem(MakeNewbie(new FletcherTools(1000))); + bank.DropItem(MakeNewbie(new DovetailSaw(1000))); + bank.DropItem(MakeNewbie(new MortarPestle(1000))); + bank.DropItem(MakeNewbie(new ScribesPen(1000))); + bank.DropItem(MakeNewbie(new TinkerTools(1000))); + + // A few dye tubs + bank.DropItem(new Dyes()); + bank.DropItem(new DyeTub()); + bank.DropItem(new DyeTub()); + bank.DropItem(new BlackDyeTub()); + bank.DropItem(new DyeTub { DyedHue = 0x485, Redyable = false }); + + // Some food + bank.DropItem(MakeNewbie(new Apple(1000))); + + // Resources + bank.DropItem(MakeNewbie(new Feather(1000))); + bank.DropItem(MakeNewbie(new BoltOfCloth(1000))); + bank.DropItem(MakeNewbie(new BlankScroll(1000))); + bank.DropItem(MakeNewbie(new Hides(1000))); + bank.DropItem(MakeNewbie(new Bandage(1000))); + bank.DropItem(MakeNewbie(new Bottle(1000))); + bank.DropItem(MakeNewbie(new Log(1000))); + + bank.DropItem(MakeNewbie(new IronIngot(5000))); + bank.DropItem(MakeNewbie(new DullCopperIngot(5000))); + bank.DropItem(MakeNewbie(new ShadowIronIngot(5000))); + bank.DropItem(MakeNewbie(new CopperIngot(5000))); + bank.DropItem(MakeNewbie(new BronzeIngot(5000))); + bank.DropItem(MakeNewbie(new GoldIngot(5000))); + bank.DropItem(MakeNewbie(new AgapiteIngot(5000))); + bank.DropItem(MakeNewbie(new VeriteIngot(5000))); + bank.DropItem(MakeNewbie(new ValoriteIngot(5000))); + + // Reagents + bank.DropItem(MakeNewbie(new BlackPearl(1000))); + bank.DropItem(MakeNewbie(new Bloodmoss(1000))); + bank.DropItem(MakeNewbie(new Garlic(1000))); + bank.DropItem(MakeNewbie(new Ginseng(1000))); + bank.DropItem(MakeNewbie(new MandrakeRoot(1000))); + bank.DropItem(MakeNewbie(new Nightshade(1000))); + bank.DropItem(MakeNewbie(new SulfurousAsh(1000))); + bank.DropItem(MakeNewbie(new SpidersSilk(1000))); + + // Some extra starting gold + bank.DropItem(MakeNewbie(new Gold(9000))); + + // 5 blank recall runes + for (var i = 0; i < 5; ++i) + { + bank.DropItem(MakeNewbie(new RecallRune())); + } + + AddPowerScrolls(bank); + } + + private static void AddPowerScrolls(BankBox bank) + { + var bag = new Bag(); + + for (var i = 0; i < PowerScroll.Skills.Count; ++i) + { + bag.DropItem(new PowerScroll(PowerScroll.Skills[i], 120.0)); + } + + bag.DropItem(new StatCapScroll(250)); + + bank.DropItem(bag); + } + public class TCHelpGump : Gump { public TCHelpGump() : base(40, 40) diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index 341134029..6b7cb8a24 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -220,7 +220,7 @@ namespace Server.Spells } } - if (Core.Expansion == Expansion.AOS) + if (Core.AOS) { for (var i = 0; i < m.Aggressors.Count; ++i) { @@ -257,6 +257,16 @@ namespace Server.Spells return false; } + public static bool GetEastToWest(IPoint3D from,IPoint3D 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); + } + public static bool CanRevealCaster(Mobile m) => m is BaseCreature { Controlled: false }; public static void GetSurfaceTop(ref IPoint3D p) diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs index 9f63b90be..4d4216a20 100644 --- a/Projects/UOContent/Spells/Fifth/PoisonField.cs +++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs @@ -38,12 +38,7 @@ namespace Server.Spells.Fifth SpellHelper.GetSurfaceTop(ref p); - var dx = Caster.Location.X - p.X; - var dy = Caster.Location.Y - p.Y; - var rx = (dx - dy) * 44; - var ry = (dx + dy) * 44; - - bool eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B); diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs index 1123b1476..df1ea840c 100644 --- a/Projects/UOContent/Spells/Fourth/FireField.cs +++ b/Projects/UOContent/Spells/Fourth/FireField.cs @@ -38,12 +38,7 @@ namespace Server.Spells.Fourth SpellHelper.GetSurfaceTop(ref p); - var dx = Caster.Location.X - p.X; - var dy = Caster.Location.Y - p.Y; - var rx = (dx - dy) * 44; - var ry = (dx + dy) * 44; - - bool eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); Effects.PlaySound(new Point3D(p), Caster.Map, 0x20C); diff --git a/Projects/UOContent/Spells/Seventh/EnergyField.cs b/Projects/UOContent/Spells/Seventh/EnergyField.cs index 8e7fa4d85..0562fd4b0 100644 --- a/Projects/UOContent/Spells/Seventh/EnergyField.cs +++ b/Projects/UOContent/Spells/Seventh/EnergyField.cs @@ -38,13 +38,7 @@ namespace Server.Spells.Seventh SpellHelper.GetSurfaceTop(ref p); - var dx = Caster.Location.X - p.X; - var dy = Caster.Location.Y - p.Y; - var rx = (dx - dy) * 44; - var ry = (dx + dy) * 44; - - bool eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; - + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B); diff --git a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs index f75edd8de..73110b457 100644 --- a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs +++ b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs @@ -37,12 +37,7 @@ namespace Server.Spells.Sixth SpellHelper.GetSurfaceTop(ref p); - var dx = Caster.Location.X - p.X; - var dy = Caster.Location.Y - p.Y; - var rx = (dx - dy) * 44; - var ry = (dx + dy) * 44; - - bool eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B); diff --git a/Projects/UOContent/Spells/Third/WallOfStone.cs b/Projects/UOContent/Spells/Third/WallOfStone.cs index 0169fb479..c03f403e5 100644 --- a/Projects/UOContent/Spells/Third/WallOfStone.cs +++ b/Projects/UOContent/Spells/Third/WallOfStone.cs @@ -35,12 +35,7 @@ namespace Server.Spells.Third SpellHelper.GetSurfaceTop(ref p); - var dx = Caster.Location.X - p.X; - var dy = Caster.Location.Y - p.Y; - var rx = (dx - dy) * 44; - var ry = (dx + dy) * 44; - - bool eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; + var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p); Effects.PlaySound(new Point3D(p), Caster.Map, 0x1F6); diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index e0ef6c535..aab3a5561 100755 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -8,14 +8,16 @@ ..\..\Distribution\Assemblies - - + + + + @@ -28,18 +30,20 @@ - - + + + false - + - + + @@ -52,21 +56,4 @@ - - - - - true - Generated - - - - - - - - - - - diff --git a/Projects/UOContent/Utilities/Types.cs b/Projects/UOContent/Utilities/Types.cs index 993708398..fc707a7bc 100644 --- a/Projects/UOContent/Utilities/Types.cs +++ b/Projects/UOContent/Utilities/Types.cs @@ -86,7 +86,7 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsParsable(Type t) => - IsChar(t) || IsString(t) || IsType(t, OfGuid) || + IsChar(t) || IsType(t, OfGuid) || IsType(t, OfTimeSpan) || IsNumeric(t) || IsDecimal(t) || t.IsDefined(OfParsable, false); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -126,59 +126,59 @@ namespace Server try { constructed = Enum.Parse(type, value ?? "", true); + return null; } catch { return "That is not a valid enumeration member."; } } - else if (IsType(type, OfType)) + + if (IsType(type, OfType)) { try { constructed = AssemblyHandler.FindTypeByName(value); - if (constructed == null) - { - return "No type with that name was found."; - } + return constructed == null ? "No type with that name was found." : null; } catch { return "No type with that name was found."; } } - else if (value == null) + + if (value == null) { constructed = null; + return null; } - else if (value.StartsWithOrdinal("0x") && IsNumeric(type)) + + if (IsType(type, OfString)) + { + constructed = value; + return null; + } + + if (value.StartsWithOrdinal("0x") && IsNumeric(type)) { try { constructed = Convert.ChangeType(Convert.ToUInt64(value[2..], 16), type); + return null; } catch { return "That is not properly formatted."; } } - else if (IsParsable(type)) + + if (IsParsable(type)) { try { constructed = Parse(type, value); - } - catch - { - return "That is not properly formatted."; - } - } - else - { - try - { - constructed = Convert.ChangeType(value, type); + return null; } catch { @@ -186,13 +186,20 @@ namespace Server } } - if (isSerial) // mutate back + try { - constructed = (Serial)(constructed ?? Serial.MinusOne); - } + constructed = Convert.ChangeType(value, type); + if (isSerial) // mutate back + { + constructed = (Serial)(constructed ?? Serial.MinusOne); + } - constructed = constructed; - return null; + return null; + } + catch + { + return "That is not properly formatted."; + } } } } diff --git a/Projects/UOContent/World Saves/AutoArchive.cs b/Projects/UOContent/World Saves/AutoArchive.cs new file mode 100755 index 000000000..7d46d6168 --- /dev/null +++ b/Projects/UOContent/World Saves/AutoArchive.cs @@ -0,0 +1,455 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Toolkit.HighPerformance; +using Server.Compression; +using Server.Logging; + +namespace Server.Saves +{ + public enum ArchivePeriod + { + Hourly, + Daily, + Monthly + } + + public enum CompressionFormat + { + None, + Zip, + GZip, + Zstd, + } + + public static class AutoArchive + { + private static readonly ILogger logger = LogFactory.GetLogger(typeof(AutoArchive)); + + private static string _tempArchivePath; + private static int _isArchiving; + private static DateTime _nextHourlyArchive; + private static DateTime _nextDailyArchive; + private static DateTime _nextMonthlyArchive; + private static CompressionFormat _compressionFormat; + private static bool _enablePruning; + + public static Action Archive { get; set; } + public static Action Prune { get; set; } + public static string ArchivePath { get; private set; } + public static string BackupPath { get; private set; } + public static string AutomaticBackupPath { get; private set; } + + public static void Configure() + { + var tempArchivePath = ServerConfiguration.GetSetting("autoArchive.tempArchivePath", "temp"); + _tempArchivePath = PathUtility.GetFullPath(tempArchivePath); + + var backupPath = ServerConfiguration.GetOrUpdateSetting("autoArchive.backupPath", "Backups"); + BackupPath = PathUtility.GetFullPath(backupPath); + AutomaticBackupPath = Path.Combine(BackupPath, "Automatic"); + + var archivePath = ServerConfiguration.GetOrUpdateSetting("autoArchive.archivePath", "Archives"); + ArchivePath = PathUtility.GetFullPath(archivePath); + + var useLocalArchives = ServerConfiguration.GetOrUpdateSetting("autoArchive.archiveLocally", true); + _enablePruning = ServerConfiguration.GetOrUpdateSetting("autoArchive.enableArchivePruning", true); + + _compressionFormat = ServerConfiguration.GetOrUpdateSetting("autoArchive.compressionFormat", CompressionFormat.Zstd); + + if (useLocalArchives) + { + Archive = AutoArchiveLocally; + } + + if (_enablePruning) + { + Prune = PruneBackups; + } + + // Restores an archive file placed in the Saves folder. Supports all compression formats. + RestoreFromArchive(); + + DateTimeOffset now = Core.Now; + var date = now.Date; + _nextHourlyArchive = date.AddHours(now.Hour); + _nextDailyArchive = date; + _nextMonthlyArchive = date.AddDays(1 - now.Day); + + // Do a local archive rollup & prune + AutoArchiveLocally(); + } + + public static void Initialize() + { + EventSink.WorldSavePostSnapshot += Backup; + } + + private static void Backup(WorldSavePostSnapshotEventArgs args) + { + if (!Directory.Exists(args.OldSavePath)) + { + return; + } + + Directory.CreateDirectory(AutomaticBackupPath); + var backupPath = Path.Combine(AutomaticBackupPath, Utility.GetTimeStamp()); + Directory.Move(args.OldSavePath, backupPath); + + logger.Information($"Created backup at {backupPath}"); + + Archive?.Invoke(); + } + + private static void RestoreFromArchive() + { + var savePath = Path.Combine(Core.BaseDirectory, ServerConfiguration.GetSetting("world.savePath", "Saves")); + if (!Directory.Exists(savePath)) + { + return; + } + + foreach (var file in PathsByTimestampName(savePath, true)) + { + if (RestoreFromFile(file, savePath)) + { + File.Delete(file); + break; + } + } + } + + private static bool RestoreFromFile(string file, string savePath) + { + var fi = new FileInfo(file); + var fileName = fi.Name; + + if (!TryGetDate(fileName[..fileName.IndexOfOrdinal(".")], out _)) + { + return false; + } + + logger.Information($"Restoring latest world save from archive {fileName}"); + + var tempPath = PathUtility.EnsureRandomPath(_tempArchivePath); + var successful = fileName.EndsWithOrdinal(".tar.zst") + ? ZstdArchive.ExtractToDirectory(fi.FullName, tempPath) + : TarArchive.ExtractToDirectory(fi.FullName, tempPath); + + if (!successful) + { + logger.Information($"Failed to extract {fi.Name}"); + return false; + } + + foreach (var folder in PathsByTimestampName(tempPath)) + { + Directory.Delete(savePath, true); + var dirInfo = new DirectoryInfo(folder); + logger.Information($"Restoring backup {dirInfo.Name}"); + Directory.Move(folder, savePath); + break; + } + + Directory.Delete(tempPath, true); + + return true; + } + + public static void PruneBackups() + { + if (Directory.Exists(ArchivePath)) + { + // Maintain a total of 66 of the most recent archives + PruneLocalArchives(ArchivePeriod.Hourly, 24); + PruneLocalArchives(ArchivePeriod.Daily, 30); + PruneLocalArchives(ArchivePeriod.Monthly, 12); + } + + if (!Directory.Exists(AutomaticBackupPath)) + { + return; + } + + var allFolders = Directory.EnumerateDirectories(AutomaticBackupPath); + var threshold = Core.Now.AddMonths(-1); + + foreach (var folder in allFolders) + { + var dirName = new DirectoryInfo(folder).Name; + + if (!TryGetDate(dirName, out var date)) + { + continue; + } + + if (date < threshold) + { + logger.Information($"Pruning old backup {folder}"); + Directory.Delete(folder, true); + } + } + } + + private static void PruneLocalArchives(ArchivePeriod period, int minRetained) + { + var periodStr = period.ToString(); + var archivePath = Path.Combine(ArchivePath, periodStr); + if (!Directory.Exists(archivePath)) + { + return; + } + + var periodLowerStr = periodStr.ToLowerInvariant(); + foreach (var archive in PathsByTimestampName(archivePath, true)) + { + if (minRetained > 0) + { + minRetained--; + continue; + } + + var fi = new FileInfo(archive); + logger.Information($"Pruning {periodLowerStr} archive {fi.Name}"); + File.Delete(archive); + } + } + + public static void AutoArchiveLocally() + { + var date = Core.Now; + + if (date < _nextHourlyArchive && date < _nextDailyArchive && date < _nextMonthlyArchive) + { + return; + } + + if (Interlocked.CompareExchange(ref _isArchiving, 0, 1) == 1) + { + return; + } + + ThreadPool.UnsafeQueueUserWorkItem( + _ => + { + if (date >= _nextHourlyArchive) + { + Rollup(ArchivePeriod.Hourly); + _nextHourlyArchive = _nextHourlyArchive.AddHours(1); + } + + if (date >= _nextDailyArchive) + { + Rollup(ArchivePeriod.Daily); + _nextDailyArchive = _nextDailyArchive.AddDays(1); + } + + if (date >= _nextMonthlyArchive) + { + Rollup(ArchivePeriod.Monthly); + _nextMonthlyArchive = _nextMonthlyArchive.AddMonths(1); + } + + Prune?.Invoke(); + _isArchiving = 0; + }, + null + ); + } + + private static void Rollup(ArchivePeriod archivePeriod) + { + if (!Directory.Exists(AutomaticBackupPath)) + { + return; + } + + var currentRangeStart = Core.Now.ArchivePeriodStart(archivePeriod); + var allFolders = Directory.EnumerateDirectories(AutomaticBackupPath); + + var items = new Dictionary>(); + foreach (var path in allFolders) + { + var dirName = new DirectoryInfo(path).Name; + + if (!TryGetDate(dirName, out var date)) + { + continue; + } + + var rangeStart = date.ArchivePeriodStart(archivePeriod); + + // Only archive the past + if (rangeStart >= currentRangeStart) + { + continue; + } + + if (items.TryGetValue(rangeStart, out var value)) + { + value.Add(date, path); + } + else + { + value = new SortedDictionary(new DescendingComparer()) + { + { date, path } + }; + + items.Add(rangeStart, value); + } + } + + var archivePeriodStr = archivePeriod.ToString(); + var archivePath = PathUtility.EnsureDirectory(Path.Combine(ArchivePath, archivePeriodStr)); + var archivePeriodStrLower = archivePeriodStr.ToLowerInvariant(); + var extension = _compressionFormat.GetFileExtension(); + + // Leave behind 1 hourly for daily, 1 daily for monthly. + var minimum = archivePeriod != ArchivePeriod.Monthly ? 1 : 0; + + foreach (var (rangeStart, sortedBackups) in items) + { + var backups = sortedBackups.Values; + if (backups.Count <= minimum) + { + continue; + } + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + + var fileName = $"{rangeStart.ToTimeStamp(archivePeriod)}{extension}"; + var archiveFilePath = Path.Combine(archivePath, fileName); + + var archiveCreated = CreateArchive(archiveFilePath, AutomaticBackupPath, backups); + + if (archiveCreated) + { + var elapsed = stopWatch.Elapsed.TotalSeconds; + logger.Information($"Created {archivePeriodStrLower} archive at {archiveFilePath} ({elapsed:F2} seconds)"); + + var i = minimum; + foreach (var backup in backups) + { + // Keep the latest one, but delete the rest. + if (i-- > 0) + { + continue; + } + + Directory.Delete(backup, true); + } + } + else + { + logger.Warning($"Failed to create {archivePeriodStrLower} archive"); + } + + stopWatch.Stop(); + } + } + + private static bool CreateArchive(string archiveFilePath, string relativeTo, IEnumerable backups) => + _compressionFormat == CompressionFormat.Zstd + ? ZstdArchive.CreateFromPaths(backups, archiveFilePath, relativeTo) + : TarArchive.CreateFromPaths(backups, archiveFilePath, relativeTo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string ToTimeStamp(this DateTime date, ArchivePeriod archivePeriod) => + archivePeriod switch + { + ArchivePeriod.Monthly => date.ToString("yyyy-MM"), + ArchivePeriod.Daily => date.ToString("yyyy-MM-dd"), + ArchivePeriod.Hourly => date.ToString("yyyy-MM-dd-HH"), + _ => date.ToTimeStamp() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static DateTime ArchivePeriodStart(this DateTime date, ArchivePeriod archivePeriod) => + archivePeriod switch + { + ArchivePeriod.Monthly => date.Date.AddDays(-(date.Day - 1)), + ArchivePeriod.Daily => date.Date, + ArchivePeriod.Hourly => date.Date.AddHours(date.Hour), + _ => date + }; + + private static IEnumerable PathsByTimestampName(string path, bool files = false) + { + var allItems = files ? Directory.EnumerateFiles(path) : Directory.GetDirectories(path); + var items = new SortedDictionary(new DescendingComparer()); + foreach (var item in allItems) + { + string name; + if (files) + { + var fileName = new FileInfo(item).Name; + name = fileName[..fileName.IndexOf('.')]; + } + else + { + name = new DirectoryInfo(item).Name; + } + + if (TryGetDate(name, out var date)) + { + // Might give wrong results if there is a file that matches: + // Example: 2021-09-01, and 2021-09-01-00 + items[date] = item; + } + } + + return items.Values; + } + + private static bool TryGetDate(string value, out DateTime date) + { + Span parts = stackalloc int[] { 0, 1, 1, 0, 0, 0 }; + + try + { + var i = 0; + foreach (var part in value.Tokenize('-')) + { + parts[i++] = int.Parse(part); + } + + if (i == 0) + { + date = DateTime.MinValue; + return false; + } + + date = new DateTime( + parts[0], + parts[1], + parts[2], + parts[3], + parts[4], + parts[5], + DateTimeKind.Utc + ); + return true; + } + catch + { + date = DateTime.MinValue; + return false; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string GetFileExtension(this CompressionFormat compressionFormat) => + compressionFormat switch + { + CompressionFormat.Zip => ".zip", + CompressionFormat.GZip => ".tar.gz", + CompressionFormat.Zstd => ".tar.zst", + _ => ".tar" + }; + } +} diff --git a/Projects/UOContent/World Saves/AutoSave.cs b/Projects/UOContent/World Saves/AutoSave.cs new file mode 100644 index 000000000..96051e6a1 --- /dev/null +++ b/Projects/UOContent/World Saves/AutoSave.cs @@ -0,0 +1,132 @@ +using System; +using Server.Misc; + +namespace Server.Saves +{ + public class AutoSave : Timer + { + private static Timer _autoSave; + private static bool _enabled; + private static DateTime _nextSave; + + public static TimeSpan Delay { get; private set; } + public static TimeSpan Warning { get; private set; } + + public static bool SavesEnabled + { + get => _enabled; + set + { + _enabled = value; + + if (value) + { + SetNextSave(Core.Now.ToSystemLocalTime()); + _autoSave ??= new AutoSave(); + _autoSave.Start(); + } + else + { + _autoSave?.Stop(); + _autoSave = null; + } + } + } + + public static void Configure() + { + Delay = ServerConfiguration.GetOrUpdateSetting("autosave.saveDelay", TimeSpan.FromMinutes(5.0)); + Warning = ServerConfiguration.GetOrUpdateSetting("autosave.warningDelay", TimeSpan.Zero); + } + + public static void Initialize() + { + SavesEnabled = true; + } + + public static void ResetAutoSave(TimeSpan saveDelay, TimeSpan warningDelay) + { + if (saveDelay != Delay || warningDelay != Warning) + { + Delay = saveDelay; + Warning = warningDelay; + + ServerConfiguration.SetSetting("autosave.saveDelay", Delay); + ServerConfiguration.SetSetting("autosave.warningDelay", Warning); + } + + SavesEnabled = true; + } + + private static void SetNextSave(DateTime now) + { + var timeOfDay = now.TimeOfDay; + var delay = Delay.Ticks; + + _nextSave = (now + TimeSpan.FromTicks(delay - timeOfDay.Ticks % delay) - Warning).ToUniversalTime(); + } + + public AutoSave() : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) + { + } + + protected override void OnTick() + { + if (_nextSave > Core.Now) + { + return; + } + + if (SavesEnabled && !AutoRestart.Restarting && !Core.Closing && !World.Saving) + { + if (Warning > TimeSpan.Zero) + { + BroadcastWarning(); + StartTimer(Warning, Save); + } + else + { + Save(); + } + } + + _nextSave += Delay; + } + + private static void BroadcastWarning() + { + var s = (int)Warning.TotalSeconds; + var m = s / 60; + s %= 60; + + if (m > 0 && s > 0) + { + World.Broadcast( + 0x35, + true, + "The world will save in {0} minute{1} and {2} second{3}.", + m, + m != 1 ? "s" : "", + s, + s != 1 ? "s" : "" + ); + } + else if (m > 0) + { + World.Broadcast(0x35, true, "The world will save in {0} minute{1}.", m, m != 1 ? "s" : ""); + } + else + { + World.Broadcast(0x35, true, "The world will save in {0} second{1}.", s, s != 1 ? "s" : ""); + } + } + + public static void Save() + { + if (!AutoRestart.Restarting && World.Running) + { + World.Save(); + } + } + } +} diff --git a/Projects/UOContent/World Saves/SaveCommands.cs b/Projects/UOContent/World Saves/SaveCommands.cs new file mode 100644 index 000000000..811239a94 --- /dev/null +++ b/Projects/UOContent/World Saves/SaveCommands.cs @@ -0,0 +1,60 @@ +using Server.Saves; + +namespace Server.Commands +{ + public static class SaveCommands + { + public static void Initialize() + { + CommandSystem.Register("Save", AccessLevel.Administrator, Save_OnCommand); + CommandSystem.Register("SetSaves", AccessLevel.Administrator, SetAutoSaves_OnCommand); + CommandSystem.Register("AutoSave", AccessLevel.Administrator, SetAutoSaves_OnCommand); + CommandSystem.Register("SaveFrequency", AccessLevel.Administrator, SetSaveFrequency_OnCommand); + CommandSystem.Register("PruneArchives", AccessLevel.Administrator, PruneArchives_OnCommand); + } + + [Usage("PruneArchives"), Description("Prunes archives folder.")] + private static void PruneArchives_OnCommand(CommandEventArgs e) + { + AutoArchive.PruneBackups(); + } + + [Usage("Save"), Description("Saves the world.")] + private static void Save_OnCommand(CommandEventArgs e) + { + AutoSave.Save(); + } + + [Usage("AutoSave "), Description("Enables or disables automatic shard saving.")] + public static void SetAutoSaves_OnCommand(CommandEventArgs e) + { + if (e.Length == 1) + { + + var enabled = AutoSave.SavesEnabled = e.GetBoolean(0); + e.Mobile.SendMessage("Saves have been {0}.", enabled ? "enabled" : "disabled"); + } + else + { + e.Mobile.SendMessage("Format: AutoSave "); + } + } + + [Usage("SaveFrequency [warning duration]"), Description("Sets the save frequency starting at midnight local to the server.")] + public static void SetSaveFrequency_OnCommand(CommandEventArgs e) + { + if (e.Length < 1) + { + e.Mobile.SendMessage("Format: SaveFrequency [warning duration]"); + return; + } + + var saveDelay = e.GetTimeSpan(0); + var warningDelay = e.Length >= 2 ? e.GetTimeSpan(1) : AutoSave.Warning; + + AutoSave.ResetAutoSave(saveDelay, warningDelay); + + e.Mobile.SendMessage("Save frequency has been updated."); + } + } +} diff --git a/README.md b/README.md index 09c87f142..9cd03b05e 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,6 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc ===== ##### Ultima Online Server Emulator for the modern era! -[![.NET](https://img.shields.io/badge/.NET-%205.0-5C2D91)](https://dotnet.microsoft.com/download/dotnet/5.0) -
-![Windows](https://img.shields.io/badge/-server%202019-0078D6?logo=windows) -![OSX](https://img.shields.io/badge/-big%20sur-222222?logo=apple&logoColor=white) -![Debian](https://img.shields.io/badge/-buster-A81D33?logo=debian) -![Ubuntu](https://img.shields.io/badge/-20LTS-E95420?logo=ubuntu&logoColor=white) -![CentOS](https://img.shields.io/badge/-8.3-262577?logo=centos&logoColor=white) -![Fedora](https://img.shields.io/badge/-33-0B57A4?logo=fedora&logoColor=white) -![RedHat](https://img.shields.io/badge/-8-BE0000?logo=red%20hat&logoColor=white) -
[![GitHub license](https://img.shields.io/github/license/modernuo/ModernUO?color=blue)](https://github.com/modernuo/ModernUO/blob/master/LICENSE) [![GitHub stars](https://img.shields.io/github/stars/modernuo/ModernUO?logo=github)](https://github.com/modernuo/ModernUO/stargazers) [![GitHub issues](https://img.shields.io/github/issues/modernuo/ModernUO?logo=github)](https://github.com/modernuo/ModernUO/issues) @@ -23,38 +13,53 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc [![GitHub build](https://img.shields.io/github/workflow/status/modernuo/ModernUO/Build?logo=github)](https://github.com/modernuo/ModernUO/actions) [![AzurePipelines build](https://dev.azure.com/modernuo/modernuo/_apis/build/status/Build?branchName=main)](https://dev.azure.com/modernuo/modernuo/_build/latest?definitionId=1&branchName=main) -## Building the server -#### Requirements -- [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) -- [Visual Studio 2019/2022 (v16.10 or higher)](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) or [Rider 2021.2 or higher](https://www.jetbrains.com/rider/download) - - Older versions will show code errors. - - Build the solution in the IDE to make errors go away. - - Visual Studio must be restarted once after the first build as well. +## Requirements +#### Supported Operating Systems +[![Windows 8.1/10/2016/2019](https://img.shields.io/badge/-server%202019-0078D6?logo=windows)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2019) +![MacOS 10/11](https://img.shields.io/badge/-big%20sur-222222?logo=apple&logoColor=white) +[![Debian 9/10](https://img.shields.io/badge/-buster-A81D33?logo=debian)](https://www.debian.org/distrib/) +[![Ubuntu 16/18/20 LTS](https://img.shields.io/badge/-20LTS-E95420?logo=ubuntu&logoColor=white)](https://ubuntu.com/download/server) +[![CentOS 7/8](https://img.shields.io/badge/-8.3-262577?logo=centos&logoColor=white)](https://www.centos.org/download/) +[![Fedora 32/33/34](https://img.shields.io/badge/-33-0B57A4?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) -#### Publishing Builds -- Using terminal or powershell: `./publish.cmd [release|debug (default: release)] [os]` +#### Running the server +[![.NET](https://img.shields.io/badge/.NET-%205.0-5C2D91)](https://dotnet.microsoft.com/download/dotnet/5.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/.NET-%205.0.10%20SDK-5C2D91)](https://dotnet.microsoft.com/download/dotnet/5.0) + +#### Supported IDEs +[Jetbrains Rider 2021.2](https://www.jetbrains.com/rider/download) +[Visual Studio 2019](https://visualstudio.microsoft.com/downloads) +
+###### Note: VS Code is not currently supported. + +## Getting Started +- Install prerequisite [requirements](https://github.com/modernuo/ModernUO#requirements) +- Clone this repository (or download the [latest](https://github.com/modernuo/ModernUO/archive/refs/heads/main.zip)): + - `git clone https://github.com/modernuo/ModernUO.git` +- Open `ModernUO.sln` to start developing + +## Building/Publishing +- Run `./publish.cmd [release|debug (default: release)] [os]` - `os` - [Supported operating systems](https://github.com/dotnet/core/blob/master/release-notes/5.0/5.0-supported-os.md) - `win` - Windows 8.1/10/2016/2019 - `osx` - MacOS 10.13+/11.0 (High Sierra, Mojave, Catalina, & Big Sur) - `ubuntu.16.04`, `ubuntu.18.04` `ubuntu.20.04` - Ubuntu LTS - `debian.9`, `debian.10` - Debian - `centos.7`, `centos.8` - CentOS - - `fedora.32`, `fedora.33` - Fedora + - `fedora.32`, `fedora.33`, `fedora.34` - Fedora - `rhel.7`, `rhel.8` - Redhat - If blank, the operating system running the build is used -## Deploying / Running Server +**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 -- Copy `Distribution` directory to the production server - -#### Requirements -- [.NET 5 Runtime](https://dotnet.microsoft.com/download/dotnet/5.0) - -#### Running -- `dotnet ModernUO.dll` - -#### Cleaning -- `dotnet clean` +- Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory on the server ## Thanks - RunUO Team & Community diff --git a/SPONSORS.md b/SPONSORS.md index 0fded84a3..769bced1f 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -21,3 +21,9 @@ We greatly appreciate the support! Use one of the following platforms below: #### Venmo [Venmo | Sabresite](https://venmo.com/code?user_id=1834446441414656966) + +#### Bitcoin +37QmRWTCjVoNWycMpo1t5JnYVDmJTGSJ9W + +#### Ethereum +0x7A9D76F497d4Ee150Ada0ea0455fF3f6e8F3b6b8 diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 76fdea608..68a241499 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -84,3 +84,34 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License notice for wepoll +--------------------------- +wepoll - epoll for Windows +https://github.com/piscisaureus/wepoll + +Copyright 2012-2020, Bert Belder +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index dbaaf944a..4f721fc49 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ jobs: displayName: 'Install .NET 5' inputs: packageType: sdk - version: 5.0.302 + version: 5.0.401 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release win displayName: 'Build' @@ -62,7 +62,7 @@ jobs: displayName: 'Install .NET 5' inputs: packageType: sdk - version: 5.0.302 + version: 5.0.401 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release $(os) displayName: 'Build' diff --git a/version.json b/version.json index 629bc3bab..dd101024d 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.2" + "version": "0.8.3" }