void CClient::addPromptConsoleFunction( LPCTSTR pszFunction, LPCTSTR pszSysmessage, bool bUnicode ) { ADDTOCALLSTACK("CClient::addPromptConsoleFunction"); // Target a verb at some object . ASSERT(pszFunction); m_Prompt_Text = pszFunction; addPromptConsole( CLIMODE_PROMPT_SCRIPT_VERB, pszSysmessage, 0, 0, bUnicode ); }
bool CClient::Cmd_Use_Item( CItem *pItem, bool fTestTouch, bool fScript ) { ADDTOCALLSTACK("CClient::Cmd_Use_Item"); // Assume we can see the target. // called from Event_DoubleClick if ( !pItem ) return false; if ( pItem->m_Can & CAN_I_FORCEDC ) fTestTouch = false; if ( fTestTouch ) { if ( !fScript ) { CItemContainer *pContainer = dynamic_cast<CItemContainer *>(pItem->GetParent()); if ( pContainer ) { // protect from ,snoop - disallow picking from not opened containers bool isInOpenedContainer = false; if ( pContainer->GetType() == IT_EQ_TRADE_WINDOW ) isInOpenedContainer = true; else { CClient::OpenedContainerMap_t::iterator itContainerFound = m_openedContainers.find(pContainer->GetUID().GetPrivateUID()); if ( itContainerFound != m_openedContainers.end() ) { DWORD dwTopContainerUID = (((*itContainerFound).second).first).first; DWORD dwTopMostContainerUID = (((*itContainerFound).second).first).second; CPointMap ptOpenedContainerPosition = ((*itContainerFound).second).second; const CObjBaseTemplate *pObjTop = pItem->GetTopLevelObj(); const CObjBase *pObjParent = pContainer->GetParentObj(); DWORD dwTopContainerUID_ToCheck = 0; if ( pObjParent ) dwTopContainerUID_ToCheck = pObjParent->GetUID().GetPrivateUID(); else dwTopContainerUID_ToCheck = pObjTop->GetUID().GetPrivateUID(); if ( (dwTopMostContainerUID == pObjTop->GetUID().GetPrivateUID()) && (dwTopContainerUID == dwTopContainerUID_ToCheck) ) { if ( pObjTop->IsChar() ) { // probably a pickup check from pack if pCharTop != this? isInOpenedContainer = true; } else { const CItem *pItemTop = static_cast<const CItem *>(pObjTop); if ( pItemTop && (pItemTop->IsType(IT_SHIP_HOLD) || pItemTop->IsType(IT_SHIP_HOLD_LOCK)) && (pItemTop->GetTopPoint().GetRegion(REGION_TYPE_MULTI) == m_pChar->GetTopPoint().GetRegion(REGION_TYPE_MULTI)) ) isInOpenedContainer = true; else if ( ptOpenedContainerPosition.GetDist(pObjTop->GetTopPoint()) <= 3 ) isInOpenedContainer = true; } } } } if ( !isInOpenedContainer ) { SysMessageDefault(DEFMSG_REACH_UNABLE); return false; } } } // CanTouch handles priv level compares for chars if ( !m_pChar->CanUse(pItem, false) ) { if ( !m_pChar->CanTouch(pItem) ) SysMessage((m_pChar->IsStatFlag(STATF_DEAD)) ? g_Cfg.GetDefaultMsg(DEFMSG_REACH_GHOST) : g_Cfg.GetDefaultMsg(DEFMSG_REACH_FAIL)); else SysMessageDefault(DEFMSG_REACH_UNABLE); return false; } } if ( IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_ITEMDCLICK) ) { if ( pItem->OnTrigger(ITRIG_DCLICK, m_pChar) == TRIGRET_RET_TRUE ) return true; } CItemBase *pItemDef = pItem->Item_GetDef(); bool bIsEquipped = pItem->IsItemEquipped(); if ( pItemDef->IsTypeEquippable() && !bIsEquipped && pItemDef->GetEquipLayer() ) { bool bMustEquip = true; if ( pItem->IsTypeSpellbook() ) bMustEquip = false; else if ( (pItem->IsType(IT_LIGHT_OUT) || pItem->IsType(IT_LIGHT_LIT)) && !pItem->IsItemInContainer() ) bMustEquip = false; if ( bMustEquip ) { if ( !m_pChar->CanMove(pItem) ) return false; if ( !m_pChar->CanCarry(pItem) ) { SysMessageDefault(DEFMSG_MSG_HEAVY); return false; } if ( !m_pChar->ItemEquip(pItem, NULL, true) ) return false; } } CItemSpawn *pSpawn = static_cast<CItemSpawn *>(pItem->m_uidSpawnItem.ItemFind()); // remove this item from its spawn when players DClick it from ground, no other way to take it out. if ( pSpawn ) pSpawn->DelObj(pItem->GetUID()); SetTargMode(); m_Targ_PrvUID = m_Targ_UID; m_Targ_UID = pItem->GetUID(); m_tmUseItem.m_pParent = pItem->GetParent(); // store item location to check later if it was not moved // Use types of items (specific to client) switch ( pItem->GetType() ) { case IT_TRACKER: { if ( !m_pChar->Skill_Tracking(pItem->m_uidLink) ) { if ( pItem->m_uidLink.IsValidUID() ) SysMessageDefault(DEFMSG_TRACKING_UNABLE); addTarget(CLIMODE_TARG_LINK, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_TRACKER_ATTUNE)); } return true; } case IT_SHAFT: case IT_FEATHER: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) { CScriptTriggerArgs args("sm_bolts"); if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_bolts")); } case IT_FISH_POLE: // Just be near water ? addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_FISHING_PROMT), true); return true; case IT_DEED: addTargetDeed(pItem); return true; case IT_EQ_BANK_BOX: case IT_EQ_VENDOR_BOX: if ( !fScript ) g_Log.Event(LOGL_WARN|LOGM_CHEAT, "%lx:Cheater '%s' is using 3rd party tools to open bank box\n", GetSocketID(), m_pAccount->GetName()); return false; case IT_CONTAINER_LOCKED: case IT_SHIP_HOLD_LOCK: if ( !m_pChar->GetContainerCreate(LAYER_PACK)->ContentFindKeyFor(pItem) ) { SysMessageDefault(DEFMSG_ITEMUSE_LOCKED); if ( !IsPriv(PRIV_GM) ) return false; } case IT_CORPSE: case IT_SHIP_HOLD: case IT_CONTAINER: case IT_TRASH_CAN: { CItemContainer *pPack = static_cast<CItemContainer *>(pItem); if ( !pPack ) return false; if ( !m_pChar->Skill_Snoop_Check(pPack) ) { if ( !addContainerSetup(pPack) ) return false; } const CItemCorpse *pCorpseItem = static_cast<const CItemCorpse *>(pPack); if ( m_pChar->CheckCorpseCrime(pCorpseItem, true, true) ) SysMessageDefault(DEFMSG_LOOT_CRIMINAL_ACT); return true; } case IT_GAME_BOARD: { if ( !pItem->IsTopLevel() ) { SysMessageDefault(DEFMSG_ITEMUSE_GAMEBOARD_FAIL); return false; } CItemContainer *pBoard = static_cast<CItemContainer *>(pItem); ASSERT(pBoard); pBoard->Game_Create(); addContainerSetup(pBoard); return true; } case IT_BBOARD: addBulletinBoard(static_cast<CItemContainer *>(pItem)); return true; case IT_SIGN_GUMP: { GUMP_TYPE gumpid = pItemDef->m_ttContainer.m_gumpid; if ( !gumpid ) return false; addGumpTextDisp(pItem, gumpid, pItem->GetName(), pItem->IsIndividualName() ? pItem->GetName() : NULL); return true; } case IT_BOOK: case IT_MESSAGE: { if ( !addBookOpen(pItem) ) SysMessageDefault(DEFMSG_ITEMUSE_BOOK_FAIL); return true; } case IT_STONE_GUILD: case IT_STONE_TOWN: return true; case IT_POTION: { if ( !m_pChar->CanMove(pItem) ) { SysMessageDefault(DEFMSG_ITEMUSE_POTION_FAIL); return false; } if ( RES_GET_INDEX(pItem->m_itPotion.m_Type) == SPELL_Poison ) { // If we click directly on poison potion, we will drink poison and get ill. // To use it on Poisoning skill, the skill will request to target the potion. m_pChar->OnSpellEffect(SPELL_Poison, m_pChar, pItem->m_itSpell.m_spelllevel, NULL); return true; } if ( RES_GET_INDEX(pItem->m_itPotion.m_Type) == SPELL_Explosion ) { // Throw explosion potion if ( !m_pChar->ItemPickup(pItem, 1) ) // put the potion in our hand return false; pItem->m_itPotion.m_tick = 4; // countdown to explode pItem->m_itPotion.m_ignited = 1; // ignite it pItem->m_uidLink = m_pChar->GetUID(); pItem->SetTimeout(TICK_PER_SEC); m_tmUseItem.m_pParent = pItem->GetParent(); addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_POTION_TARGET), true, true, pItem->m_itPotion.m_tick * TICK_PER_SEC); return true; } m_pChar->Use_Drink(pItem); return true; } case IT_ANIM_ACTIVE: SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_ITEM_IN_USE)); return false; case IT_CLOCK: addObjMessage(m_pChar->GetTopSector()->GetLocalGameTime(), pItem); return true; case IT_SPAWN_ITEM: case IT_SPAWN_CHAR: { pSpawn = static_cast<CItemSpawn *>(pItem); if ( !pSpawn ) return false; if ( pSpawn->m_currentSpawned ) { SysMessageDefault(DEFMSG_ITEMUSE_SPAWN_NEG); pSpawn->KillChildren(); // Removing existing objects spawned from it ( RESET ). } else { SysMessageDefault(DEFMSG_ITEMUSE_SPAWN_RESET); pSpawn->OnTick(true); // Forcing the spawn to work and create some objects ( START ). } return true; } case IT_SHRINE: { if ( m_pChar->OnSpellEffect(SPELL_Resurrection, m_pChar, 1000, pItem) ) return true; SysMessageDefault(DEFMSG_ITEMUSE_SHRINE); return true; } case IT_SHIP_TILLER: pItem->Speak(g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_TILLERMAN), HUE_TEXT_DEF, TALKMODE_SAY, FONT_NORMAL); return true; case IT_WAND: case IT_SCROLL: { SPELL_TYPE spell = static_cast<SPELL_TYPE>(RES_GET_INDEX(pItem->m_itWeapon.m_spell)); CSpellDef *pSpellDef = g_Cfg.GetSpellDef(spell); if ( !pSpellDef ) return false; if ( IsSetMagicFlags(MAGICF_PRECAST) && !pSpellDef->IsSpellType(SPELLFLAG_NOPRECAST) ) { int skill; if ( !pSpellDef->GetPrimarySkill(&skill, NULL) ) return false; m_tmSkillMagery.m_Spell = spell; // m_atMagery.m_Spell m_pChar->m_atMagery.m_Spell = spell; m_pChar->Skill_Start(static_cast<SKILL_TYPE>(skill)); return true; } return Cmd_Skill_Magery(spell, pItem); } case IT_RUNE: { if ( !m_pChar->CanMove(pItem, true) ) return false; addPromptConsole(CLIMODE_PROMPT_NAME_RUNE, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_RUNE_NAME), pItem->GetUID()); return true; } case IT_CARPENTRY: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) { CScriptTriggerArgs args("sm_carpentry"); if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_carpentry")); } case IT_FORGE: // Solve for the combination of this item with another. addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_FORGE)); return true; case IT_ORE: return m_pChar->Skill_Mining_Smelt(pItem, NULL); case IT_INGOT: return Cmd_Skill_Smith(pItem); case IT_KEY: case IT_KEYRING: { if ( pItem->GetTopLevelObj() != m_pChar && !m_pChar->IsPriv(PRIV_GM) ) { SysMessageDefault(DEFMSG_ITEMUSE_KEY_FAIL); return false; } addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_KEY_PROMT), false, true); return true; } case IT_BANDAGE: // SKILL_HEALING, or SKILL_VETERINARY addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_BANDAGE_PROMT), false, false); return true; case IT_BANDAGE_BLOOD: // Clean the bandages. case IT_COTTON: // use on a spinning wheel. case IT_WOOL: // use on a spinning wheel. case IT_YARN: // Use this on a loom. case IT_THREAD: // Use this on a loom. case IT_COMM_CRYSTAL: addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_TARGET_PROMT), false, false); return true; case IT_CARPENTRY_CHOP: case IT_LOCKPICK: // Use on a locked thing. case IT_SCISSORS: addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_TARGET_PROMT), false, true); return true; case IT_WEAPON_MACE_PICK: if ( bIsEquipped || !IsSetOF(OF_NoDClickTarget) ) { // Mine at the location TCHAR *pszTemp = Str_GetTemp(); sprintf(pszTemp, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_MACEPICK_TARG), pItem->GetName()); addTarget(CLIMODE_TARG_USE_ITEM, pszTemp, true, true); return true; } case IT_WEAPON_SWORD: case IT_WEAPON_FENCE: case IT_WEAPON_AXE: case IT_WEAPON_MACE_SHARP: case IT_WEAPON_MACE_STAFF: case IT_WEAPON_MACE_SMITH: { if ( bIsEquipped || !IsSetOF(OF_NoDClickTarget) ) addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_WEAPON_PROMT), false, true); return true; } case IT_WEAPON_MACE_CROOK: if ( bIsEquipped || !IsSetOF(OF_NoDClickTarget) ) addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_CROOK_PROMT), false, true); return true; case IT_FISH: SysMessageDefault(DEFMSG_ITEMUSE_FISH_FAIL); return true; case IT_TELESCOPE: SysMessageDefault(DEFMSG_ITEMUSE_TELESCOPE); return true; case IT_MAP: addDrawMap(static_cast<CItemMap *>(pItem)); return true; case IT_CANNON_BALL: { TCHAR *pszTemp = Str_GetTemp(); sprintf(pszTemp, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_CBALL_PROMT), pItem->GetName()); addTarget(CLIMODE_TARG_USE_ITEM, pszTemp); return true; } case IT_CANNON_MUZZLE: { if ( !m_pChar->CanUse(pItem, false) ) return false; // Make sure the cannon is loaded. if ( !(pItem->m_itCannon.m_Load & 1) ) { addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_CANNON_POWDER)); return true; } if ( !(pItem->m_itCannon.m_Load & 2) ) { addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_CANNON_SHOT)); return true; } addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_CANNON_TARG), false, true); return true; } case IT_CRYSTAL_BALL: // Gaze into the crystal ball. return true; case IT_SEED: case IT_PITCHER_EMPTY: { TCHAR *pszTemp = Str_GetTemp(); sprintf(pszTemp, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_PITCHER_TARG), pItem->GetName()); addTarget(CLIMODE_TARG_USE_ITEM, pszTemp, true); return true; } case IT_SPELLBOOK: addSpellbookOpen(pItem); return true; case IT_SPELLBOOK_NECRO: addSpellbookOpen(pItem, 101); return true; case IT_SPELLBOOK_PALA: addSpellbookOpen(pItem, 201); return true; case IT_SPELLBOOK_BUSHIDO: addSpellbookOpen(pItem, 401); return true; case IT_SPELLBOOK_NINJITSU: addSpellbookOpen(pItem, 501); return true; case IT_SPELLBOOK_ARCANIST: addSpellbookOpen(pItem, 601); return true; case IT_SPELLBOOK_MYSTIC: addSpellbookOpen(pItem, 678); return true; case IT_SPELLBOOK_BARD: addSpellbookOpen(pItem, 701); return true; case IT_HAIR_DYE: { if ( !m_pChar->LayerFind(LAYER_BEARD) && !m_pChar->LayerFind(LAYER_HAIR) ) { SysMessageDefault(DEFMSG_ITEMUSE_DYE_NOHAIR); return true; } Dialog_Setup(CLIMODE_DIALOG, g_Cfg.ResourceGetIDType(RES_DIALOG, "d_hair_dye"), 0, pItem); return true; } case IT_DYE: addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_DYE_VAT)); return true; case IT_DYE_VAT: addTarget(CLIMODE_TARG_USE_ITEM, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_DYE_TARG), false, true); return true; case IT_MORTAR: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) { CScriptTriggerArgs args("sm_alchemy"); if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_alchemy")); } case IT_CARTOGRAPHY: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) { CScriptTriggerArgs args("sm_cartography"); if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_cartography")); } case IT_COOKING: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) { CScriptTriggerArgs args("sm_cooking"); if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_cooking")); } case IT_TINKER_TOOLS: { if ( IsTrigUsed(TRIGGER_SKILLMENU) ) { CScriptTriggerArgs args("sm_tinker"); if ( m_pChar->OnTrigger("@SkillMenu", m_pChar, &args) == TRIGRET_RET_TRUE ) return true; } return Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, "sm_tinker")); } case IT_SEWING_KIT: { TCHAR *pszTemp = Str_GetTemp(); sprintf(pszTemp, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SEWKIT_PROMT), pItem->GetName()); addTarget(CLIMODE_TARG_USE_ITEM, pszTemp); return true; } case IT_SCROLL_BLANK: Cmd_Skill_Inscription(); return true; default: { // An NPC could use it this way. if ( m_pChar->Use_Item(pItem) ) return true; break; } } SysMessageDefault(DEFMSG_ITEMUSE_CANTTHINK); return false; }
bool CClient::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from script { ADDTOCALLSTACK("CClient::r_Verb"); EXC_TRY("Verb"); // NOTE: This can be called directly from a RES_WEBPAGE script. // So do not assume we are a game client ! // NOTE: Mostly called from CChar::r_Verb // NOTE: Little security here so watch out for dangerous scripts ! ASSERT(pSrc); LPCTSTR pszKey = s.GetKey(); // Old ver if ( s.IsKeyHead( "SET", 3 ) && ( g_Cfg.m_Functions.ContainsKey( pszKey ) == false ) ) { PLEVEL_TYPE ilevel = g_Cfg.GetPrivCommandLevel( "SET" ); if ( ilevel > GetPrivLevel() ) return( false ); ASSERT( m_pChar ); addTargetVerb( pszKey+3, s.GetArgRaw()); return( true ); } if ( toupper( pszKey[0] ) == 'X' && ( g_Cfg.m_Functions.ContainsKey( pszKey ) == false ) ) { PLEVEL_TYPE ilevel = g_Cfg.GetPrivCommandLevel( "SET" ); if ( ilevel > GetPrivLevel() ) return( false ); // Target this command verb on some other object. ASSERT( m_pChar ); addTargetVerb( pszKey+1, s.GetArgRaw()); return( true ); } int index = FindTableSorted( s.GetKey(), sm_szVerbKeys, COUNTOF(sm_szVerbKeys)-1 ); switch (index) { case CV_ADD: if ( s.HasArgs()) { // FindItemName ??? TCHAR * pszArgs = s.GetArgStr(); if ( !IsValidGameObjDef( static_cast<LPCTSTR>(pszArgs) ) ) { g_Log.EventWarn("Invalid ADD argument '%s'\n", pszArgs); SysMessageDefault( DEFMSG_CMD_INVALID ); return true; } RESOURCE_ID rid = g_Cfg.ResourceGetID( RES_QTY, const_cast<LPCTSTR &>(reinterpret_cast<LPTSTR &>(pszArgs))); if (( rid.GetResType() == RES_CHARDEF ) || ( rid.GetResType() == RES_SPAWN )) { m_Targ_PrvUID.InitUID(); return Cmd_CreateChar(static_cast<CREID_TYPE>(rid.GetResIndex()), SPELL_Summon, false ); } ITEMID_TYPE id = static_cast<ITEMID_TYPE>(rid.GetResIndex()); return Cmd_CreateItem( id ); } else { if ( IsValidDef( "d_add" ) ) Dialog_Setup( CLIMODE_DIALOG, g_Cfg.ResourceGetIDType(RES_DIALOG, "d_add"), 0, this->GetChar() ); else Menu_Setup( g_Cfg.ResourceGetIDType( RES_MENU, "MENU_ADDITEM")); } break; case CV_ADDBUFF: { TCHAR * ppArgs[11]; Str_ParseCmds( s.GetArgStr(), ppArgs, COUNTOF(ppArgs)); int iArgs[4]; for ( int idx = 0; idx < 4; ++idx ) { if ( !IsStrNumeric(ppArgs[idx])) { DEBUG_ERR(("Invalid AddBuff argument number %u\n", idx+1)); return true; } iArgs[idx] = Exp_GetVal( ppArgs[idx] ); } if (iArgs[0] < BI_START || iArgs[0] > BI_QTY/* || iArgs[0] == 0x3EB || iArgs[0] == 0x3EC*/) { // 0x3eb and 0x3ec among some others does not exists now, which doesn't mean they won't fill them and, since nothing happens when wrong id is sent, we can let them be sent. DEBUG_ERR(("Invalid AddBuff icon ID\n")); break; } LPCTSTR Args[7]; size_t ArgsCount = 0; for ( int i = 0; i < 7; ++i ) { Args[i] = ppArgs[i + 4]; if ( Args[i] != NULL ) ArgsCount++; } addBuff(static_cast<BUFF_ICONS>(iArgs[0]), iArgs[1], iArgs[2], static_cast<WORD>(iArgs[3]), Args, ArgsCount); } break; case CV_REMOVEBUFF: { BUFF_ICONS IconId = static_cast<BUFF_ICONS>(s.GetArgVal()); if (IconId < BI_START || IconId > BI_QTY/* || IconId == 0x3EB || IconId == 0x3EC*/) { DEBUG_ERR(("Invalid RemoveBuff icon ID\n")); break; } removeBuff(IconId); } break; case CV_ADDCLILOC: // Add cliloc in @ClientTooltip trigger { TCHAR * ppLocArgs[256]; size_t qty = Str_ParseCmds(s.GetArgRaw(), ppLocArgs, COUNTOF(ppLocArgs), ","); DWORD clilocid = Exp_GetVal(ppLocArgs[0]); CGString LocArgs; for ( size_t y = 1 ; y < qty; y++ ) { if ( LocArgs.GetLength() ) LocArgs += "\t"; LocArgs += ( !strcmp(ppLocArgs[y], "NULL") ? " " : ppLocArgs[y] ); } if ( g_Cfg.m_wDebugFlags & DEBUGF_SCRIPTS ) g_Log.EventDebug("SCRIPT: addcliloc(%lu,'%s')\n", clilocid, static_cast<LPCTSTR>(LocArgs)); this->m_TooltipData.Add(new CClientTooltip(clilocid, LocArgs)); } break; case CV_ADDCONTEXTENTRY: { TCHAR * ppLocArgs[20]; if ( Str_ParseCmds(s.GetArgRaw(), ppLocArgs, COUNTOF(ppLocArgs), ",") > 4 ) { DEBUG_ERR(("Bad AddContextEntry usage: Function takes maximum of 4 arguments!\n")); return true; } if (m_pPopupPacket == NULL) { DEBUG_ERR(("Bad AddContextEntry usage: Not used under a @ContextMenuRequest/@itemContextMenuRequest trigger!\n")); return true; } for ( int i = 0; i < 4; i++ ) { if ( i > 1 && IsStrEmpty(ppLocArgs[i]) ) continue; if ( !IsStrNumeric(ppLocArgs[i]) ) { DEBUG_ERR(("Bad AddContextEntry usage: Argument %d must be a number!\n", i+1)); return true; } } int entrytag = Exp_GetVal(ppLocArgs[0]); if ( entrytag < 100 ) { DEBUG_ERR(("Bad AddContextEntry usage: TextEntry < 100 is reserved for server usage!\n")); return true; } m_pPopupPacket->addOption(static_cast<WORD>(entrytag), static_cast<DWORD>(Exp_GetVal(ppLocArgs[1])), static_cast<WORD>(Exp_GetVal(ppLocArgs[2])), static_cast<WORD>(Exp_GetVal(ppLocArgs[3]))); } break; case CV_ARROWQUEST: { INT64 piVal[3]; Str_ParseCmds( s.GetArgRaw(), piVal, COUNTOF(piVal)); addArrowQuest( static_cast<int>(piVal[0]), static_cast<int>(piVal[1]), static_cast<int>(piVal[2]) ); #ifdef _ALPHASPHERE // todo: should use a proper container for these, since the arrows are lost // when the client logs out, and also newer clients support multiple // arrows if ( piVal[0] && piVal[1] && m_pChar ) { m_pChar->SetKeyNum("ARROWQUEST_X", piVal[0]); m_pChar->SetKeyNum("ARROWQUEST_Y", piVal[1]); } else { m_pChar->DeleteKey("ARROWQUEST_X"); m_pChar->DeleteKey("ARROWQUEST_Y"); } #endif } break; case CV_BADSPAWN: { // Loop the world searching for bad spawns bool fFound = false; for ( int m = 0; m < 256 && !fFound; m++ ) { if ( !g_MapList.m_maps[m] ) continue; for ( int d = 0; d < g_MapList.GetSectorQty(m) && !fFound; d++ ) { CSector *pSector = g_World.GetSector(m, d); if ( !pSector ) continue; CItem *pNext; CItem *pItem = STATIC_CAST <CItem*>(pSector->m_Items_Inert.GetHead()); for ( ; pItem != NULL && !fFound; pItem = pNext ) { pNext = pItem->GetNext(); if ( pItem->IsType(IT_SPAWN_ITEM) || pItem->IsType(IT_SPAWN_CHAR) ) { CItemSpawn *pSpawn = static_cast<CItemSpawn*>(pItem); CResourceDef *pDef = pSpawn->FixDef(); if ( !pDef ) { RESOURCE_ID_BASE rid = ( pItem->IsType(IT_SPAWN_ITEM) ? pItem->m_itSpawnItem.m_ItemID : pItem->m_itSpawnChar.m_CharID); CPointMap pt = pItem->GetTopPoint(); m_pChar->Spell_Teleport(pt, true, false); m_pChar->m_Act_Targ = pItem->GetUID(); SysMessagef("Bad spawn (0%lx, id=%s). Set as ACT", (DWORD)pItem->GetUID(), g_Cfg.ResourceGetName(rid)); fFound = true; } } } } } if ( ! fFound ) SysMessage(g_Cfg.GetDefaultMsg( DEFMSG_NO_BAD_SPAWNS )); } break; case CV_BANKSELF: // open my own bank addBankOpen( m_pChar, static_cast<LAYER_TYPE>(s.GetArgVal())); break; case CV_CAST: { SPELL_TYPE spell = static_cast<SPELL_TYPE>(g_Cfg.ResourceGetIndexType(RES_SPELL, s.GetArgStr())); const CSpellDef * pSpellDef = g_Cfg.GetSpellDef(spell); if (pSpellDef == NULL) return true; CObjBase * pObjSrc = dynamic_cast<CObjBase *>(pSrc); if ( IsSetMagicFlags( MAGICF_PRECAST ) && !pSpellDef->IsSpellType( SPELLFLAG_NOPRECAST ) ) { int skill; if (!pSpellDef->GetPrimarySkill(&skill, NULL)) return true; m_tmSkillMagery.m_Spell = spell; // m_atMagery.m_Spell m_pChar->m_atMagery.m_Spell = spell; if (pObjSrc != NULL) { m_Targ_UID = pObjSrc->GetUID(); // default target. m_Targ_PrvUID = pObjSrc->GetUID(); } else { m_Targ_UID.ClearUID(); m_Targ_PrvUID.ClearUID(); } m_pChar->Skill_Start(static_cast<SKILL_TYPE>(skill)); break; } else Cmd_Skill_Magery(spell, pObjSrc); } break; case CV_CHARLIST: { // usually just a gm command new PacketChangeCharacter(this); CharDisconnect(); // since there is no undoing this in the client. SetTargMode( CLIMODE_SETUP_CHARLIST ); } break; case CV_CTAGLIST: if ( ! strcmpi( s.GetArgStr(), "log" )) pSrc = &g_Serv; m_TagDefs.DumpKeys(pSrc, "CTAG."); break; case CV_CLEARCTAGS: { if ( s.HasArgs() ) { LPCTSTR pszArgs = s.GetArgStr(); SKIP_SEPARATORS(pszArgs); m_TagDefs.ClearKeys(pszArgs); } else { m_TagDefs.ClearKeys(); } } break; case CV_CLOSEPAPERDOLL: { CChar *pChar = m_pChar; if ( s.HasArgs() ) { CGrayUID uid = s.GetArgVal(); pChar = uid.CharFind(); } if ( pChar ) closeUIWindow(pChar, 0x01); } break; case CV_CLOSEPROFILE: { CChar *pChar = m_pChar; if ( s.HasArgs() ) { CGrayUID uid = s.GetArgVal(); pChar = uid.CharFind(); } if ( pChar ) closeUIWindow(pChar, 0x08); } break; case CV_CLOSESTATUS: { CChar *pChar = m_pChar; if ( s.HasArgs() ) { CGrayUID uid = s.GetArgVal(); pChar = uid.CharFind(); } if ( pChar ) closeUIWindow(pChar, 0x02); } break; case CV_DYE: if ( s.HasArgs() ) { CGrayUID uid(s.GetArgVal()); CObjBase *pObj = uid.ObjFind(); if ( pObj ) addDyeOption(pObj); } break; case CV_EVERBTARG: m_Prompt_Text = s.GetArgStr(); addPromptConsole( CLIMODE_PROMPT_TARG_VERB, m_Targ_Text.IsEmpty() ? "Enter the verb" : "Enter the text", m_Targ_UID ); break; case CV_EXTRACT: // sort of like EXPORT but for statics. // Opposite of the "UNEXTRACT" command if ( ! s.HasArgs()) { SysMessage( g_Cfg.GetDefaultMsg( DEFMSG_EXTRACT_USAGE ) ); } else { TCHAR * ppArgs[2]; Str_ParseCmds( s.GetArgStr(), ppArgs, COUNTOF( ppArgs )); m_Targ_Text = ppArgs[0]; // Point at the options, if any m_tmTile.m_ptFirst.InitPoint(); // Clear this first m_tmTile.m_Code = CV_EXTRACT; // set extract code. m_tmTile.m_id = Exp_GetVal(ppArgs[1]); // extract id. addTarget( CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_EXTRACT_AREA ), true ); } break; case CV_UNEXTRACT: // Create item from script. // Opposite of the "EXTRACT" command if ( ! s.HasArgs()) { SysMessage( g_Cfg.GetDefaultMsg( DEFMSG_UNEXTRACT_USAGE ) ); } else { TCHAR * ppArgs[2]; Str_ParseCmds( s.GetArgStr(), ppArgs, COUNTOF( ppArgs )); m_Targ_Text = ppArgs[0]; // Point at the options, if any m_tmTile.m_ptFirst.InitPoint(); // Clear this first m_tmTile.m_Code = CV_UNEXTRACT; // set extract code. m_tmTile.m_id = Exp_GetVal(ppArgs[1]); // extract id. addTarget( CLIMODE_TARG_UNEXTRACT, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_MULTI_POS ), true ); } break; case CV_GMPAGE: m_Targ_Text = s.GetArgStr(); if ( !m_Targ_Text.IsEmpty() && !strnicmp( m_Targ_Text, "ADD ", 4 ) ) { Cmd_GM_Page( m_Targ_Text + 4 ); break; } addPromptConsole( CLIMODE_PROMPT_GM_PAGE_TEXT, g_Cfg.GetDefaultMsg( DEFMSG_GMPAGE_PROMPT ) ); break; case CV_GOTARG: // go to my (preselected) target. { ASSERT(m_pChar); CObjBase * pObj = m_Targ_UID.ObjFind(); if ( pObj != NULL ) { CPointMap po = pObj->GetTopLevelObj()->GetTopPoint(); CPointMap pnt = po; pnt.MoveN( DIR_W, 3 ); DWORD wBlockFlags = m_pChar->GetMoveBlockFlags(); pnt.m_z = g_World.GetHeightPoint2( pnt, wBlockFlags ); // ??? Get Area m_pChar->m_dirFace = pnt.GetDir( po, m_pChar->m_dirFace ); // Face the player m_pChar->Spell_Teleport( pnt, true, false ); } } break; case CV_INFO: // We could also get ground tile info. addTarget( CLIMODE_TARG_OBJ_INFO, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_ITEM_INFO ), true, false ); break; case CV_INFORMATION: SysMessage( g_Serv.GetStatusString( 0x22 )); SysMessage( g_Serv.GetStatusString( 0x24 )); break; case CV_LAST: // Fake Previous target. if ( GetTargMode() >= CLIMODE_MOUSE_TYPE ) { ASSERT(m_pChar); CObjBase * pObj = m_pChar->m_Act_Targ.ObjFind(); if ( pObj != NULL ) { Event_Target(GetTargMode(), pObj->GetUID(), pObj->GetUnkPoint()); addTargetCancel(); } break; } return( false ); case CV_LINK: // link doors m_Targ_UID.InitUID(); addTarget( CLIMODE_TARG_LINK, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_LINK_ITEM ) ); break; case CV_MENU: Menu_Setup( g_Cfg.ResourceGetIDType( RES_MENU, s.GetArgStr())); break; case CV_MIDILIST: { INT64 piMidi[64]; size_t iQty = Str_ParseCmds( s.GetArgStr(), piMidi, COUNTOF(piMidi)); if ( iQty > 0 ) { addMusic( static_cast<MIDI_TYPE>(piMidi[ Calc_GetRandVal( iQty ) ]) ); } } break; case CV_NUDGE: if ( ! s.HasArgs()) { SysMessage( "Usage: NUDGE dx dy dz" ); } else { m_Targ_Text = s.GetArgRaw(); m_tmTile.m_ptFirst.InitPoint(); // Clear this first m_tmTile.m_Code = CV_NUDGE; addTarget( CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_NUDGE_AREA ), true ); } break; case CV_NUKE: m_Targ_Text = s.GetArgRaw(); m_tmTile.m_ptFirst.InitPoint(); // Clear this first m_tmTile.m_Code = CV_NUKE; // set nuke code. addTarget( CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_NUKE_AREA ), true ); break; case CV_NUKECHAR: m_Targ_Text = s.GetArgRaw(); m_tmTile.m_ptFirst.InitPoint(); // Clear this first m_tmTile.m_Code = CV_NUKECHAR; // set nuke code. addTarget( CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_NUKE_CHAR_AREA ), true ); break; case CV_OPENPAPERDOLL: { CChar *pChar = m_pChar; if ( s.HasArgs() ) { CGrayUID uid = s.GetArgVal(); pChar = uid.CharFind(); } if ( pChar ) addCharPaperdoll(pChar); } break; case CV_PAGE: Cmd_GM_PageCmd( s.GetArgStr()); break; case CV_REPAIR: addTarget( CLIMODE_TARG_REPAIR, g_Cfg.GetDefaultMsg( DEFMSG_SELECT_ITEM_REPAIR ) ); break; case CV_FLUSH: #ifndef _MTNETWORK g_NetworkOut.flush(this); #else g_NetworkManager.flush(GetNetState()); #endif break; case CV_RESEND: addReSync(); break; case CV_SAVE: g_World.Save(s.GetArgVal() != 0); break; case CV_SCROLL: // put a scroll up. addScrollResource( s.GetArgStr(), SCROLL_TYPE_UPDATES ); break; case CV_SENDPACKET: SendPacket( s.GetArgStr() ); break; case CV_SELF: // Fake self target. if ( GetTargMode() >= CLIMODE_MOUSE_TYPE ) { ASSERT(m_pChar); Event_Target(GetTargMode(), m_pChar->GetUID(), m_pChar->GetTopPoint()); addTargetCancel(); break; } return( false ); case CV_SHOWSKILLS: addSkillWindow(static_cast<SKILL_TYPE>(g_Cfg.m_iMaxSkill)); // Reload the real skills break; case CV_SKILLMENU: // Just put up another menu. Cmd_Skill_Menu( g_Cfg.ResourceGetIDType( RES_SKILLMENU, s.GetArgStr())); break; case CV_SKILLSELECT: Event_Skill_Use( g_Cfg.FindSkillKey( s.GetArgStr() ) ); break; case CV_SUMMON: // from the spell skill script. // m_Targ_PrvUID should already be set. return Cmd_CreateChar(static_cast<CREID_TYPE>(g_Cfg.ResourceGetIndexType( RES_CHARDEF, s.GetArgStr())), SPELL_Summon, true ); case CV_SMSG: case CV_SYSMESSAGE: SysMessage( s.GetArgStr() ); break; case CV_SYSMESSAGEF: //There is still an issue with numbers not resolving properly when %i,%d,or other numeric format code is in use { TCHAR * pszArgs[4]; size_t iArgQty = Str_ParseCmds( s.GetArgRaw(), pszArgs, COUNTOF(pszArgs) ); if ( iArgQty < 2 ) { g_Log.EventError("SysMessagef with less than 1 args for the given text\n"); return false; } if ( iArgQty > 4 ) { g_Log.EventError("Too many arguments given to SysMessagef (max = text + 3\n"); return false; } //strip quotes if any if ( *pszArgs[0] == '"' ) pszArgs[0]++; for (TCHAR * pEnd = pszArgs[0] + strlen( pszArgs[0] ) - 1; pEnd >= pszArgs[0]; pEnd-- ) { if ( *pEnd == '"' ) { *pEnd = '\0'; break; } } SysMessagef( pszArgs[0], pszArgs[1], pszArgs[2] ? pszArgs[2] : 0, pszArgs[3] ? pszArgs[3] : 0); }break; case CV_SMSGU: case CV_SYSMESSAGEUA: { TCHAR * pszArgs[5]; size_t iArgQty = Str_ParseCmds( s.GetArgRaw(), pszArgs, COUNTOF(pszArgs) ); if ( iArgQty > 4 ) { // Font and mode are actually ignored here, but they never made a difference // anyway.. I'd like to keep the syntax similar to SAYUA NCHAR szBuffer[ MAX_TALK_BUFFER ]; CvtSystemToNUNICODE( szBuffer, COUNTOF(szBuffer), pszArgs[4], -1 ); addBarkUNICODE( szBuffer, NULL, static_cast<HUE_TYPE>(Exp_GetVal(pszArgs[0])), TALKMODE_SYSTEM, FONT_NORMAL, pszArgs[3] ); } } break; case CV_SMSGL: case CV_SYSMESSAGELOC: { TCHAR * ppArgs[256]; size_t iArgQty = Str_ParseCmds( s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), "," ); if ( iArgQty > 1 ) { int hue = -1; if ( ppArgs[0] ) hue = Exp_GetVal( ppArgs[0] ); int iClilocId = Exp_GetVal( ppArgs[1] ); if ( hue == -1 ) hue = HUE_TEXT_DEF; CGString CArgs; for ( size_t i = 2; i < iArgQty; i++ ) { if ( CArgs.GetLength() ) CArgs += "\t"; CArgs += ( !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i] ); } addBarkLocalized(iClilocId, NULL, static_cast<HUE_TYPE>(hue), TALKMODE_SYSTEM, FONT_NORMAL, CArgs.GetPtr()); } } break; case CV_SMSGLEX: case CV_SYSMESSAGELOCEX: { TCHAR * ppArgs[256]; size_t iArgQty = Str_ParseCmds( s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), "," ); if ( iArgQty > 2 ) { int hue = -1; int affix = 0; if ( ppArgs[0] ) hue = Exp_GetVal( ppArgs[0] ); int iClilocId = Exp_GetVal( ppArgs[1] ); if ( ppArgs[2] ) affix = Exp_GetVal( ppArgs[2] ); if ( hue == -1 ) hue = HUE_TEXT_DEF; CGString CArgs; for ( size_t i = 4; i < iArgQty; i++ ) { if ( CArgs.GetLength() ) CArgs += "\t"; CArgs += ( !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i] ); } addBarkLocalizedEx( iClilocId, NULL, static_cast<HUE_TYPE>(hue), TALKMODE_SYSTEM, FONT_NORMAL, static_cast<AFFIX_TYPE>(affix), ppArgs[3], CArgs.GetPtr() ); } } break; case CV_TELE: Cmd_Skill_Magery(SPELL_Teleport, dynamic_cast<CObjBase *>(pSrc)); break; case CV_TILE: if ( ! s.HasArgs()) { SysMessage( "Usage: TILE z-height item1 item2 itemX" ); } else { m_Targ_Text = s.GetArgStr(); // Point at the options m_tmTile.m_ptFirst.InitPoint(); // Clear this first m_tmTile.m_Code = CV_TILE; addTarget( CLIMODE_TARG_TILE, "Pick 1st corner:", true ); } break; case CV_VERSION: // "SHOW VERSION" SysMessage(g_szServerDescription); break; case CV_WEBLINK: addWebLaunch( s.GetArgStr()); break; default: if ( r_LoadVal( s )) { CGString sVal; if ( r_WriteVal( s.GetKey(), sVal, pSrc )) { // if ( !s.IsKeyHead( "CTAG.", 5 ) && !s.IsKeyHead( "CTAG0.", 6 ) ) // We don't want output related to ctag // SysMessagef( "%s = %s", (LPCTSTR) s.GetKey(), (LPCTSTR) sVal ); // feedback on what we just did. return( true ); } } return( CScriptObj::r_Verb( s, pSrc )); // used in the case of web pages to access server level things.. } return true; EXC_CATCH; EXC_DEBUG_START; EXC_ADD_SCRIPTSRC; EXC_DEBUG_END; return false; }
bool CClient::r_Verb(CScript &s, CTextConsole *pSrc) // Execute command from script { ADDTOCALLSTACK("CClient::r_Verb"); EXC_TRY("Verb"); // NOTE: This can be called directly from a RES_WEBPAGE script. // So do not assume we are a game client ! // NOTE: Mostly called from CChar::r_Verb // NOTE: Little security here so watch out for dangerous scripts ! ASSERT(pSrc); LPCTSTR pszKey = s.GetKey(); // Old ver if ( s.IsKeyHead("SET", 3) && !g_Cfg.m_Functions.ContainsKey(pszKey) ) { PLEVEL_TYPE ilevel = g_Cfg.GetPrivCommandLevel("SET"); if ( ilevel > GetPrivLevel() ) return false; ASSERT(m_pChar); addTargetVerb(pszKey + 3, s.GetArgRaw()); return true; } if ( (toupper(pszKey[0]) == 'X') && !g_Cfg.m_Functions.ContainsKey(pszKey) ) { PLEVEL_TYPE ilevel = g_Cfg.GetPrivCommandLevel("SET"); if ( ilevel > GetPrivLevel() ) return false; // Target this command verb on some other object. ASSERT(m_pChar); addTargetVerb(pszKey + 1, s.GetArgRaw()); return true; } int index = FindTableSorted(s.GetKey(), sm_szVerbKeys, COUNTOF(sm_szVerbKeys) - 1); switch ( index ) { case CV_ADD: { if ( s.HasArgs() ) { TCHAR *ppArgs[2]; size_t iArgQty = Str_ParseCmds(s.GetArgStr(), ppArgs, COUNTOF(ppArgs)); if ( !IsValidGameObjDef(static_cast<LPCTSTR>(ppArgs[0])) ) { g_Log.EventWarn("Invalid ADD argument '%s'\n", ppArgs[0]); SysMessageDefault(DEFMSG_CMD_INVALID); return true; } RESOURCE_ID rid = g_Cfg.ResourceGetID(RES_QTY, const_cast<LPCTSTR &>(ppArgs[0])); m_tmAdd.m_id = rid.GetResIndex(); m_tmAdd.m_amount = (iArgQty > 1) ? static_cast<WORD>(maximum(ATOI(ppArgs[1]), 1)) : 1; if ( (rid.GetResType() == RES_CHARDEF) || (rid.GetResType() == RES_SPAWN) ) { m_Targ_PrvUID.InitUID(); return addTargetChars(CLIMODE_TARG_ADDCHAR, static_cast<CREID_TYPE>(m_tmAdd.m_id), false); } else return addTargetItems(CLIMODE_TARG_ADDITEM, static_cast<ITEMID_TYPE>(m_tmAdd.m_id)); break; } if ( IsValidDef("d_add") ) Dialog_Setup(CLIMODE_DIALOG, g_Cfg.ResourceGetIDType(RES_DIALOG, "d_add"), 0, m_pChar); else Menu_Setup(g_Cfg.ResourceGetIDType(RES_MENU, "MENU_ADDITEM")); break; } case CV_ADDBUFF: { TCHAR *ppArgs[11]; Str_ParseCmds(s.GetArgStr(), ppArgs, COUNTOF(ppArgs)); int iArgs[4]; for ( int i = 0; i < 4; ++i ) { if ( !IsStrNumeric(ppArgs[i]) ) { DEBUG_ERR(("Invalid AddBuff argument number %u\n", i + 1)); return true; } iArgs[i] = Exp_GetVal(ppArgs[i]); } if ( (iArgs[0] < 0) || (iArgs[0] > USHRT_MAX) ) { DEBUG_ERR(("Invalid AddBuff icon ID\n")); break; } LPCTSTR pszArgs[7]; size_t iArgQty = 0; for ( int i = 0; i < 7; ++i ) { pszArgs[i] = ppArgs[i + 4]; if ( pszArgs[i] != NULL ) ++iArgQty; } addBuff(static_cast<BUFF_ICONS>(iArgs[0]), static_cast<DWORD>(iArgs[1]), static_cast<DWORD>(iArgs[2]), static_cast<WORD>(iArgs[3]), pszArgs, iArgQty); break; } case CV_REMOVEBUFF: { BUFF_ICONS IconId = static_cast<BUFF_ICONS>(s.GetArgVal()); if ( (IconId < 0) || (IconId > USHRT_MAX) ) { DEBUG_ERR(("Invalid RemoveBuff icon ID\n")); break; } removeBuff(IconId); break; } case CV_ADDCLILOC: { // Add cliloc in @ClientTooltip trigger TCHAR *ppArgs[256]; size_t iArgQty = Str_ParseCmds(s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), ","); DWORD dwClilocId = static_cast<DWORD>(Exp_GetLLVal(ppArgs[0])); CGString sVal; for ( size_t i = 1; i < iArgQty; ++i ) { if ( sVal.GetLength() ) sVal += "\t"; sVal += !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i]; } if ( g_Cfg.m_wDebugFlags & DEBUGF_SCRIPTS ) g_Log.EventDebug("SCRIPT: addcliloc(%lu,'%s')\n", dwClilocId, static_cast<LPCTSTR>(sVal)); m_TooltipData.Add(new CClientTooltip(dwClilocId, sVal)); break; } case CV_ADDCONTEXTENTRY: { TCHAR *ppArgs[20]; if ( Str_ParseCmds(s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), ",") > 4 ) { DEBUG_ERR(("Bad AddContextEntry usage: Function takes maximum of 4 arguments!\n")); return true; } if ( !m_pPopupPacket ) { DEBUG_ERR(("Bad AddContextEntry usage: Not used under a @ContextMenuRequest/@itemContextMenuRequest trigger!\n")); return true; } for ( int i = 0; i < 4; ++i ) { if ( (i > 1) && IsStrEmpty(ppArgs[i]) ) continue; if ( !IsStrNumeric(ppArgs[i]) ) { DEBUG_ERR(("Bad AddContextEntry usage: Argument %d must be a number!\n", i + 1)); return true; } } int iTextEntry = Exp_GetVal(ppArgs[0]); if ( iTextEntry < 100 ) { DEBUG_ERR(("Bad AddContextEntry usage: TextEntry < 100 is reserved for server usage!\n")); return true; } m_pPopupPacket->addOption(static_cast<WORD>(iTextEntry), static_cast<DWORD>(Exp_GetLLVal(ppArgs[1])), static_cast<WORD>(Exp_GetLLVal(ppArgs[2])), static_cast<WORD>(Exp_GetLLVal(ppArgs[3]))); break; } case CV_ARROWQUEST: { INT64 piVal[3]; Str_ParseCmds(s.GetArgRaw(), piVal, COUNTOF(piVal)); addArrowQuest(static_cast<WORD>(piVal[0]), static_cast<WORD>(piVal[1]), static_cast<DWORD>(piVal[2])); break; } case CV_BADSPAWN: { // Loop the world searching for bad spawns bool fFound = false; CItem *pItem = NULL; CSector *pSector = NULL; CResourceDef *pSpawnDef = NULL; for ( int m = 0; (m < 256) && !fFound; ++m ) { if ( !g_MapList.m_maps[m] ) continue; for ( int s = 0; (s < g_MapList.GetSectorQty(m)) && !fFound; ++s ) { pSector = g_World.GetSector(m, s); if ( !pSector ) continue; for ( pItem = static_cast<CItem *>(pSector->m_Items_Timer.GetHead()); (pItem != NULL) && !fFound; pItem = pItem->GetNext() ) { if ( pItem->IsType(IT_SPAWN_ITEM) || pItem->IsType(IT_SPAWN_CHAR) ) { pSpawnDef = static_cast<CItemSpawn *>(pItem)->FixDef(); if ( !pSpawnDef ) { RESOURCE_ID_BASE rid = pItem->IsType(IT_SPAWN_ITEM) ? pItem->m_itSpawnItem.m_ItemID : pItem->m_itSpawnChar.m_CharID; CPointMap pt = pItem->GetTopPoint(); m_pChar->Spell_Teleport(pt, true, false); m_pChar->m_Act_Targ = pItem->GetUID(); SysMessagef("Bad spawn (0%lx, id=%s). Set as ACT", static_cast<DWORD>(pItem->GetUID()), g_Cfg.ResourceGetName(rid)); fFound = true; } } } } } if ( !fFound ) SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_NO_BAD_SPAWNS)); break; } case CV_BANKSELF: { addBankOpen(m_pChar, LAYER_BANKBOX); break; } case CV_CAST: { SPELL_TYPE spell = static_cast<SPELL_TYPE>(g_Cfg.ResourceGetIndexType(RES_SPELL, s.GetArgStr())); const CSpellDef *pSpellDef = g_Cfg.GetSpellDef(spell); if ( !pSpellDef ) return true; CObjBase *pObjSrc = dynamic_cast<CObjBase *>(pSrc); if ( IsSetMagicFlags(MAGICF_PRECAST) && !pSpellDef->IsSpellType(SPELLFLAG_NOPRECAST) ) { int iSkill; if ( !pSpellDef->GetPrimarySkill(&iSkill, NULL) ) return true; m_tmSkillMagery.m_Spell = spell; // m_atMagery.m_Spell m_pChar->m_atMagery.m_Spell = spell; if ( pObjSrc ) { m_Targ_UID = pObjSrc->GetUID(); // default target. m_Targ_PrvUID = pObjSrc->GetUID(); } else { m_Targ_UID.ClearUID(); m_Targ_PrvUID.ClearUID(); } m_pChar->Skill_Start(static_cast<SKILL_TYPE>(iSkill)); break; } else Cmd_Skill_Magery(spell, pObjSrc); break; } case CV_CHANGEFACE: // open 'face selection' dialog (enhanced clients only) { addGumpDialog(CLIMODE_DIALOG, NULL, 0, NULL, 0, 0, 0, m_pChar, CLIMODE_DIALOG_FACESELECTION); break; } case CV_CHARLIST: // usually just a gm command { if ( !PacketChangeCharacter::CanSendTo(m_NetState) ) break; new PacketChangeCharacter(this); CharDisconnect(); // since there is no undoing this in the client. SetTargMode(CLIMODE_SETUP_CHARLIST); break; } case CV_CTAGLIST: { if ( !strcmpi(s.GetArgStr(), "log") ) pSrc = &g_Serv; m_TagDefs.DumpKeys(pSrc, "CTAG."); break; } case CV_CLEARCTAGS: { if ( s.HasArgs() ) { LPCTSTR pszArgs = s.GetArgStr(); SKIP_SEPARATORS(pszArgs); m_TagDefs.ClearKeys(pszArgs); } else m_TagDefs.ClearKeys(); break; } case CV_CLOSEPAPERDOLL: { const CChar *pChar = s.HasArgs() ? static_cast<CGrayUID>(s.GetArgVal()).CharFind() : m_pChar; if ( pChar ) closeUIWindow(pChar, 0x1); break; } case CV_CLOSEPROFILE: { const CChar *pChar = s.HasArgs() ? static_cast<CGrayUID>(s.GetArgVal()).CharFind() : m_pChar; if ( pChar ) closeUIWindow(pChar, 0x8); break; } case CV_CLOSESTATUS: { const CChar *pChar = s.HasArgs() ? static_cast<CGrayUID>(s.GetArgVal()).CharFind() : m_pChar; if ( pChar ) closeUIWindow(pChar, 0x2); break; } case CV_CODEXOFWISDOM: { INT64 piArgs[2]; size_t iArgQty = Str_ParseCmds(s.GetArgStr(), piArgs, COUNTOF(piArgs)); if ( iArgQty < 1 ) { SysMessage("Usage: CODEXOFWISDOM TopicID [ForceOpen]"); break; } addCodexOfWisdom(static_cast<DWORD>(piArgs[0]), static_cast<bool>(piArgs[1])); break; } case CV_DYE: { const CObjBase *pObj = s.HasArgs() ? static_cast<CGrayUID>(s.GetArgVal()).ObjFind() : NULL; if ( pObj ) addDyeOption(pObj); break; } case CV_EVERBTARG: { m_Prompt_Text = s.GetArgStr(); addPromptConsole(CLIMODE_PROMPT_TARG_VERB, m_Targ_Text.IsEmpty() ? "Enter the verb" : "Enter the text", m_Targ_UID); break; } case CV_EXTRACT: { // sort of like EXPORT but for statics. // Opposite of the "UNEXTRACT" command TCHAR *ppArgs[2]; size_t iArgQty = Str_ParseCmds(s.GetArgStr(), ppArgs, COUNTOF(ppArgs)); if ( iArgQty < 2 ) { SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_EXTRACT_USAGE)); break; } m_Targ_Text = ppArgs[0]; // point at the options (if any) m_tmTile.m_ptFirst.InitPoint(); // clear this first m_tmTile.m_Code = CV_EXTRACT; // set extract code m_tmTile.m_id = Exp_GetVal(ppArgs[1]); // extract id addTarget(CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_EXTRACT_AREA), true); break; } case CV_UNEXTRACT: { // Create item from script. // Opposite of the "EXTRACT" command TCHAR *ppArgs[2]; size_t iArgQty = Str_ParseCmds(s.GetArgStr(), ppArgs, COUNTOF(ppArgs)); if ( iArgQty < 2 ) { SysMessage(g_Cfg.GetDefaultMsg(DEFMSG_UNEXTRACT_USAGE)); break; } m_Targ_Text = ppArgs[0]; // point at the options (if any) m_tmTile.m_ptFirst.InitPoint(); // clear this first m_tmTile.m_Code = CV_UNEXTRACT; // set extract code m_tmTile.m_id = Exp_GetVal(ppArgs[1]); // extract id addTarget(CLIMODE_TARG_UNEXTRACT, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_MULTI_POS), true); break; } case CV_GMPAGE: { m_Targ_Text = s.GetArgStr(); if ( !m_Targ_Text.IsEmpty() && !strnicmp(m_Targ_Text, "ADD ", 4) ) { Cmd_GM_Page(m_Targ_Text + 4); break; } addPromptConsole(CLIMODE_PROMPT_GM_PAGE_TEXT, g_Cfg.GetDefaultMsg(DEFMSG_GMPAGE_PROMPT)); break; } case CV_GOTARG: // go to my (preselected) target. { ASSERT(m_pChar); CObjBase *pObj = m_Targ_UID.ObjFind(); if ( pObj ) { CPointMap pt = pObj->GetTopLevelObj()->GetTopPoint(); m_pChar->m_dirFace = m_pChar->GetDir(pObj, m_pChar->m_dirFace); m_pChar->Spell_Teleport(pt, true, false); } break; } case CV_INFO: { // We could also get ground tile info. addTarget(CLIMODE_TARG_OBJ_INFO, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_ITEM_INFO), true, false); break; } case CV_INFORMATION: { SysMessage(g_Serv.GetStatusString(0x22)); SysMessage(g_Serv.GetStatusString(0x24)); break; } case CV_LAST: // fake previous target { if ( GetTargMode() >= CLIMODE_MOUSE_TYPE ) { ASSERT(m_pChar); CObjBase *pObj = m_pChar->m_Act_Targ.ObjFind(); if ( pObj ) { Event_Target(GetTargMode(), pObj->GetUID(), pObj->GetTopPoint()); addTargetCancel(); } break; } return false; } case CV_LINK: // link doors { m_Targ_UID.InitUID(); addTarget(CLIMODE_TARG_LINK, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_LINK_ITEM)); break; } case CV_MAPWAYPOINT: { INT64 piVal[2]; size_t iArgQty = Str_ParseCmds(s.GetArgRaw(), piVal, COUNTOF(piVal)); if ( iArgQty < 2 ) { SysMessage("Usage: MAPWAYPOINT uid type"); break; } CObjBase *pObj = static_cast<CGrayUID>(piVal[0]).ObjFind(); addMapWaypoint(pObj, static_cast<MAPWAYPOINT_TYPE>(piVal[1])); break; } case CV_MENU: { Menu_Setup(g_Cfg.ResourceGetIDType(RES_MENU, s.GetArgStr())); break; } case CV_MIDILIST: { INT64 piMidi[64]; size_t iArgQty = Str_ParseCmds(s.GetArgStr(), piMidi, COUNTOF(piMidi)); if ( iArgQty > 0 ) addMusic(static_cast<MIDI_TYPE>(piMidi[Calc_GetRandVal(iArgQty)])); break; } case CV_NUDGE: { if ( !s.HasArgs() ) { SysMessage("Usage: NUDGE dx dy dz"); break; } m_Targ_Text = s.GetArgRaw(); m_tmTile.m_ptFirst.InitPoint(); // clear this first m_tmTile.m_Code = CV_NUDGE; addTarget(CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_NUDGE_AREA), true); break; } case CV_NUKE: { m_Targ_Text = s.GetArgRaw(); m_tmTile.m_ptFirst.InitPoint(); // clear this first m_tmTile.m_Code = CV_NUKE; // set nuke code addTarget(CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_NUKE_AREA), true); break; } case CV_NUKECHAR: { m_Targ_Text = s.GetArgRaw(); m_tmTile.m_ptFirst.InitPoint(); // clear this first m_tmTile.m_Code = CV_NUKECHAR; // set nuke code addTarget(CLIMODE_TARG_TILE, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_NUKE_CHAR_AREA), true); break; } case CV_OPENPAPERDOLL: { const CChar *pChar = s.HasArgs() ? static_cast<CGrayUID>(s.GetArgVal()).CharFind() : m_pChar; if ( pChar ) addCharPaperdoll(pChar); break; } case CV_OPENTRADEWINDOW: { TCHAR *ppArgs[2]; Str_ParseCmds(s.GetArgStr(), ppArgs, COUNTOF(ppArgs)); CChar *pChar = ppArgs[0] ? static_cast<CGrayUID>(Exp_GetLLVal(ppArgs[0])).CharFind() : NULL; if ( pChar ) { CItem *pItem = ppArgs[1] ? static_cast<CGrayUID>(Exp_GetLLVal(ppArgs[1])).ItemFind() : NULL; Cmd_SecureTrade(pChar, pItem); } break; } case CV_PAGE: Cmd_GM_PageCmd(s.GetArgStr()); break; case CV_REPAIR: addTarget(CLIMODE_TARG_REPAIR, g_Cfg.GetDefaultMsg(DEFMSG_SELECT_ITEM_REPAIR)); break; case CV_FLUSH: #ifndef _MTNETWORK g_NetworkOut.flush(this); #else g_NetworkManager.flush(m_NetState); #endif break; case CV_RESEND: addReSync(); break; case CV_SAVE: g_World.Save(s.GetArgVal() != 0); break; case CV_SCROLL: // put a scroll up. addScrollResource(s.GetArgStr(), SCROLL_TYPE_UPDATES); break; case CV_SENDPACKET: SendPacket(s.GetArgStr()); break; case CV_SELF: // fake self target if ( GetTargMode() >= CLIMODE_MOUSE_TYPE ) { ASSERT(m_pChar); Event_Target(GetTargMode(), m_pChar->GetUID(), m_pChar->GetTopPoint()); addTargetCancel(); break; } return false; case CV_SHOWSKILLS: addSkillWindow(static_cast<SKILL_TYPE>(g_Cfg.m_iMaxSkill)); // reload the real skills break; case CV_SKILLMENU: Cmd_Skill_Menu(g_Cfg.ResourceGetIDType(RES_SKILLMENU, s.GetArgStr())); break; case CV_SKILLSELECT: Event_Skill_Use(g_Cfg.FindSkillKey(s.GetArgStr())); break; case CV_SUMMON: { ASSERT(m_pChar); const CSpellDef *pSpellDef = g_Cfg.GetSpellDef(SPELL_Summon); if ( !pSpellDef ) return false; m_pChar->m_Act_Targ = m_pChar->GetUID(); m_pChar->m_Act_TargPrv = m_pChar->GetUID(); if ( pSpellDef->IsSpellType(SPELLFLAG_TARG_OBJ|SPELLFLAG_TARG_XYZ) ) { m_tmSkillMagery.m_Spell = SPELL_Summon; m_tmSkillMagery.m_SummonID = static_cast<CREID_TYPE>(g_Cfg.ResourceGetIndexType(RES_CHARDEF, s.GetArgStr())); LPCTSTR pszPrompt = g_Cfg.GetDefaultMsg(DEFMSG_SELECT_MAGIC_TARGET); if ( !pSpellDef->m_sTargetPrompt.IsEmpty() ) pszPrompt = pSpellDef->m_sTargetPrompt; int iSpellTimeout = static_cast<int>(GetDefNum("SPELLTIMEOUT")); if ( !iSpellTimeout ) iSpellTimeout = g_Cfg.m_iSpellTimeout * TICK_PER_SEC; addTarget(CLIMODE_TARG_SKILL_MAGERY, pszPrompt, pSpellDef->IsSpellType(SPELLFLAG_TARG_XYZ), pSpellDef->IsSpellType(SPELLFLAG_HARM), iSpellTimeout); break; } else { m_pChar->m_atMagery.m_Spell = SPELL_Summon; m_pChar->m_atMagery.m_SummonID = static_cast<CREID_TYPE>(g_Cfg.ResourceGetIndexType(RES_CHARDEF, s.GetArgStr())); if ( IsSetMagicFlags(MAGICF_PRECAST) && !pSpellDef->IsSpellType(SPELLFLAG_NOPRECAST) ) { m_pChar->Spell_CastDone(); break; } else { int iSkill; if ( !pSpellDef->GetPrimarySkill(&iSkill, NULL) ) return false; m_pChar->Skill_Start(static_cast<SKILL_TYPE>(iSkill)); } } break; } case CV_SMSG: case CV_SYSMESSAGE: SysMessage(s.GetArgStr()); break; case CV_SYSMESSAGEF: // there is still an issue with numbers not resolving properly when %i,%d,or other numeric format code is in use { TCHAR *ppArgs[4]; size_t iArgQty = Str_ParseCmds(s.GetArgRaw(), ppArgs, COUNTOF(ppArgs)); if ( iArgQty < 2 ) { g_Log.EventError("SysMessagef with less than 1 args for the given text\n"); return false; } if ( iArgQty > 4 ) { g_Log.EventError("Too many arguments given to SysMessagef (max = text + 3\n"); return false; } if ( *ppArgs[0] == '"' ) // skip quotes ++ppArgs[0]; for ( TCHAR *pEnd = ppArgs[0] + strlen(ppArgs[0]) - 1; pEnd >= ppArgs[0]; --pEnd ) { if ( *pEnd == '"' ) // skip quotes { *pEnd = '\0'; break; } } SysMessagef(ppArgs[0], ppArgs[1], ppArgs[2] ? ppArgs[2] : 0, ppArgs[3] ? ppArgs[3] : 0); break; } case CV_SMSGU: case CV_SYSMESSAGEUA: { TCHAR *ppArgs[5]; size_t iArgQty = Str_ParseCmds(s.GetArgRaw(), ppArgs, COUNTOF(ppArgs)); if ( iArgQty > 4 ) { // Font and mode are actually ignored here, but they never made a difference anyway.. I'd like to keep the syntax similar to SAYUA NCHAR szBuffer[MAX_TALK_BUFFER]; CvtSystemToNUNICODE(szBuffer, COUNTOF(szBuffer), ppArgs[4], -1); addBarkUNICODE(szBuffer, NULL, static_cast<HUE_TYPE>(Exp_GetLLVal(ppArgs[0])), TALKMODE_SYSTEM, FONT_NORMAL, ppArgs[3]); } break; } case CV_SMSGL: case CV_SYSMESSAGELOC: { TCHAR *ppArgs[256]; size_t iArgQty = Str_ParseCmds(s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), ","); if ( iArgQty > 1 ) { HUE_TYPE hue = HUE_TEXT_DEF; if ( ATOI(ppArgs[0]) > 0 ) hue = static_cast<HUE_TYPE>(Exp_GetLLVal(ppArgs[0])); DWORD dwClilocId = static_cast<DWORD>(Exp_GetLLVal(ppArgs[1])); CGString sVal; for ( size_t i = 2; i < iArgQty; ++i ) { if ( sVal.GetLength() ) sVal += "\t"; sVal += !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i]; } addBarkLocalized(dwClilocId, NULL, hue, TALKMODE_SYSTEM, FONT_NORMAL, sVal.GetPtr()); } break; } case CV_SMSGLEX: case CV_SYSMESSAGELOCEX: { TCHAR *ppArgs[256]; size_t iArgQty = Str_ParseCmds(s.GetArgRaw(), ppArgs, COUNTOF(ppArgs), ","); if ( iArgQty > 2 ) { HUE_TYPE hue = HUE_TEXT_DEF; AFFIX_TYPE affix = AFFIX_APPEND; if ( ATOI(ppArgs[0]) > 0 ) hue = static_cast<HUE_TYPE>(Exp_GetLLVal(ppArgs[0])); DWORD dwClilocId = static_cast<DWORD>(Exp_GetLLVal(ppArgs[1])); if ( ppArgs[2] ) affix = static_cast<AFFIX_TYPE>(Exp_GetLLVal(ppArgs[2])); CGString sVal; for ( size_t i = 4; i < iArgQty; ++i ) { if ( sVal.GetLength() ) sVal += "\t"; sVal += !strcmp(ppArgs[i], "NULL") ? " " : ppArgs[i]; } addBarkLocalizedEx(dwClilocId, NULL, hue, TALKMODE_SYSTEM, FONT_NORMAL, affix, ppArgs[3], sVal.GetPtr()); } break; } case CV_TELE: Cmd_Skill_Magery(SPELL_Teleport, dynamic_cast<CObjBase *>(pSrc)); break; case CV_TILE: if ( !s.HasArgs() ) { SysMessage("Usage: TILE z-height item1 item2 itemX"); break; } m_Targ_Text = s.GetArgStr(); // point at the options m_tmTile.m_ptFirst.InitPoint(); // clear this first m_tmTile.m_Code = CV_TILE; addTarget(CLIMODE_TARG_TILE, "Pick 1st corner:", true); break; case CV_VERSION: SysMessage(g_szServerDescription); break; case CV_WEBLINK: addWebLaunch(s.GetArgStr()); break; default: if ( r_LoadVal(s) ) { CGString sVal; if ( r_WriteVal(s.GetKey(), sVal, pSrc) ) return true; } return CScriptObj::r_Verb(s, pSrc); // used in the case of web pages to access server level things } return true; EXC_CATCH; EXC_DEBUG_START; EXC_ADD_SCRIPTSRC; EXC_DEBUG_END; return false; }