int CItemMulti::Multi_IsComponentOf( const CItem * pItem ) const { // NOTE: Doors can actually move so this won't work for them ! ASSERT(pItem); if ( ! Multi_IsPartOf( pItem )) return( -1 ); const CItemBaseMulti * pMultiDef = Multi_GetDef(); if ( pMultiDef == NULL ) return( -1 ); CPointMap pt = pItem->GetTopPoint(); int xdiff = pt.m_x - GetTopPoint().m_x ; int ydiff = pt.m_y - GetTopPoint().m_y; int iQty = pMultiDef->m_Components.GetCount(); for ( int i=0; i<iQty; i++ ) { if ( xdiff != pMultiDef->m_Components[i].m_dx || ydiff != pMultiDef->m_Components[i].m_dy ) continue; return( i ); } return( -1 ); }
int CItemMulti::Ship_ListObjs( CObjBase ** ppObjList ) { // List all the objects in the structure. // Move the ship and everything on the deck // If too much stuff. then some will fall overboard. hehe. if ( ! IsTopLevel()) return 0; int iMaxDist = Multi_GetMaxDist(); // always list myself first. All other items must see my new region ! int iCount = 0; ppObjList[iCount++] = this; CWorldSearch AreaChar( GetTopPoint(), iMaxDist ); while ( iCount < MAX_MULTI_LIST_OBJS ) { CChar * pChar = AreaChar.GetChar(); if ( pChar == NULL ) break; if ( pChar->IsClient()) { pChar->GetClient()->addPause(); // get rid of flicker. for anyone even seeing this. } if ( ! m_pRegion->IsInside2d( pChar->GetTopPoint())) continue; int zdiff = pChar->GetTopZ() - GetTopZ(); if ( abs( zdiff ) > 3 ) continue; ppObjList[iCount++] = pChar; } CWorldSearch AreaItem( GetTopPoint(), iMaxDist ); while ( iCount < MAX_MULTI_LIST_OBJS ) { CItem * pItem = AreaItem.GetItem(); if ( pItem == NULL ) break; if ( pItem == this ) // already listed. continue; if ( ! Multi_IsPartOf( pItem )) { if ( ! m_pRegion->IsInside2d( pItem->GetTopPoint())) continue; if ( ! pItem->IsMovable()) continue; int zdiff = pItem->GetTopZ() - GetTopZ(); if ( abs( zdiff ) > 3 ) continue; } ppObjList[iCount++] = pItem; } return( iCount ); }
bool CChar::Use_Train_PickPocketDip( CItem *pItem, bool fSetup ) { ADDTOCALLSTACK("CChar::Use_Train_PickPocketDip"); // IT_TRAIN_PICKPOCKET // Train dummy. ASSERT(pItem); if ( Skill_GetBase(SKILL_STEALING) > g_Cfg.m_iSkillPracticeMax ) { SysMessageDefault(DEFMSG_ITEMUSE_PDUMMY_SKILL); return true; } if ( !pItem->IsTopLevel() ) { badpickpocket: SysMessageDefault(DEFMSG_ITEMUSE_PDUMMY_P); return true; } int dx = GetTopPoint().m_x - pItem->GetTopPoint().m_x; int dy = GetTopPoint().m_y - pItem->GetTopPoint().m_y; bool fNS = (pItem->GetDispID() == ITEMID_PICKPOCKET_NS || pItem->GetDispID() == ITEMID_PICKPOCKET_NS2); if ( fNS ) { if ( !(!dx && abs(dy) < 2) ) goto badpickpocket; } else { if ( !(!dy && abs(dx) < 2) ) goto badpickpocket; } if ( fSetup ) { if ( Skill_GetActive() == NPCACT_TRAINING ) return true; m_Act_TargPrv = m_uidWeapon; m_Act_Targ = pItem->GetUID(); Skill_Start(NPCACT_TRAINING); } else if ( !Skill_UseQuick(SKILL_STEALING, Calc_GetRandLLVal(40)) ) { pItem->Sound(0x041); pItem->SetAnim(fNS ? ITEMID_PICKPOCKET_NS_FX : ITEMID_PICKPOCKET_EW_FX, 3 * TICK_PER_SEC); UpdateAnimate(ANIM_ATTACK_WEAPON); } else { SysMessageDefault(DEFMSG_ITEMUSE_PDUMMY_OK); //pItem->Sound(0x033); } return true; }
// We are creating a char from the current char and the corpse. // Move the items from the corpse back onto us. bool CChar::RaiseCorpse( CItemCorpse * pCorpse ) { ADDTOCALLSTACK("CChar::RaiseCorpse"); if ( !pCorpse ) return false; if ( pCorpse->GetCount() > 0 ) { CItemContainer *pPack = GetPackSafe(); CItem *pItemNext = NULL; for ( CItem *pItem = pCorpse->GetContentHead(); pItem != NULL; pItem = pItemNext ) { pItemNext = pItem->GetNext(); if ( pItem->IsType(IT_HAIR) || pItem->IsType(IT_BEARD) ) // hair on corpse was copied! continue; if ( pItem->GetContainedLayer() ) ItemEquip(pItem); else if ( pPack ) pPack->ContentAdd(pItem); } pCorpse->ContentsDump( GetTopPoint()); // drop left items on ground } UpdateAnimate((pCorpse->m_itCorpse.m_facing_dir & 0x80) ? ANIM_DIE_FORWARD : ANIM_DIE_BACK, true, true); pCorpse->Delete(); return true; }
void CItemCommCrystal::OnMoveFrom() { // Being removed from the top level. CSector * pSector = GetTopPoint().GetSector(); ASSERT(pSector); pSector->RemoveListenItem(); }
bool CItemMulti::Multi_DeedConvert( CChar * pChar ) { // Turn a multi back into a deed. // If it has chests with stuff inside then refuse to do so ? // This item specifically will morph into a deed. (so keys will change!) CWorldSearch Area( GetTopPoint(), Multi_GetMaxDist() ); while (true) { CItem * pItem = Area.GetItem(); if ( pItem == NULL ) break; if ( ! Multi_IsPartOf( pItem )) continue; const CItemContainer* pCont = dynamic_cast <const CItemContainer*>(pItem); if ( pCont == NULL ) continue; if ( pCont->IsEmpty()) continue; // Can't re-deed this with the container full. if ( pChar ) { pChar->SysMessage( "Containers are not empty" ); } return( false ); } // Save the key code for the multi. return( false ); }
void CItemSpawn::AddObj(CGrayUID uid) { ADDTOCALLSTACK("CitemSpawn:AddObj"); // NOTE: This function is also called when loading spawn items // on server startup. In this case, some objs UID still invalid // (not loaded yet) so just proceed without any checks. bool bIsSpawnChar = IsType(IT_SPAWN_CHAR); if ( !g_Serv.IsLoading() ) { if ( !uid.IsValidUID() ) return; if ( bIsSpawnChar ) // IT_SPAWN_CHAR can only spawn NPCs { CChar *pChar = uid.CharFind(); if ( !pChar || !pChar->m_pNPC ) return; } else if ( !uid.ItemFind() ) // IT_SPAWN_ITEM can only spawn items return; CItemSpawn *pPrevSpawn = static_cast<CItemSpawn*>(uid.ObjFind()->m_uidSpawnItem.ItemFind()); if ( pPrevSpawn ) { if ( pPrevSpawn == this ) // obj already linked to this spawn return; pPrevSpawn->DelObj(uid); // obj linked to other spawn, remove the link before proceed } } BYTE iMax = maximum(GetAmount(), 1); for (BYTE i = 0; i < iMax; i++ ) { if ( !m_obj[i].IsValidUID() ) { m_obj[i] = uid; m_currentSpawned++; // objects are linked to the spawn at each server start if ( !g_Serv.IsLoading() ) { uid.ObjFind()->m_uidSpawnItem = GetUID(); if ( bIsSpawnChar ) { CChar *pChar = uid.CharFind(); ASSERT(pChar->m_pNPC); pChar->StatFlag_Set(STATF_Spawned); pChar->m_ptHome = GetTopPoint(); pChar->m_pNPC->m_Home_Dist_Wander = static_cast<WORD>(m_itSpawnChar.m_DistMax); } } break; } } if ( !g_Serv.IsLoading() ) ResendTooltip(); }
// 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 ); }
void CItemSpawn::GenerateChar(CResourceDef *pDef) { ADDTOCALLSTACK("CitemSpawn:GenerateChar"); if ( !IsTopLevel() ) return; RESOURCE_ID_BASE rid = pDef->GetResourceID(); if ( rid.GetResType() == RES_SPAWN ) { const CRandGroupDef *pSpawnGroup = static_cast<const CRandGroupDef *>(pDef); ASSERT(pSpawnGroup); size_t i = pSpawnGroup->GetRandMemberIndex(); if ( i != pSpawnGroup->BadMemberIndex() ) rid = pSpawnGroup->GetMemberID(i); } if ( (rid.GetResType() != RES_CHARDEF) && (rid.GetResType() != RES_UNKNOWN) ) return; CChar *pChar = CChar::CreateBasic(static_cast<CREID_TYPE>(rid.GetResIndex())); if ( !pChar ) return; CPointMap pt = GetTopPoint(); pChar->NPC_LoadScript(true); pChar->StatFlag_Set(STATF_Spawned); pChar->MoveTo(pt); // Check if the NPC can spawn in this region CRegionBase *pRegion = GetTopPoint().GetRegion(REGION_TYPE_AREA); if ( !pRegion || (pRegion->IsGuarded() && pChar->Noto_IsEvil()) ) { pChar->Delete(); return; } AddObj(pChar->GetUID()); pChar->NPC_CreateTrigger(); // removed from NPC_LoadScript() and triggered after char placement and attachment to the spawnitem pChar->Update(); size_t iCount = GetTopSector()->GetCharComplexity(); if ( iCount > g_Cfg.m_iMaxCharComplexity ) g_Log.Event(LOGL_WARN, "%d chars at %s. Sector too complex!\n", iCount, GetTopSector()->GetBasePoint().WriteUsed()); }
bool CItemMulti::MultiRealizeRegion() { // Add/move a region for the multi so we know when we are in it. // RETURN: ignored. DEBUG_CHECK( IsType(IT_MULTI) || IsType(IT_SHIP) ); ASSERT( IsTopLevel()); const CItemBaseMulti * pMultiDef = Multi_GetDef(); if ( pMultiDef == NULL ) { DEBUG_ERR(( "Bad Multi type 0%x, uid=0%x\n", GetID(), (DWORD) GetUID())); return false; } if ( m_pRegion == NULL ) { RESOURCE_ID rid; rid.SetPrivateUID( GetUID()); m_pRegion = new CRegionWorld( rid ); } // Get Background region. CPointMap pt = GetTopPoint(); const CRegionWorld * pRegionBack = dynamic_cast <CRegionWorld*> (pt.GetRegion( REGION_TYPE_AREA )); ASSERT( pRegionBack ); ASSERT( pRegionBack != m_pRegion ); // Create the new region rectangle. CRectMap rect; reinterpret_cast<CGRect&>(rect) = pMultiDef->m_rect; rect.OffsetRect( pt.m_x, pt.m_y ); m_pRegion->SetRegionRect( rect ); m_pRegion->m_pt = pt; DWORD dwFlags; if ( IsType(IT_SHIP)) { dwFlags = REGION_FLAG_SHIP; } else { // Houses get some of the attribs of the land around it. dwFlags = pRegionBack->GetRegionFlags(); } dwFlags |= pMultiDef->m_dwRegionFlags; m_pRegion->SetRegionFlags( dwFlags ); TCHAR *pszTemp = Str_GetTemp(); sprintf(pszTemp, "%s (%s)", (LPCTSTR) pRegionBack->GetName(), (LPCTSTR) GetName()); m_pRegion->SetName(pszTemp); return m_pRegion->RealizeRegion(); }
void CItemStone::SetTownName() { ADDTOCALLSTACK("CItemStone::SetTownName"); // For town stones. if ( ! IsTopLevel()) return; CRegionBase * pArea = GetTopPoint().GetRegion(( IsType(IT_STONE_TOWN)) ? REGION_TYPE_AREA : REGION_TYPE_ROOM ); if ( pArea ) { pArea->SetName( GetIndividualName()); } }
void CItem::Spawn_GenerateChar( CResourceDef * pDef ) { if ( ! IsTopLevel()) return; // creatures can only be top level. if ( m_itSpawnChar.m_current >= GetAmount()) return; int iComplexity = GetTopSector()->GetCharComplexity(); if ( iComplexity > g_Cfg.m_iMaxCharComplexity ) { DEBUG_MSG(( "Spawn uid=0%lx too complex (%d>%d)\n", GetUID(), iComplexity, g_Cfg.m_iMaxCharComplexity )); return; } int iDistMax = m_itSpawnChar.m_DistMax; RESOURCE_ID_BASE rid = pDef->GetResourceID(); if ( rid.GetResType() == RES_SPAWN ) { const CRandGroupDef * pSpawnGroup = STATIC_CAST <const CRandGroupDef *>(pDef); ASSERT(pSpawnGroup); int i = pSpawnGroup->GetRandMemberIndex(); if ( i >= 0 ) { rid = pSpawnGroup->GetMemberID(i); } } CREID_TYPE id; if ( rid.GetResType() == RES_CHARDEF || rid.GetResType() == RES_UNKNOWN ) { id = (CREID_TYPE) rid.GetResIndex(); } else { return; } CChar * pChar = CChar::CreateNPC( id ); if ( pChar == NULL ) return; ASSERT(pChar->m_pNPC); m_itSpawnChar.m_current ++; pChar->Memory_AddObjTypes( this, MEMORY_ISPAWNED ); // Move to spot "near" the spawn item. pChar->MoveNearObj( this, iDistMax ); if ( iDistMax ) { pChar->m_ptHome = GetTopPoint(); pChar->m_pNPC->m_Home_Dist_Wander = iDistMax; } pChar->Update(); }
void CChar::Use_MoonGate( CItem * pItem ) { ADDTOCALLSTACK("CChar::Use_MoonGate"); ASSERT(pItem); CPointBase pt = pItem->m_itTelepad.m_pntMark; if ( pItem->IsType(IT_MOONGATE) ) { // RES_MOONGATES // What gate are we at ? size_t i = 0; size_t iCount = g_Cfg.m_MoonGates.GetCount(); for ( ; i < iCount; i++ ) { if ( GetTopPoint().GetDist(g_Cfg.m_MoonGates[i]) <= UO_MAP_VIEW_SIZE ) break; } // Set it's current destination based on the moon phases. // ensure iTrammelPhrase isn't smaller than iFeluccaPhase, to avoid uint underflow in next calculation unsigned int iTrammelPhase = g_World.GetMoonPhase(false) % iCount; unsigned int iFeluccaPhase = g_World.GetMoonPhase(true) % iCount; if ( iTrammelPhase < iFeluccaPhase ) iTrammelPhase += iCount; size_t iMoongateIndex = (i + (iTrammelPhase - iFeluccaPhase)) % iCount; ASSERT(g_Cfg.m_MoonGates.IsValidIndex(iMoongateIndex)); pt = g_Cfg.m_MoonGates[iMoongateIndex]; } if ( m_pNPC ) { if ( pItem->m_itTelepad.m_fPlayerOnly ) return; if ( m_pNPC->m_Brain == NPCBRAIN_GUARD ) // guards won't leave the guarded region { CRegionBase *pArea = pt.GetRegion(REGION_TYPE_MULTI|REGION_TYPE_AREA); if ( !pArea || !pArea->IsGuarded() ) return; } if ( Noto_IsCriminal() ) // criminals won't enter on guarded regions { CRegionBase *pArea = pt.GetRegion(REGION_TYPE_MULTI|REGION_TYPE_AREA); if ( !pArea || pArea->IsGuarded() ) return; } } bool bCheckAntiMagic = pItem->IsAttr(ATTR_DECAY); bool bDisplayEffect = !pItem->m_itTelepad.m_fQuiet; Spell_Teleport(pt, true, bCheckAntiMagic, bDisplayEffect); }
bool CItemMulti::Multi_CreateComponent( ITEMID_TYPE id, int dx, int dy, int dz, DWORD dwKeyCode ) { CItem * pItem = CreateTemplate( id ); ASSERT(pItem); CPointMap pt = GetTopPoint(); pt.m_x += dx; pt.m_y += dy; pt.m_z += dz; bool fNeedKey = false; switch ( pItem->GetType() ) { case IT_KEY: // it will get locked down with the house ? case IT_SIGN_GUMP: case IT_SHIP_TILLER: pItem->m_itKey.m_lockUID.SetPrivateUID( dwKeyCode ); // Set the key id for the key/sign. fNeedKey = true; break; case IT_DOOR: pItem->SetType(IT_DOOR_LOCKED); fNeedKey = true; break; case IT_CONTAINER: pItem->SetType(IT_CONTAINER_LOCKED); fNeedKey = true; break; case IT_SHIP_SIDE: pItem->SetType(IT_SHIP_SIDE_LOCKED); break; case IT_SHIP_HOLD: pItem->SetType(IT_SHIP_HOLD_LOCK); break; } pItem->SetAttr( ATTR_MOVE_NEVER | (m_Attr&(ATTR_MAGIC|ATTR_INVIS))); pItem->SetHue( GetHue()); pItem->m_uidLink = GetUID(); // lock it down with the structure. if ( pItem->IsTypeLockable() || pItem->IsTypeLocked()) { pItem->m_itContainer.m_lockUID.SetPrivateUID( dwKeyCode ); // Set the key id for the door/key/sign. pItem->m_itContainer.m_lock_complexity = 10000; // never pickable. } pItem->MoveTo( pt ); return( fNeedKey ); }
void CItemSpawn::GenerateChar(CResourceDef *pDef) { ADDTOCALLSTACK("CitemSpawn:GenerateChar"); RESOURCE_ID_BASE rid = pDef->GetResourceID(); if ( rid.GetResType() == RES_SPAWN ) { const CRandGroupDef *pSpawnGroup = static_cast<const CRandGroupDef *>(pDef); ASSERT(pSpawnGroup); size_t i = pSpawnGroup->GetRandMemberIndex(); if ( i != pSpawnGroup->BadMemberIndex() ) rid = pSpawnGroup->GetMemberID(i); } if ( (rid.GetResType() != RES_CHARDEF) && (rid.GetResType() != RES_UNKNOWN) ) return; CPointMap pt = GetTopPoint(); CRegionBase *pRegion = pt.GetRegion(REGION_TYPE_AREA); if ( !pRegion ) return; CChar *pChar = CChar::CreateBasic(static_cast<CREID_TYPE>(rid.GetResIndex())); if ( !pChar ) return; pChar->NPC_LoadScript(true); pChar->StatFlag_Set(STATF_Spawned); // Try placing the char near the spawn if ( !pChar->MoveNearObj(this, m_itSpawnChar.m_DistMax) || !pChar->CanSeeLOS(pt) ) { // If this fails, try placing the char over the spawn if ( !pChar->MoveTo(pt) ) { DEBUG_ERR(("Spawn UID:0%lx is unable to place a character inside the world.\n", static_cast<DWORD>(GetUID()))); pChar->Delete(); return; } } AddObj(pChar->GetUID()); pChar->NPC_CreateTrigger(); // removed from NPC_LoadScript() and triggered after char placement and attachment to the spawnitem pChar->Update(); size_t iCount = GetTopSector()->GetCharComplexity(); if ( iCount > g_Cfg.m_iMaxCharComplexity ) g_Log.Event(LOGL_WARN, "%" FMTSIZE_T " chars at %s. Sector too complex!\n", iCount, GetTopSector()->GetBasePoint().WriteUsed()); }
CItem * CItemMulti::Multi_FindItemType( IT_TYPE type ) const { // Find a part of this multi nearby. if ( ! IsTopLevel()) return( NULL ); CWorldSearch Area( GetTopPoint(), Multi_GetMaxDist() ); while (true) { CItem * pItem = Area.GetItem(); if ( pItem == NULL ) return( NULL ); if ( ! Multi_IsPartOf( pItem )) continue; if ( pItem->IsType( type )) return( pItem ); } }
bool CItemMulti::r_LoadVal( CScript & s ) { LOCKDATA; EXC_TRY(("r_LoadVal('%s %s')", s.GetKey(), s.GetArgStr())); if ( s.IsKeyHead( "REGION.", 7 )) { if ( ! IsTopLevel()) { MoveTo( GetTopPoint()); // Put item on the ground here. } ASSERT( m_pRegion ); CScript script( s.GetKey()+7, s.GetArgStr()); return( m_pRegion->r_LoadVal( script ) ); } return CItem::r_LoadVal(s); EXC_CATCH("CItemMulti"); return false; }
CItemCorpse *CChar::FindMyCorpse( bool ignoreLOS, int iRadius ) const { ADDTOCALLSTACK("CChar::FindMyCorpse"); // If they are standing on their own corpse then res the corpse ! CWorldSearch Area(GetTopPoint(), iRadius); for (;;) { CItem *pItem = Area.GetItem(); if ( !pItem ) break; if ( !pItem->IsType(IT_CORPSE) ) continue; CItemCorpse *pCorpse = dynamic_cast<CItemCorpse*>(pItem); if ( !pCorpse || (pCorpse->m_uidLink != GetUID()) ) continue; if ( pCorpse->m_itCorpse.m_BaseID != m_prev_id ) // not morphed type continue; if ( !ignoreLOS && !CanSeeLOS(pCorpse) ) continue; return pCorpse; } return NULL; }
// timer expired, should I grow? bool CItem::Plant_OnTick() { ADDTOCALLSTACK("CItem::Plant_OnTick"); ASSERT(IsType(IT_CROPS) || IsType(IT_FOLIAGE)); // If it is in a container, kill it. if ( !IsTopLevel() ) return false; // Make sure the darn thing isn't moveable SetAttr(ATTR_MOVE_NEVER); Plant_SetTimer(); // No tree stuff below here if ( IsAttr(ATTR_INVIS) ) // if it's invis, take care of it here and return { SetHue(HUE_DEFAULT); ClrAttr(ATTR_INVIS); Update(); return true; } const CItemBase *pItemDef = Item_GetDef(); ITEMID_TYPE iGrowID = pItemDef->m_ttCrops.m_idGrow; if ( iGrowID == -1 ) { // Some plants generate a fruit on the ground when ripe. ITEMID_TYPE iFruitID = static_cast<ITEMID_TYPE>(RES_GET_INDEX(pItemDef->m_ttCrops.m_idGrow)); if ( m_itCrop.m_ReapFruitID ) iFruitID = static_cast<ITEMID_TYPE>(RES_GET_INDEX(m_itCrop.m_ReapFruitID)); if ( !iFruitID ) return true; // Put a fruit on the ground if not already here. CWorldSearch AreaItems(GetTopPoint()); for (;;) { CItem *pItem = AreaItems.GetItem(); if ( !pItem ) { CItem *pItemFruit = CItem::CreateScript(iFruitID); ASSERT(pItemFruit); pItemFruit->MoveToDecay(GetTopPoint(), 10 * g_Cfg.m_iDecay_Item); break; } if ( pItem->IsType(IT_FRUIT) || pItem->IsType(IT_REAGENT_RAW) ) break; } // NOTE: ??? The plant should cycle here as well ! iGrowID = pItemDef->m_ttCrops.m_idReset; } if ( iGrowID ) { SetID(static_cast<ITEMID_TYPE>(RES_GET_INDEX(iGrowID))); Update(); return true; } // some plants go dormant again ? // m_itCrop.m_Fruit_ID = iTemp; return true; }
bool CChar::Use_Train_Dummy( CItem * pItem, bool fSetup ) { ADDTOCALLSTACK("CChar::Use_Train_Dummy"); // IT_TRAIN_DUMMY // Dummy animation timer prevents over dclicking. ASSERT(pItem); SKILL_TYPE skill = Fight_GetWeaponSkill(); if ( g_Cfg.IsSkillFlag(skill, SKF_RANGED) ) // do not allow archery training on dummys { SysMessageDefault(DEFMSG_ITEMUSE_TDUMMY_ARCH); return false; } char skilltag[32]; sprintf(skilltag, "OVERRIDE.PracticeMax.SKILL_%d", skill &~0xD2000000); CVarDefCont *pSkillTag = pItem->GetKey(skilltag, true); int iMaxSkill = pSkillTag ? static_cast<int>(pSkillTag->GetValNum()) : g_Cfg.m_iSkillPracticeMax; if ( Skill_GetBase(skill) > iMaxSkill ) { SysMessageDefault(DEFMSG_ITEMUSE_TDUMMY_SKILL); return false; } if ( !pItem->IsTopLevel() ) { baddumy: SysMessageDefault(DEFMSG_ITEMUSE_TDUMMY_P); return false ; } // Check location int dx = GetTopPoint().m_x - pItem->GetTopPoint().m_x; int dy = GetTopPoint().m_y - pItem->GetTopPoint().m_y; if ( pItem->GetDispID() == ITEMID_DUMMY1 ) { if ( !(!dx && abs(dy) < 2) ) goto baddumy; } else { if ( !(!dy && abs(dx) < 2) ) goto baddumy; } if ( fSetup ) { if ( Skill_GetActive() == NPCACT_TRAINING ) return true; UpdateAnimate(ANIM_ATTACK_WEAPON); m_Act_TargPrv = m_uidWeapon; m_Act_Targ = pItem->GetUID(); Skill_Start(NPCACT_TRAINING); } else { pItem->SetAnim(static_cast<ITEMID_TYPE>(pItem->GetID() + 1), 3 * TICK_PER_SEC); pItem->Sound(0x033); Skill_UseQuick(skill, Calc_GetRandLLVal(40)); } 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; }
void CItemSpawn::GenerateChar(CResourceDef * pDef) { ADDTOCALLSTACK("CitemSpawn:GenerateChar"); if ( !IsTopLevel() || ( m_itSpawnChar.m_current >= GetAmount() ) || ( GetTopSector()->GetCharComplexity() > g_Cfg.m_iMaxCharComplexity )) return; int iDistMax = m_itSpawnChar.m_DistMax; RESOURCE_ID_BASE rid = pDef->GetResourceID(); if ( rid.GetResType() == RES_SPAWN ) { const CRandGroupDef * pSpawnGroup = STATIC_CAST <const CRandGroupDef *>(pDef); ASSERT(pSpawnGroup); size_t i = pSpawnGroup->GetRandMemberIndex(); if ( i != pSpawnGroup->BadMemberIndex() ) { rid = pSpawnGroup->GetMemberID(i); } } if (( rid.GetResType() != RES_CHARDEF ) && ( rid.GetResType() != RES_UNKNOWN )) return; CREID_TYPE id = static_cast<CREID_TYPE>(rid.GetResIndex()); bool isBadPlaceToSpawn = false; CChar * pChar = CChar::CreateBasic(id); if( pChar == NULL ) { return; } pChar->NPC_LoadScript(true); AddObj(pChar->GetUID()); pChar->m_uidSpawnItem = GetUID(); // SpawnItem for this char pChar->StatFlag_Set( STATF_Spawned ); pChar->MoveTo(GetTopPoint()); pChar->NPC_CreateTrigger(); //Removed from NPC_LoadScript() and triggered after char placement if( pChar->GetRegion() == NULL ) { isBadPlaceToSpawn = true; } else if( pChar->GetRegion()->IsGuarded() && pChar->Noto_IsEvil() ) { isBadPlaceToSpawn = true; } // Deny definitely known a bad place to spawn (like red NPCs in guarded areas) // Usually caused by wide range near the edge of the towns if( isBadPlaceToSpawn ) { pChar->Delete(); //m_itSpawnChar.m_current--; return; } ASSERT(pChar->m_pNPC); if ( iDistMax ) { pChar->m_ptHome = GetTopPoint(); pChar->m_pNPC->m_Home_Dist_Wander = static_cast<WORD>(iDistMax); } pChar->Update(); }
bool CItemMulti::Ship_Face( DIR_TYPE dir ) { // Change the direction of the ship. ASSERT( IsTopLevel()); DEBUG_CHECK( m_pRegion ); if ( m_pRegion == NULL ) { return false; } int i=0; for ( ; true; i++ ) { if ( i >= COUNTOF(sm_Ship_FaceDir)) return( false ); if ( dir == sm_Ship_FaceDir[i] ) break; } int iFaceOffset = Ship_GetFaceOffset(); ITEMID_TYPE idnew = (ITEMID_TYPE) ( GetID() - iFaceOffset + i ); const CItemBaseMulti * pMultiNew = Multi_GetDef( idnew ); if ( pMultiNew == NULL ) { return false; } const CItemBaseMulti * pMultiDef = Multi_GetDef(); ASSERT( pMultiDef); int iTurn = dir - sm_Ship_FaceDir[ iFaceOffset ]; // ?? Are there blocking items in the way of the turn ? // CRectMap // Reorient everything on the deck CObjBase * ppObjs[MAX_MULTI_LIST_OBJS+1]; int iCount = Ship_ListObjs( ppObjs ); int i2 = 0; for ( i=0; i<iCount; i++ ) { CObjBase * pObj = ppObjs[i]; CPointMap pt = pObj->GetTopPoint(); if ( pObj->IsItem()) { CItem * pItem = STATIC_CAST <CItem*> (pObj); ASSERT( pItem ); if ( pItem == this ) { m_pRegion->UnRealizeRegion(); pItem->SetID(idnew); // Adjust the region to be the new shape/area. MultiRealizeRegion(); pItem->Update(); continue; } // Is this a ship component ? transform it. //int i = Multi_IsComponentOf( pItem ); if ( Multi_IsPartOf( pItem ) ) //i>=0 ) { pItem->SetDispID( pMultiNew->m_Components[i2].m_id ); pt = GetTopPoint(); pt.m_x += pMultiNew->m_Components[i2].m_dx; pt.m_y += pMultiNew->m_Components[i2].m_dy; pt.m_z += pMultiNew->m_Components[i2].m_dz; pItem->MoveTo( pt ); i2++; continue; } } // -2,6 = left. // +2,-6 = right. // +-4 = flip around int iTmp; int xdiff = GetTopPoint().m_x - pt.m_x; int ydiff = GetTopPoint().m_y - pt.m_y; switch ( iTurn ) { case 2: // right case (2-DIR_QTY): iTmp = xdiff; xdiff = ydiff; ydiff = -iTmp; break; case -2: // left. case (DIR_QTY-2): iTmp = xdiff; xdiff = -ydiff; ydiff = iTmp; break; default: // u turn. xdiff = -xdiff; ydiff = -ydiff; break; } pt.m_x = GetTopPoint().m_x + xdiff; pt.m_y = GetTopPoint().m_y + ydiff; pObj->MoveTo( pt ); if ( pObj->IsChar()) { // Turn creatures as well. CChar * pChar = STATIC_CAST <CChar*> (pObj); ASSERT(pChar); pChar->m_dirFace = GetDirTurn( pChar->m_dirFace, iTurn ); pChar->RemoveFromView(); pChar->Update(); } } m_itShip.m_DirFace = dir; return( true ); }
bool CChar::Use_Train_ArcheryButte( CItem * pButte, bool fSetup ) { ADDTOCALLSTACK("CChar::Use_Train_ArcheryButte"); // IT_ARCHERY_BUTTE ASSERT(pButte); ITEMID_TYPE AmmoID; if ( GetDist(pButte) < 2 ) // if we are standing right next to the butte, retrieve the arrows/bolts { if ( pButte->m_itArcheryButte.m_AmmoCount == 0 ) { SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_EMPTY); return true; } AmmoID = pButte->m_itArcheryButte.m_AmmoType; CItemBase *pAmmoDef = CItemBase::FindItemBase(AmmoID); if ( pAmmoDef ) { TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_REM), pAmmoDef->GetName(), (pButte->m_itArcheryButte.m_AmmoCount == 1) ? "" : g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_REMS)); Emote(pszMsg); CItem *pRemovedAmmo = CItem::CreateBase(AmmoID); ASSERT(pRemovedAmmo); pRemovedAmmo->SetAmount(pButte->m_itArcheryButte.m_AmmoCount); ItemBounce(pRemovedAmmo); } // Clear the target pButte->m_itArcheryButte.m_AmmoType = ITEMID_NOTHING; pButte->m_itArcheryButte.m_AmmoCount = 0; return true; } SKILL_TYPE skill = Fight_GetWeaponSkill(); if ( !g_Cfg.IsSkillFlag(skill, SKF_RANGED) ) { SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_WS); return true; } if ( Skill_GetBase(skill) > g_Cfg.m_iSkillPracticeMax ) { SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_SKILL); return true; } // Make sure we have some ammo CItem *pWeapon = m_uidWeapon.ItemFind(); ASSERT(pWeapon); const CItemBase *pWeaponDef = pWeapon->Item_GetDef(); // Determine ammo type CVarDefCont *pVarAmmoType = pWeapon->GetDefKey("AMMOTYPE", true); RESOURCE_ID_BASE rid; LPCTSTR t_Str; if ( pVarAmmoType ) { t_Str = pVarAmmoType->GetValStr(); rid = static_cast<RESOURCE_ID_BASE>(g_Cfg.ResourceGetID(RES_ITEMDEF, t_Str)); } else { rid = pWeaponDef->m_ttWeaponBow.m_idAmmo; } AmmoID = static_cast<ITEMID_TYPE>(rid.GetResIndex()); // If there is a different ammo type on the butte currently, tell us to remove the current type first if ( (pButte->m_itArcheryButte.m_AmmoType != ITEMID_NOTHING) && (pButte->m_itArcheryButte.m_AmmoType != AmmoID) ) { SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_X); return true; } // We need to be correctly aligned with the target before we can use it // For the south facing butte, we need to have the same X value and a Y > 2 // For the east facing butte, we need to have the same Y value and an X > 2 if ( !pButte->IsTopLevel() ) { badalign: SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_P); return true; } int targDistX = GetTopPoint().m_x - pButte->GetTopPoint().m_x; int targDistY = GetTopPoint().m_y - pButte->GetTopPoint().m_y; if ( (pButte->GetID() == ITEMID_ARCHERYBUTTE_S) || (pButte->GetID() == ITEMID_MONGBATTARGET_S) ) { if ( !(targDistX == 0 && targDistY > 2) ) goto badalign; } else { if ( !(targDistY == 0 && targDistX > 2) ) goto badalign; } if ( !CanSeeLOS(pButte, LOS_NB_WINDOWS) ) //we should be able to shoot through a window { SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_BLOCK); return true; } if ( fSetup ) { if ( Skill_GetActive() == NPCACT_TRAINING ) return true; UpdateAnimate(ANIM_ATTACK_WEAPON); m_Act_TargPrv = m_uidWeapon; m_Act_Targ = pButte->GetUID(); Skill_Start(NPCACT_TRAINING); return true; } CVarDefCont *pCont = pWeapon->GetDefKey("AMMOCONT",true); if ( m_pPlayer && AmmoID ) { int iFound = 1; if ( pCont ) { //check for UID CGrayUID uidCont = static_cast<DWORD>(pCont->GetValNum()); CItemContainer *pNewCont = dynamic_cast<CItemContainer*>(uidCont.ItemFind()); if ( !pNewCont ) //if no UID, check for ITEMID_TYPE { t_Str = pCont->GetValStr(); RESOURCE_ID_BASE rContid = static_cast<RESOURCE_ID_BASE>(g_Cfg.ResourceGetID(RES_ITEMDEF, t_Str)); ITEMID_TYPE ContID = static_cast<ITEMID_TYPE>(rContid.GetResIndex()); if ( ContID ) pNewCont = dynamic_cast<CItemContainer*>(ContentFind(rContid)); } if ( pNewCont ) iFound = pNewCont->ContentConsume(RESOURCE_ID(RES_ITEMDEF, AmmoID)); else iFound = ContentConsume(RESOURCE_ID(RES_ITEMDEF, AmmoID)); } else iFound = ContentConsume(RESOURCE_ID(RES_ITEMDEF, AmmoID)); if ( iFound ) { SysMessageDefault(DEFMSG_ITEMUSE_ARCHB_NOAMMO); return(true); } } // OK...go ahead and fire at the target // Check the skill bool fSuccess = Skill_UseQuick(skill, Calc_GetRandLLVal(40)); // determine animation parameters CVarDefCont *pVarAnim = pWeapon->GetDefKey("AMMOANIM", true); CVarDefCont *pVarAnimColor = pWeapon->GetDefKey("AMMOANIMHUE", true); CVarDefCont *pVarAnimRender = pWeapon->GetDefKey("AMMOANIMRENDER", true); ITEMID_TYPE AmmoAnim; DWORD AmmoHue; DWORD AmmoRender; if ( pVarAnim ) { t_Str = pVarAnim->GetValStr(); rid = static_cast<RESOURCE_ID_BASE>(g_Cfg.ResourceGetID(RES_ITEMDEF, t_Str)); AmmoAnim = static_cast<ITEMID_TYPE>(rid.GetResIndex()); } else AmmoAnim = static_cast<ITEMID_TYPE>(pWeaponDef->m_ttWeaponBow.m_idAmmoX.GetResIndex()); AmmoHue = pVarAnimColor ? static_cast<DWORD>(pVarAnimColor->GetValNum()) : 0; AmmoRender = pVarAnimRender ? static_cast<DWORD>(pVarAnimRender->GetValNum()) : 0; pButte->Effect(EFFECT_BOLT, AmmoAnim, this, 16, 0, false, AmmoHue, AmmoRender); pButte->Sound(0x224); // Did we destroy the ammo? const CItemBase *pAmmoDef = NULL; if ( AmmoID ) pAmmoDef = CItemBase::FindItemBase(AmmoID); if ( !fSuccess ) { // Small chance of destroying the ammo if ( pAmmoDef && !Calc_GetRandVal(10) ) { TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_DEST), pAmmoDef->GetName()); Emote(pszMsg, NULL, true); return true; } static LPCTSTR const sm_Txt_ArcheryButte_Failure[] = { g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_MISS_1), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_MISS_2), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_MISS_3), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_MISS_4) }; Emote(sm_Txt_ArcheryButte_Failure[Calc_GetRandVal(COUNTOF(sm_Txt_ArcheryButte_Failure))]); } else { // Very small chance of destroying another arrow if ( pAmmoDef && !Calc_GetRandVal(50) && pButte->m_itArcheryButte.m_AmmoCount ) { TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_SPLIT), pAmmoDef->GetName()); Emote(pszMsg, NULL, true); return true; } static LPCTSTR const sm_Txt_ArcheryButte_Success[] = { g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_HIT_1), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_HIT_2), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_HIT_3), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_ARCHB_HIT_4) }; Emote(sm_Txt_ArcheryButte_Success[Calc_GetRandVal(COUNTOF(sm_Txt_ArcheryButte_Success))]); } // Update the target if ( AmmoID ) { pButte->m_itArcheryButte.m_AmmoType = AmmoID; pButte->m_itArcheryButte.m_AmmoCount++; } return true; }
bool CChar::Use_Seed( CItem * pSeed, CPointMap * pPoint ) { ADDTOCALLSTACK("CChar::Use_Seed"); // Use the seed at the current point on the ground or some new point that i can touch. // IT_SEED from IT_FRUIT ASSERT(pSeed); CPointMap pt; if ( pPoint ) pt = *pPoint; else if ( pSeed->IsTopLevel() ) pt = pSeed->GetTopPoint(); else pt = GetTopPoint(); if ( !CanTouch(pt) ) { SysMessageDefault(DEFMSG_MSG_SEED_REACH); return false; } // is there soil here ? IT_DIRT if ( !IsPriv(PRIV_GM) && !g_World.IsItemTypeNear(pt, IT_DIRT, 0, false) ) { SysMessageDefault(DEFMSG_MSG_SEED_TARGSOIL); return(false); } const CItemBase *pItemDef = pSeed->Item_GetDef(); ITEMID_TYPE idReset = static_cast<ITEMID_TYPE>(RES_GET_INDEX(pItemDef->m_ttFruit.m_idReset)); if ( idReset == 0 ) { SysMessageDefault(DEFMSG_MSG_SEED_NOGOOD); return false; } // Already a plant here ? CWorldSearch AreaItems(pt); for (;;) { CItem *pItem = AreaItems.GetItem(); if ( !pItem ) break; if ( pItem->IsType(IT_TREE) || pItem->IsType(IT_FOLIAGE) ) // there's already a tree here { SysMessageDefault(DEFMSG_MSG_SEED_ATREE); return false; } if ( pItem->IsType(IT_CROPS) ) // there's already a plant here pItem->Delete(); } // plant it and consume the seed. CItem *pPlant = CItem::CreateScript(idReset, this); ASSERT(pPlant); pPlant->MoveToUpdate(pt); if ( pPlant->IsType(IT_CROPS) || pPlant->IsType(IT_FOLIAGE) ) { pPlant->m_itCrop.m_ReapFruitID = pSeed->GetID(); pPlant->Plant_CropReset(); } else { pPlant->SetDecayTime(10 * g_Cfg.m_iDecay_Item); } pSeed->ConsumeAmount(); return true; }
bool CChar::CanSeeLOS( const CPointMap &ptDst, CPointMap *pptBlock, int iMaxDist, word wFlags, bool bCombatCheck ) const { ADDTOCALLSTACK("CChar::CanSeeLOS"); // WARNING: CanSeeLOS is an expensive function (lot of calculations but most importantly it has to read the UO files, and file I/O is slow). if ( (m_pPlayer && (g_Cfg.m_iAdvancedLos & ADVANCEDLOS_PLAYER)) || (m_pNPC && (g_Cfg.m_iAdvancedLos & ADVANCEDLOS_NPC)) ) return CanSeeLOS_New(ptDst, pptBlock, iMaxDist, wFlags); // Max distance of iMaxDist // Line of sight check // NOTE: if not blocked. pptBlock is undefined. // 3D LOS later - real LOS, i.e. we can't shoot through the floor, but can shoot through the hole in it if ( !bCombatCheck && IsPriv(PRIV_GM) ) // If i'm checking the LOS during a combat, i don't want to shoot through the walls even if i'm a GM return true; CPointMap ptSrc = GetTopPoint(); int iDist = ptSrc.GetDist(ptDst); if ( iDist > iMaxDist ) { blocked: if ( pptBlock ) *pptBlock = ptSrc; return false; // blocked } // Walk towards the object. If any spot is too far above our heads then we can not see what's behind it. int iDistTry = 0; while ( --iDist >= 0 ) { DIR_TYPE dir = ptSrc.GetDir(ptDst); dword dwBlockFlags; if ( dir % 2 && !IsSetEF(EF_NoDiagonalCheckLOS) ) // test only diagonal dirs { CPointMap ptTest = ptSrc; DIR_TYPE dirTest1 = (DIR_TYPE)(dir - 1); // get 1st ortogonal DIR_TYPE dirTest2 = (DIR_TYPE)(dir + 1); // get 2nd ortogonal if ( dirTest2 == DIR_QTY ) // roll over dirTest2 = DIR_N; ptTest.Move(dirTest1); dwBlockFlags = CAN_C_SWIM|CAN_C_WALK|CAN_C_FLY; char z = g_World.GetHeightPoint2(ptTest, dwBlockFlags, true); short zDiff = (short)(abs(z - ptTest.m_z)); if ( (zDiff > PLAYER_HEIGHT) || (dwBlockFlags & (CAN_I_BLOCK|CAN_I_DOOR)) ) // blocked { ptTest = ptSrc; ptTest.Move(dirTest2); { dwBlockFlags = CAN_C_SWIM|CAN_C_WALK|CAN_C_FLY; z = g_World.GetHeightPoint2(ptTest, dwBlockFlags, true); zDiff = (short)(abs(z - ptTest.m_z)); if ( zDiff > PLAYER_HEIGHT ) goto blocked; if ( dwBlockFlags & (CAN_I_BLOCK|CAN_I_DOOR) ) { ptSrc = ptTest; goto blocked; } } } ptTest.m_z = z; } if ( iDist ) { ptSrc.Move(dir); // NOTE: The dir is very coarse and can change slightly. dwBlockFlags = CAN_C_SWIM|CAN_C_WALK|CAN_C_FLY; char z = g_World.GetHeightPoint2(ptSrc, dwBlockFlags, true); short zDiff = (short)(abs(z - ptSrc.m_z)); if ( (zDiff > PLAYER_HEIGHT) || (dwBlockFlags & (CAN_I_BLOCK|CAN_I_DOOR)) || (iDistTry > iMaxDist) ) goto blocked; ptSrc.m_z = z; ++iDistTry; } } if ( abs(ptSrc.m_z - ptDst.m_z) >= 20 ) return false; return true; // made it all the way to the object with no obstructions. }
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::Do_Use_Item(CItem *pItem, bool fLink) { ADDTOCALLSTACK("CChar::Do_Use_Item"); if (!pItem) return false; if (m_pNPC && (IsTrigUsed(TRIGGER_DCLICK) || IsTrigUsed(TRIGGER_ITEMDCLICK))) // for players, DClick was called before this function { if (pItem->OnTrigger(ITRIG_DCLICK, this) == TRIGRET_RET_TRUE) return false; } CItemSpawn *pSpawn = static_cast<CItemSpawn *>(pItem->m_uidSpawnItem.ItemFind()); if (pSpawn) pSpawn->DelObj(pItem->GetUID()); // remove this item from it's spawn when DClicks it int fAction = true; switch(pItem->GetType()) { case IT_ITEM_STONE: { // Give them this item if (pItem->m_itItemStone.m_wAmount == USHRT_MAX) { SysMessageDefault(DEFMSG_MSG_IT_IS_DEAD); return true; } if (pItem->m_itItemStone.m_wRegenTime) { if (pItem->IsTimerSet()) { SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_MSG_STONEREG_TIME), pItem->GetTimerDiff() / TICK_PER_SEC); return true; } pItem->SetTimeout(pItem->m_itItemStone.m_wRegenTime * TICK_PER_SEC); } ItemBounce(CItem::CreateTemplate(pItem->m_itItemStone.m_ItemID, GetPackSafe(), this)); if (pItem->m_itItemStone.m_wAmount != 0) { pItem->m_itItemStone.m_wAmount--; if (pItem->m_itItemStone.m_wAmount == 0) pItem->m_itItemStone.m_wAmount = USHRT_MAX; } return true; } case IT_SEED: return Use_Seed(pItem, NULL); case IT_BEDROLL: return Use_BedRoll(pItem); case IT_KINDLING: return Use_Kindling(pItem); case IT_SPINWHEEL: { if (fLink) return false; // Just make them spin pItem->SetAnim(static_cast<ITEMID_TYPE>(pItem->GetID() + 1), 2 * TICK_PER_SEC); SysMessageDefault(DEFMSG_ITEMUSE_SPINWHEEL); return true; } case IT_TRAIN_DUMMY: { if (fLink) return false; Use_Train_Dummy(pItem, true); return true; } case IT_TRAIN_PICKPOCKET: { if (fLink) return false; Use_Train_PickPocketDip(pItem, true); return true; } case IT_ARCHERY_BUTTE: { if (fLink) return false; Use_Train_ArcheryButte(pItem, true); return true; } case IT_LOOM: { if (fLink) return false; SysMessageDefault(DEFMSG_ITEMUSE_LOOM); return true; } case IT_BEE_HIVE: { if (fLink) return false; // Get honey from it ITEMID_TYPE id = ITEMID_NOTHING; if (!pItem->m_itBeeHive.m_honeycount) SysMessageDefault(DEFMSG_ITEMUSE_BEEHIVE); else { switch(Calc_GetRandVal(3)) { case 1: id = ITEMID_JAR_HONEY; break; case 2: id = ITEMID_BEE_WAX; break; } } if (id) { ItemBounce(CItem::CreateScript(id, this)); pItem->m_itBeeHive.m_honeycount--; } else { SysMessageDefault(DEFMSG_ITEMUSE_BEEHIVE_STING); OnTakeDamage(Calc_GetRandVal(5), this, DAMAGE_POISON | DAMAGE_GENERAL); } pItem->SetTimeout(15 * 60 * TICK_PER_SEC); return true; } case IT_MUSICAL: { if (!Skill_Wait(SKILL_MUSICIANSHIP)) { m_Act_Targ = pItem->GetUID(); Skill_Start(SKILL_MUSICIANSHIP); } break; } case IT_CROPS: case IT_FOLIAGE: { // Pick cotton/hay/etc fAction = pItem->Plant_Use(this); break; } case IT_FIGURINE: { // Create the creature here if (Use_Figurine(pItem) != NULL) pItem->Delete(); return true; } case IT_TRAP: case IT_TRAP_ACTIVE: { // Activate the trap (plus any linked traps) int iDmg = pItem->Use_Trap(); if (CanTouch(pItem->GetTopLevelObj()->GetTopPoint())) OnTakeDamage(iDmg, NULL, DAMAGE_HIT_BLUNT | DAMAGE_GENERAL); break; } case IT_SWITCH: { // Switches can be linked to gates and doors and such. // Flip the switch graphic. pItem->SetSwitchState(); break; } case IT_PORT_LOCKED: if (!fLink && !IsPriv(PRIV_GM)) { SysMessageDefault(DEFMSG_ITEMUSE_PORT_LOCKED); return true; } case IT_PORTCULIS: // Open a metal gate vertically pItem->Use_Portculis(); break; case IT_DOOR_LOCKED: if (!ContentFindKeyFor(pItem)) { SysMessageDefault(DEFMSG_MSG_KEY_DOORLOCKED); if (!pItem->IsTopLevel()) return false; if (pItem->IsAttr(ATTR_MAGIC)) // show it's magic face { ITEMID_TYPE id = (GetDispID() & DOOR_NORTHSOUTH) ? ITEMID_DOOR_MAGIC_SI_NS : ITEMID_DOOR_MAGIC_SI_EW; CItem *pFace = CItem::CreateBase(id); ASSERT(pFace); pFace->MoveToDecay(pItem->GetTopPoint(), 4 * TICK_PER_SEC); } if (!IsPriv(PRIV_GM)) return true; } case IT_DOOR_OPEN: case IT_DOOR: { bool fOpen = pItem->Use_DoorNew(fLink); if (fLink || !fOpen) // don't link if we are just closing the door return true; } break; case IT_SHIP_PLANK: { // Close the plank if I'm inside the ship if (m_pArea->IsFlag(REGION_FLAG_SHIP) && m_pArea->GetResourceID() == pItem->m_uidLink) { if (pItem->m_itShipPlank.m_itSideType == IT_SHIP_SIDE_LOCKED && !ContentFindKeyFor(pItem)) { SysMessageDefault(DEFMSG_ITEMUSE_SHIPSIDE); return true; } return pItem->Ship_Plank(false); } else if (pItem->IsTopLevel()) { // Teleport to plank if I'm outside the ship CPointMap pntTarg = pItem->GetTopPoint(); pntTarg.m_z++; Spell_Teleport(pntTarg, true, false, false); } return true; } case IT_SHIP_SIDE_LOCKED: if (!ContentFindKeyFor(pItem)) { SysMessageDefault(DEFMSG_ITEMUSE_SHIPSIDE); return true; } case IT_SHIP_SIDE: // Open the plank pItem->Ship_Plank(true); return true; case IT_GRAIN: case IT_GRASS: case IT_GARBAGE: case IT_FRUIT: case IT_FOOD: case IT_FOOD_RAW: case IT_MEAT_RAW: { if (fLink) return false; Use_Eat(pItem); return true; } case IT_POTION: case IT_DRINK: case IT_PITCHER: case IT_WATER_WASH: case IT_BOOZE: { if (fLink) return false; Use_Drink(pItem); return true; } case IT_LIGHT_OUT: // can the light be lit? case IT_LIGHT_LIT: // can the light be doused? fAction = pItem->Use_Light(); break; case IT_CLOTHING: case IT_ARMOR: case IT_ARMOR_LEATHER: case IT_SHIELD: case IT_WEAPON_MACE_CROOK: case IT_WEAPON_MACE_PICK: case IT_WEAPON_MACE_SMITH: case IT_WEAPON_MACE_SHARP: case IT_WEAPON_SWORD: case IT_WEAPON_FENCE: case IT_WEAPON_BOW: case IT_WEAPON_AXE: case IT_WEAPON_XBOW: case IT_WEAPON_MACE_STAFF: case IT_JEWELRY: case IT_WEAPON_THROWING: { if (fLink) return false; return ItemEquip(pItem); } case IT_WEB: { if (fLink) return false; Use_Item_Web(pItem); return true; } case IT_SPY_GLASS: { if (fLink) return false; // Spyglass will tell you the moon phases static LPCTSTR const sm_sPhases[8] = { g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M1), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M2), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M3), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M4), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M5), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M6), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M7), g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_M8) }; SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_TR), sm_sPhases[g_World.GetMoonPhase(false)]); SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SPYGLASS_FE), sm_sPhases[g_World.GetMoonPhase(true)]); if (m_pArea && m_pArea->IsFlag(REGION_FLAG_SHIP)) ObjMessage(pItem->Use_SpyGlass(this), this); return true; } case IT_SEXTANT: { if (fLink) return false; if ((GetTopPoint().m_map <= 1) && (GetTopPoint().m_x > UO_SIZE_X_REAL)) // dungeons and T2A lands ObjMessage(g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SEXTANT_T2A), this); else { TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_ITEMUSE_SEXTANT), m_pArea->GetName(), pItem->Use_Sextant(GetTopPoint())); ObjMessage(pszMsg, this); } return true; } default: fAction = false; } return fAction | MASK_RETURN_FOLLOW_LINKS; }
bool CItemMulti::r_Verb( CScript & s, CTextConsole * pSrc ) // Execute command from script { LOCKDATA; EXC_TRY(("r_Verb('%s %s',%x)", s.GetKey(), s.GetArgStr(), pSrc)); // Speaking in this ships region. // return: true = command for the ship. //"One (direction*)", " (Direction*), one" Moves ship one tile in desired direction and stops. //"Slow (direction*)" Moves ship slowly in desired direction (see below for possible directions). int iCmd = FindTableSorted( s.GetKey(), sm_szVerbKeys, COUNTOF( sm_szVerbKeys )-1 ); if ( iCmd < 0 ) { return( CItem::r_Verb( s, pSrc )); } if ( ! pSrc ) return( false ); switch ( iCmd ) { case SHV_MULTICREATE: { CGrayUID uid( s.GetArgVal() ); CChar * pSrc = uid.CharFind(); Multi_Create( pSrc, 0 ); } return true; } if ( IsAttr(ATTR_MOVE_NEVER) || ! IsTopLevel() ) return false; CChar * pChar = pSrc->GetChar(); // Only key holders can command the ship ??? // if ( pChar && pChar->ContentFindKeyFor( pItem )) // Find the tiller man object. CItem * pTiller = Multi_GetSign(); ASSERT( pTiller ); // Get current facing dir. DIR_TYPE DirFace = sm_Ship_FaceDir[ Ship_GetFaceOffset() ]; int DirMoveChange; LPCTSTR pszSpeak = NULL; switch ( iCmd ) { case SHV_SHIPSTOP: // "Furl sail" // "Stop" Stops current ship movement. if ( ! m_itShip.m_fSail ) return( false ); Ship_Stop(); break; case SHV_SHIPFACE: // Face this direction. do not change the direction of movement. if ( ! s.HasArgs()) return( false ); return Ship_Face( GetDirStr( s.GetArgStr())); case SHV_SHIPMOVE: // Move one space in this direction. // Does NOT protect against exploits ! if ( ! s.HasArgs()) return( false ); m_itShip.m_DirMove = GetDirStr( s.GetArgStr()); return Ship_Move( (DIR_TYPE) m_itShip.m_DirMove ); case SHV_SHIPGATE: // Move the whole ship and contents to another place. if ( ! s.HasArgs()) return( false ); { CPointMap ptdelta = g_Cfg.GetRegionPoint( s.GetArgStr()); if ( ! ptdelta.IsValidPoint()) return( false ); ptdelta -= GetTopPoint(); return Ship_MoveDelta( ptdelta ); } break; case SHV_SHIPTURNLEFT: // "Port", // "Turn Left", DirMoveChange = -2; doturn: if ( m_itShip.m_fAnchored ) { anchored: pszSpeak = "The anchor is down <SEX Sir/Mam>!"; break; } m_itShip.m_DirMove = GetDirTurn( DirFace, DirMoveChange ); Ship_Face( (DIR_TYPE) m_itShip.m_DirMove ); break; case SHV_SHIPTURNRIGHT: // "Turn Right", // "Starboard", // Turn Right DirMoveChange = 2; goto doturn; case SHV_SHIPDRIFTLEFT: // "Left", // "Drift Left", DirMoveChange = -2; dodirmovechange: if ( m_itShip.m_fAnchored ) goto anchored; if ( ! Ship_SetMoveDir( GetDirTurn( DirFace, DirMoveChange ))) return( false ); break; case SHV_SHIPDRIFTRIGHT: // "Right", // "Drift Right", DirMoveChange = 2; goto dodirmovechange; case SHV_SHIPBACK: // "Back", // Move ship backwards // "Backward", // Move ship backwards // "Backwards", // Move ship backwards DirMoveChange = 4; goto dodirmovechange; case SHV_SHIPFORE: // "Forward" // "Foreward", // Moves ship forward. // "Unfurl sail", // Moves ship forward. DirMoveChange = 0; goto dodirmovechange; case SHV_SHIPFORELEFT: // "Forward left", DirMoveChange = -1; goto dodirmovechange; case SHV_SHIPFORERIGHT: // "forward right", DirMoveChange = 1; goto dodirmovechange; case SHV_SHIPBACKLEFT: // "backward left", // "back left", DirMoveChange = -3; goto dodirmovechange; case SHV_SHIPBACKRIGHT: // "backward right", // "back right", DirMoveChange = 3; goto dodirmovechange; case SHV_SHIPANCHORRAISE: // "Raise Anchor", if ( ! m_itShip.m_fAnchored ) { pszSpeak = "The anchor is already up <SEX Sir/Mam>"; break; } m_itShip.m_fAnchored = false; break; case SHV_SHIPANCHORDROP: // "Drop Anchor", if ( m_itShip.m_fAnchored ) { pszSpeak = "The anchor is already down <SEX Sir/Mam>"; break; } m_itShip.m_fAnchored = true; Ship_Stop(); break; case SHV_SHIPTURN: // "Turn around", // Turns ship around and proceeds. // "Come about", // Turns ship around and proceeds. DirMoveChange = 4; goto doturn; case SHV_SHIPUP: // "Up" { if ( ! IsAttr(ATTR_MAGIC )) return( false ); CPointMap pt; pt.m_z = PLAYER_HEIGHT; if ( Ship_MoveDelta( pt )) { pszSpeak = "As you command <SEX Sir/Mam>"; } else { pszSpeak = "Can't do that <SEX Sir/Mam>"; } } break; case SHV_SHIPDOWN: // "Down" { if ( ! IsAttr(ATTR_MAGIC )) return( false ); CPointMap pt; pt.m_z = -PLAYER_HEIGHT; if ( Ship_MoveDelta( pt )) { pszSpeak = "As you command <SEX Sir/Mam>"; } else { pszSpeak = "Can't do that <SEX Sir/Mam>"; } } break; case SHV_SHIPLAND: // "Land" { if ( ! IsAttr(ATTR_MAGIC )) return( false ); signed char zold = GetTopZ(); CPointMap pt = GetTopPoint(); pt.m_z = zold; SetTopZ( -UO_SIZE_Z ); // bottom of the world where i won't get in the way. WORD wBlockFlags = CAN_I_WATER; signed char z = g_World.GetHeightPoint( pt, wBlockFlags ); SetTopZ( zold ); // restore z for now. pt.InitPoint(); pt.m_z = z - zold; if ( pt.m_z ) { Ship_MoveDelta( pt ); pszSpeak = "As you command <SEX Sir/Mam>"; } else { pszSpeak = "We have landed <SEX Sir/Mam>"; } } break; default: return( false ); } if ( pChar ) { if ( pszSpeak == NULL ) { static LPCTSTR const sm_pszAye[] = { "Aye", "Aye Cap'n", "Aye <SEX Sir/Mam>", }; pszSpeak = sm_pszAye[ Calc_GetRandVal( COUNTOF( sm_pszAye )) ]; } TCHAR szText[ MAX_TALK_BUFFER ]; strcpy( szText, pszSpeak ); pChar->ParseText( szText, &g_Serv ); pTiller->Speak( szText, 0, TALKMODE_SAY, FONT_NORMAL ); } return false; EXC_CATCH("CItemMulti"); return true; }
bool CChar::Use_Repair( CItem * pItemArmor ) { ADDTOCALLSTACK("CChar::Use_Repair"); // Attempt to repair the item. // If it is repairable. if ( !pItemArmor || !pItemArmor->Armor_IsRepairable() ) { SysMessageDefault(DEFMSG_REPAIR_NOT); return false; } if ( pItemArmor->IsItemEquipped() ) { SysMessageDefault(DEFMSG_REPAIR_WORN); return false; } if ( !CanUse(pItemArmor, true) ) { SysMessageDefault(DEFMSG_REPAIR_REACH); return false; } // Quickly use arms lore skill, but don't gain any skill until later on int iArmsLoreDiff = Calc_GetRandVal(30); if ( !Skill_UseQuick(SKILL_ARMSLORE, iArmsLoreDiff, false) ) { // apply arms lore skillgain for failure Skill_Experience(SKILL_ARMSLORE, -iArmsLoreDiff); SysMessageDefault(DEFMSG_REPAIR_UNK); return false; } if ( pItemArmor->m_itArmor.m_Hits_Cur >= pItemArmor->m_itArmor.m_Hits_Max ) { SysMessageDefault(DEFMSG_REPAIR_FULL); return false; } m_Act_p = g_World.FindItemTypeNearby(GetTopPoint(), IT_ANVIL, 2, false); if ( !m_Act_p.IsValidPoint() ) { SysMessageDefault(DEFMSG_REPAIR_ANVIL); return false; } CItemBase *pItemDef = pItemArmor->Item_GetDef(); ASSERT(pItemDef); // Use up some raw materials to repair. int iTotalHits = pItemArmor->m_itArmor.m_Hits_Max; int iDamageHits = pItemArmor->m_itArmor.m_Hits_Max - pItemArmor->m_itArmor.m_Hits_Cur; int iDamagePercent = IMULDIV(100, iDamageHits, iTotalHits); size_t iMissing = ResourceConsumePart(&(pItemDef->m_BaseResources), 1, iDamagePercent / 2, true); if ( iMissing != pItemDef->m_BaseResources.BadIndex() ) { // Need this to repair. const CResourceDef *pCompDef = g_Cfg.ResourceGetDef(pItemDef->m_BaseResources.GetAt(iMissing).GetResourceID()); SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_LACK_1), pCompDef ? pCompDef->GetName() : g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_LACK_2)); return false; } UpdateDir(m_Act_p); UpdateAnimate(ANIM_ATTACK_1H_SLASH); // quarter the skill to make it. // + more damaged items should be harder to repair. // higher the percentage damage the closer to the skills to make it. size_t iRes = pItemDef->m_SkillMake.FindResourceType(RES_SKILL); if ( iRes == pItemDef->m_SkillMake.BadIndex() ) return false; CResourceQty RetMainSkill = pItemDef->m_SkillMake[iRes]; int iSkillLevel = static_cast<int>(RetMainSkill.GetResQty()) / 10; int iDifficulty = IMULDIV(iSkillLevel, iDamagePercent, 100); if ( iDifficulty < iSkillLevel / 4 ) iDifficulty = iSkillLevel / 4; // apply arms lore skillgain now LPCTSTR pszText; Skill_Experience(SKILL_ARMSLORE, iArmsLoreDiff); bool fSuccess = Skill_UseQuick(static_cast<SKILL_TYPE>(RetMainSkill.GetResIndex()), iDifficulty); if ( fSuccess ) { pItemArmor->m_itArmor.m_Hits_Cur = static_cast<WORD>(iTotalHits); pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_1); } else { /***************************** // not sure if this is working! ******************************/ // Failure if ( !Calc_GetRandVal(6) ) { pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_2); pItemArmor->m_itArmor.m_Hits_Max--; pItemArmor->m_itArmor.m_Hits_Cur--; } else if ( !Calc_GetRandVal(3) ) { pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_3); pItemArmor->m_itArmor.m_Hits_Cur--; } else pszText = g_Cfg.GetDefaultMsg( DEFMSG_REPAIR_4 ); iDamagePercent = Calc_GetRandVal(iDamagePercent); // some random amount } ResourceConsumePart(&(pItemDef->m_BaseResources), 1, iDamagePercent / 2, false); if ( pItemArmor->m_itArmor.m_Hits_Cur <= 0 ) pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_5); TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_MSG), pszText, pItemArmor->GetName()); Emote(pszMsg); if ( pItemArmor->m_itArmor.m_Hits_Cur <= 0 ) pItemArmor->Delete(); else pItemArmor->UpdatePropertyFlag(AUTOTOOLTIP_FLAG_DURABILITY); return fSuccess; }