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 = "
Prototype
-function onSpellTargetSelect( pCaster, myTarget, spellID )
+function onSpellTargetSelect( myTarget, pCaster, spellID )
Purpose
@@ -4043,7 +4043,7 @@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,