diff --git a/data/dfndata/items/gmmenu/spawners.dfn b/data/dfndata/items/gmmenu/spawners.dfn index 342ebcb4f..a5fc82e11 100644 --- a/data/dfndata/items/gmmenu/spawners.dfn +++ b/data/dfndata/items/gmmenu/spawners.dfn @@ -7,6 +7,7 @@ interval=1 5 visible=1 decay=0 movable=2 +script=2205 } [orcspawn] diff --git a/data/dfndata/items/lootlists.dfn b/data/dfndata/items/lootlists.dfn index 7a62a53fd..0638b0eee 100644 --- a/data/dfndata/items/lootlists.dfn +++ b/data/dfndata/items/lootlists.dfn @@ -962,4 +962,53 @@ 100|PaintedPlagueMask 100|PaintedDaemonMask 100|PaintedEvilClownMask +} + +// Treasure Map Level 0 +[LOOTLIST TreasureMapLvl0Loot] +{//approximately 1% +990|blank +10|treasuremaplvl0 +} + +// Treasure Map Level 1 +[LOOTLIST TreasureMapLvl1Loot] +{//approximately 1% +990|blank +10|treasuremaplvl1 +} + +// Treasure Map Level 2 +[LOOTLIST TreasureMapLvl2Loot] +{//approximately 1% +990|blank +10|treasuremaplvl2 +} + +// Treasure Map Level 3 +[LOOTLIST TreasureMapLvl3Loot] +{//approximately 1% +990|blank +10|treasuremaplvl3 +} + +// Treasure Map Level 4 +[LOOTLIST TreasureMapLvl4Loot] +{//approximately 1% +990|blank +10|treasuremaplvl4 +} + +// Treasure Map Level 5 +[LOOTLIST TreasureMapLvl5Loot] +{//approximately 1% +990|blank +10|treasuremaplvl5 +} + +// Treasure Map Level 6 +[LOOTLIST TreasureMapLvl6Loot] +{//approximately 1% +990|blank +10|treasuremaplvl6 } \ No newline at end of file diff --git a/data/dictionaries/dictionary.CSY b/data/dictionaries/dictionary.CSY index d4bd5100f..f32bf29a4 100644 --- a/data/dictionaries/dictionary.CSY +++ b/data/dictionaries/dictionary.CSY @@ -3431,6 +3431,7 @@ 6301=Z této knihy se může učit pouze velmistr alchymista. 6302=Tyto informace jste se již dozvěděli. 6303=Naučili jste se vyrábět předměty ze skla. K výrobě těchto předmětů budete muset najít horníky, kteří budou těžit jemný písek. +6304=Musíte mít volnou ruku, abyste mohli pít lektvar. // [7000-7499] AI Scripts 7000=Jsi zločinec a nemáš přístup ke své bankovní schránce. 7001=Který předmět si přeješ uložit do své bankovní schránky? @@ -4679,7 +4680,6 @@ 11619=chlebový bochník 11620=pánev sušenek 11621=koláč -11622=muffiny 11622=pečený quiche 11623=pečený masový koláč 11624=pizza s uzeninou @@ -4715,6 +4715,7 @@ 11654=Pro přípravu jídla musíte být v blízkosti zdroje tepla. 11655=Musíte být v blízkosti mlýna, abyste mohli vyrábět mouku. 11656=Musíte být v blízkosti pece, abyste mohli péct jídlo. +11657=muffiny // 11801 - 12000 Dovednost řemeslné výroby // Strana 1 - Dřevěné předměty 11801=Náprava diff --git a/data/dictionaries/dictionary.ENG b/data/dictionaries/dictionary.ENG index 6c7523274..f63ea6d16 100644 --- a/data/dictionaries/dictionary.ENG +++ b/data/dictionaries/dictionary.ENG @@ -3431,6 +3431,7 @@ 6301=Only a Grandmaster Alchemist can learn from this book. 6302=You have already learned this information. 6303=You have learned to make items from glass. You will need to find miners to mine fine sand for you to make these items. +6304=You must have a free hand to drink a potion. // [7000-7499] AI Scripts 7000=Thou art a criminal and cannot access thy bank box. 7001=Which item do you wish to deposit in your bank box? @@ -4679,7 +4680,6 @@ 11619=bread loaf 11620=pan of cookies 11621=cake -11622=muffins 11622=baked quiche 11623=baked meat pie 11624=sausage pizza @@ -4715,6 +4715,7 @@ 11654=You must be near a heat source to cook food. 11655=You must be near a mill to create flour. 11656=You must be near an oven to bake food. +11657=muffins // 11801 - 12000 Tinkering Crafting Skill // Page 1 - Wooden Items 11801=Axle diff --git a/data/dictionaries/dictionary.FRE b/data/dictionaries/dictionary.FRE index 43bacc548..e15ef13b8 100644 --- a/data/dictionaries/dictionary.FRE +++ b/data/dictionaries/dictionary.FRE @@ -3595,6 +3595,7 @@ 6301=Seul un grand maître alchimiste peut apprendre de ce livre. 6302=Vous avez déjà appris cette information. 6303=Vous avez appris à fabriquer des objets en verre. Vous devrez trouver des mineurs pour extraire du sable fin pour fabriquer ces objets. +6304=Vous devez avoir les mains libres pour boire une potion. // [7000-7499] AI Scripts 7000=Tu es un criminel et tu ne peux pas accéder à ton coffre de banque. 7001=Quel objet souhaitez-vous déposer dans votre coffret bancaire ? @@ -4835,7 +4836,6 @@ 11619=miche de pain 11620=panier de biscuits 11621=gâteau -11622=muffins 11622=quiche au four 11623=tarte à la viande cuite au four 11624=pizza à la saucisse @@ -4871,6 +4871,7 @@ 11654=Il faut être près d'une source de chaleur pour cuire les aliments. 11655=Il faut être près d'un moulin pour créer de la farine. 11656=Il faut être près d'un four pour cuire des aliments. +11657=muffins // 11801 - 12000 Compétence en artisanat de bricolage // Page 1 - Objets en bois 11801=Axe diff --git a/data/dictionaries/dictionary.GER b/data/dictionaries/dictionary.GER index 859817185..ae581671c 100644 --- a/data/dictionaries/dictionary.GER +++ b/data/dictionaries/dictionary.GER @@ -3431,6 +3431,7 @@ 6301=Nur ein Großmeister-Alchemist kann aus diesem Buch lernen. 6302=Sie haben diese Informationen bereits erfahren. 6303=Du hast gelernt, Gegenstände aus Glas herzustellen. Sie müssen Bergleute finden, die feinen Sand abbauen, damit Sie diese Gegenstände herstellen können. +6304=Sie müssen freie Hand haben, um einen Trank zu trinken. // [7000-7499] AI Scripts 7000=Du bist ein Verbrecher und kannst nicht auf dein Bankschließfach zugreifen. 7001=Welchen Gegenstand möchten Sie in Ihr Bankschließfach einzahlen? @@ -4679,7 +4680,6 @@ 11619=Brotlaib 11620=Keksdose 11621=Kuchen -11622=Muffins 11622=gebackene Quiche 11623=gebackene Fleischpastete 11624=Wurstpizza @@ -4715,6 +4715,7 @@ 11654=Du musst in der Nähe einer Wärmequelle sein, um Essen zu kochen. 11655=Du musst in der Nähe einer Mühle sein, um Mehl herzustellen. 11656=Du musst in der Nähe eines Ofens sein, um Essen zu backen. +11657=Muffins // 11801 - 12000 Handwerkliches Geschick // Seite 1 - Hölzerne Gegenstände 11801=Achse diff --git a/data/dictionaries/dictionary.ITA b/data/dictionaries/dictionary.ITA index ff1ce1bc3..4ab0bff98 100644 --- a/data/dictionaries/dictionary.ITA +++ b/data/dictionaries/dictionary.ITA @@ -3431,6 +3431,7 @@ 6301=Solo un Grande Maestro Alchimista può imparare da questo libro. 6302=Hai già appreso questa informazione. 6303=Hai imparato a creare oggetti in vetro. Dovrai trovare minatori che estraggano sabbia fine per poter realizzare questi oggetti. +6304=Devi avere una mano libera per bere una pozione. // [7000-7499] AI Scripts 7000=Tu sei un criminale e non puoi accedere alla tua cassaforte. 7001=Quale oggetto desidera depositare nella sua cassetta di sicurezza? @@ -4679,7 +4680,6 @@ 11619=pagnotta di pane 11620=torta di biscotti 11621=torta -11622=muffin 11622=quiche al forno 11623=pasticcio di carne al forno 11624=pizza con salsiccia @@ -4715,6 +4715,7 @@ 11654=Devi essere vicino a una fonte di calore per cucinare il cibo. 11655=Devi essere vicino a un mulino per creare farina. 11656=Devi essere vicino a un forno per cuocere il cibo. +11657=muffin // 11801 - 12000 Abilità Artigianato Tinkering // Pagina 1 - Oggetti in legno 11801=Assale diff --git a/data/dictionaries/dictionary.POL b/data/dictionaries/dictionary.POL index 11a6ad2eb..ed50092f8 100644 --- a/data/dictionaries/dictionary.POL +++ b/data/dictionaries/dictionary.POL @@ -3431,6 +3431,7 @@ 6301=Tylko Wielki Mistrz Alchemik może uczyć się z tej książki. 6302=Te informacje już znasz. 6303=Nauczyłeś się robić przedmioty ze szkła. Będziesz musiał znaleźć górników, którzy wydobędą drobny piasek, aby móc wyprodukować te przedmioty. +6304=Musisz mieć wolną rękę, żeby wypić miksturę. // [7000-7499] AI Scripts 7000=Jesteś przestępcą i nie masz dostępu do swojej skrytki bankowej. 7001=Którą pozycję chcesz umieścić w swojej skrytce bankowej? @@ -4679,7 +4680,6 @@ 11619=bochenek chleba 11620=panka ciasteczek 11621=ciasto -11622=muffiny 11622=pieczony quiche 11623=pieczony placek z mięsem 11624=pizza z kiełbasą @@ -4715,6 +4715,7 @@ 11654=Musisz być w pobliżu źródła ciepła, aby gotować jedzenie. 11655=Musisz być w pobliżu młyna, aby wytworzyć mąkę. 11656=Musisz być w pobliżu piekarnika, aby upiec jedzenie. +11657=muffiny // 11801 - 12000 Umiejętność majsterkowania // Strona 1 - Przedmioty z drewna 11801=Oś diff --git a/data/dictionaries/dictionary.PTG b/data/dictionaries/dictionary.PTG index d1f329c46..aaa586b1c 100644 --- a/data/dictionaries/dictionary.PTG +++ b/data/dictionaries/dictionary.PTG @@ -3431,6 +3431,7 @@ 6301=Apenas um Grão-Mestre Alquimista pode aprender com este livro. 6302=Você já aprendeu esta informação. 6303=Você aprendeu a fazer itens de vidro. Você precisará encontrar mineiros para extrair areia fina para fazer esses itens. +6304=Você deve ter a mão livre para beber uma poção. // [7000-7499] AI Scripts 7000=Tu és um criminoso e não podes aceder à tua caixa bancária. 7001=Que artigo deseja depositar na sua caixa bancária? @@ -4679,7 +4680,6 @@ 11619=pão de pão 11620=panha de biscoitos 11621=bolo -11622=muffins 11622=quiché cozido 11623=torta de carne assada 11624=sausage pizza @@ -4715,6 +4715,7 @@ 11654=É preciso estar perto de uma fonte de calor para cozinhar os alimentos. 11655=Tem de estar perto de um moinho para criar farinha. 11656=Tem de estar perto de um forno para cozer alimentos. +11657=muffins // 11801 - 12000 Habilidade de Artesanato de Sininho // Página 1 - Artigos de madeira 11801=Eixo diff --git a/data/dictionaries/dictionary.SPA b/data/dictionaries/dictionary.SPA index 418c01cf9..3a1d28694 100644 --- a/data/dictionaries/dictionary.SPA +++ b/data/dictionaries/dictionary.SPA @@ -3431,6 +3431,7 @@ 6301=Sólo un Gran Maestro Alquimista puede aprender de este libro. 6302=Ya has aprendido esta información. 6303=Has aprendido a fabricar objetos de vidrio. Necesitará encontrar mineros que extraigan arena fina para poder fabricar estos artículos. +6304=Debes tener las manos libres para beber una poción. // [7000-7499] Guiones de IA 7000=Eres un delincuente y no puedes acceder a tu caja bancaria. 7001=¿Qué artículo desea depositar en su caja? @@ -4679,7 +4680,6 @@ 11619=pan de pan 11620=pan de galletas 11621=pastel -11622=magdalenas 11622=quiche al horno 11623=pastel de carne al horno 11624=pizza de salchicha @@ -4715,6 +4715,7 @@ 11654=Debes estar cerca de una fuente de calor para cocinar los alimentos. 11655=Debes estar cerca de un molino para crear harina. 11656=Debes estar cerca de un horno para cocer alimentos. +11657=magdalenas // 11801 - 12000 Habilidad de Artesanía // Página 1 - Artículos de madera 11801=Eje diff --git a/data/dictionaries/dictionary.ZRO b/data/dictionaries/dictionary.ZRO index 62cf605eb..af75b17d7 100644 --- a/data/dictionaries/dictionary.ZRO +++ b/data/dictionaries/dictionary.ZRO @@ -3426,6 +3426,7 @@ 6301=Only a Grandmaster Alchemist can learn from this book. 6302=You have already learned this information. 6303=You have learned to make items from glass. You will need to find miners to mine fine sand for you to make these items. +6304=You must have a free hand to drink a potion. // [7000-7499] AI Scripts 7000=Thou art a criminal and cannot access thy bank box. 7001=Which item do you wish to deposit in your bank box? @@ -4674,7 +4675,6 @@ 11619=bread loaf 11620=pan of cookies 11621=cake -11622=muffins 11622=baked quiche 11623=baked meat pie 11624=sausage pizza @@ -4710,6 +4710,7 @@ 11654=You must be near a heat source to cook food. 11655=You must be near a mill to create flour. 11656=You must be near an oven to bake food. +11657=muffins // 11801 - 12000 Tinkering Crafting Skill // Page 1 - Wooden Items 11801=Axle diff --git a/data/js/combat/leechstats.js b/data/js/combat/leechstats.js new file mode 100644 index 000000000..fc1086fdf --- /dev/null +++ b/data/js/combat/leechstats.js @@ -0,0 +1,56 @@ +function onEquip( pEquipper, iEquipped ) +{ + pEquipper.AddScriptTrigger( 7003 ); +} + +// Remove script trigger on unequip +function onUnequip( pUnequipper, iUnequipped ) +{ + pUnequipper.RemoveScriptTrigger( 7003 ); +} + +function onDamageDeal( attacker, damaged, damageValue, damageType ) +{ + // Fetch weapon in main hand or secondary hand + var iWeapon = attacker.FindItemLayer( 0x01 ); + if( !ValidateObject( iWeapon )) + { + iWeapon = attacker.FindItemLayer( 0x02 ); + } + + if( ValidateObject( iWeapon )) + { // Apply leech effects based on weapon properties + ApplyLeech( attacker, damaged, damageValue, iWeapon, 'healthLeech', 30 ); + ApplyLeech( attacker, damaged, damageValue, iWeapon, 'staminaLeech', 100 ); + ApplyLeech( attacker, damaged, damageValue, iWeapon, 'manaLeech', 40 ); + } + + return true; +} + +function ApplyLeech( attacker, damaged, damageValue, weapon, leechType, multiplier ) +{ + // Get the leech amount for the specified leech type from the weapon + var leechPercentVal = weapon[ leechType ]; + if( leechPercentVal > 0 ) + { + // Calculate the percent of health restored to the attacker + var leechAmt = Math.round( damageValue * ( leechPercentVal / 100 ) * ( multiplier/100 )); + + // Apply the leech effect based on the leech type + switch( leechType ) + { + case 'healthLeech': + attacker.Heal( leechAmt ); + break; + case 'staminaLeech': + attacker.stamina += leechAmt; + break; + case 'manaLeech': + attacker.mana += leechAmt; + break; + } + + attacker.SoundEffect( 0x44D, true ); + } +} \ No newline at end of file diff --git a/data/js/commands/targeting/get.js b/data/js/commands/targeting/get.js index 365d3a2b3..24ec9a986 100644 --- a/data/js/commands/targeting/get.js +++ b/data/js/commands/targeting/get.js @@ -169,6 +169,9 @@ function onCallback0( socket, ourObj ) case "SECTIONID": socket.SysMessage( ourObj.sectionID ); break; + case "SWINGSPEEDINC": + socket.SysMessage( swingSpeedIncrease ); + break; case "SHOULDSAVE": socket.SysMessage( ourObj.shouldSave ); break; @@ -294,30 +297,30 @@ function HandleGetItem( socket, ourItem, uKey ) case "DESC": socket.SysMessage( ourItem.desc ); break; - case "DEF": - socket.SysMessage( ourItem.Resist( 1 )); - break; case "DEF": case "RESISTARMOR": - socket.SysMessage( ourObj.Resist( 1 )); + socket.SysMessage( ourItem.Resist( 1 )); break; case "RESISTLIGHT": - socket.SysMessage( ourObj.Resist( 2 )); + socket.SysMessage( ourItem.Resist( 2 )); break; case "RESISTWATER": - socket.SysMessage( ourObj.Resist( 3 )); + socket.SysMessage( ourItem.Resist( 3 )); break; case "RESISTCOLD": - socket.SysMessage( ourObj.Resist( 4 )); + socket.SysMessage( ourItem.Resist( 4 )); break; case "RESISTFIRE": - socket.SysMessage( ourObj.Resist( 5 )); + socket.SysMessage( ourItem.Resist( 5 )); break; case "RESISTENERGY": - socket.SysMessage( ourObj.Resist( 6 )); + socket.SysMessage( ourItem.Resist( 6 )); break; case "RESISTPOISON": - socket.SysMessage( ourObj.Resist( 7 )); + socket.SysMessage( ourItem.Resist( 7 )); + break; + case "LOWERSTATREQ": + socket.SysMessage( ourObj.lowerStatReq ); break; case "ARMORCLASS": case "ARMOURCLASS": @@ -598,8 +601,27 @@ function HandleGetChar( socket, ourChar, uKey ) break; case "ARMOUR": case "ARMOR": + case "RESISTARMOR": socket.SysMessage( ourChar.Resist( 1 )); break; + case "RESISTLIGHT": + socket.SysMessage( ourChar.Resist( 2 )); + break; + case "RESISTWATER": + socket.SysMessage( ourChar.Resist( 3 )); + break; + case "RESISTCOLD": + socket.SysMessage( ourChar.Resist( 4 )); + break; + case "RESISTFIRE": + socket.SysMessage( ourChar.Resist( 5 )); + break; + case "RESISTENERGY": + socket.SysMessage( ourChar.Resist( 6 )); + break; + case "RESISTPOISON": + socket.SysMessage( ourChar.Resist( 7 )); + break; case "MAXHP": socket.SysMessage( ourChar.maxhp ); break; diff --git a/data/js/commands/targeting/set.js b/data/js/commands/targeting/set.js index e9b4f3226..366d8bfcf 100644 --- a/data/js/commands/targeting/set.js +++ b/data/js/commands/targeting/set.js @@ -98,6 +98,10 @@ function onCallback0( socket, ourObj ) ourObj.Resist( 7, nVal ); okMsg( socket ); break; + case "LOWERSTATREQ": + ourItem.lowerStatReq = nVal; + okMsg( socket ); + break; case "HP": case "HEALTH": ourObj.health = nVal; @@ -169,6 +173,10 @@ function onCallback0( socket, ourObj ) ourObj.tempdex = nVal; okMsg( socket ); break; + case "SWINGSPEEDINC": + ourObj.swingSpeedIncrease = nVal; + okMsg( socket ); + break; case "WIPABLE": case "WIPEABLE": ourObj.wipable = ( nVal == 1 ); diff --git a/data/js/item/holidays/addondeedgump.js b/data/js/item/holidays/addondeedgump.js index 36e94e3e3..1f0851f6e 100644 --- a/data/js/item/holidays/addondeedgump.js +++ b/data/js/item/holidays/addondeedgump.js @@ -181,7 +181,7 @@ function CheckForNearbyDoors( myTarget, itemToCheck, pSocket ) } } - if( myTarget.DistanceTo(itemToCheck) <= 2 ) + if( myTarget.DistanceTo( itemToCheck ) <= 2 ) { return true; } diff --git a/data/js/item/holidays/candy.js b/data/js/item/holidays/candy.js index cbe3cdae9..ed007641a 100644 --- a/data/js/item/holidays/candy.js +++ b/data/js/item/holidays/candy.js @@ -18,7 +18,6 @@ function onUseChecked(pUser, iUsed) } else { - socket.SysMessage("test 1"); if( Acidity <= 30 ) { pUser.SetTempTag( "Acidity", Acidity += 5 ); @@ -26,7 +25,6 @@ function onUseChecked(pUser, iUsed) if ( Toothach == 0) { - socket.SysMessage("test 2"); pUser.SetTempTag( "toothach", 1 ); pUser.StartTimer( 1000, 0, true ); } diff --git a/data/js/item/holidays/halloweenmasks.js b/data/js/item/holidays/halloweenmasks.js index ac1919dd4..f1f031843 100644 --- a/data/js/item/holidays/halloweenmasks.js +++ b/data/js/item/holidays/halloweenmasks.js @@ -3,7 +3,7 @@ function onCreateDFN( objMade, objType ) if( objType == 0 ) { var maskname = ""; - switch(objMade.GetTag( "paintedmask" )) + switch( objMade.GetTag( "paintedmask" )) { case 1: maskname = "A Evil Clown Mask"; break; case 2: maskname = "A Daemon Mask"; break; diff --git a/data/js/item/holidays/headonaspike.js b/data/js/item/holidays/headonaspike.js index 8da857603..44b8b6192 100644 --- a/data/js/item/holidays/headonaspike.js +++ b/data/js/item/holidays/headonaspike.js @@ -4,7 +4,7 @@ function onUseChecked( pUser, iUsed ) var headspike = new Gump; var head = 0; - switch (iUsed.sectionID) + switch( iUsed.sectionID ) { case "MrsTroubleMakersHeadOnASpike": head = 30522; break; case "BrutrinsHeadOnASpike": head = 30522; break; @@ -20,7 +20,7 @@ function onUseChecked( pUser, iUsed ) default: head = 30522; } - headspike.AddGump(100, 100, head); - headspike.Send(pUser); + headspike.AddGump( 100, 100, head ); + headspike.Send( pUser ); headspike.Free(); } \ No newline at end of file diff --git a/data/js/item/holidays/holidaypottedplant.js b/data/js/item/holidays/holidaypottedplant.js index 0cc81122f..df0948203 100644 --- a/data/js/item/holidays/holidaypottedplant.js +++ b/data/js/item/holidays/holidaypottedplant.js @@ -11,14 +11,14 @@ function onUseChecked( pUser, iUsed ) PottedPlantGump( pUser, iUsed ); } -function PottedPlantGump(pUser, iUsed) +function PottedPlantGump( pUser, iUsed ) { var socket = pUser.socket; socket.tempObj = iUsed; var pottedPlant = new Gump; pottedPlant.AddPage( 0 ); - pottedPlant.AddBackground(0, 0, 360, 195, 0xA28); + pottedPlant.AddBackground( 0, 0, 360, 195, 0xA28 ); pottedPlant.AddPage( 1 ); pottedPlant.AddText( 45, 15, 0, "Choose a Potted Plant:" ); @@ -35,7 +35,7 @@ function PottedPlantGump(pUser, iUsed) pottedPlant.Free(); } -function onGumpPress(pSock, pButton, gumpData) +function onGumpPress( pSock, pButton, gumpData ) { var pUser = pSock.currentChar; var iUsed = pSock.tempObj; @@ -48,7 +48,7 @@ function onGumpPress(pSock, pButton, gumpData) } var pottedplant = ""; - if ( pButton >= 1 && pButton <= 5 ) + if( pButton >= 1 && pButton <= 5 ) { var plantIds = [0x11C8, 0x11C9, 0x11CA, 0x11CB, 0x11CC]; pottedplant = "0x" + ( plantIds[pButton - 1] ).toString( 16 ); diff --git a/data/js/item/holidays/pumpkins.js b/data/js/item/holidays/pumpkins.js index e63df2678..68cb883a8 100644 --- a/data/js/item/holidays/pumpkins.js +++ b/data/js/item/holidays/pumpkins.js @@ -16,7 +16,7 @@ function onCreateDFN( objMade, objType ) if( objMade.id == 0x0C6A || objMade.id == 0x0C6B ) { - objMade.weight = Math.floor(Math.random() * ( 2500 - 1200 + 1 ) + 1200); + objMade.weight = Math.floor( Math.random() * ( 2500 - 1200 + 1 ) + 1200 ); } else if( objMade.id == 0x0C6C ) { @@ -72,22 +72,22 @@ function onUseChecked( pUser, iUsed ) // Randomize countdown length, if enabled if( randomizePumpkinCountdown ) { - iUsed.speed = RandomNumber(iUsed.speed - 1, iUsed.speed + 1); + iUsed.speed = RandomNumber( iUsed.speed - 1, iUsed.speed + 1 ); } // Item's speed forms the basis of the countdownTime var countdownTime = iUsed.speed * 1000; // Start the initial timer that shows the first number over the character/object's head - iUsed.StartTimer(200, 1, true); + iUsed.StartTimer( 200, 1, true ); // Start timers with IDs from 2, and count until we reach item's speed + 1 var iCount = 2; - for (iCount = 2; iCount < (iUsed.speed + 2); iCount++) + for( iCount = 2; iCount < ( iUsed.speed + 2 ); iCount++ ) { - iUsed.StartTimer((iCount - 1) * 1000, iCount, true); + iUsed.StartTimer(( iCount - 1) * 1000, iCount, true ); } - socket.CustomTarget(0, GetDictionaryEntry(1348, socket.language)); //Now would be a good time to throw it! + socket.CustomTarget( 0, GetDictionaryEntry( 1348, socket.language )); //Now would be a good time to throw it! } return false; } @@ -96,7 +96,7 @@ function onCallback0( socket, ourObj ) { var mChar = socket.currentChar; var iUsed = socket.tempObj; - if ( mChar && mChar.isChar && iUsed && iUsed.isItem ) + if( mChar && mChar.isChar && iUsed && iUsed.isItem ) { var StrangeByte = socket.GetWord( 1 ); if( StrangeByte == 0 && ourObj ) @@ -107,7 +107,7 @@ function onCallback0( socket, ourObj ) iUsed.container = null; iUsed.Teleport( ourObj ); } - else + else { socket.SysMessage( GetDictionaryEntry( 1646, socket.language )); // You cannot see that @@ -252,7 +252,7 @@ function ApplyExplosionDamage( timerObj, targetChar ) return; // Deal damage, and do criminal check for source character! - targetChar.Damage(RandomNumber( timerObj.lodamage, timerObj.hidamage ), 5, sourceChar, true ); + targetChar.Damage( RandomNumber( timerObj.lodamage, timerObj.hidamage ), 5, sourceChar, true ); // If target is an NPC, make them attack the person who threw the potion! if( targetChar.npc && targetChar.target == null && targetChar.atWar == false ) @@ -299,7 +299,7 @@ function onPickup( iPickedUp, pGrabber, containerObj ) else if( iPickedUp.GetTag( "Named" ) == 0 ) { iPickedUp.name = pGrabber.name + " Pumpkin"; - iPickedUp.SetTag("Named", 1); + iPickedUp.SetTag( "Named", 1 ); } } return true; diff --git a/data/js/item/holidays/snowpile.js b/data/js/item/holidays/snowpile.js index c2e92ecc9..58d289745 100644 --- a/data/js/item/holidays/snowpile.js +++ b/data/js/item/holidays/snowpile.js @@ -59,7 +59,7 @@ function onCallback0( socket, myTarget ) pUser.visible = 0; } - if (!socket.GetWord(1) && myTarget.isChar && myTarget.socket ) + if( !socket.GetWord( 1 ) && myTarget.isChar && myTarget.socket ) { pUser.DoAction( 0x9 ); pUser.SoundEffect( 0x145, true ); @@ -73,10 +73,10 @@ function onCallback0( socket, myTarget ) } } -function onTimer( pUser, timerID ) +function onTimer( pUser, timerID ) { var socket = pUser.socket; - if( pUser.visible == 1 || pUser.visible == 2 ) + if( pUser.visible == 1 || pUser.visible == 2 ) { pUser.visible = 0; } diff --git a/data/js/item/potion.js b/data/js/item/potion.js index d92697cfb..efbda987e 100644 --- a/data/js/item/potion.js +++ b/data/js/item/potion.js @@ -6,14 +6,25 @@ const alchemyBonusModifier = parseInt( GetServerSetting( "AlchemyBonusModifier" // Other settings const randomizePotionCountdown = false; // If true, add/remove +1/-1 seconds to explosion potion countdowns +const ReqFreeHands = true; function onUseChecked( pUser, iUsed ) { var socket = pUser.socket; - if ( pUser.visible == 1 || pUser.visible == 2 ) + var itemRHand = pUser.FindItemLayer( 0x01 ); + var itemLHand = pUser.FindItemLayer( 0x02 ); + + if( ReqFreeHands && ( itemRHand != null || itemLHand != null ) ) + { + socket.SysMessage( GetDictionaryEntry( 6304, socket.language ) );// You must have a free hand to drink a potion. + return false; + } + + if( pUser.visible == 1 || pUser.visible == 2 ) { pUser.visible = 0; } + if( socket && iUsed && iUsed.isItem ) { if( pUser.isUsingPotion ) diff --git a/data/js/jse_fileassociations.scp b/data/js/jse_fileassociations.scp index ceeac4c36..4318369ce 100644 --- a/data/js/jse_fileassociations.scp +++ b/data/js/jse_fileassociations.scp @@ -99,6 +99,7 @@ 2202=server/misc/warning_trigger.js 2203=server/misc/charges_left_tooltip.js 2204=server/network/0xDF_buffBar.js +2205=server/misc/spawnergump.js // Server Data [2500-2749] 2500=server/data/weapontypes.js @@ -334,6 +335,7 @@ // Combat Scripts [7000-7499] //------------------------------------------- 7000=combat/peacemake_effect.js +7003=combat/leechstats.js //------------------------------------------- // Misc Player Scripts [8000-8499] diff --git a/data/js/jse_objectassociations.scp b/data/js/jse_objectassociations.scp index 93f7c41d5..25cce9751 100644 --- a/data/js/jse_objectassociations.scp +++ b/data/js/jse_objectassociations.scp @@ -580,6 +580,8 @@ 0x10a4=15007 // Swords +0x0EC2=5009 //cleaver +0x0EC3=5009 //cleaver 0x0EC4=5009 //skinning knife 0x0EC5=5009 //skinning knife 0x0F60=5009 //longsword diff --git a/data/js/magic/level1targ.js b/data/js/magic/level1targ.js index a79f92a3c..d8dabced1 100644 --- a/data/js/magic/level1targ.js +++ b/data/js/magic/level1targ.js @@ -95,6 +95,7 @@ function ItemInHandCheck( mChar, mSock, spellType ) } } } + return true; } function onSpellCast( mSock, mChar, directCast, spellNum ) diff --git a/data/js/npc/ai/vendor_bdo_dispenser.js b/data/js/npc/ai/vendor_bdo_dispenser.js index 0b5739539..9d069ea22 100644 --- a/data/js/npc/ai/vendor_bdo_dispenser.js +++ b/data/js/npc/ai/vendor_bdo_dispenser.js @@ -302,7 +302,7 @@ function onSpeech( myString, pUser, myNPC ) { if( CheckBodTimers( pUser, myNPC.GetTag( "bodType" ) )) { - if( EraStringToNum( GetServerSetting( "CoreShardEra" )) <= EraStringToNum( "lbr" )) + if( EraStringToNum( GetServerSetting( "CoreShardEra" )) >= EraStringToNum( "lbr" )) { myNPC.SetTimer( Timer.MOVETIME, 1000 ); // Pause NPC in their tracks for a second myNPC.TurnToward( pUser ); diff --git a/data/js/server/data/weapontypes.js b/data/js/server/data/weapontypes.js index 42fe15a2b..0a6940e56 100644 --- a/data/js/server/data/weapontypes.js +++ b/data/js/server/data/weapontypes.js @@ -208,6 +208,10 @@ function GetWeaponType( mChar, itemID ) case 0x48B3: //gargish axe - SA weaponType = "TWOHND_AXES"; break; // Default Maces + case 0x0DF2: // Wand + case 0x0DF3: // Wand + case 0x0DF4: // Wand + case 0x0DF5: // Wand case 0x0FB4: //sledge hammer case 0x0FB5: //sledge hammer case 0x0F5C: //mace diff --git a/data/js/server/house/houseCommands.js b/data/js/server/house/houseCommands.js index e27bef044..2c7afd3f3 100644 --- a/data/js/server/house/houseCommands.js +++ b/data/js/server/house/houseCommands.js @@ -1522,8 +1522,17 @@ function DemolishHouse( pSocket, iMulti ) iMulti.RemoveTrashCont( itemInHouse ); itemInHouse.Delete(); } - else if( itemInHouse.movable == 2 ) // items placed as part of the house itself like forge/anvil in smithy + else if( itemInHouse.movable == 2 || itemInHouse.GetTag( "deed" )) // items placed as part of the house itself like forge/anvil in smithy or the addon deed { + var addonDeed = itemInHouse.GetTag( "deed" ); + if( addonDeed ) + { + var newDeed = CreateDFNItem( pSocket, pSocket.currentChar, addonDeed, 1, "ITEM", true ); + if( newDeed ) + { + pSocket.SysMessage( GetDictionaryEntry( 1970, pSocket.language )); // A deed for the house add-on has been placed in your backpack. + } + } itemInHouse.Delete(); } else if( itemInHouse.isLockedDown ) diff --git a/data/js/server/misc/spawnergump.js b/data/js/server/misc/spawnergump.js new file mode 100644 index 000000000..449f219a7 --- /dev/null +++ b/data/js/server/misc/spawnergump.js @@ -0,0 +1,248 @@ +function onUseChecked( pUser, iUsed ) +{ + var socket = pUser.socket; + var gumpID = 5037 + 0xffff; + + if( socket && iUsed && iUsed.isItem && pUser.isGM ) + { + socket.tempObj = iUsed; + socket.CloseGump( gumpID, 0 ); + spawnerGump( socket, pUser, iUsed ); + } + + return false; +} + +function spawnerGump( socket, pUser, iUsed ) +{ + var spawner = new Gump; + var powerState = ""; + var spawnName = ""; + if( iUsed.sectionalist == true ) + { + powerState = "Enabled"; + if( iUsed.type == 61 ) + { + spawnName = "Item List" + } + else + spawnName = "Npc List" + } + else + { + powerState = "Disabled"; + if( iUsed.type == 61 ) + { + spawnName = "Item" + } + else + spawnName = "Npc" + } + + var typeName = ""; + switch( iUsed.type ) + { + case 61: typeName = "Item"; break + case 62: typeName = "Npc"; break + case 125: typeName = "Escort"; break + } + + var amountLabel = ( iUsed.type != 61 ) ? "Amount" : "Spawn Item DFN"; + var applyLabel = "Apply"; + var minLabel = "Min:"; + var maxLabel = "Max:"; + + spawner.AddPage( 0 ); + + spawner.AddBackground( 40, 80, 413, 134, 5054 ); + spawner.AddCheckerTrans( 40, 80, 413, 134 ); + spawner.AddPicture( 50, 100, 7956 ); + + if( iUsed.type != 61 ) + { + spawner.AddHTMLGump( 310, 100, 178, 22, false, false, amountLabel ); + spawner.AddHTMLGump( 90, 100, 203, 22, false, false, "Spawn " + spawnName + " DFN" ); + + spawner.AddHTMLGump( 255, 155, 140, 22, false, false, "Rename" ); + + spawner.AddButton( 50, 180, 4005, 4007, 1, 0, 1 ); + spawner.AddHTMLGump( 90, 180, 140, 22, false, false, applyLabel ); + + spawner.AddHTMLGump( 80, 152, 140, 22, false, false, minLabel ); + spawner.AddHTMLGump( 160, 152, 140, 22, false, false, maxLabel ); + + spawner.AddCheckbox( 130, 180, 2152, 0, 1 ); + spawner.AddHTMLGump( 160, 185, 140, 22, false, false, "Spawnlist: " + powerState + "" ); + spawner.AddCheckbox( 300, 180, 2152, 0, 2 ); + spawner.AddHTMLGump( 330, 185, 140, 22, false, false, "Spawn Type: " + typeName + "" ); + spawner.AddBackground( 80, 120, 201, 28, 5120 ); + spawner.AddBackground( 305, 120, 134, 28, 5120 ); + spawner.AddBackground( 105, 150, 50, 25, 5120 ); + spawner.AddBackground( 190, 150, 50, 25, 5120 ); + spawner.AddBackground(305, 152, 134, 28, 5120); + + spawner.AddText( 90, 125, 0, iUsed.spawnsection ); + spawner.AddText( 315, 125, 0, iUsed.amount ); + spawner.AddText( 110, 155, 0, iUsed.mininterval ); + spawner.AddText( 195, 155, 0, iUsed.maxinterval ); + spawner.AddText(310, 152, 0, iUsed.name); + + spawner.AddTextEntry( 90, 125, 178, 20, 1153, 0, 8, iUsed.spawnsection ); + spawner.AddTextEntry( 315, 125, 115, 20, 1153, 0, 9, iUsed.amount ); + spawner.AddTextEntry( 110, 155, 40, 20, 1153, 0, 10, iUsed.mininterval ); + spawner.AddTextEntry( 195, 155, 40, 20, 1153, 0, 11, iUsed.maxinterval ); + spawner.AddTextEntry(310, 152, 140, 25, 1153, 0, 12, iUsed.name); + } + else + { + spawner.AddHTMLGump( 90, 100, 203, 22, false, false, "Spawn " + spawnName + " DFN" ); + spawner.AddHTMLGump( 255, 155, 140, 22, false, false, "Rename" ); + spawner.AddButton( 50, 180, 4005, 4007, 1, 0, 1 ); + spawner.AddHTMLGump( 90, 180, 140, 22, false, false, applyLabel ); + + spawner.AddHTMLGump( 80, 152, 140, 22, false, false, minLabel ); + spawner.AddHTMLGump( 160, 152, 140, 22, false, false, maxLabel ); + + spawner.AddCheckbox( 130, 180, 2152, 0, 1 ); + spawner.AddHTMLGump( 160, 185, 140, 22, false, false, "Spawnlist: " + powerState + "" ); + spawner.AddCheckbox( 300, 180, 2152, 0, 2 ); + spawner.AddHTMLGump( 330, 185, 140, 22, false, false, "Spawn Type: " + typeName + "" ); + + spawner.AddBackground( 80, 120, 201, 28, 5120 ); + spawner.AddBackground( 105, 150, 50, 25, 5120 ); + spawner.AddBackground( 190, 150, 50, 25, 5120 ); + spawner.AddBackground(305, 152, 134, 28, 5120); + + spawner.AddText( 90, 125, 0, iUsed.spawnsection ); + spawner.AddText( 110, 155, 0, iUsed.mininterval ); + spawner.AddText( 195, 155, 0, iUsed.maxinterval ); + spawner.AddText( 310, 152, 0, iUsed.name ); + + spawner.AddTextEntry( 90, 125, 178, 20, 1153, 0, 11, iUsed.spawnsection ); + spawner.AddTextEntry( 110, 155, 40, 20, 1153, 0, 13, iUsed.maxinterval ); + spawner.AddTextEntry( 195, 155, 40, 20, 1153, 0, 12, iUsed.mininterval ); + spawner.AddTextEntry( 310, 152, 140, 25, 1153, 0, 10, iUsed.name ); + + } + spawner.Send( socket ); + spawner.Free(); +} + +function onGumpPress( socket, pButton, gumpData ) +{ + var pUser = socket.currentChar; + var iUsed = socket.tempObj; + + var spawn = gumpData.getEdit( 0 ); + if( iUsed.type != 61 ) + { + var num = gumpData.getEdit( 1 ); + var min = gumpData.getEdit( 2 ); + var max = gumpData.getEdit( 3 ); + var name = gumpData.getEdit( 4 ); + } + else + { + var min = gumpData.getEdit( 1 ); + var max = gumpData.getEdit( 2 ); + var name = gumpData.getEdit( 3 ); + } + var OtherButton = gumpData.getButton( 0 ); + switch( pButton ) + { + case 0: + break; + case 1: + if( iUsed.type != 61 ) + { + iUsed.spawnsection = spawn; + iUsed.amount = num; + iUsed.mininterval = min; + iUsed.maxinterval = max; + } + else + { + iUsed.spawnsection = spawn; + iUsed.mininterval = min; + iUsed.maxinterval = max; + } + + if( name == null || name == " " ) + { + socket.SysMessage(GetDictionaryEntry(9270, socket.language)); // That name is too short, or no name was entered. + } + else if( name.length > 50 ) + { + pSocket.SysMessage(GetDictionaryEntry(9271, pSocket.language)); // That name is too long. Maximum 50 chars. + } + else + { + iUsed.name = name; + } + + switch( OtherButton ) + { + case 1: + if( iUsed.sectionalist == true ) + { + pUser.SysMessage( "You have disabled the spawn list. You can now add single DFN." ); + iUsed.sectionalist = false; + } + else + { + pUser.SysMessage( "You have enabled the spawn list. It will now only accept DFN lists." ); + iUsed.sectionalist = true; + } + break; + case 2: + var typeTransitions = { + 61: { nextType: 62, message: "Changed Type to Npc", resetAmount: false }, + 62: { nextType: 125, message: "Changed Type to Escort", resetAmount: false }, + 125: { nextType: 61, message: "Changed Type to Item", resetAmount: true } + }; + + var currentTransition = typeTransitions[iUsed.type]; + if( currentTransition ) + { + iUsed.type = currentTransition.nextType; + if( currentTransition.resetAmount ) + { + iUsed.amount = 0; + } + pUser.SysMessage( currentTransition.message ); + } + break; + } + spawnerGump( socket, pUser, iUsed ); + break; + } +} + +function onTooltip( spawner ) +{ + var typeName = ""; + switch( spawner.type ) + { + case 61: typeName = "Item"; break + case 62: typeName = "Npc"; break + case 125: typeName = "Escort"; break + } + var powerState = ""; + if( spawner.sectionalist == true ) + { + powerState = "Enabled"; + } + else + { + powerState = "Disabled"; + } + + var tooltipText = ""; + tooltipText = "Spawn DFN: " + spawner.spawnsection; + tooltipText += "\n" + "Spawn Type: " + typeName; + tooltipText += "\n" + "Spawn List: " + powerState; + tooltipText += "\n" + "Amount: " + "" + spawner.amount + ""; + tooltipText += "\n" + "Min Interval: " + "" + spawner.mininterval + "" + " Max Interval: " + "" + spawner.maxinterval + ""; + tooltipText += "\n" + "Region: " + "" + spawner.region.name + ""; + return tooltipText; +} \ No newline at end of file diff --git a/data/js/server/network/0xf1_freeshardServerPoll.js b/data/js/server/network/0xf1_freeshardServerPoll.js index 4b278c240..33e2f1434 100644 --- a/data/js/server/network/0xf1_freeshardServerPoll.js +++ b/data/js/server/network/0xf1_freeshardServerPoll.js @@ -1,6 +1,6 @@ // Handler for Freeshard Server Poll requests from // CUO Web (https://play.classicuo.org) -// Last Updated: 5. September, 2022 +// Last Updated: 3. Decemeber, 2024 function PacketRegistration() { @@ -27,7 +27,18 @@ function onPacketReceive( pSocket, packetNum, subCommand ) else { Console.Print( "Freeshard Server Poll Packet detected, responding..." ); - SendServerPollInfo( pSocket ); + SendServerPollInfoCompact( pSocket ); + } + break; + case 0xFF: + if( !GetServerSetting( "FREESHARDSERVERPOLL" )) + { + Console.Print( "Freeshard Server Poll Packet detected; response disabled via FREESHARDPOLL ini-setting.\n" ); + } + else + { + Console.Print( "Freeshard Server Poll Packet detected, responding..." ); + SendServerPollInfoExtended( pSocket ); } break; default: @@ -36,7 +47,7 @@ function onPacketReceive( pSocket, packetNum, subCommand ) return; } -function SendServerPollInfo( pSocket ) +function SendServerPollInfoCompact( pSocket ) { var uptime = Math.floor( GetCurrentClock() / 1000 ) - Math.floor( GetStartTime() / 1000 ); var totalAccounts = GetAccountCount(); @@ -55,8 +66,31 @@ function SendServerPollInfo( pSocket ) toSend.WriteLong( 15, uptime ); // Server Uptime toSend.WriteLong( 19, memoryHigh ); // Max memory usage - no need to send toSend.WriteLong( 23, memoryLow ); // Min memory usage - no need to send - pSocket.Send( toSend ); + pSocket.Send(toSend); toSend.Free(); Console.Print( "Done!\n" ); Console.Log( "Response sent to Freeshard Server Poll Packet (totalOnline: " + totalOnline + ", upTimeInSeconds: " + uptime ); } + +function SendServerPollInfoExtended( pSocket ) +{ + var protocolVersion = 2; + var shardName = GetServerSetting( "SERVERNAME" ); + var uptime = Math.floor( GetCurrentClock() / 1000 ) - Math.floor( GetStartTime() / 1000 ); + var totalOnline = GetPlayerCount(); + var totalItems = 0; + var totalChars = 0; + var totalMemory = 0; + var statsStr = "UOX3, Name=" + shardName + ", Age=" + Math.round( uptime / 60 / 60 ) + ", Clients=" + totalOnline + ", Items=" + totalItems + ", Chars=" + totalChars + ", Mem=" + totalMemory + "K, Ver=" + protocolVersion; + var toSend = new Packet; + + toSend.ReserveSize( statsStr.length + 2 ); + toSend.WriteByte( 0, 0x53 ); // PacketID + toSend.WriteShort( 1, statsStr.length ); // Length + toSend.WriteString( 3, statsStr, statsStr.length ); + toSend.WriteByte( statsStr.length + 1, 0x00 ); // null terminated + pSocket.Send( toSend ); + toSend.Free(); + Console.Print( "Done!\n" ); + Console.Log( "Response sent to Freeshard Server Poll Packet (totalOnline: " + totalOnline + ", upTimeInSeconds: " + uptime ); +} \ No newline at end of file diff --git a/data/js/skill/craft/cooking.js b/data/js/skill/craft/cooking.js index 654533e8a..1ce12dd8f 100644 --- a/data/js/skill/craft/cooking.js +++ b/data/js/skill/craft/cooking.js @@ -21,7 +21,7 @@ const myPage = [ [ 11611, 11612, 11613, 11614, 11615, 11616, 11617, 11618 ], // Page 3 - Baking - [ 11619, 11620, 11621, 11622, 11623, 11624, 11625, 11626, 11627, 11628, 11629 ], + [11619, 11620, 11621, 11657, 11622, 11623, 11624, 11625, 11626, 11627, 11628, 11629 ], // Page 4 - Barbecue [ 11630, 11631, 11632, 11633, 11634, 11635 ] diff --git a/data/js/skill/craft/itemdetailgump.js b/data/js/skill/craft/itemdetailgump.js index 0f4e1ad20..7ddbe18ee 100644 --- a/data/js/skill/craft/itemdetailgump.js +++ b/data/js/skill/craft/itemdetailgump.js @@ -1525,12 +1525,12 @@ function ItemDetailGump( pUser ) break; case 1501: // Dough createEntry = CreateEntries[1501]; - HARVEST = [11637, 11639]; + HARVEST = [11637, 11638]; mainSkill = parseInt( pUser.skills.cooking ); break; case 1502: // Sweet Dough createEntry = CreateEntries[1502]; - HARVEST = [11607, 11638]; + HARVEST = [11607, 11639]; mainSkill = parseInt( pUser.skills.cooking ); break; case 1503: // Cake Mix diff --git a/docs/jsdocs.html b/docs/jsdocs.html index e8b03501c..17aff972f 100644 --- a/docs/jsdocs.html +++ b/docs/jsdocs.html @@ -4030,7 +4030,7 @@

January 9th, 2022

Prototype

-

function onSpellTargetSelect( pCaster, myTarget, spellID )

+

function onSpellTargetSelect( myTarget, pCaster, spellID )

Purpose

@@ -4055,7 +4055,7 @@

January 9th, 2022

Example of usage

// Script attached to an NPC that's immune to magic, where event returns 2 to reject spell being cast
-function onSpellTargetSelect( pCaster, myTarget, spellID )
+function onSpellTargetSelect( myTarget, pCaster, spellID )
 {
 	var socket = pCaster.socket;
 	if( socket != null )
diff --git a/source/CPacketSend.cpp b/source/CPacketSend.cpp
index ba30f52d8..448966e33 100644
--- a/source/CPacketSend.cpp
+++ b/source/CPacketSend.cpp
@@ -70,7 +70,7 @@ using namespace std::string_literals;
 //|						BYTE[2] unknown5 (0x0)
 //|						BYTE[4] unknown6 (0x0)
 //|
-//|						Note: Only send once after login. Its mandatory to send it once.
+//|						Note: Only send once after login. It’s mandatory to send it once.
 //o------------------------------------------------------------------------------------------------o
 void CPCharLocBody::Log( std::ostream &outStream, bool fullHeader )
 {
@@ -1411,7 +1411,7 @@ CPPaperdoll &CPPaperdoll::operator = ( CChar &toCopy )
 //|
 //|					Packet Build
 //|						BYTE cmd
-//|						BYTE type (0x00  It starts to rain, 0x01  A fierce storm approaches., 0x02  It begins to snow, 0x03 - A storm is brewing., 0xFF  None (turns off sound effects), 0xFE (no effect?? Set temperature?)
+//|						BYTE type (0x00 – “It starts to rain”, 0x01 – “A fierce storm approaches.”, 0x02 – “It begins to snow”, 0x03 - “A storm is brewing.”, 0xFF – None (turns off sound effects), 0xFE (no effect?? Set temperature?)
 //|						BYTE num (number of weather effects on screen)
 //|						BYTE temperature
 //|
@@ -1422,8 +1422,8 @@ CPPaperdoll &CPPaperdoll::operator = ( CChar &toCopy )
 //|					Note: Weather messages are only displayed when weather starts.
 //|					Note: Weather will end automatically after 6 minutes without any weather change packets.
 //|					Note: You can totally end weather (to display a new message) by teleporting.
-//|						I think its either the 0x78 or 0x20 messages that reset it, though I
-//|						havent checked to be sure (other possibilities, 0x4F or 0x4E)
+//|						I think it’s either the 0x78 or 0x20 messages that reset it, though I
+//|						haven’t checked to be sure (other possibilities, 0x4F or 0x4E)
 //o------------------------------------------------------------------------------------------------o
 void CPWeather::InternalReset( void )
 {
@@ -2043,7 +2043,7 @@ void CPOpenGump::Serial( SERIAL toSet )
 //|						BYTE unknown (0x00)
 //|						BYTE click zLoc
 //|						BYTE[2] model # (if a static tile, 0 if a map/landscape tile)
-//|							Note: the model # shouldnt be trusted.
+//|							Note: the model # shouldn’t be trusted.
 //o------------------------------------------------------------------------------------------------o
 CPTargetCursor::CPTargetCursor()
 {
@@ -2601,7 +2601,7 @@ void CPStatWindow::TithingPoints( UI32 value )
 //|							0x07 idle
 //|							0x05 another character is online
 //|								"Another character from this account is currently online in this world.
-//|								You must either log in as that character or wait for it to time out.
+//|								You must either log in as that character or wait for it to time out.”
 //o------------------------------------------------------------------------------------------------o
 void CPIdleWarning::InternalReset( void )
 {
@@ -3107,12 +3107,12 @@ CPMultiPlacementView::CPMultiPlacementView( SERIAL toSet )
 //|						0	neither T2A NOR LBR, equal to not sending it at all,
 //|						1	is T2A, chatbutton,
 //|						2	is LBR without chatbutton,
-//|						3	is LBR with chatbutton
+//|						3	is LBR with chatbutton…
 //|						8013	LBR + chatbutton + AOS enabled
 //|
 //|						Note1: this message is send immediately after login.
-//|						Note2: on OSI  servers this controls features OSI enables/disables via upgrade codes.
-//|						Note3: a 3 doesnt seem to hurt older (NON LBR) clients.
+//|						Note2: on OSI  servers this controls features OSI enables/disables via “upgrade codes.”
+//|						Note3: a 3 doesn’t seem to “hurt” older (NON LBR) clients.
 //o------------------------------------------------------------------------------------------------o
 CPEnableClientFeatures::CPEnableClientFeatures( CSocket *mSock )
 {
@@ -6118,7 +6118,7 @@ void CPObjectInfo::Objects( CItem& mItem, CChar& mChar )
 //|						BYTE[2] Font
 //|						BYTE[4] Language
 //|						BYTE[30] Name
-//|						BYTE[?][2] Msg  Null Terminated (blockSize - 48)
+//|						BYTE[?][2] Msg – Null Terminated (blockSize - 48)
 //|
 //|					The various types of text is as follows:
 //|						0x00 - Normal
@@ -6294,7 +6294,7 @@ void CPUnicodeSpeech::GhostIt( [[maybe_unused]] UI08 method )
 //|						BYTE[2] Font
 //|						BYTE[4] Language
 //|						BYTE[30] Name
-//|						BYTE[?][2] Msg  Null Terminated (blockSize - 48)
+//|						BYTE[?][2] Msg – Null Terminated (blockSize - 48)
 //|
 //|					The various types of text is as follows:
 //|						0x00 - Normal
@@ -6572,7 +6572,7 @@ void CPSecureTrading::Name( const std::string& nameFollowing )
 //o------------------------------------------------------------------------------------------------o
 //| Purpose		-	Handles outgoing packet with server response to all names request
 //o------------------------------------------------------------------------------------------------o
-//|	Notes		-	Packet: 0x98 (All-names 3D)
+//|	Notes		-	Packet: 0x98 (All-names “3D”)
 //|					Size: Variable
 //|
 //|					Packet Build
@@ -6588,7 +6588,7 @@ void CPSecureTrading::Name( const std::string& nameFollowing )
 //|						Client asks for name of object with ID x.
 //|						Server has to reply with ID + name
 //|						Client automatically knows names of items.
-//|						Hence it only asks only for NPC/Player names nearby, but shows bars of items plus NPCs.
+//|						Hence it only asks only for NPC/Player names nearby, but shows bars of items plus NPC’s.
 //|
 //|						Client request has 7 bytes, server-reply 37
 //|						Triggered by Crtl + Shift.
@@ -6641,7 +6641,7 @@ void CPAllNames3D::Object( CBaseObject& obj )
 //|								BYTE[var] null terminated line
 //|						Note:
 //|						server side: # of pages equals value given in 0x93/0xd4
-//|						EACH page # given. If empty: # lines: 0 + terminator (=3 0s)
+//|						EACH page # given. If empty: # lines: 0 + terminator (=3 0’s)
 //|						client side:  # of pages always 1. if 2 pages changed, client generates 2 packets.
 //o------------------------------------------------------------------------------------------------o
 void CPBookPage::IncLength( UI08 amount )
@@ -7009,7 +7009,7 @@ bool CPNewSpellBook::ClientCanReceive( CSocket *mSock )
 //|							BYTE[4]	Serial
 //|							BYTE Damage // how much damage was done ?
 //|
-//|						Note: displays damage above the npc/players head.
+//|						Note: displays damage above the npc/player’s head.
 //o------------------------------------------------------------------------------------------------o
 void CPDisplayDamage::InternalReset( void )
 {
@@ -7473,6 +7473,28 @@ void CPToolTip::CopyItemData( CItem& cItem, size_t &totalStringLen, bool addAmou
 		tempEntry.ourText = oldstrutil::number( cItem.GetTempVar( CITV_MOREZ ));
 		FinalizeData( tempEntry, totalStringLen );
 	}
+
+	if( cItem.GetManaLeech() > 0 )
+	{
+		tempEntry.stringNum = 1060427; // hit mana leech ~1_val~%
+		tempEntry.ourText = oldstrutil::number( cItem.GetManaLeech() );
+		FinalizeData( tempEntry, totalStringLen );
+	}
+
+	if( cItem.GetStaminaLeech() > 0 )
+	{
+		tempEntry.stringNum = 1060430; // hit stamina leech ~1_val~%
+		tempEntry.ourText = oldstrutil::number( cItem.GetStaminaLeech() );
+		FinalizeData( tempEntry, totalStringLen );
+	}
+
+	if( cItem.GetHealthLeech() > 0 )
+	{
+		tempEntry.stringNum = 1060422; // hit life leech ~1_val~%
+		tempEntry.ourText = oldstrutil::number( cItem.GetHealthLeech() );
+		FinalizeData( tempEntry, totalStringLen );
+	}
+
 	if( cItem.GetType() == IT_SPELLCHANNELING )
 	{
 		tempEntry.stringNum = 1060482; // spell channeling
@@ -7552,7 +7574,15 @@ void CPToolTip::CopyItemData( CItem& cItem, size_t &totalStringLen, bool addAmou
 			if( cItem.GetSpeed() > 0 )
 			{
 				tempEntry.stringNum = 1061167; // weapon speed ~1_val~
-				tempEntry.ourText = oldstrutil::number( cItem.GetSpeed() );
+				if( cwmWorldState->ServerData()->ExpansionCoreShardEra() >= ER_ML )
+				{
+					R64 wpnSpeedInSeconds = std::round(( 40000.0 / ( 200 * cItem.GetSpeed() ) * 0.5 ) * 10 ) / 10;
+					tempEntry.ourText = oldstrutil::format( "%.1fs", wpnSpeedInSeconds );
+				}
+				else
+				{
+					tempEntry.ourText = oldstrutil::number( cItem.GetSpeed() );
+				}
 				FinalizeData( tempEntry, totalStringLen );
 			}
 
@@ -7665,10 +7695,77 @@ void CPToolTip::CopyItemData( CItem& cItem, size_t &totalStringLen, bool addAmou
 				FinalizeData( tempEntry, totalStringLen );
 			}
 
-			if( cItem.GetStrength() > 1 )
+			if( cItem.GetSwingSpeedIncrease() > 0 )
+			{
+				tempEntry.stringNum = 1060486; // swing speed increase ~1_val~%
+				tempEntry.ourText = oldstrutil::number( cItem.GetSwingSpeedIncrease() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( cItem.GetHitChance() > 0 )
+			{
+				tempEntry.stringNum = 1060415; // hit chance increase ~1_val~%
+				tempEntry.ourText = oldstrutil::number( cItem.GetHitChance() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( cItem.GetDefenseChance() > 0 )
+			{
+				tempEntry.stringNum = 1060408; // defense chance increase ~1_val~%
+				tempEntry.ourText = oldstrutil::number( cItem.GetDefenseChance() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( cItem.GetHealthBonus() > 0 )
+			{
+				tempEntry.stringNum = 1060431; // hit point increase ~1_val~
+				tempEntry.ourText = oldstrutil::number( cItem.GetHealthBonus() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( cItem.GetStaminaBonus() > 0 )
+			{
+				tempEntry.stringNum = 1060484; // stamina increase ~1_val~
+				tempEntry.ourText = oldstrutil::number( cItem.GetStaminaBonus() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( cItem.GetManaBonus() > 0 )
+			{
+				tempEntry.stringNum = 1060439; // mana increase ~1_val~
+				tempEntry.ourText = oldstrutil::number( cItem.GetManaBonus() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			const SI16 strReq = ( cItem.GetStrength() * ( 100 - cItem.GetLowerStatReq() )) / 100;
+			const SI16 dexReq = ( cItem.GetDexterity() * ( 100 - cItem.GetLowerStatReq() )) / 100;
+			const SI16 intReq = ( cItem.GetIntelligence() * ( 100 - cItem.GetLowerStatReq() )) / 100;
+
+			if( strReq > 0 )
 			{
 				tempEntry.stringNum = 1061170; // strength requirement ~1_val~
-				tempEntry.ourText = oldstrutil::number( cItem.GetStrength() );
+				tempEntry.ourText = oldstrutil::number( strReq );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( dexReq > 0 )
+			{
+				tempEntry.stringNum = 1042971; // ~1_NOTHING~
+				tempEntry.ourText = oldstrutil::format( "dexterity requirement %s", oldstrutil::number( dexReq ).c_str() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( intReq > 0 )
+			{
+				tempEntry.stringNum = 1042971; // ~1_NOTHING~
+				tempEntry.ourText = oldstrutil::format( "intelligence requirement %s", oldstrutil::number( intReq ).c_str() );
+				FinalizeData( tempEntry, totalStringLen );
+			}
+
+			if( cItem.GetLowerStatReq() > 0 )
+			{
+				tempEntry.stringNum = 1060435; // lower requirements ~1_val~%
+				tempEntry.ourText = oldstrutil::number( cItem.GetLowerStatReq() );
 				FinalizeData( tempEntry, totalStringLen );
 			}
 		}
@@ -8036,9 +8133,9 @@ bool CPSellList::CanSellItems( CChar &mChar, CChar &vendor )
 //|						 BYTE[2] len
 //|						 BYTE subcmd
 //|						 BYTE[ len - 4 ] submessage
-//|							Submessage 0  Display Bulletin Board
+//|							Submessage 0 – Display Bulletin Board
 //|							BYTE[4] Board serial
-//|							BYTE[22] board name (default is bulletin board, the rest nulls)
+//|							BYTE[22] board name (default is “bulletin board”, the rest nulls)
 //|							BYTE[4] unknown/ID?
 //|							BYTE[4] zero (0)
 //o------------------------------------------------------------------------------------------------o
@@ -8118,7 +8215,7 @@ CPOpenMessageBoard::CPOpenMessageBoard( CSocket *mSock )
 //|							BYTE subjectLen
 //|							BYTE[subjectLen] subject (null terminated string)
 //|							BYTE timeLen
-//|							BYTE[timeLen] time (null terminated string with time of posting) (Day 1 @ 11:28)
+//|							BYTE[timeLen] time (null terminated string with time of posting) (“Day 1 @ 11:28”)
 //o------------------------------------------------------------------------------------------------o
 //|					Subcommand: 0x2 (Message Summary)
 //|					Size: Variable
@@ -8135,7 +8232,7 @@ CPOpenMessageBoard::CPOpenMessageBoard( CSocket *mSock )
 //|							BYTE subjectLen
 //|							BYTE[subjectLen] subject (null terminated string)
 //|							BYTE timeLen
-//|							BYTE[timeLen] time (null terminated string with time of posting) (Day 1 @ 11:28)
+//|							BYTE[timeLen] time (null terminated string with time of posting) (“Day 1 @ 11:28”)
 //|							BYTE[5] Unknown (01 90 03 F7 00)
 //|							BYTE numlines
 //|							For each line:
diff --git a/source/Changelog.txt b/source/Changelog.txt
index 951bb51d4..deb4a8839 100644
--- a/source/Changelog.txt
+++ b/source/Changelog.txt
@@ -1,24 +1,98 @@
+21/01/2025 - Dragon Slayer
+	Fixed damage dealt in combat was incorrectly modified while applying defense modifiers. (combat.cpp)
+
+07/12/2024 - Dragon Slayer
+	Updated the packet 0xf1 to reflect the current CUO Web Ping while maintaining backward compatibility with older ping statuses. (0xf1_freeshardServerPoll.js) (Thanks Karasho and Xuri)
 
 13/08/2024 - Dragon Slayer/Xuri
 	The onAttack/onDefense JS Events have been updated with three new parameters 	(hitStatus, hitLocation, damage) and will now also trigger when attacks miss.
-  
+
 12/08/2024 - Dragon Slayer
-	Flipped Imbuing and Mysticism Skill around.
-  
+	Fixed an issue where Imbuing and Mysticism skills were listed in wrong order in skills.dfn and enums.h
+
+05/08/2024 - Dragon Slayer Update
+	Resolved an issue with cooking functionality.
+	Fixed a duplicated dictionary number.
+	Corrected the required items for crafting sweet dough and regular dough.
+
+04/08/2024 - Dragon Slayer
+	Added Run a skillcheck for NPC to give them a chance to gain skill - if that option is enabled in ini (xuri)
+
+16/07/2024 - Dragon Slayer
+	Added fallback in 'add menu for older clients that don't support buttontileart (Thanks Xuri)(gumps.cpp)
+
+07/07/2024 - Dragon Slayer
+	Added Requirement to drink potion hand has to be free, This can be disabled within the potion.js file by switching the reqfreehand to false. (js potion).
+
+04/07/2024 - Dragon Slayer
+	Fixed Cleaver ID missing in the jse_objectassociations.scp for carving corpses
+
+18/06/2024 - Dragon Slayer
+	Added three new DFN tags for Items:
+		HEALTHLEECH=# 	// It gives an attacker the ability to leech health from his opponent every time he successfully delivers a hit adds it to himself.
+		STAMINALEECH=# 	// It gives an attacker the ability to leech Stamina from his opponent every time he successfully delivers a hit adds it to himself.
+		MANALEECH=# 	// It gives an attacker the ability to leech Mana from his opponent every time he successfully delivers a hit adds it to himself.
+	These are also available as JS Engine object properties: .healthLeech, .staminaLeech and .manaLeech
+	Added leechstats.js file that controls the combat for the properties. (script 7003)
+	To add this script to a weapon only. add in SCRIPT=7003, HEALTHLEECH=# or STAMINALEECH=# or MANALEECH=# it can also be all three on the weapon.
+
+16/06/2024 - Dragon Slayer/Xuri
+	Added new DFN tags for Items and Chars:
+		SPEEDINCREASE=# 		// item and char property that increases base weapon swing speed by the specified percentage
+	These are also available as JS Engine object properties: .speedIncrease
+	Added INI Settings:
+		SWINGSPEEDINCREASECAP=#  	// Max cap for for swing speed increase a char can have.
+	Combat code updated with era-specific weapon swing speed calculations for different eras (LBR or earlier/AoS/ML onwards).
+        Note that speeds are still defined with flat values in DFNs regardless of era, but for ML and beyond speed
+ 	will be displayed as swing delay in seconds in item tooltip.
+
+14/06/2024 - Dragon Slayer
+	Added two new DFN tags for Items:
+		HITCHANCE=# 		// Increases the player's chance to hit a target with wrestling, melee and ranged weapons.
+		DEFENSECHANCE=# 	// Increases the wearer's chance that his opponents' swings (or arrows/bolts) will miss.
+	These are also available as JS Engine object properties: .hitChance, .defenseChance
+
+13/06/2024 - Dragon Slayer
+	Added three More AOS Props
+	-HEALTHBONUS=#
+	-MANABONUS=#
+	-STAMINABONUS=#
+	Add this properties to any weapon/armor/jewlery will give the player more hp/mana/stam why its equiped. depending on number you add with it
+	These are also available as JS Engine object properties: .healthBonus, .staminaBonus, .manaBonus
+
+06/06/2024 - Dragon Slayer
+	Fixed Accepting bods, When the expansion is to to lbr or later.
+
+29/05/2024 - Dragon Slayer
+	House add-on deeds are now returned when an add-on is present in the house on demolish.
+
+27/05/2024 - Dragon Slayer
+	Added Missing Wand ID's to combat weapon type in core and in js.
+
 13/05/2024 - Dragon Slayer
 	Added New Shield Type 107 so shield ID's no longer have to be in hard code for shields to work properly
 
+11/05/2024 - Dragon Slayer
+	Updated 'get command (js/commands/targeting/get.js) to use correct object reference when getting resist values for items, and added support for getting resist values for chars
+
 09/05/2024 - Dragon Slayer
 	Added ArtifactRarity AOS Property for items
 	-ARTIFACTRARITY=#
 	-Artifact Rarity is an item property that is visible on some artifacts and meant to give players an idea of how rare the item is. Items with rarity 1 are supposed to be common while rarity 12 are extremely scarce.
 
+05/05/2024 - Dragon Slayer
+	Added Spawner Gump and attached to the base_spawner, just another option for making editing spawners without using commands. (js spawnergump)
+	-Just double click any spawner you create from the addmenu or add from spawners.dfn file, gump will appear if you create a spawner with commands you would have to attach the script directly to it.
 
-1/05/2024 - Dragon Slayer/Xuri
+01/05/2024 - Dragon Slayer/Xuri
 	Fixed AutoUnequipAttempt function in clumsy.js, createfood.js level1target.js, will no longer fail on casting and return to hardcode.
 	Fixed createfood to check for reagents on cast.
 	Fixed many places missing proper null checks and refresh checks in magic js scripts.
 
+30/04/2024 - Dragon Slayer/Xuri/Maarc
+	Added Support for new AOS property tag.
+	-LOWERSTATREQ - Lowers the strength requirements to wear an object.
+
 27/04/2024 - Dragon Slayer/Xuri
 	Fixed an issue where non-corpse containers - including treasure chests in dungeons - would decay and leave all their contents on the ground.
 	Converted LOOTDECAYSWITHCORPSE ini setting to two new settings, one for player corpses, one for NPC corpses:
diff --git a/source/SEFunctions.cpp b/source/SEFunctions.cpp
index 1700d8e55..6ec92ec01 100644
--- a/source/SEFunctions.cpp
+++ b/source/SEFunctions.cpp
@@ -5086,6 +5086,9 @@ JSBool SE_GetServerSetting( JSContext *cx, [[maybe_unused]] JSObject *obj, uintN
 			case 349:	 // LOOTDECAYSWITHPLAYERCORPSE
 				*rval = BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->NpcCorpseLootDecay() );
 				break;
+			case 353:	// SWINGSPEEDINCREASECAP
+				*rval = INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->SwingSpeedIncreaseCap() ));
+				break;
 			default:
 				ScriptError( cx, "GetServerSetting: Invalid server setting name provided" );
 				return false;
diff --git a/source/UOXJSPropertyEnums.h b/source/UOXJSPropertyEnums.h
index 45517f28d..874440fb4 100644
--- a/source/UOXJSPropertyEnums.h
+++ b/source/UOXJSPropertyEnums.h
@@ -339,6 +339,9 @@ enum CC_Properties
 	CCP_SPAWNSERIAL,
 	CCP_SPATTACK,
 	CCP_SPDELAY,
+	CCP_SWINGSPEEDINCREASE,
+	CCP_HITCHANCE,
+	CCP_DEFENSECHANCE,
 	CCP_AITYPE,
 	CCP_SPLIT,
 	CCP_SPLITCHANCE,
@@ -445,6 +448,7 @@ enum CI_Properties
 	CIP_DECAYTIME,
 	CIP_LODAMAGE,
 	CIP_HIDAMAGE,
+	CIP_LOWERSTATREQ,
 	CIP_AC,
 	CIP_DEF,
 	CIP_RESISTCOLD,
@@ -461,6 +465,9 @@ enum CI_Properties
 	CIP_DAMAGEPOISON,
 	CIP_DAMAGERAIN,
 	CIP_DAMAGESNOW,
+	CIP_HITCHANCE,
+	CIP_DEFENSECHANCE,
+
 	CIP_ARTIFACTRARITY,
 	CIP_NAME2,
 	CIP_ISITEM,
@@ -496,7 +503,9 @@ enum CI_Properties
 	CIP_SECTIONALIST,
 	CIP_MININTERVAL,
 	CIP_MAXINTERVAL,
-
+	CIP_HEALTHLEECH,
+	CIP_STAMINALEECH,
+	CIP_MANALEECH,
 	CIP_ISNEWBIE,
 	CIP_ISDISPELLABLE,
 	CIP_MADEWITH,
@@ -517,6 +526,10 @@ enum CI_Properties
 	CIP_ISCONTTYPE,
 	CIP_CARVESECTION,
 	CIP_SPEED,
+	CIP_SWINGSPEEDINCREASE,
+	CIP_HEALTHBONUS,
+	CIP_STAMINABONUS,
+	CIP_MANABONUS,
 	CIP_MULTI,
 	CIP_AMMOID,
 	CIP_AMMOHUE,
@@ -534,7 +547,6 @@ enum CI_Properties
 	CIP_ISITEMHELD,
 	CIP_SECTIONID,
 	CIP_STEALABLE,
-
 	CIP_LOCKDDOWNS,
 	CIP_MAXLOCKDOWNS,
 	CIP_TRASHCONTAINERS,
diff --git a/source/UOXJSPropertyFuncs.cpp b/source/UOXJSPropertyFuncs.cpp
index 5fc2a3a82..9c1dc93f1 100644
--- a/source/UOXJSPropertyFuncs.cpp
+++ b/source/UOXJSPropertyFuncs.cpp
@@ -675,8 +675,19 @@ JSBool CItemProps_getProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp
 			case CIP_DAMAGEPOISON:		*vp = BOOLEAN_TO_JSVAL( gPriv->GetWeatherDamage( POISON ));	break;
 			case CIP_DAMAGERAIN:		*vp = BOOLEAN_TO_JSVAL( gPriv->GetWeatherDamage( RAIN ));	break;
 			case CIP_DAMAGESNOW:		*vp = BOOLEAN_TO_JSVAL( gPriv->GetWeatherDamage( SNOW ));	break;
+			case CIP_SWINGSPEEDINCREASE:	*vp = INT_TO_JSVAL( gPriv->GetSwingSpeedIncrease() );			break;
 			case CIP_SPEED:			*vp = INT_TO_JSVAL( gPriv->GetSpeed() );			break;
+			case CIP_LOWERSTATREQ:		*vp = INT_TO_JSVAL( gPriv->GetLowerStatReq() );			break;
+			case CIP_HEALTHLEECH:	*vp = INT_TO_JSVAL( gPriv->GetHealthLeech() );			break;
+			case CIP_STAMINALEECH:	*vp = INT_TO_JSVAL( gPriv->GetStaminaLeech() );			break;
+			case CIP_MANALEECH:		*vp = INT_TO_JSVAL( gPriv->GetManaLeech() );			break;
+			case CIP_HITCHANCE:	*vp = INT_TO_JSVAL( gPriv->GetHitChance() );			break;
+			case CIP_DEFENSECHANCE:	*vp = INT_TO_JSVAL( gPriv->GetDefenseChance() );			break;
+			case CIP_HEALTHBONUS:		*vp = INT_TO_JSVAL( gPriv->GetHealthBonus() );			break;
+			case CIP_STAMINABONUS:		*vp = INT_TO_JSVAL( gPriv->GetStaminaBonus() );			break;
+			case CIP_MANABONUS:		*vp = INT_TO_JSVAL( gPriv->GetManaBonus() );			break;
 			case CIP_ARTIFACTRARITY:		*vp = INT_TO_JSVAL( gPriv->GetArtifactRarity() );			break;
+
 			case CIP_NAME2:
 				tString = JS_NewStringCopyZ( cx, gPriv->GetName2().c_str() );
 				*vp = STRING_TO_JSVAL( tString );
@@ -1322,7 +1333,18 @@ JSBool CItemProps_setProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp
 			case CIP_DAMAGERAIN:	gPriv->SetWeatherDamage( RAIN, encaps.toBool() );			break;
 			case CIP_DAMAGESNOW:	gPriv->SetWeatherDamage( SNOW, encaps.toBool() );			break;
 			case CIP_SPEED:			gPriv->SetSpeed( static_cast( encaps.toInt() ));		break;
+			case CIP_LOWERSTATREQ:	gPriv->SetLowerStatReq( static_cast( encaps.toInt() ));	break;
+			case CIP_SWINGSPEEDINCREASE:	gPriv->SetSwingSpeedIncrease( static_cast( encaps.toInt() ));	break;
+			case CIP_HEALTHLEECH:	gPriv->SetHealthLeech( static_cast( encaps.toInt() ));	break;
+			case CIP_STAMINALEECH:	gPriv->SetStaminaLeech( static_cast( encaps.toInt() ));	break;
+			case CIP_MANALEECH:		gPriv->SetManaLeech( static_cast( encaps.toInt() ));	break;
+			case CIP_HITCHANCE:	gPriv->SetHitChance( static_cast( encaps.toInt() ));	break;
+			case CIP_DEFENSECHANCE:	gPriv->SetDefenseChance( static_cast( encaps.toInt() ));	break;
+			case CIP_HEALTHBONUS:		gPriv->SetHealthBonus( static_cast( encaps.toInt() ));	break;
+			case CIP_STAMINABONUS:		gPriv->SetStaminaBonus( static_cast( encaps.toInt() ));	break;
+			case CIP_MANABONUS:		gPriv->SetManaBonus( static_cast( encaps.toInt() ));	break;
 			case CIP_ARTIFACTRARITY:	gPriv->SetArtifactRarity( static_cast( encaps.toInt() ));	break;
+
 			case CIP_NAME2:			gPriv->SetName2( encaps.toString() );						break;
 			case CIP_RACE:			gPriv->SetRace( static_cast( encaps.toInt() ));		break;
 			case CIP_MAXHP:			gPriv->SetMaxHP( static_cast( encaps.toInt() ));		break;
@@ -1996,6 +2018,9 @@ JSBool CCharacterProps_getProperty( JSContext *cx, JSObject *obj, jsval id, jsva
 			case CCP_HOUSEICONS:	*vp = BOOLEAN_TO_JSVAL( gPriv->ViewHouseAsIcon() );			break;
 			case CCP_SPATTACK:		*vp = INT_TO_JSVAL( gPriv->GetSpAttack() );					break;
 			case CCP_SPDELAY:		*vp = INT_TO_JSVAL( gPriv->GetSpDelay() );					break;
+			case CCP_SWINGSPEEDINCREASE:	*vp = INT_TO_JSVAL( gPriv->GetSwingSpeedIncrease() );		break;
+			case CCP_HITCHANCE:		*vp = INT_TO_JSVAL( gPriv->GetHitChance() );				break;
+			case CCP_DEFENSECHANCE:	*vp = INT_TO_JSVAL( gPriv->GetDefenseChance() );			break;
 			case CCP_AITYPE:		*vp = INT_TO_JSVAL( gPriv->GetNpcAiType() );				break;
 			case CCP_SPLIT:			*vp = INT_TO_JSVAL( gPriv->GetSplit() );					break;
 			case CCP_SPLITCHANCE:	*vp = INT_TO_JSVAL( gPriv->GetSplitChance() );				break;
@@ -2501,6 +2526,9 @@ JSBool CCharacterProps_setProperty( JSContext *cx, JSObject *obj, jsval id, jsva
 			case CCP_HOUSEICONS:	gPriv->SetViewHouseAsIcon( encaps.toBool() );				break;
 			case CCP_SPATTACK:		gPriv->SetSpAttack( static_cast( encaps.toInt() ));	break;
 			case CCP_SPDELAY:		gPriv->SetSpDelay( static_cast( encaps.toInt() ));	break;
+			case CCP_SWINGSPEEDINCREASE:	gPriv->SetSwingSpeedIncrease( static_cast( encaps.toInt() ));		break;
+			case CCP_HITCHANCE:		gPriv->SetHitChance( static_cast( encaps.toInt() ));	break;
+			case CCP_DEFENSECHANCE:	gPriv->SetDefenseChance( static_cast( encaps.toInt() ));	break;
 			case CCP_AITYPE:		gPriv->SetNPCAiType( static_cast( encaps.toInt() ));	break;
 			case CCP_SPLIT:			gPriv->SetSplit( static_cast( encaps.toInt() ));		break;
 			case CCP_SPLITCHANCE:	gPriv->SetSplitChance( static_cast( encaps.toInt() ));break;
diff --git a/source/UOXJSPropertySpecs.h b/source/UOXJSPropertySpecs.h
index 452169395..620b4c558 100644
--- a/source/UOXJSPropertySpecs.h
+++ b/source/UOXJSPropertySpecs.h
@@ -357,6 +357,9 @@ inline JSPropertySpec CCharacterProps[] =
 	{ "houseicons",		CCP_HOUSEICONS,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "spattack",		CCP_SPATTACK,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "spdelay",		CCP_SPDELAY,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "swingSpeedIncrease",	CCP_SWINGSPEEDINCREASE,	JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "hitChance",		CCP_HITCHANCE,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "defenseChance",	CCP_DEFENSECHANCE,	JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "aitype",			CCP_AITYPE,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "split",			CCP_SPLIT,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "splitchance",	CCP_SPLITCHANCE,	JSPROP_ENUMANDPERM, nullptr, nullptr },
@@ -477,6 +480,7 @@ inline JSPropertySpec CItemProps[] =
 	{ "damagePoison",	CIP_DAMAGEPOISON,	JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "damageRain",		CIP_DAMAGERAIN,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "damageSnow",		CIP_DAMAGESNOW,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "lowerStateReq",	CIP_LOWERSTATREQ,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "name2",			CIP_NAME2,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "isChar",			CIP_ISCHAR,			JSPROP_ENUMPERMRO, nullptr, nullptr },
 	{ "isItem",			CIP_ISITEM,			JSPROP_ENUMPERMRO, nullptr, nullptr },
@@ -535,7 +539,20 @@ inline JSPropertySpec CItemProps[] =
 	{ "ammoFXHue",		CIP_AMMOFXHUE,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "ammoFXRender",	CIP_AMMOFXRENDER,	JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "speed",			CIP_SPEED,			JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "swingSpeedIncrease",		CIP_SWINGSPEEDINCREASE ,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+
+	{ "healthLeech",	CIP_HEALTHLEECH,	JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "staminaLeech",	CIP_STAMINALEECH,	JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "manaLeech",		CIP_MANALEECH,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+
+	{ "hitChance",		CIP_HITCHANCE,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "defenseChance",	CIP_DEFENSECHANCE,		JSPROP_ENUMANDPERM, nullptr, nullptr },
+
+	{ "healthBonus",	CIP_HEALTHBONUS,	JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "staminaBonus",	CIP_STAMINABONUS,	JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "manaBonus",		CIP_MANABONUS,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "artifactRarity",	CIP_ARTIFACTRARITY,	JSPROP_ENUMANDPERM, nullptr, nullptr },
+
 	{ "multi",			CIP_MULTI,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "maxRange",		CIP_MAXRANGE,		JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "baseRange",		CIP_BASERANGE,		JSPROP_ENUMANDPERM, nullptr, nullptr },
diff --git a/source/cBaseObject.cpp b/source/cBaseObject.cpp
index 506c247ac..027ba2bc6 100644
--- a/source/cBaseObject.cpp
+++ b/source/cBaseObject.cpp
@@ -94,6 +94,17 @@ const SI16			DEFBASE_KILLS		= 0;
 const UI16			DEFBASE_RESIST 		= 0;
 const bool			DEFBASE_NAMEREQUESTACTIVE = 0;
 const ExpansionRuleset	DEFBASE_ORIGIN	= ER_UO;
+const SI16			DEFBASE_SWINGSPEEDINCREASE = 0;
+const SI16			DEFBASE_HEALTHLEECH = 0;
+const SI16			DEFBASE_STAMINALEECH = 0;
+const SI16			DEFBASE_MANALEECH = 0;
+
+const SI16			DEFBASE_HITCHANCE = 0;
+const SI16			DEFBASE_DEFENSECHANCE = 0;
+
+const SI16			DEFBASE_HEALTHBONUS = 0;
+const SI16			DEFBASE_STAMINABONOS = 0;
+const SI16			DEFBASE_MANABONUS = 0;
 
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CBaseObject constructor
@@ -110,7 +121,9 @@ loDamage( DEFBASE_LODAMAGE ), weight( DEFBASE_WEIGHT ),
 mana( DEFBASE_MANA ), stamina( DEFBASE_STAMINA ), scriptTrig( DEFBASE_SCPTRIG ), st2( DEFBASE_STR2 ), dx2( DEFBASE_DEX2 ),
 in2( DEFBASE_INT2 ), FilePosition( DEFBASE_FP ),
 poisoned( DEFBASE_POISONED ), carve( DEFBASE_CARVE ), oldLocX( 0 ), oldLocY( 0 ), oldLocZ( 0 ), oldTargLocX( 0 ), oldTargLocY( 0 ),
-fame( DEFBASE_FAME ), karma( DEFBASE_KARMA ), kills( DEFBASE_KILLS ), subRegion( DEFBASE_SUBREGION ), nameRequestActive( DEFBASE_NAMEREQUESTACTIVE ), origin( DEFBASE_ORIGIN )
+fame( DEFBASE_FAME ), karma( DEFBASE_KARMA ), kills( DEFBASE_KILLS ), subRegion( DEFBASE_SUBREGION ), nameRequestActive( DEFBASE_NAMEREQUESTACTIVE ), origin( DEFBASE_ORIGIN ),
+healthBonus( DEFBASE_HEALTHBONUS ),staminaBonus( DEFBASE_STAMINABONOS ), manaBonus( DEFBASE_MANABONUS ), hitChance( DEFBASE_HITCHANCE ), defenseChance( DEFBASE_DEFENSECHANCE ),
+healthLeech( DEFBASE_HEALTHLEECH ), staminaLeech( DEFBASE_STAMINALEECH ), manaLeech( DEFBASE_MANALEECH ), swingSpeedIncrease( DEFBASE_SWINGSPEEDINCREASE )
 {
 	multis = nullptr;
 	tempMulti = INVALIDSERIAL;
@@ -793,6 +806,7 @@ bool CBaseObject::DumpBody( std::ostream &outStream ) const
 	outStream << "Intelligence=" + std::to_string( intelligence ) + "," + std::to_string( temp_in2 ) + newLine;
 	outStream << "Strength=" + std::to_string( strength ) + "," + std::to_string( temp_st2 ) + newLine;
 	outStream << "HitPoints=" + std::to_string( hitpoints ) + newLine;
+	outStream << "SwingSpeedInc=" + std::to_string( GetSwingSpeedIncrease() ) + newLine;
 	outStream << "Race=" + std::to_string( race ) + newLine;
 	outStream << "Visible=" + std::to_string( visible ) + newLine;
 	outStream << "Disabled=" << ( IsDisabled() ? "1" : "0" ) << newLine;
@@ -1036,6 +1050,46 @@ void CBaseObject::IncHP( SI16 amtToChange )
 	SetHP( hitpoints + amtToChange );
 }
 
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::GetHitChance()
+//|					CBaseObject::SetHitChance()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Defense Chance of the Item(s) Equiped or Character
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetHitChance( void ) const
+{
+	return hitChance;
+}
+void CBaseObject::SetHitChance( SI16 newValue )
+{
+	hitChance = newValue;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::GetDefenseChance()
+//|					CBaseObject::SetDefenseChance()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Defense Chance of the Item(s) Equiped or Character
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetDefenseChance( void ) const
+{
+	return defenseChance;
+}
+void CBaseObject::SetDefenseChance( SI16 newValue )
+{
+	defenseChance = newValue;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CBaseObject::GetDir()
 //|					CBaseObject::SetDir()
@@ -1568,6 +1622,27 @@ Point3_st CBaseObject::GetLocation( void ) const
 	return Point3_st( x, y, z );
 }
 
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::GetSwingSpeedIncrease()
+//|					CBaseObject::SetSwingSpeedBonus()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the item's Swing Speed Bonus property (in percentage), which 
+//|					adjusts the base swing speed of the equipped weapon, or characters
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetSwingSpeedIncrease( void ) const
+{
+	return swingSpeedIncrease;
+}
+void CBaseObject::SetSwingSpeedIncrease( SI16 newValue )
+{
+	swingSpeedIncrease = newValue;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CBaseObject::GetStrength2()
 //|					CBaseObject::SetStrength2()
@@ -1631,6 +1706,126 @@ void CBaseObject::SetIntelligence2( SI16 nVal )
 	}
 }
 
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::GetHealthLeech()
+//|					CBaseObject::SetHealthLeech()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Health Leech
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetHealthLeech( void ) const
+{
+	return healthLeech;
+}
+void CBaseObject::SetHealthLeech( SI16 nVal )
+{
+	healthLeech = nVal;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
+//|	Function	-	CBaseObject::GetHealthBonus()
+//|					CBaseObject::SetHealthBonus()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the health max var associated with the object. For chars, it's the
+//|					bonuses (via armour and such)
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetHealthBonus( void ) const
+{
+	return healthBonus;
+}
+void CBaseObject::SetHealthBonus( SI16 nVal )
+{
+	healthBonus = nVal;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::GetStaminaLeech()
+//|					CBaseObject::SetStaminaLeech()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Stamina Leech
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetStaminaLeech( void ) const
+{
+	return staminaLeech;
+}
+void CBaseObject::SetStaminaLeech( SI16 nVal )
+{
+	staminaLeech = nVal;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
+//|	Function	-	CBaseObject::GetStaminaBonus()
+//|					CBaseObject::SetStaminaBonus()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the stamina max var associated with the object. For chars, it's the
+//|					bonuses (via armour and such)
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetStaminaBonus( void ) const
+{
+	return staminaBonus;
+}
+void CBaseObject::SetStaminaBonus( SI16 nVal )
+{
+	staminaBonus = nVal;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::GetManaLeech()
+//|					CBaseObject::SetManaLeech()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Mana Leech
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetManaLeech( void ) const
+{
+	return manaLeech;
+}
+void CBaseObject::SetManaLeech( SI16 nVal )
+{
+	manaLeech = nVal;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
+//|	Function	-	CBaseObject::GetManaBonus()
+//|					CBaseObject::SetManaBonus()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Mana max var associated with the object. For chars, it's the
+//|					bonuses (via armour and such)
+//o------------------------------------------------------------------------------------------------o
+SI16 CBaseObject::GetManaBonus( void ) const
+{
+	return manaBonus;
+}
+void CBaseObject::SetManaBonus( SI16 nVal )
+{
+	manaBonus = nVal;
+
+	if( CanBeObjType( OT_ITEM ))
+	{
+		( static_cast( this ))->UpdateRegion();
+	}
+}
+
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CBaseObject::IncStrength()
 //o------------------------------------------------------------------------------------------------o
@@ -1661,6 +1856,65 @@ void CBaseObject::IncIntelligence( SI16 toInc )
 	SetIntelligence( intelligence + toInc );
 }
 
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::IncSwingSpeedIncrease()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Increments the object's swing speed bonus value
+//o------------------------------------------------------------------------------------------------o
+void CBaseObject::IncSwingSpeedIncrease( SI16 toInc )
+{
+	SetSwingSpeedIncrease( swingSpeedIncrease + toInc );
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::IncHealthLeech()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Increments the object's Health Leech Points value
+//o------------------------------------------------------------------------------------------------o
+void CBaseObject::IncHealthLeech( SI16 toInc )
+{
+	SetHealthLeech( healthLeech + toInc );
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::IncStaminaLeech()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Increments the object's Stamina Leech Points value
+//o------------------------------------------------------------------------------------------------o
+void CBaseObject::IncStaminaLeech( SI16 toInc )
+{
+	SetStaminaLeech( staminaLeech + toInc );
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::IncManaLeech()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Increments the object's Mana Leech Points value
+//o------------------------------------------------------------------------------------------------o
+void CBaseObject::IncManaLeech( SI16 toInc )
+{
+	SetManaLeech( manaLeech + toInc );
+}
+
+//|	Function	-	CBaseObject::IncHitChance()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Increments the object's Hit Chance value
+//o------------------------------------------------------------------------------------------------o
+void CBaseObject::IncHitChance( SI16 toInc )
+{
+	SetHitChance( hitChance + toInc );
+}
+
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CBaseObject::IncDefenseChance()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Increments the object's Hit Chance value
+//o------------------------------------------------------------------------------------------------o
+void CBaseObject::IncDefenseChance( SI16 toInc )
+{
+	SetDefenseChance( defenseChance + toInc );
+}
+
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CBaseObject::DumpFooter()
 //o------------------------------------------------------------------------------------------------o
@@ -2022,6 +2276,11 @@ bool CBaseObject::HandleLine( std::string &UTag, std::string &data )
 			{
 				st2	= oldstrutil::value( data );
 			}
+			else if( UTag == "SWINGSPEEDINC" )
+			{
+				SetSwingSpeedIncrease( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
+				rValue = true;
+			}
 			else if( UTag == "SCPTRIG" )
 			{
 				//scriptTrig	= oldstrutil::value(data);
@@ -2544,7 +2803,7 @@ void CBaseObject::CopyData( CBaseObject *target )
 	target->SetIntelligence2( GetIntelligence2() );
 	target->SetPoisoned( GetPoisoned() );
 	target->SetWeight( GetWeight() );
-
+	target->SetSwingSpeedIncrease( GetSwingSpeedIncrease() );
 	target->SetKarma( karma );
 	target->SetFame( fame );
 	target->SetKills( kills );
diff --git a/source/cBaseObject.h b/source/cBaseObject.h
index 638e826f7..527f6c3b7 100644
--- a/source/cBaseObject.h
+++ b/source/cBaseObject.h
@@ -69,16 +69,25 @@ class CBaseObject
 	SI16				dexterity;
 	SI16				intelligence;
 	SI16				hitpoints;
+	SI16				hitChance;
+	SI16				defenseChance;
 	VisibleTypes		visible;
 	SI16				hiDamage;
 	SI16				loDamage;
 	SI32				weight;
 	SI16				mana;
 	SI16				stamina;
+	SI16				swingSpeedIncrease;
+	SI16				healthLeech;
+	SI16				staminaLeech;
+	SI16				manaLeech;
 	UI16				scriptTrig;
 	SI16				st2;
 	SI16				dx2;
 	SI16				in2;
+	SI16				healthBonus;
+	SI16				staminaBonus;
+	SI16				manaBonus;
 	mutable SI32		FilePosition;
 	SERIAL				tempMulti;
 	std::string			name;
@@ -223,6 +232,17 @@ class CBaseObject
 	virtual void			SetHP( SI16 newValue );
 	void					IncHP( SI16 amtToChange );
 
+	virtual SI16			GetSwingSpeedIncrease( void ) const;
+	virtual void			SetSwingSpeedIncrease( SI16 newValue );
+
+	void					IncSwingSpeedIncrease( SI16 toInc = 1 );
+
+	virtual SI16			GetHitChance( void ) const;
+	virtual void			SetHitChance( SI16 newValue );
+
+	virtual SI16			GetDefenseChance( void ) const;
+	virtual void			SetDefenseChance( SI16 newValue );
+
 	void					SetDir( UI08 newDir, bool sendUpdate = true );
 	UI08					GetDir( void ) const;
 
@@ -256,6 +276,32 @@ class CBaseObject
 	void					IncDexterity( SI16 toInc = 1 );
 	void					IncIntelligence( SI16 toInc = 1 );
 
+
+	SI16					GetHealthLeech( void ) const;
+	virtual void			SetHealthLeech( SI16 nVal );
+
+	SI16					GetStaminaLeech( void ) const;
+	virtual void			SetStaminaLeech( SI16 nVal );
+
+	SI16					GetManaLeech( void ) const;
+	virtual void			SetManaLeech( SI16 nVal );
+
+	void					IncHealthLeech( SI16 toInc = 1 );
+	void					IncStaminaLeech( SI16 toInc = 1 );
+	void					IncManaLeech( SI16 toInc = 1 );
+
+	void					IncHitChance( SI16 toInc = 1 );
+	void					IncDefenseChance( SI16 toInc = 1 );
+
+	SI16					GetHealthBonus( void ) const;
+	virtual void			SetHealthBonus( SI16 nVal );
+
+	SI16					GetStaminaBonus( void ) const;
+	virtual void			SetStaminaBonus( SI16 nVal );
+
+	SI16					GetManaBonus( void ) const;
+	virtual void			SetManaBonus( SI16 nVal );
+
 	virtual void			PostLoadProcessing( void );
 	virtual bool			LoadRemnants( void ) = 0;
 
diff --git a/source/cChar.cpp b/source/cChar.cpp
index 7aa03f6fa..319737117 100644
--- a/source/cChar.cpp
+++ b/source/cChar.cpp
@@ -2411,6 +2411,8 @@ void CChar::CopyData( CChar *target )
 	target->SetNextAct( nextAct );
 	target->SetSquelched( GetSquelched() );
 	target->SetMeditating( IsMeditating() );
+	target->SetHitChance( GetHitChance() );
+	target->SetDefenseChance( GetDefenseChance() );
 	target->SetStealth( stealth );
 	target->SetRunning( running );
 	target->SetRace( GetRace() );
@@ -2920,6 +2922,19 @@ bool CChar::WearItem( CItem *toWear )
 			IncDexterity2( itemLayers[tLayer]->GetDexterity2() );
 			IncIntelligence2( itemLayers[tLayer]->GetIntelligence2() );
 
+			IncSwingSpeedIncrease( itemLayers[tLayer]->GetSwingSpeedIncrease() );
+
+			IncHealthLeech( itemLayers[tLayer]->GetHealthLeech() );
+			IncStaminaLeech( itemLayers[tLayer]->GetStaminaLeech() );
+			IncManaLeech( itemLayers[tLayer]->GetManaLeech() );
+
+      IncHitChance( itemLayers[tLayer]->GetHitChance() );
+			IncDefenseChance( itemLayers[tLayer]->GetDefenseChance() );
+
+			IncHealthBonus( itemLayers[tLayer]->GetHealthBonus() );
+			IncStaminaBonus( itemLayers[tLayer]->GetStaminaBonus() );
+			IncManaBonus( itemLayers[tLayer]->GetManaBonus() );
+
 			if( toWear->IsPostLoaded() )
 			{
 				if( itemLayers[tLayer]->GetPoisoned() )
@@ -2979,6 +2994,20 @@ bool CChar::TakeOffItem( ItemLayers Layer )
 		IncStrength2( -itemLayers[Layer]->GetStrength2() );
 		IncDexterity2( -itemLayers[Layer]->GetDexterity2() );
 		IncIntelligence2( -itemLayers[Layer]->GetIntelligence2() );
+
+		IncSwingSpeedIncrease( -itemLayers[Layer]->GetSwingSpeedIncrease() );
+
+    IncHealthLeech( -itemLayers[Layer]->GetHealthLeech() );
+		IncStaminaLeech( -itemLayers[Layer]->GetStaminaLeech() );
+		IncManaLeech( -itemLayers[Layer]->GetManaLeech() );
+
+		IncHitChance( -itemLayers[Layer]->GetHitChance() );
+		IncDefenseChance( -itemLayers[Layer]->GetDefenseChance() );
+
+		IncHealthBonus( -itemLayers[Layer]->GetHealthBonus() );
+		IncStaminaBonus( -itemLayers[Layer]->GetStaminaBonus() );
+		IncManaBonus( -itemLayers[Layer]->GetManaBonus() );
+
 		if( itemLayers[Layer]->GetPoisoned() )
 		{
 			if( itemLayers[Layer]->GetPoisoned() > GetPoisoned() )
@@ -3134,6 +3163,8 @@ bool CChar::DumpBody( std::ostream &outStream ) const
 	//-------------------------------------------------------------------------------------------
 	outStream << "CanRun=" + std::to_string((( CanRun() && IsNpc() ) ? 1 : 0 )) + newLine;
 	outStream << "CanAttack=" + std::to_string(( GetCanAttack() ? 1 : 0 )) + newLine;
+	outStream << "HitChance=" + std::to_string( GetHitChance() ) + newLine;
+	outStream << "DefChance=" + std::to_string( GetDefenseChance() ) + newLine;
 	outStream << "AllMove=" + std::to_string(( AllMove() ? 1 : 0 )) + newLine;
 	outStream << "IsNpc=" + std::to_string(( IsNpc() ? 1 : 0 )) + newLine;
 	outStream << "IsShop=" + std::to_string(( IsShop() ? 1 : 0 )) + newLine;
@@ -3787,7 +3818,7 @@ UI16 CChar::GetMaxHP( void )
 		oldRace			= GetRace();
 
 	}
-	return maxHP;
+	return maxHP + GetHealthBonus();
 }
 void CChar::SetMaxHP( UI16 newmaxhp, UI16 newoldstr, RACEID newoldrace )
 {
@@ -3830,7 +3861,7 @@ void CChar::SetFixedMaxHP( SI16 newmaxhp )
 SI16 CChar::GetMaxMana( void )
 {
 	if(( maxMana_oldint != GetIntelligence() || oldRace != GetRace() ) && !GetMaxManaFixed() )
-		//if int/race changed since last calculation, recalculate maxHp
+		//if int/race changed since last calculation, recalculate maxMana
 	{
 		CRace *pRace = Races->Race( GetRace() );
 
@@ -3847,7 +3878,7 @@ SI16 CChar::GetMaxMana( void )
 		oldRace			= GetRace();
 
 	}
-	return maxMana;
+	return maxMana + GetManaBonus();
 }
 void CChar::SetMaxMana( SI16 newmaxmana, UI16 newoldint, RACEID newoldrace )
 {
@@ -3889,7 +3920,7 @@ void CChar::SetFixedMaxMana( SI16 newmaxmana )
 //o------------------------------------------------------------------------------------------------o
 SI16 CChar::GetMaxStam( void )
 {
-	// If dex/race changed since last calculation, recalculate maxHp
+	// If dex/race changed since last calculation, recalculate maxStam
 	if(( maxStam_olddex != GetDexterity() || oldRace != GetRace() ) && !GetMaxStamFixed() )
 	{
 		CRace *pRace = Races->Race( GetRace() );
@@ -3907,7 +3938,7 @@ SI16 CChar::GetMaxStam( void )
 		oldRace			= GetRace();
 
 	}
-	return maxStam;
+	return maxStam + GetStaminaBonus();
 }
 void CChar::SetMaxStam( SI16 newmaxstam, UI16 newolddex, RACEID newoldrace )
 {
@@ -4372,6 +4403,11 @@ bool CChar::HandleLine( std::string &UTag, std::string &data )
 					SetDead(( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )) == 1 ));
 					rValue = true;
 				}
+				else if( UTag == "DEFCHANCE" )
+				{
+					SetDefenseChance( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
+					rValue = true;
+				}
 				break;
 			case 'E':
 				if( UTag == "EMOTION" )
@@ -4460,7 +4496,12 @@ bool CChar::HandleLine( std::string &UTag, std::string &data )
 				}
 				break;
 			case 'H':
-				if( UTag == "HUNGER" )
+				if( UTag == "HITCHANCE" )
+				{
+					SetHitChance( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
+					rValue = true;
+				}
+				else if( UTag == "HUNGER" )
 				{
 					SetHunger( static_cast( std::stoi( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
 					rValue = true;
@@ -5593,6 +5634,74 @@ void CChar::SetIntelligence2( SI16 nVal )
 	UpdateRegion();
 }
 
+//o------------------------------------------------------------------------------------------------o
+//| Function	-	CChar::SetHealthBonus()
+//o------------------------------------------------------------------------------------------------o
+//| Purpose		-	Sets bonus Hits stat for character
+//o------------------------------------------------------------------------------------------------o
+void CChar::SetHealthBonus( SI16 nVal )
+{
+	CBaseObject::SetHealthBonus( nVal );
+	Dirty( UT_HITPOINTS );
+	UpdateRegion();
+}
+
+//o------------------------------------------------------------------------------------------------o
+//| Function	-	CChar::SetStaminaBonus()
+//o------------------------------------------------------------------------------------------------o
+//| Purpose		-	Sets bonus Stam stat for character
+//o------------------------------------------------------------------------------------------------o
+void CChar::SetStaminaBonus( SI16 nVal )
+{
+	CBaseObject::SetStaminaBonus( nVal );
+	Dirty( UT_STAMINA );
+	UpdateRegion();
+}
+
+//o------------------------------------------------------------------------------------------------o
+//| Function	-	CChar::SetManaBonus()
+//o------------------------------------------------------------------------------------------------o
+//| Purpose		-	Sets bonus Mana stat for character
+//o------------------------------------------------------------------------------------------------o
+void CChar::SetManaBonus( SI16 nVal )
+{
+	CBaseObject::SetManaBonus( nVal );
+	Dirty( UT_MANA );
+	UpdateRegion();
+}
+
+//o------------------------------------------------------------------------------------------------o
+//| Function	-	CChar::IncHealthBonus()
+//o------------------------------------------------------------------------------------------------o
+//| Purpose		-	Increments GetHealthBonus (modifications) by toAdd
+//o------------------------------------------------------------------------------------------------o
+void CChar::IncHealthBonus( SI16 toAdd )
+{
+	SetHealthBonus( static_cast( GetHealthBonus() + toAdd ));
+}
+
+//o------------------------------------------------------------------------------------------------o
+//| Function	-	CChar::IncStaminaBonus()
+//| Date		-	26 May 2024
+//o------------------------------------------------------------------------------------------------o
+//| Purpose		-	Increments GetBonusStam (modifications) by toAdd
+//o------------------------------------------------------------------------------------------------o
+void CChar::IncStaminaBonus( SI16 toAdd )
+{
+	SetStaminaBonus( static_cast( GetStaminaBonus() + toAdd ));
+}
+
+//o------------------------------------------------------------------------------------------------o
+//| Function	-	CChar::IncManaBonus()
+//| Date		-	26 May 2024
+//o------------------------------------------------------------------------------------------------o
+//| Purpose		-	Increments GetBonusMana (modifications) by toAdd
+//o------------------------------------------------------------------------------------------------o
+void CChar::IncManaBonus( SI16 toAdd )
+{
+	SetManaBonus( static_cast( GetManaBonus() + toAdd ));
+}
+
 //o------------------------------------------------------------------------------------------------o
 //| Function	-	CChar::IncStamina()
 //o------------------------------------------------------------------------------------------------o
diff --git a/source/cChar.h b/source/cChar.h
index ed4cc90ba..ae387a58c 100644
--- a/source/cChar.h
+++ b/source/cChar.h
@@ -628,6 +628,15 @@ class CChar : public CBaseObject
 	virtual void	SetStrength2( SI16 newValue ) override;
 	virtual void	SetDexterity2( SI16 newValue ) override;
 	virtual void	SetIntelligence2( SI16 newValue ) override;
+
+	virtual void	SetHealthBonus( SI16 newValue ) override;
+	virtual void	SetStaminaBonus( SI16 newValue ) override;
+	virtual void	SetManaBonus( SI16 newValue ) override;
+
+	void			IncHealthBonus( SI16 toAdd = 1 );
+	void			IncStaminaBonus( SI16 toAdd = 1 );
+	void			IncManaBonus( SI16 toAdd = 1 );
+
 	void			IncStamina( SI16 toInc );
 	void			IncMana( SI16 toInc );
 	void			SetMaxLoyalty( UI16 newMaxLoyalty );
diff --git a/source/cItem.cpp b/source/cItem.cpp
index 3a2acd106..b668ef29e 100644
--- a/source/cItem.cpp
+++ b/source/cItem.cpp
@@ -94,6 +94,7 @@ const UI16			DEFITEM_REGIONNUM 		= 255;
 const UI16			DEFITEM_TEMPLASTTRADED	= 0;
 const SI08			DEFITEM_STEALABLE	 	= 1;
 const SI16			DEFITEM_ARTIFACTRARITY = 0;
+const SI16			DEFITEM_LOWERSTATREQ	= 0;
 
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CItem()
@@ -108,7 +109,7 @@ spd( DEFITEM_SPEED ), maxHp( DEFITEM_MAXHP ), amount( DEFITEM_AMOUNT ),
 layer( DEFITEM_LAYER ), type( DEFITEM_TYPE ), offspell( DEFITEM_OFFSPELL ), entryMadeFrom( DEFITEM_ENTRYMADEFROM ),
 creator( DEFITEM_CREATOR ), gridLoc( DEFITEM_GRIDLOC ), weightMax( DEFITEM_WEIGHTMAX ), baseWeight( DEFITEM_BASEWEIGHT ), maxItems( DEFITEM_MAXITEMS ),
 maxRange( DEFITEM_MAXRANGE ), baseRange( DEFITEM_BASERANGE ), maxUses( DEFITEM_MAXUSES ), usesLeft( DEFITEM_USESLEFT ), regionNum( DEFITEM_REGIONNUM ), 
-tempLastTraded( DEFITEM_TEMPLASTTRADED ), stealable( DEFITEM_STEALABLE ), artifactRarity(DEFITEM_ARTIFACTRARITY)
+tempLastTraded( DEFITEM_TEMPLASTTRADED ), stealable( DEFITEM_STEALABLE ), artifactRarity( DEFITEM_ARTIFACTRARITY ), lowerStatReq( DEFITEM_LOWERSTATREQ )
 {
 	spells[0]	= spells[1] = spells[2] = 0;
 	value[0]	= value[1] = value[2] = 0;
@@ -560,6 +561,23 @@ void CItem::SetArtifactRarity( SI16 newValue )
 	UpdateRegion();
 }
 
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CItem::GetLowerStatReq()
+//|					CItem::GetLowerStatReq()
+//|	Date		-	30 April, 2024
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the Stat Requirements of the object
+//o------------------------------------------------------------------------------------------------o
+SI16 CItem::GetLowerStatReq( void ) const
+{
+	return lowerStatReq;
+}
+void CItem::SetLowerStatReq( SI16 newValue )
+{
+	lowerStatReq = newValue;
+	UpdateRegion();
+}
+
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CItem::GetName2()
 //|					CItem::SetName2()
@@ -1654,13 +1672,20 @@ auto CItem::CopyData( CItem *target ) -> void
 	target->SetRndValueRate( GetRndValueRate() );
 	target->SetSpawn( GetSpawn() );
 	target->SetSpeed( GetSpeed() );
+	target->SetHitChance( GetHitChance() );
+	target->SetDefenseChance( GetDefenseChance() );
+
 	target->SetArtifactRarity( GetArtifactRarity() );
+
 	target->SetSpell( 0, GetSpell( 0 ));
 	target->SetSpell( 1, GetSpell( 1 ));
 	target->SetSpell( 2, GetSpell( 2 ));
 	target->SetStamina( GetStamina() );
 	target->SetStrength( GetStrength() );
 	target->SetStrength2( GetStrength2() );
+	target->SetHealthLeech( GetHealthLeech() );
+	target->SetStaminaLeech( GetStaminaLeech() );
+	target->SetManaLeech( GetManaLeech() );
 	target->SetTitle( GetTitle() );
 	target->SetType( GetType() );
 	target->SetBuyValue( GetBuyValue() );
@@ -1671,12 +1696,16 @@ auto CItem::CopyData( CItem *target ) -> void
 	target->SetWeightMax( GetWeightMax() );
 	target->SetBaseWeight( GetBaseWeight() );
 	target->SetMaxItems( GetMaxItems() );
+	target->SetHealthBonus( GetHealthBonus() );
+	target->SetStaminaBonus( GetStaminaBonus() );
+	target->SetManaBonus( GetManaBonus() );
 	//target->SetWipeable( IsWipeable() );
 	target->SetPriv( GetPriv() );
 	target->SetBaseRange( GetBaseRange() );
 	target->SetMaxRange( GetMaxRange() );
 	target->SetMaxUses( GetMaxUses() );
 	target->SetUsesLeft( GetUsesLeft() );
+	target->SetLowerStatReq( GetLowerStatReq() );
 	target->SetStealable( GetStealable() );
 
 	// Set damage types on new item
@@ -1753,10 +1782,15 @@ bool CItem::DumpBody( std::ostream &outStream ) const
 	outStream << "BaseWeight=" + std::to_string( GetBaseWeight() ) + newLine;
 	outStream << "MaxItems=" + std::to_string( GetMaxItems() ) + newLine;
 	outStream << "MaxHP=" + std::to_string( GetMaxHP() ) + newLine;
+	outStream << "HitChance=" + std::to_string( GetHitChance() ) + newLine;
+	outStream << "DefenseChance=" + std::to_string( GetDefenseChance() ) + newLine;
 	outStream << "Speed=" + std::to_string( GetSpeed() ) + newLine;
+	outStream << "BonusStats=" + std::to_string( GetHealthBonus() ) + "," + std::to_string( GetStaminaBonus() ) + "," + std::to_string( GetManaBonus() ) + newLine;
 	outStream << "ArtifactRarity=" + std::to_string( GetArtifactRarity() ) + newLine;
 	outStream << "Movable=" + std::to_string( GetMovable() ) + newLine;
 	outStream << "Priv=" + std::to_string( GetPriv() ) + newLine;
+	outStream << "LowerStatReq=" + std::to_string( GetLowerStatReq() ) + newLine;
+	outStream << "LeechStats=" + std::to_string( GetHealthLeech() ) + "," + std::to_string( GetStaminaLeech() ) + "," + std::to_string( GetManaLeech() ) + newLine;
 	outStream << "Value=" + std::to_string( GetBuyValue() ) + "," + std::to_string( GetSellValue() ) + "," + std::to_string( GetVendorPrice() ) + newLine;
 	outStream << "Restock=" + std::to_string( GetRestock() ) + newLine;
 	outStream << "AC=" + std::to_string( GetArmourClass() ) + newLine;
@@ -1843,6 +1877,13 @@ bool CItem::HandleLine( std::string &UTag, std::string &data )
 					bools = static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 ));
 					rValue = true;
 				}
+				else if( UTag == "BONUSSTATS" )
+				{
+				    SetHealthBonus( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 )));
+				    SetStaminaBonus( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0 )));
+				    SetManaBonus( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[2], "//" )), nullptr, 0 )));
+				    rValue = true;
+				}
 				break;
 			case 'C':
 				if( UTag == "CONT" )
@@ -1882,6 +1923,11 @@ bool CItem::HandleLine( std::string &UTag, std::string &data )
 					SetDye( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )) == 1 );
 					rValue = true;
 				}
+				else if( UTag == "DEFENSECHANCE" )
+				{
+					SetDefenseChance( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
+					rValue = true;
+				}
 				break;
 			case 'E':
 				if( UTag == "ENTRYMADEFROM" )
@@ -1928,6 +1974,11 @@ bool CItem::HandleLine( std::string &UTag, std::string &data )
 					SetWeatherDamage( HEAT, static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )) == 1 );
 					rValue = true;
 				}
+				else if( UTag == "HITCHANCE" )
+				{
+					SetHitChance( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
+					rValue = true;
+				}
 				break;
 			case 'L':
 				if( UTag == "LAYER" )
@@ -1945,6 +1996,18 @@ bool CItem::HandleLine( std::string &UTag, std::string &data )
 					SetWeatherDamage( LIGHTNING, static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )) == 1 );
 					rValue = true;
 				}
+				else if( UTag == "LOWERSTATREQ" )
+				{
+					SetLowerStatReq( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0 )));
+					rValue = true;
+				}
+				else if( UTag == "LEECHSTATS" )
+				{
+					SetHealthLeech( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 )));
+					SetStaminaLeech( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0 )));
+					SetManaLeech( static_cast( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[2], "//" )), nullptr, 0 )));
+					rValue = true;
+				}
 				break;
 			case 'M':
 				if( UTag == "MAXITEMS" )
diff --git a/source/cItem.h b/source/cItem.h
index 4b220999e..62c7cb13a 100644
--- a/source/cItem.h
+++ b/source/cItem.h
@@ -49,6 +49,7 @@ class CItem : public CBaseObject
 	SERIAL			creator;		// Store the serial of the player made this item
 	SI08			gridLoc;
 	SI16			artifactRarity;
+	SI16			lowerStatReq;
 	SI32			weightMax;		// Maximum weight a container can hold
 	SI32			baseWeight;		// Base weight of item. Applied when item is created for the first time, based on weight. Primarily used to determine base weight of containers
 	UI16			maxItems;		// Maximum amount of items a container can hold
@@ -114,6 +115,9 @@ class CItem : public CBaseObject
 	virtual SI16	GetArtifactRarity(void) const;
 	virtual void	SetArtifactRarity(SI16 newValue);
 
+	SI16			GetLowerStatReq( void ) const;
+	void			SetLowerStatReq( SI16 newValue );
+
 	auto			GetStealable() const -> UI08;
 	auto			SetStealable( UI08 newValue ) -> void;
 
diff --git a/source/cPlayerAction.cpp b/source/cPlayerAction.cpp
index 5d58ccbd5..cf4b217c2 100644
--- a/source/cPlayerAction.cpp
+++ b/source/cPlayerAction.cpp
@@ -698,15 +698,19 @@ bool CPIEquipItem::Handle( void )
 	if( k == ourChar )
 	{
 		bool canWear = false;
-		if( i->GetStrength() > k->GetStrength() )
+		const SI16 scaledStrength = ( i->GetStrength() * ( 100 - i->GetLowerStatReq() )) / 100;
+		const SI16 scaledDexterity = ( i->GetDexterity() * ( 100 - i->GetLowerStatReq() )) / 100;
+		const SI16 scaledIntelligence = ( i->GetIntelligence() * ( 100 - i->GetLowerStatReq() )) / 100;
+
+		if( scaledStrength > k->GetStrength() )
 		{
 			tSock->SysMessage( 1188 ); // You are not strong enough to use that. (NOTE: Should these messages use color 0x096a to stand out and replicate hard coded client message?)
 		}
-		else if( i->GetDexterity() > k->GetDexterity() )
+		else if( scaledDexterity > k->GetDexterity() )
 		{
 			tSock->SysMessage( 1189 ); // You are not agile enough to use that.
 		}
-		else if( i->GetIntelligence() > k->GetIntelligence() )
+		else if( scaledIntelligence > k->GetIntelligence() )
 		{
 			tSock->SysMessage( 1190 ); // You are not smart enough to use that.
 		}
diff --git a/source/cServerData.cpp b/source/cServerData.cpp
index 3f57745a8..baebacaf7 100644
--- a/source/cServerData.cpp
+++ b/source/cServerData.cpp
@@ -369,7 +369,8 @@ const std::map CServerData::uox3IniCaseValue
 	{"SECRETSHARDKEY"s, 346},
 	{"MOONGATEFACETS"s, 347},
 	{"AUTOUNEQUIPPEDCASTING"s, 348},
-	{"LOOTDECAYSWITHNPCCORPSE"s, 349}
+	{"LOOTDECAYSWITHNPCCORPSE"s, 349},
+	{"SWINGSPEEDINCREASECAP"s, 353}
 };
 constexpr auto MAX_TRACKINGTARGETS = 128;
 constexpr auto SKILLTOTALCAP = 7000;
@@ -723,6 +724,7 @@ auto CServerData::ResetDefaults() -> void
 	PetCombatTraining( true );
 	HirelingCombatTraining( true );
 	NpcCombatTraining( false );
+	SwingSpeedIncreaseCap( 60 );
 	WeaponDamageBonusType( 2 );
 
 	CheckPetControlDifficulty( true );
@@ -5196,7 +5198,7 @@ auto CServerData::SaveIni( const std::string &filename ) -> bool
 		ofsOutput << "SHOWITEMRESISTSTATS=" << ( ShowItemResistStats() ? 1 : 0 ) << '\n';
 		ofsOutput << "SHOWWEAPONDAMAGETYPES=" << ( ShowWeaponDamageTypes() ? 1 : 0 ) << '\n';
 		ofsOutput << "WEAPONDAMAGEBONUSTYPE=" << static_cast( WeaponDamageBonusType() ) << '\n';
-
+		ofsOutput << "WEAPONSWINGSPEEDINCREASECAP=" << SwingSpeedIncreaseCap() << '\n';
 		ofsOutput << "}" << '\n';
 
 		ofsOutput << '\n' << "[magic]" << '\n' << "{" << '\n';
@@ -5395,6 +5397,20 @@ auto CServerData::TrackingRedisplayTime( UI16 value ) -> void
 	trackingMsgRedisplayTimer = value;
 }
 
+//o------------------------------------------------------------------------------------------------o
+//|	Function	-	CServerData::SwingSpeedIncreaseCap()
+//o------------------------------------------------------------------------------------------------o
+//|	Purpose		-	Gets/Sets the for swing speed cap propertie
+//o------------------------------------------------------------------------------------------------o
+auto CServerData::SwingSpeedIncreaseCap() const -> SI16
+{
+	return swingSpeedIncreaseCap;
+}
+auto CServerData::SwingSpeedIncreaseCap( SI16 value ) -> void
+{
+	swingSpeedIncreaseCap = value;
+}
+
 
 //o------------------------------------------------------------------------------------------------o
 //|	Function	-	CServerData::ParseIni()
@@ -6565,6 +6581,9 @@ auto CServerData::HandleLine( const std::string& tag, const std::string& value )
 		case 349:	 // LOOTDECAYSWITHNPCCORPSE
 			NpcCorpseLootDecay( static_cast( std::stoul( value, nullptr, 0 )) != 0 );
 			break;
+		case 353:	// SWINGSPEEDINCREASE
+			SwingSpeedIncreaseCap( std::stof( value ));
+			break;
 		default:
 			rValue = false;
 			break;
diff --git a/source/cServerData.h b/source/cServerData.h
index d770645c8..3abf534e0 100644
--- a/source/cServerData.h
+++ b/source/cServerData.h
@@ -400,6 +400,7 @@ class CServerData
 	SI16		combatNpcBaseFleeAt;			//	% of HP where an NPC will flee, if it's not defined for them
 	SI16		combatNpcBaseReattackAt;		//	% of HP where an NPC will resume attacking
 	SI16		combatAttackStamina;			//	Amount of stamina lost when hitting an opponent
+	SI16		swingSpeedIncreaseCap;			//	The Cap for swing speed property
 
 	// Start & Location Settings
 	std::vector<__STARTLOCATIONDATA__>	startlocations;
@@ -953,6 +954,9 @@ class CServerData
 	auto		BODsFromCraftedItemsOnly( bool value ) -> void;
 	auto		BODsFromCraftedItemsOnly() const -> bool;
 
+	auto		SwingSpeedIncreaseCap( SI16 value ) -> void;
+	SI16		SwingSpeedIncreaseCap() const;
+
 	auto		MaxControlSlots( UI08 value ) -> void;
 	UI08		MaxControlSlots() const;
 
diff --git a/source/combat.cpp b/source/combat.cpp
index 46c3308e4..a0b5abc06 100644
--- a/source/combat.cpp
+++ b/source/combat.cpp
@@ -801,6 +801,10 @@ UI08 CHandleCombat::GetWeaponType( CItem *i )
 		case 0x48B3: //gargish axe - SA
 			return TWOHND_AXES;
 		// Default Maces
+		case 0x0DF2: // Wand
+		case 0x0DF3: // Wand
+		case 0x0DF4: // Wand
+		case 0x0DF5: // Wand
 		case 0x13E3: //smith's hammer
 		case 0x13E4: //smith's hammer
 		case 0x13B3: //club
@@ -2645,7 +2649,7 @@ SI16 CHandleCombat::ApplyDefenseModifiers( WeatherType damageType, CChar *mChar,
 
 	if( getDef > 0 )
 	{
-		damage -= static_cast(( static_cast( getDef ) * static_cast( attSkill )) / 750 );
+		damage -= static_cast( getDef );
 	}
 
 	return static_cast( std::round( damage ));
@@ -2877,18 +2881,24 @@ bool CHandleCombat::HandleCombat( CSocket *mSock, CChar& mChar, CChar *ourTarg )
 				R32 attHitChanceBonus = 0;
 				R32 defDefenseChanceBonus = 0;
 				R32 maxAttHitChanceBonus = 45;
+
 				if( cwmWorldState->ServerData()->ExpansionCombatHitChance() >= ER_SA && mChar.GetBodyType() == BT_GARGOYLE )
 				{
 					// If attacker is a Gargoyle player, and ExpansionCombatHitChance is ER_SA or higher, use 50 as hitchance bonus cap instead of 45
 					maxAttHitChanceBonus = 50;
 				}
 				
-				// Fetch bonuses to hitChance/defenseChance from AoS item properties, when implemented
-				//attHitChanceBonus = GetAttackerHitChanceBonus();
-				//defDefenseChanceBonus = GetDefenderDefenseChanceBonus();
+				// Fetch bonuses to hitChance/defenseChance from AoS item properties or characters
+				attHitChanceBonus = mChar.GetHitChance();
+				defDefenseChanceBonus = mChar.GetDefenseChance();
+
+				// Clamp to ensure valid bonus ranges (e.g., no multiplier below 1)
+				R32 effectiveAttHitChanceBonus = std::max(-99.0f, std::min( attHitChanceBonus, maxAttHitChanceBonus )); // Cap at -99 and maxAttHitChanceBonus
+				R32 effectiveDefDefenseChanceBonus = std::max(-99.0f, std::min( defDefenseChanceBonus, 45.0f )); // Cap at -99 and 45
 
-				R32 attackerHitChance = ( static_cast( attackSkill / 10 ) + 20 ) * ( 100 + std::min( attHitChanceBonus, static_cast( maxAttHitChanceBonus )));
-				R32 defenderDefenseChance = ( static_cast( defendSkill / 10 ) + 20 ) * ( 100 + std::min( defDefenseChanceBonus, static_cast( 45 )));
+				// Calculate the attacker's hit chance and defender's defense chance
+				R32 attackerHitChance = ( static_cast( attackSkill / 10 ) + 20 ) * ( 100 + effectiveAttHitChanceBonus );
+				R32 defenderDefenseChance = ( static_cast( defendSkill / 10 ) + 20 ) * ( 100 + effectiveDefDefenseChanceBonus );
 				hitChance = ( attackerHitChance / ( defenderDefenseChance * 2 )) * 100;
 
 				// Always leave at least 2% chance to hit
@@ -3449,7 +3459,8 @@ R32 CHandleCombat::GetCombatTimeout( CChar *mChar )
 	}
 
 	SI32 getOffset	= 0;
-	SI32 baseValue	= 15000;
+	SI32 baseValue = ( cwmWorldState->ServerData()->ExpansionCoreShardEra() <= ER_LBR ) ? 15000 :
+					(( cwmWorldState->ServerData()->ExpansionCoreShardEra() < ER_ML ) ? 80000 : 40000 );
 
 	CChar *ourTarg = mChar->GetTarg();
 
@@ -3474,18 +3485,49 @@ R32 CHandleCombat::GetCombatTimeout( CChar *mChar )
 		}
 	}
 
+	SI32 speedBonus = mChar->GetSwingSpeedIncrease();
+	
+	// Swing Speed Increase Cap per AOS
+	if ( speedBonus > cwmWorldState->ServerData()->SwingSpeedIncreaseCap() )
+	{
+		speedBonus = cwmWorldState->ServerData()->SwingSpeedIncreaseCap();
+	}
+
 	//Allow faster strikes on fleeing targets
 	if( ValidateObject( ourTarg ))
 	{
 		if( ourTarg->GetNpcWander() == WT_FLEE || ourTarg->GetNpcWander() == WT_SCARED )
 		{
-			baseValue = 10000;
+			baseValue = ( cwmWorldState->ServerData()->ExpansionCoreShardEra() <= ER_LBR ) ? 10000 : 
+						(( cwmWorldState->ServerData()->ExpansionCoreShardEra() < ER_ML ) ? 53333 : 26680 );
 		}
 	}
 
 	R32 globalAttackSpeed = cwmWorldState->ServerData()->GlobalAttackSpeed(); //Defaults to 1.0
 
-	getDelay = ( baseValue / ( getDelay * getOffset )) / globalAttackSpeed;
+	R32 speedFactor = 1 + speedBonus / 10.0f;
+
+	// Prevent zero or negative multipliers
+	if( speedFactor < 0.1f )
+		speedFactor = 0.1f;
+
+
+	if( cwmWorldState->ServerData()->ExpansionCoreShardEra() <= ER_LBR )
+	{
+		// Weapon swing delay in LBR and earlier
+		getDelay = baseValue / ( getDelay * getOffset * speedFactor ) / globalAttackSpeed;
+	}
+	else if( cwmWorldState->ServerData()->ExpansionCoreShardEra() < ER_ML )
+	{
+		// Weapon swing delay in SE or earlier
+		getDelay = ( baseValue / ( getDelay * getOffset * speedFactor ) / 4 - 0.5 ) / globalAttackSpeed;
+	}
+	else
+	{
+		// Weapon swing delay in ML or later
+		getDelay = ( baseValue / ( getDelay * getOffset * speedFactor ) * 0.5 ) / globalAttackSpeed;
+	}
+
 	return getDelay;
 }
 
diff --git a/source/gumps.cpp b/source/gumps.cpp
index c655634d2..3b2d56cb0 100644
--- a/source/gumps.cpp
+++ b/source/gumps.cpp
@@ -669,10 +669,21 @@ void BuildAddMenuGump( CSocket *s, UI16 m )
 			if( tag.data()[0] != '<' && tag.data()[0] != ' ' )	// it actually has a picture, well bugger me! :>
 			{
 				// Draw a frame for the item to make it stand out a touch more.
-				toSend.addCommand( oldstrutil::format( "checkertrans %u %u %u %u", xOffset + 7, yOffset + 9, 110, 110 ));
-				toSend.addCommand( oldstrutil::format( "buttontileart %u %u 0x0a9f 0x0aa1 %u %u %u %u %u %u %u", xOffset, yOffset, 1, 0, buttonnum, std::stoi( tag, nullptr, 0 ), 0, 25, 25 ));
-				toSend.addCommand( oldstrutil::format( "tooltip 1042971 @%s@", data.c_str() ));
-				toSend.addCommand( oldstrutil::format( "croppedtext %u %u %u %u %u %u", xOffset + 15, yOffset + 85, 100, 20, 50, linenum++ ));
+				if( s->ClientVerShort() <= CVS_70160 )
+				{
+					// Fallback for older clients that don't support buttontileart
+					toSend.addCommand( oldstrutil::format("resizepic %u %u %u %u %u", xOffset, yOffset, 0x53, 65, 100 ));
+					toSend.addCommand( oldstrutil::format("checkertrans %u %u %u %u", xOffset + 7, yOffset + 9, 52, 82 ));
+					toSend.addCommand( oldstrutil::format("tilepic %u %u %i", xOffset + 5, yOffset + 10, std::stoi(tag, nullptr, 0) ));
+					toSend.addCommand( oldstrutil::format("croppedtext %u %u %u %u %u %u", xOffset, yOffset + 65, 65, 20, 55, linenum++ ));
+				}
+				else
+				{
+					toSend.addCommand( oldstrutil::format( "checkertrans %u %u %u %u", xOffset + 7, yOffset + 9, 110, 110 ));
+					toSend.addCommand( oldstrutil::format( "buttontileart %u %u 0x0a9f 0x0aa1 %u %u %u %u %u %u %u", xOffset, yOffset, 1, 0, buttonnum, std::stoi( tag, nullptr, 0 ), 0, 25, 25 ));
+					toSend.addCommand( oldstrutil::format( "tooltip 1042971 @%s@", data.c_str() ));
+					toSend.addCommand( oldstrutil::format( "croppedtext %u %u %u %u %u %u", xOffset + 15, yOffset + 85, 100, 20, 50, linenum++ ));
+				}
 				toSend.addText( data );
 				xOffset += XOFFSET;
 				if( xOffset > 640 )
diff --git a/source/items.cpp b/source/items.cpp
index a95e80f70..5951c4a36 100644
--- a/source/items.cpp
+++ b/source/items.cpp
@@ -139,7 +139,13 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
 				}
 				break;
 			case DFNTAG_AC:				applyTo->SetArmourClass( static_cast( ndata ));	break;
+			case DFNTAG_HEALTHLEECH:	applyTo->SetHealthLeech( static_cast( ndata ));		break;
+			case DFNTAG_STAMINALEECH:	applyTo->SetStaminaLeech( static_cast( ndata ));		break;
+			case DFNTAG_MANALEECH:		applyTo->SetManaLeech( static_cast( ndata ));		break;
 			case DFNTAG_BASERANGE:		applyTo->SetBaseRange( static_cast( ndata ));		break;
+			case DFNTAG_HEALTHBONUS:		applyTo->SetHealthBonus( static_cast( ndata ));		break;
+			case DFNTAG_STAMINABONUS:		applyTo->SetStaminaBonus( static_cast( ndata ));		break;
+			case DFNTAG_MANABONUS:		applyTo->SetManaBonus( static_cast( ndata ));		break;
 			case DFNTAG_CREATOR:		applyTo->SetCreator( ndata );							break;
 			case DFNTAG_COLOUR:			applyTo->SetColour( static_cast( ndata ));		break;
 			case DFNTAG_COLOURLIST:		applyTo->SetColour( AddRandomColor( cdata ));			break;
@@ -230,6 +236,7 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
 			case DFNTAG_DISPELLABLE:	applyTo->SetDispellable( true );			break;
 			case DFNTAG_DISABLED:		applyTo->SetDisabled( ndata != 0 );			break;
 			case DFNTAG_DOORFLAG:		break;
+			case DFNTAG_DEFENSECHANCE:	applyTo->SetDefenseChance( static_cast( ndata ));		break;
 			case DFNTAG_GOOD:			applyTo->SetGood( static_cast( ndata ));			break;
 			case DFNTAG_GLOW:			applyTo->SetGlow( ndata );								break;
 			case DFNTAG_GLOWBC:			applyTo->SetGlowColour( static_cast( ndata ));	break;
@@ -339,6 +346,7 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
 				}
 				break;
 			case DFNTAG_HIDAMAGE:		applyTo->SetHiDamage( static_cast( ndata ));		break;
+			case DFNTAG_HITCHANCE:	applyTo->SetHitChance( static_cast( ndata ));		break;
 			case DFNTAG_HEAT:			applyTo->SetWeatherDamage( HEAT, ndata != 0 );			break;
 			case DFNTAG_ID:				// applyTo->SetId( static_cast( ndata ));				break;
 				if( ssecs.size() == 1 )
@@ -357,6 +365,7 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
 			case DFNTAG_LAYER:			applyTo->SetLayer( static_cast( ndata ));	break;
 			case DFNTAG_LIGHT:			applyTo->SetWeatherDamage( LIGHT, ndata != 0 );			break;
 			case DFNTAG_LIGHTNING:		applyTo->SetWeatherDamage( LIGHTNING, ndata != 0 );		break;
+			case DFNTAG_LOWERSTATREQ:	applyTo->SetLowerStatReq( static_cast( ndata ));	break;
 			case DFNTAG_MAXHP:			applyTo->SetMaxHP( static_cast( ndata ));			break;
 			case DFNTAG_MAXITEMS:		applyTo->SetMaxItems( static_cast( ndata ));		break;
 			case DFNTAG_MAXRANGE:		applyTo->SetMaxRange( static_cast( ndata ));		break;
@@ -549,6 +558,7 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
 			case DFNTAG_RAIN:			applyTo->SetWeatherDamage( RAIN, ndata != 0 );			break;
 			case DFNTAG_SECTIONID:		applyTo->SetSectionId( cdata );							break;
 			case DFNTAG_SK_MADE:		applyTo->SetMadeWith( static_cast( ndata ));		break;
+			case DFNTAG_SWINGSPEEDINCREASE:	applyTo->SetSwingSpeedIncrease( static_cast( ndata ));		break;
 			case DFNTAG_SPD:			applyTo->SetSpeed( static_cast( ndata ));			break;
 			case DFNTAG_STRENGTH:		applyTo->SetStrength( static_cast( ndata ));		break;
 			case DFNTAG_STRADD:			applyTo->SetStrength2( static_cast( ndata ));		break;
@@ -1821,7 +1831,8 @@ void cItem::CheckEquipment( CChar *p )
 		{
 			if( ValidateObject( i ))
 			{
-				if( i->GetStrength() > StrengthToCompare )//if strength required > character's strength
+				const SI16 scaledStrength = ( i->GetStrength() * ( 100 - i->GetLowerStatReq() )) / 100;
+				if( scaledStrength > StrengthToCompare )//if strength required > character's strength
 				{
 					itemsToUnequip.push_back( i );
 				}
diff --git a/source/magic.cpp b/source/magic.cpp
index d04ab3f97..7d49c6461 100644
--- a/source/magic.cpp
+++ b/source/magic.cpp
@@ -4499,6 +4499,11 @@ void CMagic::CastSpell( CSocket *s, CChar *caster )
 		caster->StopSpell();
 		return;
 	}
+	else if( caster->IsNpc() )
+	{
+		// Run a skillcheck for NPC to give them a chance to gain skill - if that option is enabled in ini
+		Skills->CheckSkill( caster, MAGERY, lowSkill, highSkill );
+	}
 
 	if( curSpell > 63 && static_cast(curSpell) <= spellCount && spellCount <= 70 )
 	{
diff --git a/source/npcs.cpp b/source/npcs.cpp
index 36005b420..41536fea6 100644
--- a/source/npcs.cpp
+++ b/source/npcs.cpp
@@ -1541,6 +1541,8 @@ auto CCharStuff::ApplyNpcSection( CChar *applyTo, CScriptSection *NpcCreation, s
 			case DFNTAG_NAMELIST:			SetRandomName( applyTo, cdata );		break;
 			case DFNTAG_NECROMANCY:			skillToSet = NECROMANCY;				break;
 			case DFNTAG_NINJITSU:			skillToSet = NINJITSU;					break;
+			case DFNTAG_HITCHANCE:			applyTo->SetHitChance( static_cast( ndata ));		break;
+			case DFNTAG_DEFENSECHANCE:		applyTo->SetDefenseChance( static_cast( ndata ));		break;
 			case DFNTAG_NPCWANDER:
 				if( !isGate )
 				{
@@ -1583,6 +1585,7 @@ auto CCharStuff::ApplyNpcSection( CChar *applyTo, CScriptSection *NpcCreation, s
 			case DFNTAG_PEACEMAKING:		skillToSet = PEACEMAKING;				break;
 			case DFNTAG_PROVOCATION:		skillToSet = PROVOCATION;				break;
 			case DFNTAG_POISONING:			skillToSet = POISONING;					break;
+			case DFNTAG_SWINGSPEEDINCREASE:	applyTo->SetSwingSpeedIncrease( static_cast( ndata ));		break;
 			case DFNTAG_RESISTFIRE:
 				if( ndata >= 0 )
 				{
diff --git a/source/ssection.cpp b/source/ssection.cpp
index fae41e7a1..6d89e606f 100644
--- a/source/ssection.cpp
+++ b/source/ssection.cpp
@@ -67,6 +67,7 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
 	DFN_NUMERIC,		//	DFNTAG_DOORFLAG,
 	DFN_NUMERIC,		//	DFNTAG_DYE,
 	DFN_NUMERIC,		//	DFNTAG_DYEBEARD,
+	DFN_NUMERIC,		//	DFNTAG_DEFENSECHANCE,
 	DFN_NUMERIC,		//	DFNTAG_DYEHAIR,
 	DFN_STRING,			//	DFNTAG_ELEMENTRESIST,
 	DFN_STRING,			//	DFNTAG_ERBONUS,
@@ -112,10 +113,12 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
 	DFN_NUMERIC,		//	DFNTAG_HEAT,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_HERDING,
 	DFN_NUMERIC,		//	DFNTAG_HIDAMAGE,
+	DFN_NUMERIC,		//	DFNTAG_HITCHANCE,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_HIDING,
 	DFN_NODATA,			//	DFNTAG_HIRELING,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_HP,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_HPMAX,
+	DFN_NUMERIC,		//  DFNTAG_HEALTHLEECH,
 	DFN_UPPERSTRING,	//	DFNTAG_ID,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_IMBUING,
 	DFN_NUMERIC,		//	DFNTAG_INTADD,
@@ -131,6 +134,7 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
 	DFN_NUMERIC,		//	DFNTAG_LAYER,
 	DFN_NUMERIC,		//	DFNTAG_LIGHT,
 	DFN_NUMERIC,		//	DFNTAG_LIGHTNING,
+	DFN_NUMERIC,		//	DFNTAG_LOWERSTATREQ,
 	DFN_NUMERIC,		//	DFNTAG_LOCKPICKING,
 	DFN_NUMERIC,		//	DFNTAG_LODAMAGE,
 	DFN_UPPERSTRING,	//	DFNTAG_LOOT,
@@ -141,6 +145,7 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
 	DFN_DOUBLENUMERIC,	//	DFNTAG_MAGICRESISTANCE,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_MANA,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_MANAMAX,
+	DFN_NUMERIC,		//  DFNTAG_MANALEECH,
 	DFN_NUMERIC,		//	DFNTAG_MAXHP,
 	DFN_NUMERIC,		//	DFNTAG_MAXITEMS,
 	DFN_NUMERIC,		//	DFNTAG_MAXLOYALTY,
@@ -216,6 +221,9 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
 	DFN_STRING,			//	DFNTAG_SPAWNOBJ,
 	DFN_STRING,			//	DFNTAG_SPAWNOBJLIST,
 	DFN_NUMERIC,		//	DFNTAG_SPD,
+	DFN_NUMERIC,		//  DFNTAG_HEALTHBONUS,
+	DFN_NUMERIC,		//  DFNTAG_STAMINABONUS,
+	DFN_NUMERIC,		//  DFNTAG_MANABONUS,
 	DFN_STRING,			//	DFNTAG_SPELLS,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_SPELLWEAVING,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_SPIRITSPEAK,
@@ -223,12 +231,14 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
 	DFN_NUMERIC,		//	DFNTAG_SPLITCHANCE,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_STAMINA,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_STAMINAMAX,
+	DFN_NUMERIC,		//  DFNTAG_STAMINALEECH,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_STRENGTH,
 	DFN_NUMERIC,		//	DFNTAG_STRADD,
 	DFN_NUMERIC,		//	DFNTAG_STEALABLE,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_STEALING,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_STEALTH,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_SWORDSMANSHIP,
+	DFN_NUMERIC,		//  DFNTAG_SWINGSPEEDINCREASE,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_TACTICS,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_TAILORING,
 	DFN_DOUBLENUMERIC,	//	DFNTAG_TAMING,
@@ -285,6 +295,9 @@ const std::map strToDFNTag
 	{"BLACKSMITHING"s,		DFNTAG_BLACKSMITHING},
 	{"BOWCRAFT"s,			DFNTAG_BOWCRAFT},
 	{"BUSHIDO"s,			DFNTAG_BUSHIDO},
+	{"HEALTHBONUS"s,		DFNTAG_HEALTHBONUS},
+	{"STAMINABONUS"s,		DFNTAG_STAMINABONUS},
+	{"MANABONUS"s,			DFNTAG_MANABONUS},
 	{"CAMPING"s,			DFNTAG_CAMPING},
 	{"CARPENTRY"s,			DFNTAG_CARPENTRY},
 	{"CARTOGRAPHY"s,		DFNTAG_CARTOGRAPHY},
@@ -323,6 +336,7 @@ const std::map strToDFNTag
 	{"DYEABLE"s,			DFNTAG_DYE},
 	{"DYEHAIR"s,			DFNTAG_DYEHAIR},
 	{"DYEBEARD"s,			DFNTAG_DYEBEARD},
+	{"DEFENSECHANCE"s,		DFNTAG_DEFENSECHANCE},
 	{"ELEMENTRESIST"s,		DFNTAG_ELEMENTRESIST},
 	{"ERBONUS"s,			DFNTAG_ERBONUS},	
 	{"EMOTECOLOR"s,			DFNTAG_EMOTECOLOUR},
@@ -368,10 +382,12 @@ const std::map strToDFNTag
 	{"HEAT"s,				DFNTAG_HEAT},
 	{"HERDING"s,			DFNTAG_HERDING},
 	{"HIDAMAGE"s,			DFNTAG_HIDAMAGE},
+	{"HITCHANCE"s,			DFNTAG_HITCHANCE},
 	{"HIDING"s,				DFNTAG_HIDING},
 	{"HIRELING"s,			DFNTAG_HIRELING},
 	{"HP"s,					DFNTAG_HP},
 	{"HPMAX"s,				DFNTAG_HPMAX},
+	{"HEALTHLEECH"s,		DFNTAG_HEALTHLEECH},
 	{"ID"s,					DFNTAG_ID},
 	{"IMBUING"s,			DFNTAG_IMBUING},
 	{"IN"s,					DFNTAG_INTELLIGENCE},
@@ -391,6 +407,7 @@ const std::map strToDFNTag
 	{"LAYER"s,				DFNTAG_LAYER},
 	{"LIGHT"s,				DFNTAG_LIGHT},
 	{"LIGHTNING"s,			DFNTAG_LIGHTNING},
+	{"LOWERSTATREQ"s,		DFNTAG_LOWERSTATREQ},
 	{"LOCKPICKING"s,		DFNTAG_LOCKPICKING},
 	{"LODAMAGE"s,			DFNTAG_LODAMAGE},
 	{"LOOT"s,				DFNTAG_LOOT},
@@ -401,6 +418,7 @@ const std::map strToDFNTag
 	{"MAGICRESISTANCE"s,	DFNTAG_MAGICRESISTANCE},
 	{"MANA"s,				DFNTAG_MANA},
 	{"MANAMAX"s,			DFNTAG_MANAMAX},
+	{"MANALEECH"s,			DFNTAG_MANALEECH},
 	{"MAXHP"s,				DFNTAG_MAXHP},
 	{"MAXITEMS"s,			DFNTAG_MAXITEMS},
 	{"MAXLOYALTY"s,			DFNTAG_MAXLOYALTY},
@@ -477,6 +495,7 @@ const std::map strToDFNTag
 	{"SPAWNOBJLIST"s,		DFNTAG_SPAWNOBJLIST},
 	{"SPD"s,				DFNTAG_SPD},
 	{"SPEED"s,				DFNTAG_SPD},
+	{"SWINGSPEEDINCREASE"s,	DFNTAG_SWINGSPEEDINCREASE},
 	{"SPELLS"s,				DFNTAG_SPELLS},
 	{"SPELLWEAVING"s,		DFNTAG_SPELLWEAVING},
 	{"SPIRITSPEAK"s,		DFNTAG_SPIRITSPEAK},
@@ -485,6 +504,7 @@ const std::map strToDFNTag
 	{"ST"s,					DFNTAG_STRENGTH},
 	{"STAMINA"s,			DFNTAG_STAMINA},
 	{"STAMINAMAX"s,			DFNTAG_STAMINAMAX},
+	{"STAMINALEECH"s,		DFNTAG_STAMINALEECH},
 	{"STR"s,				DFNTAG_STRENGTH},
 	{"STRENGTH"s,			DFNTAG_STRENGTH},
 	{"ST2"s,				DFNTAG_STRADD},
diff --git a/source/ssection.h b/source/ssection.h
index c652b9d3b..0c5bcfbbc 100644
--- a/source/ssection.h
+++ b/source/ssection.h
@@ -72,6 +72,7 @@ enum DFNTAGS
 	DFNTAG_DISPELLABLE,
 	DFNTAG_DISABLED,
 	DFNTAG_DOORFLAG,
+	DFNTAG_DEFENSECHANCE,
 	DFNTAG_DYE,
 	DFNTAG_DYEBEARD,
 	DFNTAG_DYEHAIR,
@@ -119,10 +120,12 @@ enum DFNTAGS
 	DFNTAG_HEAT,
 	DFNTAG_HERDING,
 	DFNTAG_HIDAMAGE,
+	DFNTAG_HITCHANCE,
 	DFNTAG_HIDING,
 	DFNTAG_HIRELING,
 	DFNTAG_HP,
 	DFNTAG_HPMAX,
+	DFNTAG_HEALTHLEECH,
 	DFNTAG_ID,
 	DFNTAG_IMBUING,
 	DFNTAG_INTADD,
@@ -138,6 +141,7 @@ enum DFNTAGS
 	DFNTAG_LAYER,
 	DFNTAG_LIGHT,
 	DFNTAG_LIGHTNING,
+	DFNTAG_LOWERSTATREQ,
 	DFNTAG_LOCKPICKING,
 	DFNTAG_LODAMAGE,
 	DFNTAG_LOOT,
@@ -148,6 +152,7 @@ enum DFNTAGS
 	DFNTAG_MAGICRESISTANCE,
 	DFNTAG_MANA,
 	DFNTAG_MANAMAX,
+	DFNTAG_MANALEECH,
 	DFNTAG_MAXHP,
 	DFNTAG_MAXITEMS,
 	DFNTAG_MAXLOYALTY,
@@ -222,7 +227,11 @@ enum DFNTAGS
 	DFNTAG_SPATTACK,
 	DFNTAG_SPAWNOBJ,
 	DFNTAG_SPAWNOBJLIST,
+	DFNTAG_SWINGSPEEDINCREASE,
 	DFNTAG_SPD,
+	DFNTAG_HEALTHBONUS,
+	DFNTAG_STAMINABONUS,
+	DFNTAG_MANABONUS,
 	DFNTAG_SPELLS,
 	DFNTAG_SPELLWEAVING,
 	DFNTAG_SPIRITSPEAK,
@@ -230,6 +239,7 @@ enum DFNTAGS
 	DFNTAG_SPLITCHANCE,
 	DFNTAG_STAMINA,
 	DFNTAG_STAMINAMAX,
+	DFNTAG_STAMINALEECH,
 	DFNTAG_STRENGTH,
 	DFNTAG_STRADD,
 	DFNTAG_STEALABLE,