Merge branch 'main' into webSupport

This commit is contained in:
Kamron Batman 2021-11-12 08:47:32 -08:00 committed by GitHub
commit 1f81bb7303
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 3126 additions and 1655 deletions

View file

@ -58,7 +58,7 @@
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
<Version>3.4.231</Version>
<Version>3.4.240</Version>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<AdditionalFiles Include=".\Rules.ruleset" />

View file

@ -10,56 +10,6 @@ Static 0x051F
Static 0x0520
2669 2073 -20
# stone
Static 0x071E
2726 2133 0
2727 2131 0
2727 2131 5
2727 2131 10
2727 2131 15
2727 2131 20
2727 2131 25
2727 2132 0
2727 2132 30
2727 2133 0
2727 2133 30
2727 2133 35
2727 2134 0
2727 2134 30
2727 2135 0
2727 2135 5
2727 2135 10
2727 2135 15
2727 2135 20
2727 2135 25
2728 2133 0
# stone stairs
Static 0x071F
2727 2134 35
2727 2135 30
# stone stairs
Static 0x0737
2727 2131 30
2727 2132 35
# stone stairs
Static 0x07A0
2726 2132 0
# stone stairs
Static 0x07A1
2728 2134 0
# stone stairs
Static 0x07A2
2728 2132 0
# stone stairs
Static 0x07DA
2726 2134 0
# bottle of ale
BeverageBottle 0x099F (Content=Ale)
2659 2187 8
@ -719,4 +669,4 @@ GlassMug 0x1F81
# glass of water
GlassMug 0x1F91 (Content=Water)
2679 2233 4
2679 2233 4

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,235 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2021 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: DictionaryMigrationRule.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
{
public class DictionaryMigrationRule : ISerializableMigrationRule
{
private const string KEY_VALUE_PAIR_DELIMITER = "----";
public string RuleName => nameof(DictionaryMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsDictionary(compilation))
{
ruleArguments = null;
return false;
}
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var serializableKeyProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"KeyEntry",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var serializableValueProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ValueEntry",
valueSymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var keyPropertyLength = serializableKeyProperty.RuleArguments?.Length ?? 0;
var valuePropertyLength = serializableValueProperty.RuleArguments?.Length ?? 0;
ruleArguments = new string[keyPropertyLength + valuePropertyLength + 6];
ruleArguments[0] = extraOptions;
ruleArguments[1] = keySymbolType.ToDisplayString();
ruleArguments[2] = serializableKeyProperty.Rule;
if (keyPropertyLength > 0)
{
Array.Copy(serializableKeyProperty.RuleArguments!, 0, ruleArguments, 3, keyPropertyLength);
}
ruleArguments[3 + keyPropertyLength] = KEY_VALUE_PAIR_DELIMITER;
ruleArguments[4 + keyPropertyLength] = valueSymbolType.ToDisplayString();
ruleArguments[5 + keyPropertyLength] = serializableValueProperty.Rule;
if (valuePropertyLength > 0)
{
Array.Copy(serializableValueProperty.RuleArguments!, 0, ruleArguments, 6 + keyPropertyLength, valuePropertyLength);
}
return true;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![2]];
var valueRuleIndex = Array.IndexOf(ruleArguments, KEY_VALUE_PAIR_DELIMITER, 4);
if (valueRuleIndex == -1)
{
throw new InvalidDataException($"Cannot find key-value delimiter in arguments for {property.Name}");
}
var keyRuleArguments = new string[valueRuleIndex - 3];
Array.Copy(ruleArguments, 3, keyRuleArguments, 0, keyRuleArguments.Length);
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[valueRuleIndex + 2]];
var valueRuleArguments = new string[ruleArguments.Length - valueRuleIndex - 2];
Array.Copy(ruleArguments, 2 + valueRuleIndex, valueRuleArguments, 0, valueRuleArguments.Length);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[1]} {propertyKeyEntry};");
source.AppendLine($"{indent}{ruleArguments[valueRuleIndex + 1]} {propertyValueEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.Dictionary<{ruleArguments[1]}, {ruleArguments[valueRuleIndex + 1]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = ruleArguments[1],
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableKeyElement, parentReference);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = ruleArguments[valueRuleIndex + 1],
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableValueElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyKeyEntry}, {propertyValueEntry});");
source.AppendLine($"{indent}}}");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var shouldTidy = ruleArguments![0].Contains("@Tidy");
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[2]];
var valueRuleIndex = Array.IndexOf(ruleArguments, KEY_VALUE_PAIR_DELIMITER, 3);
if (valueRuleIndex == -1)
{
throw new InvalidDataException($"Cannot find key-value delimiter in arguments for {property.Name}");
}
var keyRuleArguments = new string[valueRuleIndex - 3];
Array.Copy(ruleArguments, 3, keyRuleArguments, 0, keyRuleArguments.Length);
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[valueRuleIndex + 2]];
var valueRuleArguments = new string[ruleArguments.Length - valueRuleIndex - 2];
Array.Copy(ruleArguments, 2 + valueRuleIndex, valueRuleArguments, 0, valueRuleArguments.Length);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var ({propertyKeyEntry}, {propertyValueEntry}) in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = ruleArguments[1],
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableKeyElement);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = ruleArguments[valueRuleIndex + 1],
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableValueElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}
}

View file

@ -33,6 +33,7 @@ namespace SerializableMigration
new ListMigrationRule(),
new ArrayMigrationRule(),
new HashSetMigrationRule(),
new DictionaryMigrationRule(),
new KeyValuePairMigrationRule(),
new PrimitiveTypeMigrationRule(),
new PrimitiveUOTypeMigrationRule(),

View file

@ -19,6 +19,7 @@ namespace SerializationGenerator
{
public static partial class SymbolMetadata
{
public const string DICTIONARY_CLASS = "System.Collections.Generic.Dictionary`2";
public const string LIST_CLASS = "System.Collections.Generic.List`1";
public const string HASHSET_CLASS = "System.Collections.Generic.HashSet`1";
public const string IPADDRESS_CLASS = "System.Net.IPAddress";
@ -43,9 +44,9 @@ namespace SerializationGenerator
SymbolEqualityComparer.Default
) == true;
public static bool IsList(this ISymbol symbol, Compilation compilation) =>
public static bool IsDictionary(this ISymbol symbol, Compilation compilation) =>
(symbol as INamedTypeSymbol)?.ConstructedFrom.Equals(
compilation.GetTypeByMetadataName(LIST_CLASS),
compilation.GetTypeByMetadataName(DICTIONARY_CLASS),
SymbolEqualityComparer.Default
) == true;
@ -55,6 +56,12 @@ namespace SerializationGenerator
SymbolEqualityComparer.Default
) == true;
public static bool IsList(this ISymbol symbol, Compilation compilation) =>
(symbol as INamedTypeSymbol)?.ConstructedFrom.Equals(
compilation.GetTypeByMetadataName(LIST_CLASS),
SymbolEqualityComparer.Default
) == true;
public static bool IsPrimitiveFromTypeDisplayString(string type) =>
type is "bool" or "sbyte" or "short" or "int" or "long" or "byte" or "ushort"
or "uint" or "ulong" or "float" or "double" or "string" or "decimal";

View file

@ -11,6 +11,9 @@
<ProjectReference Include="..\Server\Server.csproj" />
<ProjectReference Include="..\UOContent\UOContent.csproj" />
<DataFiles Include="$(SolutionDir)\Distribution\Data\**" />
<DataFiles Update="..\..\Distribution\Data\Professions\LBR\prof.txt">
<Link>Data\Professions\LBR\prof.txt</Link>
</DataFiles>
</ItemGroup>
<Target Name="CopyData" AfterTargets="AfterBuild">
<Copy SourceFiles="@(DataFiles)" DestinationFolder="$(OutDir)\Data\%(RecursiveDir)" />

View file

@ -129,7 +129,7 @@ namespace System.Buffers
public Span<T> GetSpan(int index)
{
if (index < 0 || index > 1)
if (index is < 0 or > 1)
{
throw new ArgumentOutOfRangeException(nameof(index));
}

View file

@ -32,6 +32,10 @@ namespace Server.Network
public int Position { get; private set; }
public int Remaining => Length - Position;
// Only used for debugging!
public ReadOnlySpan<byte> First => _first;
public ReadOnlySpan<byte> Second => _second;
public CircularBufferReader(ref CircularBuffer<byte> buffer) : this(buffer.GetSpan(0), buffer.GetSpan(1))
{
}
@ -58,12 +62,9 @@ namespace Server.Network
try
{
using var sw = new StreamWriter("Packets.log", true);
using var sw = new StreamWriter("unhandled-packets.log", true);
sw.WriteLine("Client: {0}: Unhandled packet 0x{1:X2}", state, _first[0]);
Utility.FormatBuffer(sw, _first.ToArray(), new Memory<byte>(_second.ToArray()));
sw.FormatBuffer(_first, _second, Length);
sw.WriteLine();
sw.WriteLine();
}

View file

@ -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
{

View file

@ -158,6 +158,30 @@ 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()
{
var path = Path.Combine(Core.BaseDirectory, "Data/expansion.json");

View file

@ -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;
@ -248,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;

View file

@ -73,6 +73,7 @@ namespace Server.Network
internal ParserState _parserState = ParserState.AwaitingNextPacket;
internal ProtocolState _protocolState = ProtocolState.AwaitingSeed;
internal GCHandle _handle;
private bool _packetLogging;
internal enum ParserState
{
@ -97,6 +98,13 @@ namespace Server.Network
Error
}
private static string _packetLoggingPath;
public static void Configure()
{
_packetLoggingPath = ServerConfiguration.GetSetting("netstate.packetLoggingPath", Path.Combine(Core.BaseDirectory, "Packets"));
}
public static void Initialize()
{
Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive);
@ -142,6 +150,21 @@ namespace Server.Network
CreatedCallback?.Invoke(this);
}
// Only use this for debugging. This will make your server very slow!
public bool PacketLogging
{
get => _packetLogging;
set
{
_packetLogging = value;
if (_packetLogging)
{
StartPacketLog();
}
}
}
public DateTime ConnectedOn { get; }
public TimeSpan ConnectedFor => Core.Now - ConnectedOn;
@ -160,6 +183,8 @@ namespace Server.Network
set => _packetEncoder = value;
}
public int CurrentPacket { get; internal set; }
public bool SentFirstPacket { get; set; }
public bool BlockAllPackets { get; set; }
@ -484,6 +509,11 @@ namespace Server.Network
buffer.CopyFrom(span);
}
if (PacketLogging)
{
LogPacket(span, ReadOnlySpan<byte>.Empty, span.Length, false);
}
SendPipe.Writer.Advance((uint)length);
if (!_flushQueued)
@ -504,6 +534,48 @@ namespace Server.Network
}
}
private void StartPacketLog()
{
try
{
var logDir = Path.Combine(_packetLoggingPath, _toString);
PathUtility.EnsureDirectory(logDir);
var logPath = Path.Combine(logDir, "packets.log");
using var op = new StreamWriter(logPath, true);
op.WriteLine(">>>>>>>>>> Logging started {0:yyyy/MM/dd HH:mm::ss} <<<<<<<<<<", Core.Now);
op.WriteLine();
op.WriteLine();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private void LogPacket(ReadOnlySpan<byte> first, ReadOnlySpan<byte> second, int totalLength, bool incoming)
{
try
{
var logDir = Path.Combine(_packetLoggingPath, _toString);
PathUtility.EnsureDirectory(logDir);
var logPath = Path.Combine(logDir, "packets.log");
const string incomingStr = "Client -> Server";
const string outgoingStr = "Server -> Client";
using var sw = new StreamWriter(logPath, true);
sw.WriteLine($"{Core.Now:HH:mm:ss.ffff}: {(incoming ? incomingStr : outgoingStr)} 0x{first[0]:X2} (Length: {totalLength})");
sw.FormatBuffer(first, second, totalLength);
sw.WriteLine();
sw.WriteLine();
}
catch
{
// ignored
}
}
public void HandleReceive()
{
if (!_running)
@ -758,6 +830,11 @@ namespace Server.Network
UpdatePacketCount(packetId);
if (PacketLogging)
{
LogPacket(packetReader.First, packetReader.Second, packetLength, true);
}
handler.OnReceive(this, packetReader, ref packetLength);
prof?.Finish(packetLength);

View file

@ -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,

View file

@ -34,7 +34,7 @@
<Delete Files="..\..\Distribution\zlib.dll" ContinueOnError="true" />
</Target>
<ItemGroup>
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.0.2" />
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.0" />
<PackageReference Include="PollGroup" Version="1.1.0" />
<PackageReference Include="Zlib.Bindings" Version="1.5.0" />
</ItemGroup>

View file

@ -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;

View file

@ -59,6 +59,27 @@ namespace Server.Text
return result;
}
public static unsafe int ToSpacedHexString(this ReadOnlySpan<byte> bytes, Span<char> result)
{
var charsWritten = 0;
fixed (char* resultP = result)
{
for (int i = 0; i < bytes.Length; i++)
{
var resultP2 = (uint*)(resultP + charsWritten);
*resultP2 = m_Lookup32Chars[bytes[i]];
charsWritten += 2;
if (i < bytes.Length - 1)
{
*(resultP + charsWritten++) = ' ';
}
}
}
return charsWritten;
}
public static string ToDelimitedHexString(this byte[] bytes) => ((ReadOnlySpan<byte>)bytes).ToDelimitedHexString();
public static unsafe string ToDelimitedHexString(this ReadOnlySpan<byte> bytes)

View file

@ -10,7 +10,9 @@ using System.Text;
using System.Xml;
using Microsoft.Toolkit.HighPerformance;
using Server.Buffers;
using Server.Collections;
using Server.Random;
using Server.Text;
namespace Server
{
@ -632,207 +634,42 @@ namespace Server
}
}
public static void FormatBuffer(TextWriter output, Stream input, int length)
public static void FormatBuffer(this TextWriter op, ReadOnlySpan<byte> first, ReadOnlySpan<byte> second, int totalLength)
{
output.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
output.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
op.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
op.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
var byteIndex = 0;
var whole = length >> 4;
var rem = length & 0xF;
for (var i = 0; i < whole; ++i, byteIndex += 16)
if (totalLength <= 0)
{
var bytes = new StringBuilder(49);
var chars = new StringBuilder(16);
for (var j = 0; j < 16; ++j)
{
var c = input.ReadByte();
bytes.Append(c.ToString("X2"));
if (j != 7)
{
bytes.Append(' ');
}
else
{
bytes.Append(" ");
}
if (c >= 0x20 && c < 0x7F)
{
chars.Append((char)c);
}
else
{
chars.Append('.');
}
}
output.Write(byteIndex.ToString("X4"));
output.Write(" ");
output.Write(bytes.ToString());
output.Write(" ");
output.WriteLine(chars.ToString());
op.WriteLine("0000 ");
return;
}
if (rem != 0)
Span<byte> lineBytes = stackalloc byte[16];
Span<char> lineChars = stackalloc char[47];
for (var i = 0; i < totalLength; i += 16)
{
var bytes = new StringBuilder(49);
var chars = new StringBuilder(rem);
for (var j = 0; j < 16; ++j)
var length = Math.Min(totalLength - i, 16);
if (i < first.Length)
{
if (j < rem)
var firstLength = Math.Min(length, first.Length - i);
first.Slice(i, firstLength).CopyTo(lineBytes);
if (firstLength < length)
{
var c = input.ReadByte();
bytes.Append(c.ToString("X2"));
if (j != 7)
{
bytes.Append(' ');
}
else
{
bytes.Append(" ");
}
if (c >= 0x20 && c < 0x7F)
{
chars.Append((char)c);
}
else
{
chars.Append('.');
}
}
else
{
bytes.Append(" ");
second[..(length - first.Length - i)].CopyTo(lineBytes[(length - firstLength)..]);
}
}
output.Write(byteIndex.ToString("X4"));
output.Write(" ");
output.Write(bytes.ToString());
output.Write(" ");
output.WriteLine(chars.ToString());
}
}
public static void FormatBuffer(TextWriter output, params Memory<byte>[] mems)
{
output.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
output.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
var byteIndex = 0;
var length = 0;
for (var i = 0; i < mems.Length; i++)
{
length += mems[i].Length;
}
var position = 0;
var memIndex = 0;
var span = mems[memIndex].Span;
var whole = length >> 4;
var rem = length & 0xF;
for (var i = 0; i < whole; ++i, byteIndex += 16)
{
var bytes = new StringBuilder(49);
var chars = new StringBuilder(16);
for (var j = 0; j < 16; ++j)
else
{
var c = span[position++];
if (position > span.Length)
{
span = mems[memIndex++].Span;
position = 0;
}
bytes.Append(c.ToString("X2"));
if (j != 7)
{
bytes.Append(' ');
}
else
{
bytes.Append(" ");
}
if (c >= 0x20 && c < 0x7F)
{
chars.Append((char)c);
}
else
{
chars.Append('.');
}
second.Slice(i - first.Length, length).CopyTo(lineBytes);
}
output.Write(byteIndex.ToString("X4"));
output.Write(" ");
output.Write(bytes.ToString());
output.Write(" ");
output.WriteLine(chars.ToString());
}
var charsWritten = ((ReadOnlySpan<byte>)lineBytes[..length]).ToSpacedHexString(lineChars);
if (rem != 0)
{
var bytes = new StringBuilder(49);
var chars = new StringBuilder(rem);
for (var j = 0; j < 16; ++j)
{
if (j < rem)
{
var c = span[position++];
if (position > span.Length)
{
span = mems[memIndex++].Span;
position = 0;
}
bytes.Append(c.ToString("X2"));
if (j != 7)
{
bytes.Append(' ');
}
else
{
bytes.Append(" ");
}
if (c >= 0x20 && c < 0x7F)
{
chars.Append((char)c);
}
else
{
chars.Append('.');
}
}
else
{
bytes.Append(" ");
}
}
output.Write(byteIndex.ToString("X4"));
output.Write(" ");
output.Write(bytes.ToString());
output.Write(" ");
output.WriteLine(chars.ToString());
op.Write("{0:X4} ", i);
op.Write(lineChars[..charsWritten]);
op.WriteLine();
}
}
@ -1352,6 +1189,45 @@ namespace Server
set.RemoveWhere(entry => entry?.Deleted != false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Tidy<K, V>(this Dictionary<K, V> dictionary)
{
var serializable = typeof(ISerializable);
var serializableKey = typeof(K).IsAssignableTo(serializable);
var serializableValue = typeof(V).IsAssignableTo(serializable);
if (!serializableKey && !serializableValue)
{
return;
}
using var queue = PooledRefQueue<K>.Create();
foreach (var (key, value) in dictionary)
{
if (serializableKey)
{
if (key == null || ((ISerializable)key).Deleted)
{
queue.Enqueue(key);
}
}
else
{
if (value == null || ((ISerializable)value).Deleted)
{
queue.Enqueue(key);
}
}
}
while (queue.Count > 0)
{
dictionary.Remove(queue.Dequeue());
}
dictionary.TrimExcess();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int NumberOfSetBits(this ulong i)
{

View file

@ -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%
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -624,7 +624,7 @@ namespace Server.Items
public override void OnAfterDuped(Item newItem)
{
if (!(newItem is BaseClothing clothing))
if (newItem is not BaseClothing clothing)
{
return;
}

View file

@ -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;
}
}

View file

@ -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();
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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))

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.BloodBlade"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.DiscMace"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.DreadSword"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.DualPointedSpear"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.DualShortAxes"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothArmsType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothArmsType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothChestType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothChestType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothKiltType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothKiltType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothLegsType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishClothLegsType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneArmsType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneArmsType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneChestType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneChestType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneKiltType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneKiltType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneLegsType1"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishStoneLegsType2"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishTalwar"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GargishWoodenShield"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GlassStaff"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.GlassSword"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.SerpentstoneStaff"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.Talwar"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.TinkerTools"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.TinkersTools"
}

File diff suppressed because it is too large Load diff

View file

@ -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<ProfessionInfo>
{
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; }
}
}

View file

@ -119,7 +119,8 @@ namespace Server.Mobiles
p = GetSpawnPosition(2);
var eable = Map.GetItemsInRange<StainedOoze>(p, 0);
bool atLocation = eable.GetEnumerator().MoveNext();
using var enumerator = eable.GetEnumerator();
bool atLocation = enumerator.MoveNext();
eable.Free();
if (!atLocation)
{

View file

@ -252,7 +252,8 @@ namespace Server.Mobiles
p = GetSpawnPosition(2);
var eable = Map.GetItemsInRange<StainedOoze>(p, 0);
bool atLocation = eable.GetEnumerator().MoveNext();
using var enumerator = eable.GetEnumerator();
bool atLocation = enumerator.MoveNext();
eable.Free();
if (!atLocation)
{

View file

@ -803,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
@ -3204,7 +3211,6 @@ namespace Server.Mobiles
RecentlyReported ??= new List<Mobile>();
// Professions weren't verified on 1.0 RC0
if (!CharacterCreation.VerifyProfession(Profession))
{
Profession = 0;

View file

@ -0,0 +1,60 @@
using Server.Commands;
using Server.Commands.Generic;
using Server.Mobiles;
namespace Server.Network
{
public class PacketLoggingCommand : BaseCommand
{
public static void Initialize()
{
TargetCommands.Register(new PacketLoggingCommand());
}
public PacketLoggingCommand()
{
AccessLevel = AccessLevel.Developer;
Commands = new[] { "PacketLogging" };
ObjectTypes = ObjectTypes.Mobiles;
Supports = CommandSupport.AllMobiles;
Usage = "PacketLogging <on|off>";
Description = "Enables or disables packet logging for a particular user until they disconnect.";
}
public override void Execute(CommandEventArgs e, object targeted)
{
if (e.Arguments.Length == 0)
{
LogFailure("Format: PacketLogging <on|off>");
return;
}
var from = e.Mobile;
var enable = Utility.ToBoolean(e.Arguments[0]);
if (targeted is not PlayerMobile pm)
{
LogFailure("That is not a player.");
}
else if (pm.NetState == null)
{
LogFailure("The player is not connected.");
}
else if (from != pm && from.AccessLevel < pm.AccessLevel)
{
LogFailure("You do not have the required access level to do this.");
}
else
{
pm.NetState.PacketLogging = enable;
var enabled = enable ? "enabled" : "disabled";
CommandLogging.WriteLine(
from,
$"{from.AccessLevel} {CommandLogging.Format(from)} {enabled} packet logging for {pm.Account.Username} ({pm.NetState})"
);
AddResponse($"Packet logging has been {enabled} for {pm.Account.Username} ({pm.NetState})");
}
}
}
}

View file

@ -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)

View file

@ -40,7 +40,7 @@
</ProjectReference>
<PackageReference Include="MailKit" Version="2.15.0" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0" />
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.0.2" />
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.0" />
<PackageReference Include="Zlib.Bindings" Version="1.5.0" />
<PackageReference Include="Argon2.Bindings" Version="1.9.1" />
<PackageReference Include="Zstd.Binaries" Version="1.0.0" />