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/dfndata/skills/skills.dfn b/data/dfndata/skills/skills.dfn index b28b32a15..28d129929 100644 --- a/data/dfndata/skills/skills.dfn +++ b/data/dfndata/skills/skills.dfn @@ -1206,10 +1206,10 @@ SKILLPOINT=990,5,0 SKILLPOINT=1000,0,0 } -// Imbuing +// Mysticism [SKILL 55] { -NAME=IMBUING +NAME=MYSTICISM STR=0 DEX=0 INT=0 @@ -1227,10 +1227,10 @@ SKILLPOINT=990,5,0 SKILLPOINT=1000,0,0 } -// Mysticism +// Imbuing [SKILL 56] { -NAME=MYSTICISM +NAME=IMBUING STR=0 DEX=0 INT=0 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 8f3e3e691..fbd73321b 100644 --- a/data/js/commands/targeting/get.js +++ b/data/js/commands/targeting/get.js @@ -297,30 +297,27 @@ 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 "ARMORCLASS": case "ARMOURCLASS": @@ -601,8 +598,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/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/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/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/docs/jsdocs.html b/docs/jsdocs.html index 8dbe347a8..8665ea36c 100644 --- a/docs/jsdocs.html +++ b/docs/jsdocs.html @@ -4018,7 +4018,7 @@

January 9th, 2022

Prototype

-

function onSpellTargetSelect( pCaster, myTarget, spellID )

+

function onSpellTargetSelect( myTarget, pCaster, spellID )

Purpose

@@ -4043,7 +4043,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 8a093b16d..5de021acb 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. It’s 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 it’s either the 0x78 or 0x20 messages that reset it, though I
-//|						haven’t 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 # shouldn’t 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 doesn’t 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 NPC’s.
+//|						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 0’s)
+//|						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/player’s 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
@@ -7680,6 +7702,41 @@ void CPToolTip::CopyItemData( CItem& cItem, size_t &totalStringLen, bool addAmou
 				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 );
+			}
+
 			if( cItem.GetStrength() > 1 )
 			{
 				tempEntry.stringNum = 1061170; // strength requirement ~1_val~
@@ -8051,9 +8108,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
@@ -8133,7 +8190,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
@@ -8150,7 +8207,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 75e646e18..72ade98d6 100644
--- a/source/Changelog.txt
+++ b/source/Changelog.txt
@@ -1,3 +1,24 @@
+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)
+
+12/08/2024 - Dragon Slayer
+	Fixed an issue where Imbuing and Mysticism skills were listed in wrong order in skills.dfn and enums.h
+
+4/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
@@ -8,14 +29,43 @@
         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
+
+6/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.
 
+5/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
 	Fixed AutoUnequipAttempt function in clumsy.js, createfood.js level1target.js, will no longer fail on casting and return to hardcode.
diff --git a/source/UOXJSPropertyEnums.h b/source/UOXJSPropertyEnums.h
index 98fe59d47..70e1764db 100644
--- a/source/UOXJSPropertyEnums.h
+++ b/source/UOXJSPropertyEnums.h
@@ -340,6 +340,8 @@ enum CC_Properties
 	CCP_SPATTACK,
 	CCP_SPDELAY,
 	CCP_SWINGSPEEDINCREASE,
+	CCP_HITCHANCE,
+	CCP_DEFENSECHANCE,
 	CCP_AITYPE,
 	CCP_SPLIT,
 	CCP_SPLITCHANCE,
@@ -462,6 +464,9 @@ enum CI_Properties
 	CIP_DAMAGEPOISON,
 	CIP_DAMAGERAIN,
 	CIP_DAMAGESNOW,
+	CIP_HITCHANCE,
+	CIP_DEFENSECHANCE,
+
 	CIP_ARTIFACTRARITY,
 	CIP_NAME2,
 	CIP_ISITEM,
@@ -497,7 +502,9 @@ enum CI_Properties
 	CIP_SECTIONALIST,
 	CIP_MININTERVAL,
 	CIP_MAXINTERVAL,
-
+	CIP_HEALTHLEECH,
+	CIP_STAMINALEECH,
+	CIP_MANALEECH,
 	CIP_ISNEWBIE,
 	CIP_ISDISPELLABLE,
 	CIP_MADEWITH,
@@ -519,6 +526,9 @@ enum CI_Properties
 	CIP_CARVESECTION,
 	CIP_SPEED,
 	CIP_SWINGSPEEDINCREASE,
+	CIP_HEALTHBONUS,
+	CIP_STAMINABONUS,
+	CIP_MANABONUS,
 	CIP_MULTI,
 	CIP_AMMOID,
 	CIP_AMMOHUE,
diff --git a/source/UOXJSPropertyFuncs.cpp b/source/UOXJSPropertyFuncs.cpp
index c8253ec42..d5be8d5c4 100644
--- a/source/UOXJSPropertyFuncs.cpp
+++ b/source/UOXJSPropertyFuncs.cpp
@@ -677,7 +677,16 @@ JSBool CItemProps_getProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp
 			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_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 );
@@ -1324,7 +1333,16 @@ JSBool CItemProps_setProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp
 			case CIP_DAMAGESNOW:	gPriv->SetWeatherDamage( SNOW, encaps.toBool() );			break;
 			case CIP_SPEED:			gPriv->SetSpeed( 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;
@@ -1999,6 +2017,8 @@ JSBool CCharacterProps_getProperty( JSContext *cx, JSObject *obj, jsval id, jsva
 			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;
@@ -2505,6 +2525,8 @@ JSBool CCharacterProps_setProperty( JSContext *cx, JSObject *obj, jsval id, jsva
 			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 b0cf1a06a..84bc3b232 100644
--- a/source/UOXJSPropertySpecs.h
+++ b/source/UOXJSPropertySpecs.h
@@ -358,6 +358,8 @@ inline JSPropertySpec CCharacterProps[] =
 	{ "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 },
@@ -537,7 +539,19 @@ inline JSPropertySpec CItemProps[] =
 	{ "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 },
@@ -678,8 +692,8 @@ inline JSPropertySpec CSkillsProps[] =
 	{ "bushido",			BUSHIDO,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "ninjitsu",			NINJITSU,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "spellweaving",		SPELLWEAVING,		JSPROP_ENUMANDPERM, nullptr, nullptr },
-	{ "imbuing",			IMBUING,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "mysticism",			MYSTICISM,			JSPROP_ENUMANDPERM, nullptr, nullptr },
+	{ "imbuing",			IMBUING,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "throwing",			THROWING,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ "allskills",			ALLSKILLS,			JSPROP_ENUMANDPERM, nullptr, nullptr },
 	{ nullptr,				static_cast(0),			static_cast(0), nullptr, nullptr }
diff --git a/source/cBaseObject.cpp b/source/cBaseObject.cpp
index 1a05618ee..027ba2bc6 100644
--- a/source/cBaseObject.cpp
+++ b/source/cBaseObject.cpp
@@ -95,6 +95,16 @@ 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
@@ -112,7 +122,8 @@ mana( DEFBASE_MANA ), stamina( DEFBASE_STAMINA ), scriptTrig( DEFBASE_SCPTRIG ),
 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 ),
-swingSpeedIncrease( DEFBASE_SWINGSPEEDINCREASE )
+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;
@@ -1039,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()
@@ -1655,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
@@ -1695,6 +1866,55 @@ 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
diff --git a/source/cBaseObject.h b/source/cBaseObject.h
index e2f8a5377..527f6c3b7 100644
--- a/source/cBaseObject.h
+++ b/source/cBaseObject.h
@@ -69,6 +69,8 @@ class CBaseObject
 	SI16				dexterity;
 	SI16				intelligence;
 	SI16				hitpoints;
+	SI16				hitChance;
+	SI16				defenseChance;
 	VisibleTypes		visible;
 	SI16				hiDamage;
 	SI16				loDamage;
@@ -76,10 +78,16 @@ class CBaseObject
 	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;
@@ -229,6 +237,12 @@ class CBaseObject
 
 	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;
 
@@ -262,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 182409af1..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() );
@@ -2922,6 +2924,17 @@ bool CChar::WearItem( CItem *toWear )
 
 			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() )
@@ -2984,6 +2997,17 @@ bool CChar::TakeOffItem( ItemLayers Layer )
 
 		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() )
@@ -3139,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;
@@ -3792,7 +3818,7 @@ UI16 CChar::GetMaxHP( void )
 		oldRace			= GetRace();
 
 	}
-	return maxHP;
+	return maxHP + GetHealthBonus();
 }
 void CChar::SetMaxHP( UI16 newmaxhp, UI16 newoldstr, RACEID newoldrace )
 {
@@ -3835,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() );
 
@@ -3852,7 +3878,7 @@ SI16 CChar::GetMaxMana( void )
 		oldRace			= GetRace();
 
 	}
-	return maxMana;
+	return maxMana + GetManaBonus();
 }
 void CChar::SetMaxMana( SI16 newmaxmana, UI16 newoldint, RACEID newoldrace )
 {
@@ -3894,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() );
@@ -3912,7 +3938,7 @@ SI16 CChar::GetMaxStam( void )
 		oldRace			= GetRace();
 
 	}
-	return maxStam;
+	return maxStam + GetStaminaBonus();
 }
 void CChar::SetMaxStam( SI16 newmaxstam, UI16 newolddex, RACEID newoldrace )
 {
@@ -4377,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" )
@@ -4465,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;
@@ -5598,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..d692eaede 100644
--- a/source/cItem.cpp
+++ b/source/cItem.cpp
@@ -1654,13 +1654,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,6 +1678,9 @@ 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() );
@@ -1753,10 +1763,14 @@ 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 << "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 +1857,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 +1903,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 +1954,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 +1976,13 @@ 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 == "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/combat.cpp b/source/combat.cpp
index 9d0627316..6712aa0a8 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
diff --git a/source/enums.h b/source/enums.h
index 1383bbd10..16db01f95 100644
--- a/source/enums.h
+++ b/source/enums.h
@@ -373,8 +373,8 @@ enum Skills
     BUSHIDO,
     NINJITSU,
     SPELLWEAVING,
-    IMBUING,
     MYSTICISM,
+    IMBUING,
     THROWING,
 
     ALLSKILLS, // #skills+1
diff --git a/source/items.cpp b/source/items.cpp
index e82e13288..91148e3e1 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 )
diff --git a/source/npcs.cpp b/source/npcs.cpp
index 0f8400f7d..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 )
 				{
diff --git a/source/ssection.cpp b/source/ssection.cpp
index 74e19b2ae..a8b6926ce 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,
@@ -141,6 +144,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 +220,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,6 +230,7 @@ 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,
@@ -286,6 +294,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},
@@ -324,6 +335,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},
@@ -369,10 +381,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},
@@ -402,6 +416,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},
@@ -487,6 +502,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 5cd0bcf45..b53d86b9f 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,
@@ -148,6 +151,7 @@ enum DFNTAGS
 	DFNTAG_MAGICRESISTANCE,
 	DFNTAG_MANA,
 	DFNTAG_MANAMAX,
+	DFNTAG_MANALEECH,
 	DFNTAG_MAXHP,
 	DFNTAG_MAXITEMS,
 	DFNTAG_MAXLOYALTY,
@@ -224,6 +228,9 @@ enum DFNTAGS
 	DFNTAG_SPAWNOBJLIST,
 	DFNTAG_SWINGSPEEDINCREASE,
 	DFNTAG_SPD,
+	DFNTAG_HEALTHBONUS,
+	DFNTAG_STAMINABONUS,
+	DFNTAG_MANABONUS,
 	DFNTAG_SPELLS,
 	DFNTAG_SPELLWEAVING,
 	DFNTAG_SPIRITSPEAK,
@@ -231,6 +238,7 @@ enum DFNTAGS
 	DFNTAG_SPLITCHANCE,
 	DFNTAG_STAMINA,
 	DFNTAG_STAMINAMAX,
+	DFNTAG_STAMINALEECH,
 	DFNTAG_STRENGTH,
 	DFNTAG_STRADD,
 	DFNTAG_STEALABLE,