// Create the char corpse when i die (STATF_DEAD) or fall asleep (STATF_Sleeping) // Summoned (STATF_Conjured) and some others creatures have no corpse. CItemCorpse * CChar::MakeCorpse( bool fFrontFall ) { ADDTOCALLSTACK("CChar::MakeCorpse"); word wFlags = (word)(m_TagDefs.GetKeyNum("DEATHFLAGS", true)); if (wFlags & DEATH_NOCORPSE) return( NULL ); if (IsStatFlag(STATF_Conjured) && !(wFlags & (DEATH_NOCONJUREDEFFECT|DEATH_HASCORPSE))) { Effect(EFFECT_XYZ, ITEMID_FX_SPELL_FAIL, this, 1, 30); return( NULL ); } CItemCorpse *pCorpse = dynamic_cast<CItemCorpse *>(CItem::CreateScript(ITEMID_CORPSE, this)); if (pCorpse == NULL) // weird internal error return( NULL ); tchar *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_MSG_CORPSE_OF), GetName()); pCorpse->SetName(pszMsg); pCorpse->SetHue(GetHue()); pCorpse->SetCorpseType(GetDispID()); pCorpse->SetAttr(ATTR_MOVE_NEVER); pCorpse->m_itCorpse.m_BaseID = m_prev_id; // id the corpse type here ! pCorpse->m_itCorpse.m_facing_dir = m_dirFace; pCorpse->m_uidLink = GetUID(); // TO-DO: Fix corpses always turning to the same dir (DIR_N) after resend it to clients if (fFrontFall) pCorpse->m_itCorpse.m_facing_dir = static_cast<DIR_TYPE>(m_dirFace|0x80); int iDecayTimer = -1; // never decay if (IsStatFlag(STATF_DEAD)) { iDecayTimer = (m_pPlayer) ? g_Cfg.m_iDecay_CorpsePlayer : g_Cfg.m_iDecay_CorpseNPC; pCorpse->SetTimeStamp(CServerTime::GetCurrentTime().GetTimeRaw()); // death time if (Attacker_GetLast()) pCorpse->m_itCorpse.m_uidKiller = Attacker_GetLast()->GetUID(); else pCorpse->m_itCorpse.m_uidKiller.InitUID(); } else // sleeping (not dead) { pCorpse->SetTimeStamp(0); pCorpse->m_itCorpse.m_uidKiller = GetUID(); } if ((m_pNPC && m_pNPC->m_bonded) || IsStatFlag(STATF_Conjured|STATF_Sleeping)) pCorpse->m_itCorpse.m_carved = 1; // corpse of bonded and summoned creatures (or sleeping players) can't be carved if ( !(wFlags & DEATH_NOLOOTDROP) ) // move non-newbie contents of the pack to corpse DropAll( pCorpse ); pCorpse->SetKeyNum("OVERRIDE.MAXWEIGHT", g_Cfg.Calc_MaxCarryWeight(this) / 10); // set corpse maxweight to prevent weird exploits like when someone place many items on an player corpse just to make this player get stuck on resurrect pCorpse->MoveToDecay(GetTopPoint(), iDecayTimer); return( pCorpse ); }
bool CChar::NPC_CheckHirelingStatus() { ADDTOCALLSTACK("CChar::NPC_CheckHirelingStatus"); // Am i happy at the moment ? // If not then free myself. // // RETURN: // true = happy. if ( ! IsStatFlag( STATF_Pet )) return( true ); CCharBase * pCharDef = Char_GetDef(); int iFoodConsumeRate = g_Cfg.m_iRegenRate[STAT_FOOD]; unsigned int iWage = pCharDef->GetHireDayWage(); if ( ! iWage || ! iFoodConsumeRate ) return( true ); // I am hired for money not for food. unsigned int iPeriodWage = IMULDIV( iWage, iFoodConsumeRate, 24 * 60 * g_Cfg.m_iGameMinuteLength ); if ( iPeriodWage <= 0 ) iPeriodWage = 1; CItemContainer * pBank = GetBank(); if ( pBank->m_itEqBankBox.m_Check_Amount > iPeriodWage ) { pBank->m_itEqBankBox.m_Check_Amount -= iPeriodWage; } else { TCHAR* pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_WAGE_COST ), iWage); Speak(pszMsg); CChar * pOwner = NPC_PetGetOwner(); if ( pOwner ) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_HIRE_TIMEUP ) ); CItem * pMemory = Memory_AddObjTypes( pOwner, MEMORY_SPEAK ); if ( pMemory ) pMemory->m_itEqMemory.m_Action = NPC_MEM_ACT_SPEAK_HIRE; NPC_PetDesert(); return false; } // Some sort of strange bug to get here. Memory_ClearTypes( MEMORY_IPET ); StatFlag_Clear( STATF_Pet ); } return( true ); }
bool CChar::NPC_CheckWalkHere( const CPointBase & pt, const CRegionBase * pArea, WORD wBlockFlags ) const { ADDTOCALLSTACK("CChar::NPC_CheckWalkHere"); UNREFERENCED_PARAMETER(wBlockFlags); // Does the NPC want to walk here ? step on this item ? if ( !m_pNPC ) return false; if ( !pt.IsValidXY() ) return true; if ( m_pArea != NULL ) { if ( m_pNPC->m_Brain == NPCBRAIN_GUARD && !IsStatFlag(STATF_War) ) // guards will want to stay in guard range { if ( m_pArea->IsGuarded() && !pArea->IsGuarded() ) return false; } if ( Noto_IsCriminal() ) { if ( !m_pArea->IsGuarded() && pArea->IsGuarded() ) return false; } } // Is there a nasty object here that will hurt us ? CWorldSearch AreaItems(pt); for (;;) { CItem * pItem = AreaItems.GetItem(); if ( pItem == NULL ) break; if ( abs(pItem->GetTopZ() - pt.m_z) > 5 ) continue; switch ( pItem->GetType() ) { case IT_WEB: return (GetDispID() == CREID_GIANT_SPIDER) ? true : false; case IT_FIRE: return Can(CAN_C_FIRE_IMMUNE); case IT_TRAP: case IT_TRAP_ACTIVE: case IT_MOONGATE: case IT_TELEPAD: return false; default: break; } } return true; }
int CChar::NPC_GetVendorMarkup( const CChar * pChar ) const { ADDTOCALLSTACK("CChar::NPC_GetVendorMarkup"); // This vendor marks stuff up/down this percentage. // Base this on KARMA. Random is calculated at Restock time // When vendor sells to players this is the markup value. // fBuy: Client buying // RETURN: // 0-100 if ( !pChar || IsStatFlag(STATF_Pet) ) // Not on a hired vendor. return( 0 ); CVarDefCont *pVar = NULL, *pVarCharDef = NULL; CCharBase * pCharDef = Char_GetDef(); if ( pCharDef ) { // get markup value of NPC-chardef pVarCharDef = pCharDef->m_TagDefs.GetKey("VENDORMARKUP"); } int iHostility = maximum(NPC_GetHostilityLevelToward(pChar), 0); iHostility = minimum(iHostility + 15, 100); pVar = m_TagDefs.GetKey("VENDORMARKUP"); if ( pVar ) { iHostility += static_cast<int>(pVar->GetValNum()); // add NPC's markup to hostility made by karma difference } else { pVar = GetRegion()->m_TagDefs.GetKey("VENDORMARKUP"); if ( pVar ) { iHostility += static_cast<int>(pVar->GetValNum()); // if NPC is unmarked, look if the region is } else { // neither NPC nor REGION are marked, so look for the chardef if ( pVarCharDef ) { iHostility += static_cast<int>(pVarCharDef->GetValNum()); } } } return( iHostility ); }
CChar * CChar::NPC_PetGetOwner() const { ADDTOCALLSTACK("CChar::NPC_PetGetOwner"); // Assume i am a pet. Get my first (primary) owner. not just friends. used for blame . if ( !IsStatFlag(STATF_Pet) ) return NULL; CItemMemory *pMemory = Memory_FindTypes(MEMORY_IPET); if ( !pMemory ) return NULL; return pMemory->m_uidLink.CharFind(); }
void CChar::NPC_PetClearOwners(bool bResendTooltip) { ADDTOCALLSTACK("CChar::NPC_PetClearOwners"); CChar *pOwner = NPC_PetGetOwner(); Memory_ClearTypes(MEMORY_IPET|MEMORY_FRIEND); if ( m_pNPC ) m_pNPC->m_bonded = 0; // pets without owner cannot be bonded if ( NPC_IsVendor() ) { StatFlag_Clear(STATF_INVUL); if ( pOwner ) // give back to NPC owner all the stuff we are trying to sell { CItemContainer *pBankVendor = GetContainerCreate(LAYER_BANKBOX); CItemContainer *pBankOwner = pOwner->GetContainerCreate(LAYER_BANKBOX); pOwner->AddGoldToPack(pBankVendor->m_itEqBankBox.m_Check_Amount, pBankOwner); pBankVendor->m_itEqBankBox.m_Check_Amount = 0; for ( size_t i = 0; i < COUNTOF(sm_VendorLayers); i++ ) { CItemContainer *pCont = GetContainerCreate(sm_VendorLayers[i]); if ( !pCont ) continue; CItem *pItemNext = NULL; for ( CItem *pItem = pCont->GetContentHead(); pItem != NULL; pItem = pItemNext ) { pItemNext = pItem->GetNext(); pBankOwner->ContentAdd(pItem); } } } } if ( IsStatFlag(STATF_Ridden) ) { CChar *pCharRider = Horse_GetMountChar(); if ( pCharRider ) pCharRider->Horse_UnMount(); } if ( pOwner ) { if ( IsSetOF(OF_PetSlots) ) pOwner->FollowersUpdate(this, static_cast<short>(-maximum(1, GetDefNum("FOLLOWERSLOTS", true)))); if ( bResendTooltip ) ResendTooltip(); } }
bool CChar::NPC_OnHireHear( CChar * pCharSrc ) { ADDTOCALLSTACK("CChar::NPC_OnHireHear"); if ( !m_pNPC ) return false; CCharBase * pCharDef = Char_GetDef(); unsigned int iWage = pCharDef->GetHireDayWage(); if ( ! iWage ) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_NOT_FOR_HIRE ) ); return false; } CItemMemory * pMemory = Memory_FindObj( pCharSrc ); if ( pMemory ) { if ( pMemory->IsMemoryTypes(MEMORY_IPET|MEMORY_FRIEND)) { // Next gold i get goes toward hire. pMemory->m_itEqMemory.m_Action = NPC_MEM_ACT_SPEAK_HIRE; NPC_OnHirePayMore( NULL, false ); return true; } if ( pMemory->IsMemoryTypes( MEMORY_FIGHT|MEMORY_HARMEDBY|MEMORY_IRRITATEDBY )) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_NOT_WORK ) ); return false; } } if ( IsStatFlag( STATF_Pet )) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_EMPLOYED ) ); return false; } TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, Calc_GetRandVal(2) ? g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_HIRE_AMNT ) : g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_HIRE_RATE ), iWage ); Speak(pszMsg); pMemory = Memory_AddObjTypes( pCharSrc, MEMORY_SPEAK ); if ( pMemory ) pMemory->m_itEqMemory.m_Action = NPC_MEM_ACT_SPEAK_HIRE; return true; }
bool CChar::NPC_OnHirePay( CChar * pCharSrc, CItemMemory * pMemory, CItem * pGold ) { ADDTOCALLSTACK("CChar::NPC_OnHirePay"); if ( !m_pNPC || !pCharSrc || !pMemory ) return false; CCharBase * pCharDef = Char_GetDef(); if ( IsStatFlag( STATF_Pet )) { if ( ! pMemory->IsMemoryTypes(MEMORY_IPET|MEMORY_FRIEND)) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_EMPLOYED ) ); return false; } } else { unsigned int iWage = pCharDef->GetHireDayWage(); if ( iWage <= 0 ) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_NOT_FOR_HIRE ) ); return false; } if ( pGold->GetAmount() < iWage ) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_NOT_ENOUGH ) ); return false; } // NOTO_TYPE ? if ( pMemory->IsMemoryTypes( MEMORY_FIGHT|MEMORY_HARMEDBY|MEMORY_IRRITATEDBY )) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_NOT_WORK ) ); return false; } // Put all my loot cash away. ContentConsume( RESOURCE_ID(RES_TYPEDEF,IT_GOLD), INT_MAX, false, 0 ); // Mark all my stuff ATTR_OWNED - i won't give it away. ContentAttrMod( ATTR_OWNED, true ); NPC_PetSetOwner( pCharSrc ); } pMemory->m_itEqMemory.m_Action = NPC_MEM_ACT_NONE; NPC_OnHirePayMore( pGold, true ); return true; }
bool CChar::NPC_IsOwnedBy(const CChar *pChar, bool fAllowGM) const { ADDTOCALLSTACK("CChar::NPC_IsOwnedBy"); // Is pChar my master ? // BESERK will not listen to commands tho. // fAllowGM = consider GM's to be owners of all NPC's if ( !pChar ) return false; if ( pChar == this ) return true; if ( fAllowGM && pChar->IsPriv(PRIV_GM) ) return (pChar->GetPrivLevel() > GetPrivLevel()); if ( !m_pNPC || !IsStatFlag(STATF_Pet) ) // shortcut - i'm not a pet. return false; if ( m_pNPC->m_Brain == NPCBRAIN_BERSERK ) // i cannot be commanded. return false; return (Memory_FindObjTypes(pChar, MEMORY_IPET) != NULL); }
int CChar::NPC_GetVendorMarkup() const { ADDTOCALLSTACK("CChar::NPC_GetVendorMarkup"); // This vendor marks stuff up/down this percentage. // When vendor sells to players this is the markup value. // // RETURN: // +100% = double price // 0% = default price // -100% = free if ( IsStatFlag(STATF_Pet) ) // not on a hired vendor return 0; // Use char value CVarDefCont *pVar = m_TagDefs.GetKey("VENDORMARKUP"); if ( pVar ) return static_cast<int>(pVar->GetValNum()); // Use region value if ( m_pArea ) { pVar = m_pArea->m_TagDefs.GetKey("VENDORMARKUP"); if ( pVar ) return static_cast<int>(pVar->GetValNum()); } // Use chardef value CCharBase *pCharDef = Char_GetDef(); if ( pCharDef ) { pVar = pCharDef->m_TagDefs.GetKey("VENDORMARKUP"); if ( pVar ) return static_cast<int>(pVar->GetValNum()); } // Use default value return 15; }
bool CChar::NPC_PetCheckAccess(int iCmd, CChar *pChar) { ADDTOCALLSTACK("CChar::NPC_PetCheckAccess"); // Check if pChar can use the required pet command if ( !m_pNPC || !pChar ) return false; switch ( iCmd ) { case PC_COME: case PC_FOLLOW: case PC_FOLLOW_ME: case PC_STAY: case PC_STOP: { // Pet friends can use only these commands if ( Memory_FindObjTypes(pChar, MEMORY_FRIEND) ) break; } default: { // All others commands are available only to pet owner if ( !NPC_IsOwnedBy(pChar) ) return false; } } if ( IsStatFlag(STATF_DEAD) ) { // Dead bonded pets can only listen to these commands if ( !((iCmd == PC_COME) || (iCmd == PC_FOLLOW) || (iCmd == PC_FOLLOW_ME) || (iCmd == PC_STAY) || (iCmd == PC_STOP) || (iCmd == PC_RELEASE)) ) return false; } return true; }
bool CChar::NPC_OnHearPetCmd( LPCTSTR pszCmd, CChar *pSrc, bool fAllPets ) { ADDTOCALLSTACK("CChar::NPC_OnHearPetCmd"); // This should just be another speech block !!! // We own this char (pet or hireling) // pObjTarget = the m_ActTarg has been set for them to attack. // RETURN: // true = we understand this. tho we may not do what we are told. // false = this is not a command we know. // if ( GetTargMode() == CLIMODE_TARG_PET_CMD ) it needs a target. if ( !pSrc->IsClient() || m_pPlayer || !m_pNPC ) return false; m_fIgnoreNextPetCmd = false; // We clear this incase it's true from previous pet cmds. TALKMODE_TYPE mode = TALKMODE_SAY; if ( OnTriggerSpeech(true, pszCmd, pSrc, mode) ) { m_fIgnoreNextPetCmd = !fAllPets; return true; } static LPCTSTR const sm_Pet_table[] = { "ATTACK", "BOUGHT", "CASH", "COME", "DROP", // "GIVE" ? "DROP ALL", "EQUIP", "FOLLOW", "FOLLOW ME", "FRIEND", "GO", "GUARD", "GUARD ME", "KILL", "PRICE", // may have args ? "RELEASE", "SAMPLES", "SPEAK", "STATUS", "STAY", "STOCK", "STOP", "TRANSFER", "UNFRIEND" }; PC_TYPE iCmd = static_cast<PC_TYPE>(FindTableSorted(pszCmd, sm_Pet_table, COUNTOF(sm_Pet_table))); if ( iCmd < 0 ) { if ( !strnicmp(pszCmd, sm_Pet_table[PC_PRICE], 5) ) iCmd = PC_PRICE; else return false; } switch ( iCmd ) { case PC_FOLLOW: case PC_STAY: case PC_STOP: { // Pet friends can use only these commands if ( Memory_FindObjTypes(pSrc, MEMORY_FRIEND) ) break; } default: { // All others commands are avaible only to pet owner if ( !NPC_IsOwnedBy(pSrc, true) ) return false; } } if ( IsStatFlag(STATF_DEAD) ) { // Bonded NPCs still placed on world even when dead. // They can listen to commands, but not to these commands below if ( iCmd == PC_GUARD || iCmd == PC_GUARD_ME || iCmd == PC_ATTACK || iCmd == PC_KILL || iCmd == PC_TRANSFER || iCmd == PC_DROP || iCmd == PC_DROP_ALL ) return true; } bool bTargAllowGround = false; bool bCheckCrime = false; LPCTSTR pTargPrompt = NULL; CCharBase *pCharDef = Char_GetDef(); switch ( iCmd ) { case PC_ATTACK: case PC_KILL: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_ATT); bCheckCrime = true; break; case PC_COME: case PC_GUARD_ME: case PC_FOLLOW_ME: m_Act_Targ = pSrc->GetUID(); Skill_Start(NPCACT_FOLLOW_TARG); break; case PC_FOLLOW: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FOLLOW); break; case PC_FRIEND: if ( IsStatFlag(STATF_Conjured) ) { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND_SUMMONED)); return false; } pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND); break; case PC_UNFRIEND: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_UNFRIEND); break; case PC_GO: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_GO); bTargAllowGround = true; break; case PC_GUARD: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_GUARD); bCheckCrime = true; break; case PC_STAY: case PC_STOP: Skill_Start(NPCACT_STAY); break; case PC_TRANSFER: if ( IsStatFlag(STATF_Conjured) ) { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_TRANSFER_SUMMONED)); return true; } pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_TRANSFER); break; case PC_RELEASE: SoundChar(CRESND_RAND2); if ( IsStatFlag(STATF_Conjured) || (m_pNPC->m_bonded && IsStatFlag(STATF_DEAD)) ) { Delete(); return true; } Skill_Start(SKILL_NONE); NPC_PetClearOwners(); ResendTooltip(); break; case PC_DROP: { // Drop backpack items on ground // NOTE: This is also called on pet release CItemContainer *pPack = GetPack(); if ( pPack ) { pPack->ContentsDump(GetTopPoint(), ATTR_OWNED); break; } if ( NPC_CanSpeak() ) Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_CARRYNOTHING)); return true; } case PC_DROP_ALL: DropAll(NULL, ATTR_OWNED); break; case PC_SPEAK: NPC_OnPetCommand(true, pSrc); return true; case PC_EQUIP: ItemEquipWeapon(false); ItemEquipArmor(false); break; case PC_STATUS: { if ( !NPC_CanSpeak() ) break; unsigned int iWage = pCharDef->GetHireDayWage(); CItemContainer *pBank = GetBank(); TCHAR *pszMsg = Str_GetTemp(); if ( NPC_IsVendor() ) { CItemContainer *pCont = GetBank(LAYER_VENDOR_STOCK); TCHAR *pszTemp1 = Str_GetTemp(); TCHAR *pszTemp2 = Str_GetTemp(); TCHAR *pszTemp3 = Str_GetTemp(); if ( iWage ) { sprintf(pszTemp1, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_1), pBank->m_itEqBankBox.m_Check_Amount); sprintf(pszTemp2, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_2), pBank->m_itEqBankBox.m_Check_Amount / iWage); sprintf(pszTemp3, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_3), static_cast<int>(pCont->GetCount())); } else { sprintf(pszTemp1, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_1), pBank->m_itEqBankBox.m_Check_Amount); sprintf(pszTemp2, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_4), pBank->m_itEqBankBox.m_Check_Restock, pBank->GetTimerAdjusted() / 60); sprintf(pszTemp3, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_3), static_cast<int>(pCont->GetCount())); } sprintf(pszMsg, "%s %s %s", pszTemp1, pszTemp2, pszTemp3); } else if ( iWage ) { sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_DAYS_LEFT), pBank->m_itEqBankBox.m_Check_Amount / iWage); } Speak(pszMsg); return true; } case PC_CASH: { // Give up my cash total. if ( !NPC_IsVendor() ) return false; CItemContainer *pBank = GetBank(); if ( pBank ) { unsigned int iWage = pCharDef->GetHireDayWage(); TCHAR *pszMsg = Str_GetTemp(); if ( pBank->m_itEqBankBox.m_Check_Amount > iWage ) { sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_GETGOLD_1), pBank->m_itEqBankBox.m_Check_Amount - iWage); pSrc->AddGoldToPack(pBank->m_itEqBankBox.m_Check_Amount - iWage); pBank->m_itEqBankBox.m_Check_Amount = iWage; } else sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_GETGOLD_2), pBank->m_itEqBankBox.m_Check_Amount); Speak(pszMsg); } return true; } case PC_BOUGHT: if ( !NPC_IsVendor() ) return false; Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_ITEMS_BUY)); pSrc->GetClient()->addBankOpen(this, LAYER_VENDOR_EXTRA); break; case PC_PRICE: if ( !NPC_IsVendor() ) return false; pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_SETPRICE); break; case PC_SAMPLES: if ( !NPC_IsVendor() ) return false; Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_ITEMS_SAMPLE)); pSrc->GetClient()->addBankOpen(this, LAYER_VENDOR_BUYS); break; case PC_STOCK: // Magic restocking container. if ( !NPC_IsVendor() ) return false; Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_ITEMS_SELL)); pSrc->GetClient()->addBankOpen(this, LAYER_VENDOR_STOCK); break; default: return false; } if ( pTargPrompt ) { pszCmd += strlen(sm_Pet_table[iCmd]); GETNONWHITESPACE(pszCmd); pSrc->m_pClient->m_tmPetCmd.m_iCmd = iCmd; pSrc->m_pClient->m_tmPetCmd.m_fAllPets = fAllPets; pSrc->m_pClient->m_Targ_UID = GetUID(); pSrc->m_pClient->m_Targ_Text = pszCmd; pSrc->m_pClient->addTarget(CLIMODE_TARG_PET_CMD, pTargPrompt, bTargAllowGround, bCheckCrime); return true; } // Make some sound to confirm we heard it NPC_OnPetCommand(true, pSrc); return true; }
bool CChar::NPC_OnHearPetCmdTarg( int iCmd, CChar *pSrc, CObjBase *pObj, const CPointMap &pt, LPCTSTR pszArgs ) { ADDTOCALLSTACK("CChar::NPC_OnHearPetCmdTarg"); // Pet commands that required a target. if ( m_fIgnoreNextPetCmd ) { m_fIgnoreNextPetCmd = false; return false; } switch ( iCmd ) { case PC_FOLLOW: case PC_STAY: case PC_STOP: { // Pet friends can use only these commands if ( Memory_FindObjTypes(pSrc, MEMORY_FRIEND) ) break; } default: { // All others commands are avaible only to pet owner if ( !NPC_IsOwnedBy(pSrc, true) ) return false; } } if ( IsStatFlag(STATF_DEAD) ) { // Bonded NPCs still placed on world even when dead. // They can listen to commands, but not to these commands below if ( iCmd == PC_GUARD || iCmd == PC_GUARD_ME || iCmd == PC_ATTACK || iCmd == PC_KILL || iCmd == PC_TRANSFER || iCmd == PC_DROP || iCmd == PC_DROP_ALL ) return true; } bool bSuccess = false; CItem *pItemTarg = dynamic_cast<CItem *>(pObj); CChar *pCharTarg = dynamic_cast<CChar *>(pObj); switch ( iCmd ) { case PC_ATTACK: case PC_KILL: { if ( !pCharTarg || pCharTarg == pSrc ) break; bSuccess = pCharTarg->OnAttackedBy(pSrc, 1, true); // we know who told them to do this. if ( bSuccess ) bSuccess = Fight_Attack(pCharTarg, true); break; } case PC_FOLLOW: if ( !pCharTarg ) break; m_Act_Targ = pCharTarg->GetUID(); bSuccess = Skill_Start(NPCACT_FOLLOW_TARG); break; case PC_FRIEND: { if ( !pCharTarg || !pCharTarg->m_pPlayer || pCharTarg == pSrc ) { Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_CONFUSED)); break; } CItemMemory *pMemory = Memory_FindObjTypes(pCharTarg, MEMORY_FRIEND); if ( pMemory ) { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND_ALREADY)); break; } pSrc->SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND_SUCCESS1), GetName(), pCharTarg->GetName()); pCharTarg->SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND_SUCCESS2), pSrc->GetName(), GetName()); Memory_AddObjTypes(pCharTarg, MEMORY_FRIEND); m_Act_Targ = pCharTarg->GetUID(); bSuccess = Skill_Start(NPCACT_FOLLOW_TARG); break; } case PC_UNFRIEND: { if ( !pCharTarg || !pCharTarg->m_pPlayer ) { Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_CONFUSED)); break; } CItemMemory *pMemory = Memory_FindObjTypes(pCharTarg, MEMORY_FRIEND); if ( !pMemory ) { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_UNFRIEND_NOTFRIEND)); break; } pSrc->SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_UNFRIEND_SUCCESS1), GetName(), pCharTarg->GetName()); pCharTarg->SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_UNFRIEND_SUCCESS2), pSrc->GetName(), GetName()); pMemory->Delete(); m_Act_Targ = pSrc->GetUID(); bSuccess = Skill_Start(NPCACT_FOLLOW_TARG); break; } case PC_GO: if ( !pt.IsValidPoint() ) break; m_Act_p = pt; bSuccess = Skill_Start(NPCACT_GOTO); break; case PC_GUARD: if ( !pCharTarg ) break; pCharTarg->SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_GUARD_SUCCESS), GetName()); m_Act_Targ = pCharTarg->GetUID(); bSuccess = Skill_Start(NPCACT_GUARD_TARG); break; case PC_TRANSFER: if ( !pCharTarg || !pCharTarg->IsClient() ) break; if ( IsSetOF(OF_PetSlots) ) { if ( !pCharTarg->FollowersUpdate(this, static_cast<short>(maximum(1, GetDefNum("FOLLOWERSLOTS", true, true))), true) ) { pSrc->SysMessageDefault(DEFMSG_PETSLOTS_TRY_TRANSFER); break; } } bSuccess = NPC_PetSetOwner( pCharTarg ); break; case PC_PRICE: // "PRICE" the vendor item. if ( !pItemTarg || !NPC_IsVendor() || !pSrc->IsClient() ) break; if ( IsDigit(pszArgs[0]) ) // did they name a price return NPC_SetVendorPrice(pItemTarg, ATOI(pszArgs)); if ( !NPC_SetVendorPrice(pItemTarg, -1) ) // test if it can be priced return false; pSrc->m_pClient->addPromptConsole(CLIMODE_PROMPT_VENDOR_PRICE, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_SETPRICE_2), pItemTarg->GetUID(), GetUID()); return true; default: break; } // Make some sound to confirm we heard it NPC_OnPetCommand(bSuccess, pSrc); return bSuccess; }
bool CChar::NPC_OnHearPetCmd(LPCTSTR pszCmd, CChar *pSrc, bool bAllPets) { ADDTOCALLSTACK("CChar::NPC_OnHearPetCmd"); // This should just be another speech block !!! // We own this char (pet or hireling) // pObjTarget = the m_ActTarg has been set for them to attack. // RETURN: // true = we understand this. tho we may not do what we are told. // false = this is not a command we know. // if ( GetTargMode() == CLIMODE_TARG_PET_CMD ) it needs a target. if ( !m_pNPC || !pSrc->m_pClient ) return false; m_fIgnoreNextPetCmd = false; // we clear this incase it's true from previous pet commands TALKMODE_TYPE mode = TALKMODE_SAY; if ( OnTriggerSpeech(true, pszCmd, pSrc, mode) ) { m_fIgnoreNextPetCmd = !bAllPets; return true; } if ((m_pNPC->m_Brain == NPCBRAIN_BERSERK) && !pSrc->IsPriv(PRIV_GM)) return false; // Berserk npcs do not listen to any command (except if src is a GM) static LPCTSTR const sm_PetCommands[] = { "ATTACK", "BOUGHT", "CASH", "COME", "DROP", "DROP ALL", "EQUIP", "FOLLOW", "FOLLOW ME", "FRIEND", "GO", "GUARD", "GUARD ME", "KILL", "PRICE", "RELEASE", "SAMPLES", "SPEAK", "STATUS", "STAY", "STOCK", "STOP", "TRANSFER", "UNFRIEND" }; PC_TYPE iCmd = static_cast<PC_TYPE>(FindTableSorted(pszCmd, sm_PetCommands, COUNTOF(sm_PetCommands))); if ( iCmd < 0 ) { if ( !strnicmp(pszCmd, sm_PetCommands[PC_PRICE], 5) ) iCmd = PC_PRICE; else return false; } if ( !NPC_PetCheckAccess(iCmd, pSrc) ) return true; bool bTargAllowGround = false; bool bCheckCrime = false; LPCTSTR pTargPrompt = NULL; CCharBase *pCharDef = Char_GetDef(); switch ( iCmd ) { case PC_ATTACK: case PC_KILL: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_ATT); bCheckCrime = true; break; case PC_COME: case PC_FOLLOW_ME: NPC_OnHearPetCmdTarg(PC_FOLLOW, pSrc, pSrc, NULL, NULL); break; case PC_FOLLOW: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FOLLOW); break; case PC_FRIEND: if ( IsStatFlag(STATF_Conjured) ) { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND_SUMMONED)); return false; } pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_FRIEND); break; case PC_UNFRIEND: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_UNFRIEND); break; case PC_GO: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_GO); bTargAllowGround = true; break; case PC_GUARD: pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_GUARD); bCheckCrime = true; break; case PC_GUARD_ME: NPC_OnHearPetCmdTarg(PC_GUARD, pSrc, pSrc, NULL, NULL); break; case PC_STAY: case PC_STOP: Skill_Start(NPCACT_STAY); break; case PC_TRANSFER: if ( IsStatFlag(STATF_Conjured) ) { pSrc->SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_TRANSFER_SUMMONED)); return true; } pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_TARG_TRANSFER); break; case PC_RELEASE: if ( IsStatFlag(STATF_Conjured) || (m_pNPC->m_bonded && IsStatFlag(STATF_DEAD)) ) { Effect(EFFECT_XYZ, ITEMID_FX_TELE_VANISH, this, 10, 15); Sound(SOUND_TELEPORT); Delete(); return true; } SoundChar(CRESND_NOTICE); Skill_Start(SKILL_NONE); NPC_PetClearOwners(); ResendTooltip(); break; case PC_DROP: { // Drop backpack items on ground // NOTE: This is also called on pet release CItemContainer *pPack = GetContainer(LAYER_PACK); if ( pPack ) { pPack->ContentsDump(GetTopPoint(), ATTR_OWNED); break; } if ( NPC_CanSpeak() ) Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_CARRYNOTHING)); return true; } case PC_DROP_ALL: DropAll(NULL, ATTR_OWNED); break; case PC_SPEAK: NPC_PetConfirmCommand(true, pSrc); return true; case PC_EQUIP: ItemEquipWeapon(false); ItemEquipArmor(false); break; case PC_STATUS: { if ( !NPC_CanSpeak() ) break; CItemContainer *pBank = GetContainerCreate(LAYER_BANKBOX); TCHAR *pszMsg = Str_GetTemp(); if ( NPC_IsVendor() ) { CItemContainer *pCont = GetContainerCreate(LAYER_VENDOR_STOCK); TCHAR *pszTemp1 = Str_GetTemp(); TCHAR *pszTemp2 = Str_GetTemp(); TCHAR *pszTemp3 = Str_GetTemp(); if ( pCharDef->m_iHireDayWage ) { sprintf(pszTemp1, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_1), pBank->m_itEqBankBox.m_Check_Amount); sprintf(pszTemp2, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_2), pBank->m_itEqBankBox.m_Check_Amount / pCharDef->m_iHireDayWage); sprintf(pszTemp3, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_3), static_cast<int>(pCont->GetCount())); } else { sprintf(pszTemp1, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_1), pBank->m_itEqBankBox.m_Check_Amount); sprintf(pszTemp2, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_4), pBank->m_itEqBankBox.m_Check_Restock, pBank->GetTimerAdjusted() / 60); sprintf(pszTemp3, g_Cfg.GetDefaultMsg(DEFMSG_NPC_VENDOR_STAT_GOLD_3), static_cast<int>(pCont->GetCount())); } sprintf(pszMsg, "%s %s %s", pszTemp1, pszTemp2, pszTemp3); } else if ( pCharDef->m_iHireDayWage ) { sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_DAYS_LEFT), pBank->m_itEqBankBox.m_Check_Amount / pCharDef->m_iHireDayWage); } Speak(pszMsg); return true; } case PC_CASH: { // Give up my cash total. if ( !NPC_IsVendor() ) return false; CItemContainer *pBank = GetContainerCreate(LAYER_BANKBOX); if ( pBank ) { TCHAR *pszMsg = Str_GetTemp(); if ( pBank->m_itEqBankBox.m_Check_Amount > pCharDef->m_iHireDayWage ) { sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_GETGOLD_1), pBank->m_itEqBankBox.m_Check_Amount - pCharDef->m_iHireDayWage); pSrc->AddGoldToPack(pBank->m_itEqBankBox.m_Check_Amount - pCharDef->m_iHireDayWage); pBank->m_itEqBankBox.m_Check_Amount = pCharDef->m_iHireDayWage; } else sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_GETGOLD_2), pBank->m_itEqBankBox.m_Check_Amount); Speak(pszMsg); } return true; } case PC_BOUGHT: if ( !NPC_IsVendor() ) return false; Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_ITEMS_BUY)); pSrc->m_pClient->addBankOpen(this, LAYER_VENDOR_EXTRA); break; case PC_PRICE: if ( !NPC_IsVendor() ) return false; pTargPrompt = g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_SETPRICE); break; case PC_SAMPLES: if ( !NPC_IsVendor() ) return false; Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_ITEMS_SAMPLE)); pSrc->m_pClient->addBankOpen(this, LAYER_VENDOR_BUYS); break; case PC_STOCK: // Magic restocking container. if ( !NPC_IsVendor() ) return false; Speak(g_Cfg.GetDefaultMsg(DEFMSG_NPC_PET_ITEMS_SELL)); pSrc->m_pClient->addBankOpen(this, LAYER_VENDOR_STOCK); break; default: return false; } if ( pTargPrompt ) { pszCmd += strlen(sm_PetCommands[iCmd]); GETNONWHITESPACE(pszCmd); pSrc->m_pClient->m_tmPetCmd.m_iCmd = iCmd; pSrc->m_pClient->m_tmPetCmd.m_fAllPets = bAllPets; pSrc->m_pClient->m_Targ_UID = GetUID(); pSrc->m_pClient->m_Targ_Text = pszCmd; pSrc->m_pClient->addTarget(CLIMODE_TARG_PET_CMD, pTargPrompt, bTargAllowGround, bCheckCrime); return true; } // Make some sound to confirm we heard it NPC_PetConfirmCommand(true, pSrc); return true; }
bool CChar::NPC_Vendor_Restock(bool bForce, bool bFillStock) { ADDTOCALLSTACK("CChar::NPC_Vendor_Restock"); // Restock this NPC char. // Then Set the next restock time for this . if ( m_pNPC == NULL ) return false; // Make sure that we're a vendor and not a pet if ( IsStatFlag(STATF_Pet) || !NPC_IsVendor() ) return false; bool bRestockNow = true; if ( !bForce && m_pNPC->m_timeRestock.IsTimeValid() ) { // Restock occurs every 10 minutes of inactivity (unless // region tag specifies different time) CRegionWorld *region = GetRegion(); int64 restockIn = 10 * 60 * TICK_PER_SEC; if( region != NULL ) { CVarDefCont *vardef = region->m_TagDefs.GetKey("RestockVendors"); if( vardef != NULL ) restockIn = vardef->GetValNum(); if ( region->m_TagDefs.GetKey("NoRestock") != NULL ) bRestockNow = false; } if ( m_TagDefs.GetKey("NoRestock") != NULL ) bRestockNow = false; if (bRestockNow) bRestockNow = ( CServerTime::GetCurrentTime().GetTimeDiff(m_pNPC->m_timeRestock) > restockIn ); } // At restock the containers are actually emptied if ( bRestockNow ) { m_pNPC->m_timeRestock.Init(); for ( size_t i = 0; i < CountOf(sm_VendorLayers); ++i ) { CItemContainer *pCont = GetBank(sm_VendorLayers[i]); if ( !pCont ) return false; pCont->Empty(); } } if ( bFillStock ) { // An invalid restock time means that the containers are // waiting to be filled if ( !m_pNPC->m_timeRestock.IsTimeValid() ) { if ( IsTrigUsed(TRIGGER_NPCRESTOCK) ) { CCharBase *pCharDef = Char_GetDef(); ReadScriptTrig(pCharDef, CTRIG_NPCRestock, true); } // we need restock vendor money as well GetBank()->Restock(); } // remember that the stock was filled (or considered up-to-date) m_pNPC->m_timeRestock.SetCurrentTime(); } return true; }
bool CChar::Use_Item_Web( CItem * pItemWeb ) { ADDTOCALLSTACK("CChar::Use_Item_Web"); // IT_WEB // IT_EQ_STUCK // Try to break out of the web. // Or just try to damage it. // // RETURN: true = held in place. // false = walk thru it. if ( GetDispID() == CREID_GIANT_SPIDER || !pItemWeb || !pItemWeb->IsTopLevel() || IsStatFlag(STATF_DEAD|STATF_Insubstantial) || IsPriv(PRIV_GM) ) return false; // just walk through it // Try to break it. int iStr = pItemWeb->m_itWeb.m_Hits_Cur; if ( iStr == 0 ) iStr = pItemWeb->m_itWeb.m_Hits_Cur = 60 + Calc_GetRandVal(250); // Since broken webs become spider silk, we should get out of here now if we aren't in a web. CItem *pFlag = LayerFind(LAYER_FLAG_Stuck); if ( CanMove(pItemWeb, false) ) { if ( pFlag ) pFlag->Delete(); return false; } if ( pFlag ) { if ( pFlag->IsTimerSet() ) // don't allow me to try to damage it too often return true; } int iDmg = pItemWeb->OnTakeDamage(Stat_GetAdjusted(STAT_STR), this); switch ( iDmg ) { case 0: // damage blocked case 1: // web survived default: // unknown if ( GetTopPoint() == pItemWeb->GetTopPoint() ) // is character still stuck on the web? break; case 2: // web turned into silk case INT_MAX: // web destroyed if ( pFlag ) pFlag->Delete(); return false; } // Stuck in it still. if ( !pFlag ) { if ( iDmg < 0 ) return false; // First time message. pFlag = CItem::CreateBase(ITEMID_WEB1_1); ASSERT(pFlag); pFlag->SetAttr(ATTR_DECAY); pFlag->SetType(IT_EQ_STUCK); pFlag->m_uidLink = pItemWeb->GetUID(); pFlag->SetTimeout(pItemWeb->GetTimerDAdjusted()); LayerAdd(pFlag, LAYER_FLAG_Stuck); } else { if ( iDmg < 0 ) { pFlag->Delete(); return false; } SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SWEB_STUCK), pItemWeb->GetName()); } return true; }
int CChar::NPC_GetVendorMarkup( const CChar * pChar ) const { ADDTOCALLSTACK("CChar::NPC_GetVendorMarkup"); // This vendor marks stuff up/down this percentage. // Base this on KARMA. Random is calculated at Restock time // When vendor sells to players this is the markup value. // fBuy: Client buying // RETURN: // 0-100 if ( !pChar || IsStatFlag(STATF_Pet) ) // Not on a hired vendor. return( 0 ); CVarDefCont *pVarRegion = NULL, *pVarCharDef = NULL, *pVarNPC = NULL, *pVarPlayer = NULL; CCharBase * pCharDef = Char_GetDef(); // NPC CharDef int iMarkUp = 0; if ( pCharDef ) { // get markup value of NPC-chardef pVarCharDef = pCharDef->m_TagDefs.GetKey("VENDORMARKUP"); } pVarPlayer = pChar->m_TagDefs.GetKey("VENDORMARKUP"); int iHostility = maximum(NPC_GetHostilityLevelToward(pChar), 0); iHostility = minimum(iHostility + 15, 100); // go thru all possible tweaks // Region's markup pVarRegion = GetRegion()->m_TagDefs.GetKey("VENDORMARKUP"); if ( pVarRegion ) { iMarkUp = pVarRegion->GetValNum(); } // NPC's CharDef's markup if ( pVarCharDef ) { iMarkUp += pVarCharDef->GetValNum(); } // NPC's personal markup pVarNPC = m_TagDefs.GetKey("VENDORMARKUP"); if ( pVarNPC ) { iMarkUp += pVarNPC->GetValNum(); } // finally: Player char's markup pVarPlayer = pChar->m_TagDefs.GetKey("VENDORMARKUP"); if ( pVarPlayer ) { iMarkUp += pVarPlayer->GetValNum(); } // ensure that the return value is between -100 or positive iMarkUp += iHostility; if ( iMarkUp < -100 ) return (-100); else return ( iMarkUp ); }