diff --git a/data/js/commands/targeting/get.js b/data/js/commands/targeting/get.js index 365d3a2b3..b07305bd5 100644 --- a/data/js/commands/targeting/get.js +++ b/data/js/commands/targeting/get.js @@ -294,30 +294,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": @@ -598,8 +595,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/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/source/CPacketSend.cpp b/source/CPacketSend.cpp index 0b084b665..5f266e059 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 ) { @@ -7679,6 +7679,27 @@ void CPToolTip::CopyItemData( CItem& cItem, size_t &totalStringLen, bool addAmou 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~ @@ -8050,9 +8071,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 @@ -8132,7 +8153,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 @@ -8149,7 +8170,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 137aa7b8b..0e0caac46 100644 --- a/source/Changelog.txt +++ b/source/Changelog.txt @@ -4,9 +4,29 @@ 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=# diff --git a/source/UOXJSPropertyEnums.h b/source/UOXJSPropertyEnums.h index 1d0d4df98..6bda6ca60 100644 --- a/source/UOXJSPropertyEnums.h +++ b/source/UOXJSPropertyEnums.h @@ -522,6 +522,9 @@ enum CI_Properties CIP_ISCONTTYPE, CIP_CARVESECTION, CIP_SPEED, + CIP_HEALTHBONUS, + CIP_STAMINABONUS, + CIP_MANABONUS, CIP_MULTI, CIP_AMMOID, CIP_AMMOHUE, diff --git a/source/UOXJSPropertyFuncs.cpp b/source/UOXJSPropertyFuncs.cpp index dcd9dc8e9..d755e91c7 100644 --- a/source/UOXJSPropertyFuncs.cpp +++ b/source/UOXJSPropertyFuncs.cpp @@ -678,7 +678,9 @@ JSBool CItemProps_getProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp case CIP_SPEED: *vp = INT_TO_JSVAL( gPriv->GetSpeed() ); 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: @@ -1328,7 +1330,9 @@ JSBool CItemProps_setProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp case CIP_SPEED: gPriv->SetSpeed( 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; diff --git a/source/UOXJSPropertySpecs.h b/source/UOXJSPropertySpecs.h index 01bc62438..3217cf8e9 100644 --- a/source/UOXJSPropertySpecs.h +++ b/source/UOXJSPropertySpecs.h @@ -541,6 +541,9 @@ inline JSPropertySpec CItemProps[] = { "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 }, diff --git a/source/cBaseObject.cpp b/source/cBaseObject.cpp index e98c3c693..b236df496 100644 --- a/source/cBaseObject.cpp +++ b/source/cBaseObject.cpp @@ -94,9 +94,14 @@ const SI16 DEFBASE_KILLS = 0; const UI16 DEFBASE_RESIST = 0; const bool DEFBASE_NAMEREQUESTACTIVE = 0; const ExpansionRuleset DEFBASE_ORIGIN = ER_UO; + 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 //| Date - 26 July, 2000 @@ -113,7 +118,7 @@ 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 ), -hitChance( DEFBASE_HITCHANCE ), defenseChance( DEFBASE_DEFENSECHANCE ) +healthBonus( DEFBASE_HEALTHBONUS ),staminaBonus( DEFBASE_STAMINABONOS ), manaBonus( DEFBASE_MANABONUS ), hitChance( DEFBASE_HITCHANCE ), defenseChance( DEFBASE_DEFENSECHANCE ) { multis = nullptr; tempMulti = INVALIDSERIAL; @@ -1674,6 +1679,69 @@ void CBaseObject::SetIntelligence2( SI16 nVal ) } } +//o------------------------------------------------------------------------------------------------o +//| 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::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::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 diff --git a/source/cBaseObject.h b/source/cBaseObject.h index 4b5c66329..d014ce397 100644 --- a/source/cBaseObject.h +++ b/source/cBaseObject.h @@ -81,6 +81,9 @@ class CBaseObject SI16 st2; SI16 dx2; SI16 in2; + SI16 healthBonus; + SI16 staminaBonus; + SI16 manaBonus; mutable SI32 FilePosition; SERIAL tempMulti; std::string name; @@ -264,9 +267,20 @@ class CBaseObject void IncDexterity( SI16 toInc = 1 ); void IncIntelligence( 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 eb70922f6..9e9f82727 100644 --- a/source/cChar.cpp +++ b/source/cChar.cpp @@ -2925,6 +2925,10 @@ bool CChar::WearItem( CItem *toWear ) 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() ) @@ -2988,6 +2992,10 @@ bool CChar::TakeOffItem( ItemLayers Layer ) 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() ) @@ -3798,7 +3806,7 @@ UI16 CChar::GetMaxHP( void ) oldRace = GetRace(); } - return maxHP; + return maxHP + GetHealthBonus(); } void CChar::SetMaxHP( UI16 newmaxhp, UI16 newoldstr, RACEID newoldrace ) { @@ -3841,7 +3849,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() ); @@ -3858,7 +3866,7 @@ SI16 CChar::GetMaxMana( void ) oldRace = GetRace(); } - return maxMana; + return maxMana + GetManaBonus(); } void CChar::SetMaxMana( SI16 newmaxmana, UI16 newoldint, RACEID newoldrace ) { @@ -3900,7 +3908,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() ); @@ -3918,7 +3926,7 @@ SI16 CChar::GetMaxStam( void ) oldRace = GetRace(); } - return maxStam; + return maxStam + GetStaminaBonus(); } void CChar::SetMaxStam( SI16 newmaxstam, UI16 newolddex, RACEID newoldrace ) { @@ -5614,6 +5622,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 e2e515dca..c994486c5 100644 --- a/source/cItem.cpp +++ b/source/cItem.cpp @@ -1675,6 +1675,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() ); @@ -1760,6 +1763,7 @@ bool CItem::DumpBody( std::ostream &outStream ) const 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; @@ -1849,6 +1853,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" ) diff --git a/source/combat.cpp b/source/combat.cpp index 256f04c3c..181c7503c 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 diff --git a/source/items.cpp b/source/items.cpp index 314a7cb6b..04fa7e80d 100644 --- a/source/items.cpp +++ b/source/items.cpp @@ -140,6 +140,9 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect break; case DFNTAG_AC: applyTo->SetArmourClass( 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; diff --git a/source/ssection.cpp b/source/ssection.cpp index 9c2aa3565..dcd593c59 100644 --- a/source/ssection.cpp +++ b/source/ssection.cpp @@ -218,6 +218,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, @@ -287,6 +290,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}, diff --git a/source/ssection.h b/source/ssection.h index f5e939fb1..937a9af32 100644 --- a/source/ssection.h +++ b/source/ssection.h @@ -225,6 +225,9 @@ enum DFNTAGS DFNTAG_SPAWNOBJ, DFNTAG_SPAWNOBJLIST, DFNTAG_SPD, + DFNTAG_HEALTHBONUS, + DFNTAG_STAMINABONUS, + DFNTAG_MANABONUS, DFNTAG_SPELLS, DFNTAG_SPELLWEAVING, DFNTAG_SPIRITSPEAK,