uox3/source/CResponse.cpp
Xoduz 82a3ef5386 08/08/2021 - Xuri (0.99.4w)
Added support for new Skills DFN tag:
	SKILLDELAY=# 	// Delay in seconds before another skill can be used after using this skill. Overrides global delay from UOX.INI
Updated skill cSkills::SkillUse() to use skill-specific skill delays if set for a given skill, or global delay from ini if not. Also added a check to see if skill delay timer has been modified by onSkill JS event before attempting to set a default timer
Updated skill delays to better match ~pub15 era:
	Reduced global SKILLDELAY setting in UOX.INI from 2 to 1 second
	Detecting Hidden skill now has a default skill delay of 10 seconds
	Herding skill now has a default skill delay of 10 seconds
	Hiding skill now has a default skill delay of 10 seconds, with shorter delays (global skill delay) for trying to hide while in combat or for successfully hiding
	Meditation skill now has a default skill delay of 10 seconds, with shorter delays (global skill delay) for failing due to hands being occupied or already being at max mana
	Peacemaking skill now has a default skill delay of 10 seconds for failed skill checks, and 5 seconds delay for successful ones
	Poisoning skill now has a default skill delay of 10 seconds
	Provocation skill now has a default skill delay of 10 seconds
	Remove Trap skill now has a default skill delay of 10 seconds
	Stealing skill now has a default skill delay of 10 seconds
	Stealth skill now has a default skill delay of 10 seconds
	Tracking skill now has a default skill delay of 10 seconds
	Veterinary skill now has a default skill delay of 10 seconds
Fixed an issue with global JS Timer object that would reference the wrong timerIDs
Extended global JS timer object to include timers from socket as well (marked with SOCK_ prefix):
	.SOCK_SKILLDELAY	// Delay before the next time player can use another skill
	.SOCK_OBJDELAY		// Delay before next time player can use another object
	.SOCK_SPIRITSPEAK 	// Timer that tracks remaining duration of player's Spirit Speak
	.SOCK_TRACKING		// Timer that tracks remaining duration for player's Tracking skill
	.SOCK_FISHING		// Timer that tracks remaining duration for player's Fishing skill
	.SOCK_MUTETIME 		// Timer that tracks remaining duration a player remains muted
	.SOCK_TRACKINGDISPLAY 	// Timer that tracks interval between each time tracking display is updated
Updated various JS scripts to use the new Timer.TIMERNAME with .GetTimer()/.SetTimer() JS Methods for Characters and Sockets
Updated Persecution ability for player ghosts to use the skill delay of the Spirit Speak skill, if set
Updated Persecution ability to base mana damage primarily on player's Spirit Speak skill rather than their Intelligence
In order to cut down on the amount of messages sent from server to players trying to spam-use objects/skills, UOX3 now only sends a maximum of one message for object use delay and one message for skill use delay while the delay timers for these are in effect. This resets on the next successful use of an object or skill. This is tracked via bools in cSocket class:
	bool objDelayMsgShown
	bool skillDelayMsgShown
Converted DELAY tag in spells.dfn to use seconds (with decimals) instead of tenths of a second, which was not precise enough. This controls the cast time of spells. What would previously say DELAY=10 (10/10ths of a second) now says DELAY=1.0 (1 second)
Added support for new Spells DFN tag to control delay until damage takes place after casting a spell, replacing UOX.INI setting COMBATEXPLODEDELAY, which is no longer used:
	DAMAGEDELAY=# 		// Delay in seconds (decimal) until a direct damage spell deals its damage
Added support for a new Spells DFN tag to control cast recovery time, during which additional spells cannot be cast:
	RECOVERYDELAY=# 	// Delay in seconds (decimal) until player can cast another spell
Added new setting in UOX.INI to control whether player spellcasting gets interrupted upon equipping/picking up items:
	ITEMSINTERRUPTCASTING=1 	// If enabled, spellcasting gets interrupted by equipping/picking up items. Defaults to enabled.
All direct damage spells by default have a 1.0s delay before damage (and VFX) is applied, except Explosion which has a 2.5s delay
All spells by default have a cast recovery duration of 1.0s; from the moment a spellcast is completed/moment targeting cursor appears, the player will be unable to cast any other spell for the duration of the cast recovery
Reagent checks and skill-check now take place AFTER the spellcast animation, not before!
Spell cast times now follow this formula, though values set per spell and can be modified: 0.25 + ( spellCircle * 0.25 )
Overhauled spell sound FX and VFX for for all spells
Players can no longer cast spells while holding items on the cursor
Fixed Magic Trap, Magic Untrap, Magic Lock and Magic Unlock not being usable on containers in one's own backpack
Fixed an issue that caused Magic Trap to deal between 50 to 100 damage to the player. New damage: 5 to 10, depending on Magery skill
Fixed an issue where Magic Untrap spell would completely remove traps from spawn containers instead of disable them
Fixed an issue where Telekinesis spell would not set off traps on containers properly
Added additional FX for Teleport spell played at player's original location before the teleport took place
Fixed a bug where creatures summoned with the Summon Creature spell were not dispellable
Fixed an issue where the Reveal spell would not reveal players hidden using the Hiding skill
Dispel and Mass Dispel spells are no longer guaranteed to succeed at dispelling summoned creatures. Instead, the chance to dispel is based on the following simple formula, with dispelChance capped at 95% to always leave some chance for a creature to resist a dispel:
	dispelChance = ( 50 - ( targetResistSkill - casterMagerySkill )) / 1.5
		The targetResistSkill is based on the highest value of the summoned creature's spell resistance skill and the spell resistance skill of its summoner
Fixed an issue where the Create Food spell attempted to create an invalid item
Fixed issue where code that checked for valid drop locations for items didn't allow negative z values (Dragon Slayer)
Fixed issue where players could drop items on blocking map tiles like water or mountains (Dragon Slayer)
Fixed issue where some JS functions like CheckStaticFlag, CheckDynamicFlag, DoesStaticBlock, DoesDynamicBlock and DoesMapBlock would not work correctly when passed negative Z values
Fixed issue where context menu were listing skills to train from NPCs based on effective skill rather than base skill
Fixed issue where NPCs would offer skills for training even if those skills were not above the minimum 60 skillpoints
Updated clilocID used for Gump JS Method AddToolTip - as well as for some item/character tooltip data - from 1050045 to 1042971 for improved client compatibility
Human/Elf/Gargoyle NPCs with spellcasting capabilities will now play a casting animation and briefly stop moving when they cast spells
Updated NPC spellcasting code to help NPCs make a bit better choices of what spells to cast during combat. Now they take their own health percentage into account, and cast more offensive spells when at near full health vs more defensive spells as their health drops
Fixed an issue where Hirelings wouldn't always immediately start following their new owners
Fixed a bug where the Earthquake spell could incorrectly add NPC characters marked for deletion to the refresh queue
Updated skills and stats of all implemented human NPCs (including vendors) to better match the state of those NPCs around the time of Pub15
Fixed SPATTACK values for a couple of NPCs that were still using values based on spell bitmask instead of spell circle value
Updated stats of Jukan and Meer NPCs (dfndata/npc/lbrraces.dfn) to match stats from official LBR guide
Added support for an optional third parameter for Character/Socket JS Method .CustomTarget() to allow highlighting targeting cursors as neutral (0, default aura), harmful (1, red aura), helpful (2, blue aura). A value of 3 will cancel the currently active targeting cursor. New syntax:
	myChar/mySock.CustomTarget( targetID, txtToSay, cursorType ) 	// supported cursorTypes: 0 = neutral, 1 = harmful, 2 = helpful, 3 = cancel current target cursor
Various target cursors triggered by code (like hard-coded spells, some pet commands) also make use of these cursorTypes
2021-08-08 01:57:25 +08:00

1222 lines
47 KiB
C++

//o-----------------------------------------------------------------------------------------------o
//| File - CResponse.cpp
//| Date - 11/15/2003
//o-----------------------------------------------------------------------------------------------o
//| Changes - Version History
//|
//| 1.0 15th November, 2003
//| Initial implementation. Ripped out 99% of cSpeech::response
//| and relocated contents here in individual handler classes
//|
//| 1.1 16th November, 2003
//| Changed CBaseResponse::Handle() overload to WhichResponse(), and
//| locally declared it wherever it was used, also handles different
//| languages in WhichResponse() now.
//| Added an inline findString() helper function to clean up WhichResponse().
//| Now houses our vendor responses (enabling us to completely remove
//| checkForVendorResponse() and responsevendor().
//| Also handles all currently supported house speech using a single
//| class, enabling the removal of HouseSpeech().
//|
//| 1.2 17th November, 2003
//| We now make use of OSI's trigger words (the few that we actually handle,
//| currently), to remove the need to search through the text string
//o-----------------------------------------------------------------------------------------------o
#include "uox3.h"
#include "CResponse.h"
#include "regions.h"
#include "msgboard.h"
#include "townregion.h"
#include "classes.h"
#include "cEffects.h"
#include "Dictionary.h"
#include "CPacketSend.h"
#include "CJSMapping.h"
#include "cScript.h"
#include "regions.h"
#include "cGuild.h"
#include "skills.h"
#include "StringUtility.hpp"
#include <algorithm>
#include <cctype>
bool BuyShop( CSocket *s, CChar *c );
void callGuards( CChar *mChar );
inline bool findString( std::string toCheck, std::string toFind )
{
return ( toCheck.find( toFind ) != std::string::npos );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CHARLIST findNearbyNPCs( CChar *mChar, distLocs distance )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Returns a list of NPCs that are within a certain distance
//o-----------------------------------------------------------------------------------------------o
CHARLIST findNearbyNPCs( CChar *mChar, distLocs distance )
{
CHARLIST ourNpcs;
REGIONLIST nearbyRegions = MapRegion->PopulateList( mChar );
for( REGIONLIST_CITERATOR rIter = nearbyRegions.begin(); rIter != nearbyRegions.end(); ++rIter )
{
CMapRegion *CellResponse = (*rIter);
if( CellResponse == nullptr )
continue;
GenericList< CChar * > *regChars = CellResponse->GetCharList();
regChars->Push();
for( CChar *Npc = regChars->First(); !regChars->Finished(); Npc = regChars->Next() )
{
if( !ValidateObject( Npc ) || Npc == mChar || !Npc->IsNpc() || Npc->GetInstanceID() != mChar->GetInstanceID() )
continue;
if( objInRange( mChar, Npc, distance ) )
ourNpcs.push_back( Npc );
}
regChars->Pop();
}
return ourNpcs;
}
ITEMLIST findNearbyItems( CBaseObject *mObj, distLocs distance );
//o-----------------------------------------------------------------------------------------------o
//| Function - UI08 DoJSResponse( CSocket *mSock, CChar *mChar, const std::string& text )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Checks if nearby NPCs run scripts that react to speech
//o-----------------------------------------------------------------------------------------------o
UI08 DoJSResponse( CSocket *mSock, CChar *mChar, const std::string& text )
{
CChar *Npc = nullptr;
CHARLIST nearbyNPCs = findNearbyNPCs( mChar, DIST_CMDRANGE );
for( CHARLIST_ITERATOR nIter = nearbyNPCs.begin(); nIter != nearbyNPCs.end(); ++nIter )
{
Npc = (*nIter);
if( !ValidateObject( Npc ) )
continue;
if( abs( mChar->GetZ() - Npc->GetZ() ) <= 9 )
{
std::vector<UI16> scriptTriggers = Npc->GetScriptTriggers();
for( auto i : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( i );
if( toExecute != nullptr )
{
//| -1 => No such function or bad call
//| 0 => Let other NPCs and PCs see it
//| 1 => Let other PCs see it
//| 2 => Let no one else see it
SI08 rVal = -1;
if( Npc->isDisabled() )
Npc->TextMessage( nullptr, 1291, TALK, false );
else
rVal = toExecute->OnSpeech( text.c_str(), mChar, Npc );
switch( rVal )
{
case 1: // No other NPCs to see it, but PCs should
return 1;
case 2: // no one else to see it
return 2;
case 0: // Other NPCs and PCs to see it
case -1: // no function, so do nothing... NOT handled!
default:
break;
}
}
}
}
}
// How about items?
if( cwmWorldState->ServerData()->ItemsDetectSpeech() )
{
CItem *Item = nullptr;
ITEMLIST nearbyItems = findNearbyItems( mChar, DIST_CMDRANGE );
for( ITEMLIST_ITERATOR nIter = nearbyItems.begin(); nIter != nearbyItems.end(); ++nIter )
{
Item = (*nIter);
if( !ValidateObject( Item ) )
continue;
if( abs( mChar->GetZ() - Item->GetZ() ) <= 9 )
{
std::vector<UI16> scriptTriggers = Item->GetScriptTriggers();
for( auto i : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( i );
if( toExecute != nullptr )
{
SI08 rVal = -1;
if( Item->isDisabled() )
Item->TextMessage( nullptr, 1291, TALK, false );
else
rVal = toExecute->OnSpeech( text.c_str(), mChar, Item );
switch( rVal )
{
case 1: // No other NPCs to see it, but PCs should
return 1;
case 2: // no one else to see it
return 2;
case 0: // Other NPCs and PCs to see it
case -1: // no function, so do nothing... NOT handled!
default:
break;
}
}
}
}
}
}
return 0;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool WhichResponse( CSocket *mSock, CChar *mChar, std::string text )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handle responses to keywords in player speech
//o-----------------------------------------------------------------------------------------------o
bool WhichResponse( CSocket *mSock, CChar *mChar, std::string text, CChar *tChar = nullptr )
{
UI08 jsResponse = DoJSResponse( mSock, mChar, text );
if( jsResponse == 1 )
return true;
else if( jsResponse == 2 )
return false;
CBaseResponse *tResp = nullptr;
for( UI16 trigWord = mSock->FirstTrigWord(); !mSock->FinishedTrigWords(); trigWord = mSock->NextTrigWord() )
{
switch( trigWord )
{
case TW_COUNT: break;
case TW_GUARDS: callGuards( mChar ); break;
case TW_KILLS: tResp = new CKillsResponse(); break;
case TW_QUESTTAKE: tResp = new CEscortResponse(); break;
case TW_QUESTDEST: tResp = new CEscortResponse( true ); break;
case TW_TRAIN: tResp = new CTrainingResponse( trigWord, tChar ); break;
case TW_FOLLOW:
case TW_FOLLOW2: tResp = new CPetMultiResponse( strutil::upper( text ), false, TARGET_FOLLOW, 1310, false, true ); break;
case TW_COME:
case TW_FOLLOWME: tResp = new CPetComeResponse( false, strutil::upper( text ) ); break;
case TW_ALLFOLLOWME:
case TW_ALLCOME: tResp = new CPetComeResponse( true, strutil::upper( text ) ); break;
case TW_ALLFOLLOW: tResp = new CPetMultiResponse( strutil::upper( text ), false, TARGET_FOLLOW, 1310, true, true ); break;
case TW_KILL:
case TW_ATTACK: tResp = new CPetAttackResponse( false, strutil::upper( text ) ); break;
case TW_ALLKILL:
case TW_ALLATTACK:
/*{
tResp = new CPetAttackResponse( true, strutil::upper( text ) );
tResp->Handle( mSock, mChar );
delete tResp;
tResp = nullptr;
goto endResponseCheck;
}*/
tResp = new CPetAttackResponse( true, strutil::upper( text ) ); break;
case TW_FETCH:
case TW_GET:
case TW_BRING: tResp = new CPetMultiResponse( strutil::upper( text ), false, TARGET_GUARD, 1316, false, true ); break;
case TW_FRIEND: tResp = new CPetMultiResponse( strutil::upper( text ), true, TARGET_FRIEND, 1620, false, true ); break;
case TW_GUARD: tResp = new CPetMultiResponse( strutil::upper( text ), false, TARGET_GUARD, 1104, false, true ); break;
case TW_ALLGUARD:
case TW_ALLGUARDME: tResp = new CPetGuardResponse( true, strutil::upper( text ) ); break;
case TW_STOP:
case TW_STAY: tResp = new CPetStayResponse( false, strutil::upper( text ) ); break;
case TW_ALLSTOP:
case TW_ALLSTAY: tResp = new CPetStayResponse( true, strutil::upper( text ) ); break;
case TW_TRANSFER: tResp = new CPetMultiResponse( strutil::upper( text ), true, TARGET_TRANSFER, 1323, false, false ); break;
case TW_RELEASE: tResp = new CPetReleaseResponse( strutil::upper( text ) ); break;
case TW_VENDORBUY: tResp = new CVendorBuyResponse( true, strutil::upper( text ) ); break;
case TW_BUY: tResp = new CVendorBuyResponse( false, strutil::upper( text ) ); break;
case TW_VENDORSELL: tResp = new CVendorSellResponse( true, strutil::upper( text ) ); break;
case TW_SELL: tResp = new CVendorSellResponse( false, strutil::upper( text ) ); break;
case TW_VENDORVIEW: tResp = new CVendorViewResponse( true, strutil::upper( text ) ); break;
case TW_VIEW: tResp = new CVendorViewResponse( false, strutil::upper( text ) ); break;
case TW_VENDORGOLD: tResp = new CVendorGoldResponse( true, strutil::upper( text ) ); break;
case TW_COLLECT:
case TW_GOLD: tResp = new CVendorGoldResponse( false, strutil::upper( text ) ); break;
case TW_VENDORSTATUS: tResp = new CVendorStatusResponse( true, strutil::upper( text ) ); break;
case TW_STATUS: tResp = new CVendorStatusResponse( false, strutil::upper( text ) ); break;
case TW_VENDORDISMISS: tResp = new CVendorDismissResponse( true, strutil::upper( text ) ); break;
case TW_DISMISS: tResp = new CVendorDismissResponse( false, strutil::upper( text )); break;
case TW_HOUSEBAN: tResp = new CHouseMultiResponse( TARGET_HOUSEBAN, 585 ); break;
case TW_HOUSEEJECT: tResp = new CHouseMultiResponse( TARGET_HOUSEEJECT, 587 ); break;
case TW_HOUSELOCKDOWN: tResp = new CHouseMultiResponse( TARGET_HOUSELOCKDOWN, 589 ); break;
case TW_HOUSERELEASE: tResp = new CHouseMultiResponse( TARGET_HOUSERELEASE, 591 ); break;
case TW_HOUSESECURE: tResp = new CHouseMultiResponse( TARGET_HOUSESECURE, 1816 ); break;
case TW_HOUSESTRONGBOX:
{
// Special case because the phrase "I wish to place a strongbox" will also trigger
// the triggerword for placing a trash barrel!
tResp = new CHouseMultiResponse( TARGET_HOUSESTRONGBOX, -1 );
tResp->Handle( mSock, mChar );
delete tResp;
tResp = nullptr;
goto endResponseCheck; // :
}
case TW_TRASHBARREL: tResp = new CHouseMultiResponse( TARGET_HOUSETRASHBARREL, -1 ); break;
case TW_BOATFORWARD:
case TW_BOATUNFURL: tResp = new CBoatMultiResponse( BOAT_FORWARD ); break;
case TW_BOATBACKWARD: tResp = new CBoatMultiResponse( BOAT_BACKWARD ); break;
case TW_BOATLEFT: tResp = new CBoatMultiResponse( BOAT_LEFT ); break;
case TW_BOATRIGHT: tResp = new CBoatMultiResponse( BOAT_RIGHT ); break;
case TW_BOATFORWARDLEFT: tResp = new CBoatMultiResponse( BOAT_FORWARDLEFT ); break;
case TW_BOATFORWARDRIGHT: tResp = new CBoatMultiResponse( BOAT_FORWARDRIGHT ); break;
case TW_BOATBACKLEFT: tResp = new CBoatMultiResponse( BOAT_BACKWARDLEFT ); break;
case TW_BOATBACKRIGHT: tResp = new CBoatMultiResponse( BOAT_BACKWARDRIGHT ); break;
case TW_BOATONEFORWARD: tResp = new CBoatMultiResponse( BOAT_ONEFORWARD ); break;
case TW_BOATONEBACK: tResp = new CBoatMultiResponse( BOAT_ONEBACKWARD ); break;
case TW_BOATONELEFT: tResp = new CBoatMultiResponse( BOAT_ONELEFT ); break;
case TW_BOATONERIGHT: tResp = new CBoatMultiResponse( BOAT_ONERIGHT ); break;
case TW_BOATONEFORWARDLEFT: tResp = new CBoatMultiResponse( BOAT_ONEFORWARDLEFT ); break;
case TW_BOATONEFORWARDRIGHT:tResp = new CBoatMultiResponse( BOAT_ONEFORWARDRIGHT ); break;
case TW_BOATONEBACKLEFT: tResp = new CBoatMultiResponse( BOAT_ONEBACKWARDLEFT ); break;
case TW_BOATONEBACKRIGHT: tResp = new CBoatMultiResponse( BOAT_ONEBACKWARDRIGHT ); break;
case TW_BOATSLOWFORWARD: tResp = new CBoatMultiResponse( BOAT_SLOWFORWARD ); break;
case TW_BOATSLOWBACK: tResp = new CBoatMultiResponse( BOAT_SLOWBACKWARD ); break;
case TW_BOATSLOWLEFT: tResp = new CBoatMultiResponse( BOAT_SLOWLEFT ); break;
case TW_BOATSLOWRIGHT: tResp = new CBoatMultiResponse( BOAT_SLOWRIGHT ); break;
case TW_BOATSLOWFORWARDLEFT: tResp = new CBoatMultiResponse( BOAT_SLOWFORWARDLEFT ); break;
case TW_BOATSLOWFORWARDRIGHT: tResp = new CBoatMultiResponse( BOAT_SLOWFORWARDRIGHT ); break;
case TW_BOATSLOWBACKLEFT: tResp = new CBoatMultiResponse( BOAT_SLOWBACKWARDLEFT ); break;
case TW_BOATSLOWBACKRIGHT: tResp = new CBoatMultiResponse( BOAT_SLOWBACKWARDRIGHT ); break;
case TW_BOATSTOP:
case TW_STOP2:
case TW_BOATFURL: //tResp = new CBoatMultiResponse( BOAT_STOP ); break;
{
tResp = new CBoatMultiResponse( BOAT_STOP );
tResp->Handle( mSock, mChar );
delete tResp;
tResp = nullptr;
goto endResponseCheck; // :
}
case TW_BOATTURNRIGHT:
case TW_BOATSTARBOARD:
case TW_BOATTURNLEFT:
case TW_BOATPORT:
case TW_BOATTURNAROUND:
case TW_BOATANCHORDROP:
case TW_BOATANCHORRAISE:
case TW_SETNAME: tResp = new CBoatResponse( text, trigWord ); break;
case TW_RESIGN: GuildSys->Resign( mSock ); break;
default:
//This is to handle the "train <skill>" keywords
if(( trigWord >= 0x006D && trigWord <= 0x009C ) || trigWord == 0x154 || trigWord == 0x115 ||
trigWord == 0x17C || trigWord == 0x17D || trigWord == 0x17E )
{
tResp = new CTrainingResponse( trigWord, tChar );
tResp->Handle( mSock, mChar );
delete tResp;
tResp = nullptr;
goto endResponseCheck; // :
break;
}
#if defined( UOX_DEBUG_MODE )
Console.print( strutil::format("Unhandled TriggerWord sent by the client 0x%X\n", trigWord) );
#endif
break;
}
if( tResp != nullptr )
{
tResp->Handle( mSock, mChar );
delete tResp;
tResp = nullptr;
}
}
endResponseCheck:
return true;
}
CEscortResponse::CEscortResponse( bool newVal )
{
findDest = newVal;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void CEscortResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles NPC escort response to player wanting to take on the escorting job
//o-----------------------------------------------------------------------------------------------o
void CEscortResponse::Handle( CSocket *mSock, CChar *mChar )
{
// If the PC is dead then break out, The dead cannot accept quests
if( mChar->IsDead() )
return;
CHARLIST npcList = findNearbyNPCs( mChar, DIST_NEARBY );
for( CHARLIST_CITERATOR npcCtr = npcList.begin(); npcCtr != npcList.end(); ++npcCtr )
{
CChar *Npc = (*npcCtr);
if( Npc->GetQuestType() == QT_ESCORTQUEST )
{
// I WILL TAKE THEE
// If this is a request for hire and the PC is within range of the NPC and the NPC is waiting for an ESCORT
if( !findDest )
{
if( !ValidateObject( Npc->GetFTarg() ) )
{
Npc->SetOwner( mChar );
Npc->SetFTarg( mChar ); // Set the NPC to follow the PC
Npc->SetNpcWander( WT_FOLLOW );
Npc->SetTimer( tNPC_SUMMONTIME, cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_ESCORTACTIVE ) ); // Set the expire time if nobody excepts the quest
// Send out the rant about accepting the escort
Npc->TextMessage( nullptr, 1294, TALK, 0, cwmWorldState->townRegions[Npc->GetQuestDestRegion()]->GetName().c_str() );
// Remove post from message board (Mark for deletion only - will be cleaned during cleanup)
MsgBoardQuestEscortRemovePost( Npc );
// Return 1 so that we indicate that we handled the message
return;
}
else
findDest = true; // If the current NPC already has an ftarg then respond to query for quest
}
// DESTINATION
// If this is a request to find out where a NPC wants to go and the PC is within range of the NPC and the NPC is waiting for an ESCORT
if( findDest )
{
if( Npc->GetFTarg() == mChar )
Npc->TextMessage( nullptr, 1295, TALK, 0, cwmWorldState->townRegions[Npc->GetQuestDestRegion()]->GetName().c_str() ); // Send out the rant about accepting the escort
else if( !ValidateObject( Npc->GetFTarg() ) ) // If nobody has been accepted for the quest yet
Npc->TextMessage( nullptr, 1296, TALK, 0, cwmWorldState->townRegions[Npc->GetQuestDestRegion()]->GetName().c_str() ); // Send out the rant about accepting the escort
else // The must be enroute
Npc->TextMessage( nullptr, 1297, TALK, 0, cwmWorldState->townRegions[Npc->GetQuestDestRegion()]->GetName().c_str(), Npc->GetFTarg()->GetName().c_str() ); // Send out a message saying we are already being escorted
// Return success ( we handled the message )
return;
}
}
}
}
CKillsResponse::CKillsResponse( void )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void CKillsResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to see the status of their kills
//o-----------------------------------------------------------------------------------------------o
void CKillsResponse::Handle( CSocket *mSock, CChar *mChar )
{
if( !mChar->IsDead() && mSock != nullptr )
{
SI16 i = mChar->GetKills();
if( i == 0 )
mSock->sysmessage( 1288 );
else if( i > cwmWorldState->ServerData()->RepMaxKills() )
mSock->sysmessage( 1289, i );
else
mSock->sysmessage( 1290, i );
}
}
CTrainingResponse::CTrainingResponse( UI16 trigWord, CChar *tChar )
{
trigChar = tChar;
ourTrigWord = trigWord;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void CTrainingResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to train skills from NPCs
//o-----------------------------------------------------------------------------------------------o
void CTrainingResponse::Handle( CSocket *mSock, CChar *mChar )
{
if( cwmWorldState->ServerData()->NPCTrainingStatus() ) //if the player wants to train
{
constexpr auto maxsize = 512;
std::string temp;
std::string temp2;
CHARLIST npcList = findNearbyNPCs( mChar, DIST_INRANGE );
// Shuffle the npcList so it doesn't always trigger for the first NPC in the list
std::random_device rd;
std::mt19937 g( rd() );
std::shuffle( npcList.begin(), npcList.end(), g );
auto skillName = std::string("");
bool foundString = false;
for( CHARLIST_CITERATOR npcCtr = npcList.begin(); npcCtr != npcList.end(); ++npcCtr )
{
CChar *Npc = (*npcCtr);
if( ValidateObject( trigChar ) )
{
Npc = trigChar;
}
if( cwmWorldState->creatures[Npc->GetID()].IsHuman() )
{
// Stop the NPC from moving for a minute while talking with the player
Npc->SetTimer( tNPC_MOVETIME, BuildTimeValue( 60 ) );
mSock->TempObj( nullptr ); //this is to prevent errors when a player says "train <skill>" then doesn't pay the npc
SI16 skill = -1;
// Language independent implementation of training keywords
switch( ourTrigWord )
{
case 340: // anatomy
skill = ANATOMY; break;
case 109: // parrying, parry, battle, defense
skill = PARRYING; break;
case 110: // first, aid, heal, healing, medicine
skill = HEALING; break;
case 111: // hide, hiding
skill = HIDING; break;
case 112: // steal, stealing
skill = STEALING; break;
case 113: // alchemy
skill = ALCHEMY; break;
case 114: // animal, animal lore
skill = ANIMALLORE; break;
case 115: // appraising, identifying, appraise, item, identification, identify
skill = ITEMID; break;
case 116: // armslore, arms
skill = ARMSLORE; break;
case 117: // beg, begging
skill = BEGGING; break;
case 118: // blacksmith, smith, blacksmithy, smithing, blacksmithing
skill = BLACKSMITHING; break;
case 119: // bower, bow, arrow, fletcher, bowcraft, fletching
skill = BOWCRAFT; break;
case 120: // calm, peace, peacemaking
skill = PEACEMAKING; break;
case 121: // camp, camping
skill = CAMPING; break;
case 122: // carpentry, woodwork, woodworking
skill = CARPENTRY; break;
case 123: // cartography, map, mapmaking
skill = CARTOGRAPHY; break;
case 124: // cooking, cook
skill = COOKING; break;
case 125: // detect, detect hidden, hidden
skill = DETECTINGHIDDEN; break;
case 126: // entice, enticement, enticing
skill = ENTICEMENT; break;
case 127: // evaluate, intelligence, evaluating
skill = EVALUATINGINTEL; break;
case 128: // fish, fishing
skill = FISHING; break;
case 129: // incite, provoke, provoking, provocation
skill = PROVOCATION; break;
case 130: // lockpicking, lock, pick, picking, locks
skill = LOCKPICKING; break;
case 131: // magic, magery, sorcery, wizardry, mage, wizard
skill = MAGERY; break;
case 132: // resist, resisting, spells
skill = MAGICRESISTANCE; break;
case 133: // battle, tactic, tactics, fight, fighting
skill = TACTICS; break;
case 134: // peek, peeking, snoop, snooping
skill = SNOOPING; break;
case 135: // disarm, disarming, remove, removing
skill = REMOVETRAP; break;
case 136: // play, instrument, playing, music, musician, musicianship
skill = MUSICIANSHIP; break;
case 137: // poisoning, poison
skill = POISONING; break;
case 138: // ranged, missile, missiles, shoot, shooting, archery, archer
skill = ARCHERY; break;
case 139: // spirit, ghost, seance, spiritualism
skill = SPIRITSPEAK; break;
case 140: // tailoring, tailor, clothier
skill = TAILORING; break;
case 141: // tame, taming
skill = TAMING; break;
case 142: // taste, tasting
skill = TASTEID; break;
case 143: // tinker, tinkering
skill = TINKERING; break;
case 144: // vet, veterinarian, veterinary
skill = VETERINARY; break;
case 145: // forensic, forensics
skill = FORENSICS; break;
case 146: // herd, herding
skill = HERDING; break;
case 147: // tracking, track, hunt, hunting
skill = TRACKING; break;
case 148: // stealth
skill = STEALTH; break;
case 149: // inscribe, scroll, inscribing, inscription
skill = INSCRIPTION; break;
case 150: // sword, swords, blade, blades, swordsman, swordsmanship
skill = SWORDSMANSHIP; break;
case 151: // club, clubs, mace, maces
skill = MACEFIGHTING; break;
case 152: // dagger, daggers, fence, fencing, spear
skill = FENCING; break;
case 153: // hand, wrestle, wrestling
skill = WRESTLING; break;
case 154: // lumberjack, lumberjacking, woodcuttingl
skill = LUMBERJACKING; break;
case 155: // mining, mine, smelt
skill = MINING; break;
case 156: // meditate
skill = MEDITATION; break;
case 380: // necromancy
skill = NECROMANCY; break;
case 381: // chivalry
skill = CHIVALRY; break;
case 382: // focus
skill = FOCUS; break;
default:
break;
}
if( skill == -1 ) // Didn't ask to be trained in a specific skill - Leviathan fix
{
if( !Npc->CanTrain() )
{
Npc->TextMessage( mSock, 1302, TALK, false ); // I am sorry, but I have nothing to teach thee.
continue;
}
Npc->SetTrainingPlayerIn( 255 ); // Like above, this is to prevent errors when a player says "train <skill>" then doesn't pay the npc
temp = Dictionary->GetEntry( 1303 ); // I can teach thee the following skills:
UI08 skillsToTrainIn = 0;
UI08 lastCommaPos = 0;
for( UI08 j = 0; j < ALLSKILLS; ++j )
{
if( Npc->GetBaseSkill( j ) > 10 )
{
temp2= strutil::lower(cwmWorldState->skill[j].name);
if( !skillsToTrainIn ) {
temp2[0] = std::toupper( temp2[0] ); // If it's the first skill, capitalize it, and add a space in front.
temp += (" " + temp2);
}
else if( skillsToTrainIn <= 10 )
{
// Additional skills
lastCommaPos = static_cast<UI08>(temp.size()) + 1;
temp += (", " + temp2);
}
else if( skillsToTrainIn > 10 ) // to stop UOX3 from crashing/sentence being cut off if NPC is too knowledgable!
{
temp += std::string(" and possibly more");
break;
}
++skillsToTrainIn;
}
}
if( skillsToTrainIn <= 10 )
{
// Replace last comma with " and "
temp.replace( lastCommaPos, 1, " and " );
}
if( skillsToTrainIn )
{
temp += '.'; // Make last character a . just to look nicer
Npc->TextMessage( mSock, temp, TALK, false );
}
else
Npc->TextMessage( mSock, 1302, TALK, false ); // I am sorry, but I have nothing to teach thee.
}
else // They do want to learn a specific skill
{
if( !Npc->CanTrain() )
{
Npc->TextMessage( mSock, 1302, TALK, false ); // I am sorry, but I have nothing to teach thee.
continue;
}
if( Npc->GetBaseSkill( static_cast<UI08>( skill )) > 10 )
{
temp = strutil::format(maxsize, Dictionary->GetEntry( 1304 ), strutil::lower(cwmWorldState->skill[skill].name ).c_str() ); // Thou wishest to learn of %s?
if( mChar->GetBaseSkill( static_cast<UI08>( skill )) >= 250 )
{
temp += Dictionary->GetEntry( 1305 ); // I can teach thee no more than thou already knowest!
}
else
{
if( Npc->GetBaseSkill( static_cast<UI08>( skill) ) <= 250 )
{
// Very well I, can train thee up to the level of %i percent for %i gold. Pay for less and I shall teach thee less.
temp2=strutil::format(maxsize, Dictionary->GetEntry( 1306 ),(SI32)( Npc->GetBaseSkill( (UI08)skill ) / 2 / 10 ),(SI32)( Npc->GetBaseSkill( (UI08)skill ) / 2 ) - mChar->GetBaseSkill( (UI08)skill ) );
}
else
{
// Very well I, can train thee up to the level of %i percent for %i gold. Pay for less and I shall teach thee less.
temp2 = strutil::format(maxsize, Dictionary->GetEntry( 1306 ), 25, 250 - mChar->GetBaseSkill( (UI08)skill ) );
}
temp += " " + temp2 ;
mSock->TempObj( Npc );
Npc->SetTrainingPlayerIn( static_cast<UI08>(skill) );
}
Npc->TextMessage( mSock, temp, TALK, false );
}
else
Npc->TextMessage( mSock, 1307, TALK, false ); // I am sorry but I cannot train thee in that skill.
}
break;
}
}
}
}
CBasePetResponse::CBasePetResponse( const std::string &text )
{
ourText = text;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void CBasePetResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to execute pet commands
//o-----------------------------------------------------------------------------------------------o
void CBasePetResponse::Handle( CSocket *mSock, CChar *mChar )
{
TAGMAPOBJECT petTagObj = mChar->GetTag( "petTagObjSerial" );
CChar *petCommandObj = calcCharObjFromSer( petTagObj.m_IntValue );
if( ValidateObject( petCommandObj ) )
{
petTagObj.m_Destroy = TRUE;
petTagObj.m_IntValue = 0;
mChar->SetTag( "petCommandObj", petTagObj );
Handle( mSock, mChar, petCommandObj );
}
else
{
CHARLIST npcList = findNearbyNPCs( mChar, DIST_CMDRANGE );
for( CHARLIST_CITERATOR npcCtr = npcList.begin(); npcCtr != npcList.end(); ++npcCtr )
{
CChar *Npc = (*npcCtr);
if( !Handle( mSock, mChar, Npc ) )
break;
}
}
}
CPetMultiResponse::CPetMultiResponse( const std::string &text, bool restrictVal, TargetIDs targVal, SI32 dictVal, bool saidAll, bool checkControlDifficulty ) : CBasePetResponse( text )
{
isRestricted = restrictVal;
targID = targVal;
dictEntry = dictVal;
allSaid = saidAll;
checkDifficulty = checkControlDifficulty;
}
bool CPetMultiResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
if( allSaid )
{
CHARLIST npcList = findNearbyNPCs( mChar, DIST_CMDRANGE );
for( CHARLIST_CITERATOR npcCtr = npcList.begin(); npcCtr != npcList.end(); ++npcCtr )
{
CChar *Npc = ( *npcCtr );
if( ValidateObject( Npc ) && Npc->GetOwnerObj() == mChar )
{
if( Npcs->canControlPet( mChar, Npc, isRestricted, checkDifficulty ))
{
UI08 cursorType = 0;
if( targID == 25 ) // Guard
cursorType = 2; // helpful
mSock->TempObj( Npc );
mSock->TempInt( 1 );
mSock->target( 0, targID, cursorType, dictEntry );
return false;
}
}
}
}
else
{
std::string npcName = Npc->GetName();
if( npcName == "#" )
{
// If character name is #, use default name from dictionary files instead - using base entry 3000 + character's ID
npcName = Dictionary->GetEntry( 3000 + Npc->GetID() );
}
if( findString( ourText, strutil::upper( npcName )))
{
if( Npcs->canControlPet( mChar, Npc, isRestricted, checkDifficulty ) )
{
mSock->TempObj( Npc );
UI08 cursorType = 0;
if( targID == 25 ) // Guard
cursorType = 2; // helpful
mSock->target( 0, targID, cursorType, dictEntry );
return false;
}
}
}
return true;
}
CPetReleaseResponse::CPetReleaseResponse( const std::string &text ) : CBasePetResponse( text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPetReleaseResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to release pets
//o-----------------------------------------------------------------------------------------------o
bool CPetReleaseResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
std::string npcName = getNpcDictName( Npc, mSock );
if( findString( ourText, strutil::upper( npcName )))
{
if( Npcs->canControlPet( mChar, Npc, true, false ))
{
// Reduce player's control slot usage by the amount of control slots taken up by the pet
mChar->SetControlSlotsUsed( std::max(0, mChar->GetControlSlotsUsed() - Npc->GetControlSlots()));
// Release the pet
Npcs->releasePet( Npc );
return false;
}
}
return true;
}
CPetAllResponse::CPetAllResponse( bool allVal, const std::string &text ) : CBasePetResponse( text )
{
saidAll = allVal;
}
CPetGuardResponse::CPetGuardResponse( bool allVal, const std::string &text ) : CPetAllResponse( allVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPetGuardResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting their pets to guard an object
//o-----------------------------------------------------------------------------------------------o
bool CPetGuardResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
std::string npcName = getNpcDictName( Npc, mSock );
if( saidAll || findString( ourText, strutil::upper( npcName )))
{
if( Npcs->canControlPet( mChar, Npc, false, true ))
{
Npcs->stopPetGuarding( Npc );
mSock->sysmessage( 1321 ); // Your pet is now guarding you.
Npc->SetNPCAiType( AI_PET_GUARD );
Npc->SetGuarding( mChar );
mChar->SetGuarded( true );
Npc->SetFTarg( mChar );
Npc->SetNpcWander( WT_FOLLOW );
if( !saidAll )
return false;
}
}
return true;
}
CPetAttackResponse::CPetAttackResponse( bool allVal, const std::string &text ) : CPetAllResponse( allVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPetAttackResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting their pets to attack something
//o-----------------------------------------------------------------------------------------------o
bool CPetAttackResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
std::string npcName = getNpcDictName( Npc, mSock );
if( saidAll || findString( ourText, strutil::upper( npcName )))
{
if( Npcs->canControlPet( mChar, Npc, false, true ))
{
Npcs->stopPetGuarding( Npc );
mSock->TempObj( Npc );
if( saidAll )
mSock->TempInt( 1 );
mSock->target( 0, TARGET_ATTACK, 1, 1313 );
//if( !saidAll )
return false;
}
}
return true;
}
CPetComeResponse::CPetComeResponse( bool allVal, const std::string &text ) : CPetAllResponse( allVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPetComeResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting their pets to follow
//o-----------------------------------------------------------------------------------------------o
bool CPetComeResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
std::string npcName = getNpcDictName( Npc, mSock );
if( saidAll || findString( ourText, strutil::upper( npcName )))
{
if( Npcs->canControlPet( mChar, Npc, false, true ))
{
Npcs->stopPetGuarding( Npc );
Npc->SetFTarg( mChar );
Npc->SetNpcWander( WT_FOLLOW );
mSock->sysmessage( 1318 );
if( !saidAll )
return false;
}
}
return true;
}
CPetStayResponse::CPetStayResponse( bool allVal, const std::string &text ) : CPetAllResponse( allVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPetStayResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting their pets to stay
//o-----------------------------------------------------------------------------------------------o
bool CPetStayResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
std::string npcName = getNpcDictName( Npc, mSock );
if( saidAll || findString( ourText, strutil::upper( npcName )))
{
if( Npcs->canControlPet( mChar, Npc, false, true ))
{
Npcs->stopPetGuarding( Npc );
Npc->SetFTarg( nullptr );
Npc->SetTarg( nullptr );
Npc->SetAttacker( nullptr );
if( Npc->IsAtWar() )
Npc->ToggleCombat();
Npc->SetOldNpcWander( WT_NONE );
Npc->SetNpcWander( WT_NONE );
if( mSock != nullptr )
{
mSock->sysmessage( 2413, npcName.c_str() ); // %s stays put.
}
if( !saidAll )
return false;
}
}
return true;
}
CBaseVendorResponse::CBaseVendorResponse( bool vendVal, const std::string &text )
{
saidVendor = vendVal;
ourText = text;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void CBaseVendorResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to interact with a vendor
//o-----------------------------------------------------------------------------------------------o
void CBaseVendorResponse::Handle( CSocket *mSock, CChar *mChar )
{
CHARLIST npcList = findNearbyNPCs( mChar, DIST_INRANGE );
for( CHARLIST_CITERATOR npcCtr = npcList.begin(); npcCtr != npcList.end(); ++npcCtr )
{
CChar *Npc = (*npcCtr);
if( Npc->IsShop() || Npc->GetNPCAiType() == AI_PLAYERVENDOR )
{
if( !LineOfSight( mSock, mChar, Npc->GetX(), Npc->GetY(), ( Npc->GetZ() + 15 ), WALLS_CHIMNEYS + DOORS + FLOORS_FLAT_ROOFING, false ) )
continue;
std::string npcName = getNpcDictName( Npc, mSock );
if( saidVendor || findString( ourText,strutil::upper( npcName )))
{
if( !Handle( mSock, mChar, Npc ) )
break;
}
}
}
}
CVendorBuyResponse::CVendorBuyResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CVendorBuyResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to buy something from a vendor
//o-----------------------------------------------------------------------------------------------o
bool CVendorBuyResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
Npc->SetTimer( tNPC_MOVETIME, BuildTimeValue( 60 ) );
if( Npc->GetNPCAiType() == AI_PLAYERVENDOR )
{
mSock->TempObj( Npc );
Npc->TextMessage( mSock, 1333, TALK, false );
mSock->target( 0, TARGET_PLVBUY, " " );
return false;
}
else if( BuyShop( mSock, Npc ) )
return false;
return true;
}
CVendorSellResponse::CVendorSellResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CVendorSellResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to sell something to a vendor
//o-----------------------------------------------------------------------------------------------o
bool CVendorSellResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
//Check if vendor has onSell script running
std::vector<UI16> scriptTriggers = Npc->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// -1 == script doesn't exist, or returned -1
// 0 == script returned false, 0, or nothing - don't execute hard code
// 1 == script returned true or 1
if( toExecute->OnSell( mSock, Npc ) == 0 ) //if script returns false, don't continue
{
return false;
}
}
}
Npc->SetTimer( tNPC_MOVETIME, BuildTimeValue( 60 ) );
CPSellList toSend;
if( toSend.CanSellItems( (*mChar), (*Npc) ) )
mSock->Send( &toSend );
else
Npc->TextMessage( mSock, 1341, TALK, false );
return false;
}
CVendorViewResponse::CVendorViewResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CVendorViewResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to view the items a vendor has for sale
//o-----------------------------------------------------------------------------------------------o
bool CVendorViewResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
if( Npc->GetNPCAiType() == AI_PLAYERVENDOR )
{
CItem *pack = nullptr;
Npc->TextMessage( mSock, 385, TALK, false );
pack = Npc->GetPackItem();
if( ValidateObject( pack ) )
mSock->openPack( pack, true );
}
if( !saidVendor )
return false;
return true;
}
CVendorGoldResponse::CVendorGoldResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CVendorGoldResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to withdraw their earnings from their vendor
//o-----------------------------------------------------------------------------------------------o
bool CVendorGoldResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
if( Npc->GetNPCAiType() == AI_PLAYERVENDOR )
{
CChar *mChar = mSock->CurrcharObj();
if( mChar == Npc->GetOwnerObj() )
{
UI32 pay = 0, give = Npc->GetHoldG(), earned = Npc->GetHoldG();
if( Npc->GetHoldG() <= 0 )
{
Npc->TextMessage( mSock, 1326, TALK, false );
Npc->SetHoldG( 0 );
}
else if( Npc->GetHoldG() > 0 && Npc->GetHoldG() <= MAX_STACK )
{
if( Npc->GetHoldG() > 9 )
{
pay = (SI32)( Npc->GetHoldG() / 10 );
give = Npc->GetHoldG() - pay;
}
else
{
pay = Npc->GetHoldG();
give = 0;
}
Npc->SetHoldG( 0 );
}
else
{
UI32 t = Npc->GetHoldG() - MAX_STACK; // yank of 65 grand, then do calculations
Npc->SetHoldG( t );
pay = 6554;
give = 58981;
if( t > 0 )
Npc->TextMessage( mSock, 1327, TALK, false );
}
if( give )
Items->CreateScriptItem( mSock, mChar, "0x0EED", give, OT_ITEM, true );
Npc->TextMessage( mSock, 1328, TALK, 0, earned, pay, give );
}
else
Npc->TextMessage( mSock, 1329, TALK, false );
}
if( !saidVendor )
return false;
return true;
}
CVendorStatusResponse::CVendorStatusResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CVendorStatusResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to see the status of their vendor
//o-----------------------------------------------------------------------------------------------o
bool CVendorStatusResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
if( Npc->GetNPCAiType() == AI_PLAYERVENDOR )
{
CChar *mChar = mSock->CurrcharObj();
if( mChar == Npc->GetOwnerObj() )
{
if( Npc->GetHoldG() <= 0 )
{
Npc->TextMessage( mSock, 1326, TALK, false );
}
else
{
UI32 pay = 0;
if( Npc->GetHoldG() > 9 )
{
pay = (SI32)( Npc->GetHoldG() / 10 );
}
else
{
pay = Npc->GetHoldG();
}
Npc->TextMessage( mSock, 1782, TALK, 0, Npc->GetHoldG(), pay );
}
}
else
Npc->TextMessage( mSock, 1329, TALK, false );
}
if( !saidVendor )
return false;
return true;
}
CVendorDismissResponse::CVendorDismissResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text )
{
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CVendorDismissResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to dismiss their vendor
//o-----------------------------------------------------------------------------------------------o
bool CVendorDismissResponse::Handle( CSocket *mSock, CChar *mChar, CChar *Npc )
{
if( Npc->GetNPCAiType() == AI_PLAYERVENDOR )
{
CChar *mChar = mSock->CurrcharObj();
if( mChar == Npc->GetOwnerObj() )
{
Npc->Delete();
}
else
Npc->TextMessage( mSock, 1329, TALK, false );
}
if( !saidVendor )
return false;
return true;
}
CHouseMultiResponse::CHouseMultiResponse( TargetIDs targVal, SI32 dictVal )
{
targID = targVal;
dictEntry = dictVal;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void CHouseMultiResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to use house commands
//o-----------------------------------------------------------------------------------------------o
void CHouseMultiResponse::Handle( CSocket *mSock, CChar *mChar )
{
CMultiObj *realHouse = findMulti( mChar );
if( ValidateObject( realHouse ) && !realHouse->CanBeObjType( OT_BOAT ))
{
if( realHouse->CanBeObjType( OT_MULTI ))
{
std::vector<UI16> scriptTriggers = realHouse->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
//-1 == event not found
// 0 == script returned false/0
// 1 == script returned true/1
if( toExecute->OnHouseCommand( mSock, realHouse, targID ) == 1 ) //if script returns true, don't continue
{
return;
}
}
}
if( realHouse->IsOwner( mChar ) )
{
mSock->TempObj( realHouse );
mSock->target( 0, targID, 0, dictEntry );
}
}
}
}
CBoatResponse::CBoatResponse( const std::string &text, UI16 tW )
{
ourText = text;
trigWord = tW;
}
// void CBoatResponse::Handle( CSocket *mSock, CChar *mChar ) in Boats.cpp
CBoatMultiResponse::CBoatMultiResponse( BoatMoveType mType )
{
moveType = mType;
}
CBoatObj * GetBoat( CSocket *s );
//o-----------------------------------------------------------------------------------------------o
//| Function - void CBoatMultiResponse::Handle( CSocket *mSock, CChar *mChar )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles response to players wanting to use boat commands
//o-----------------------------------------------------------------------------------------------o
void CBoatMultiResponse::Handle( CSocket *mSock, CChar *mChar )
{
// Let's do some spam-prevention here
if( mChar->GetTimer( tCHAR_ANTISPAM ) > cwmWorldState->GetUICurrentTime() )
{
return;
}
else
{
mChar->SetTimer( tCHAR_ANTISPAM, BuildTimeValue( (R32)cwmWorldState->ServerData()->CheckBoatSpeed() ) );
}
CBoatObj *boat = GetBoat( mSock );
if( !ValidateObject( boat ) )
{
return;
}
CItem *tiller = calcItemObjFromSer( boat->GetTiller() );
if( boat->GetMoveType() == BOAT_ANCHORED )
{
if( ValidateObject( tiller ) )
{
tiller->TextMessage( mSock, 2024 ); // Ar, the anchor is down sir!
mSock->sysmessage( 2023 ); // You must raise the anchor to pilot the ship.
}
return;
}
boat->SetMoveType( moveType );
if( ValidateObject( tiller ) )
{
tiller->TextMessage( mSock, 10 ); // Aye, sir.
}
}