#include #include "House.h" #include "World.h" #include "WeenieFactory.h" #include "Player.h" #include "InferredPortalData.h" #include "AllegianceManager.h" #include "Client.h" #include "EmoteManager.h" #include "HouseManager.h" #define HOUSE_SAVE_INTERVAL 300.0 DEFINE_PACK(CHouseData) { pWriter->Write(_slumLordId); pWriter->Write(_ownerId); pWriter->Write(_ownerAccount); pWriter->Write(_purchaseTimestamp); pWriter->Write(_currentMaintenancePeriod); pWriter->Write(_houseType); _position.Pack(pWriter); _buy.Pack(pWriter); _rent.Pack(pWriter); _accessList.Pack(pWriter); _storageAccessList.Pack(pWriter); pWriter->Write(_allegianceAccess); pWriter->Write(_allegianceStorageAccess); pWriter->Write(_everyoneAccess); pWriter->Write(_everyoneStorageAccess); pWriter->Write(_hooksVisible); } DEFINE_UNPACK(CHouseData) { _slumLordId = pReader->Read(); _ownerId = pReader->Read(); _ownerAccount = pReader->Read(); _purchaseTimestamp = pReader->Read(); _currentMaintenancePeriod = pReader->Read(); _houseType = pReader->Read(); _position.UnPack(pReader); _buy.UnPack(pReader); _rent.UnPack(pReader); _accessList.UnPack(pReader); _storageAccessList.UnPack(pReader); _allegianceAccess = pReader->Read(); _allegianceStorageAccess = pReader->Read(); _everyoneAccess = pReader->Read(); _everyoneStorageAccess = pReader->Read(); _hooksVisible = pReader->Read(); return true; } void CHouseData::ClearOwnershipData() { _ownerId = 0; _ownerAccount = 0; _purchaseTimestamp = 0; _currentMaintenancePeriod = 0; _allegianceAccess = false; _allegianceStorageAccess = false; _everyoneAccess = false; _everyoneStorageAccess = false; _hooksVisible = true; _rent.clear(); _buy.clear(); _accessList.clear(); _storageAccessList.clear(); } void CHouseData::SetHookVisibility(bool newSetting) { _hooksVisible = newSetting; for (auto hook_id : _hookList) { CWeenieObject *hookWeenie = g_pWorld->FindObject(hook_id, true); if (!hookWeenie) continue; CHookWeenie *hook = hookWeenie->AsHook(); if (!hook) continue; hook->SetHookVisibility(newSetting); } Save(); } void CHouseData::AbandonHouse() { CWeenieObject *owner = g_pWorld->FindObject(_ownerId); if (owner) { //if the player is offline we won't find him but that's okay, we will just update his housing data when he logins in UpdateHouseData() owner->m_Qualities.RemoveDataID(HOUSEID_DID); owner->NotifyDIDStatUpdated(HOUSEID_DID); } ClearOwnershipData(); SetHookVisibility(true); if (CWeenieObject *slumLord = g_pWorld->FindObject(_slumLordId)) { slumLord->DoForcedMotion(Motion_Off); CHouseWeenie* house = slumLord->AsSlumLord()->GetHouse(); if (house) house->m_Qualities.SetString(HOUSE_OWNER_NAME_STRING, ""); } if (owner) g_pHouseManager->SendHouseData(owner->AsPlayer(), _houseId); Save(); } void CHouseData::Save() { BinaryWriter writer; Pack(&writer); g_pDBIO->CreateOrUpdateHouseData(_houseId, writer.GetData(), writer.GetSize()); } CHouseWeenie::CHouseWeenie() { m_bDontClear = true; } void CHouseWeenie::EnsureLink(CWeenieObject *source) { source->m_Qualities.SetInstanceID(HOUSE_IID, GetID()); source->m_Qualities.SetDataID(HOUSEID_DID, GetHouseDID()); if (source->AsSlumLord()) { m_Qualities.SetInstanceID(SLUMLORD_IID, source->GetID()); } if (source->AsHook()) { CHouseData *houseData = source->AsHook()->GetHouseData(); if (houseData) houseData->_hookList.insert(source->GetID()); } if (source->AsBootSpot()) { m_Qualities.SetPosition(HOUSE_BOOT_POSITION, source->GetPosition()); } // Deed_WeenieType // BootSpot_WeenieType // HousePortal_WeenieType // Storage_WeenieType } CHouseData *CHouseWeenie::GetHouseData() { return g_pHouseManager->GetHouseData(GetHouseDID()); } std::string CHouseWeenie::GetHouseOwnerName() { uint32_t owner = GetHouseOwner(); if (!owner) return ""; std::string ownerName; ownerName = g_pWorld->GetPlayerName(owner, true); if (ownerName.empty()) { if (!m_Qualities.InqString(HOUSE_OWNER_NAME_STRING, ownerName)) { ownerName = g_pDBIO->GetPlayerCharacterName(GetHouseData()->_ownerId); m_Qualities.SetString(HOUSE_OWNER_NAME_STRING, ownerName); } } return ownerName; } uint32_t CHouseWeenie::GetHouseOwner() { return GetHouseData()->_ownerId; //return InqIIDQuality(HOUSE_OWNER_IID, 0); } uint32_t CHouseWeenie::GetHouseDID() { return InqDIDQuality(HOUSEID_DID, 0); } int CHouseWeenie::GetHouseType() { return InqIntQuality(HOUSE_TYPE_INT, 0); } CSlumLordWeenie *CHouseWeenie::GetSlumLord() { if (CWeenieObject *slumlord = g_pWorld->FindObject(InqIIDQuality(SLUMLORD_IID, 0), true)) { return slumlord->AsSlumLord(); } return NULL; } bool CHouseWeenie::HasAccess(CPlayerWeenie *requester) { if (!requester) return false; CHouseData *houseData = GetHouseData(); uint32_t requesterId = requester->GetID(); uint32_t houseOwnerId = GetHouseOwner(); if (requesterId == houseData->_ownerId) return true; if (houseData->_everyoneAccess) return true; if (requester->GetClient()->GetAccountInfo().id == houseData->_ownerAccount) return true; if (std::find(houseData->_accessList.begin(), houseData->_accessList.end(), requesterId) != houseData->_accessList.end()) return true; if (houseData->_allegianceAccess) { std::string alleg; if (requester->m_Qualities.InqString(ALLEGIANCE_NAME_STRING, alleg)) { AllegianceTreeNode *ownerAllegianceNode = g_pAllegianceManager->GetTreeNode(houseOwnerId); AllegianceTreeNode *requesterAllegianceNode = g_pAllegianceManager->GetTreeNode(requesterId); if (ownerAllegianceNode && requesterAllegianceNode) { if (ownerAllegianceNode->_monarchID == requesterAllegianceNode->_monarchID) return true; else return false; } else { if (!ownerAllegianceNode) SERVER_ERROR << "House Owner:" << houseOwnerId << " may not exist. Check DB"; else SERVER_ERROR << "Requestor:" << requesterId << " may not have or be in allegiance."; } } } return HasStorageAccess(requester); //storage access automatically grants access. } bool CHouseWeenie::HasStorageAccess(CPlayerWeenie *requester) { if (!requester) return false; CHouseData *houseData = GetHouseData(); uint32_t requesterId = requester->GetID(); uint32_t houseOwnerId = GetHouseOwner(); if (requesterId == houseData->_ownerId) return true; if (houseData->_everyoneStorageAccess) return true; if (requester->GetClient()->GetAccountInfo().id == houseData->_ownerAccount) return true; if (std::find(houseData->_storageAccessList.begin(), houseData->_storageAccessList.end(), requesterId) != houseData->_storageAccessList.end()) return true; if (houseData->_allegianceStorageAccess) { std::string alleg; if (requester->m_Qualities.InqString(ALLEGIANCE_NAME_STRING, alleg)) { AllegianceTreeNode *ownerAllegianceNode = g_pAllegianceManager->GetTreeNode(houseOwnerId); AllegianceTreeNode *requesterAllegianceNode = g_pAllegianceManager->GetTreeNode(requesterId); if (ownerAllegianceNode && requesterAllegianceNode) { if (ownerAllegianceNode->_monarchID == requesterAllegianceNode->_monarchID) return true; } } } return false; } CSlumLordWeenie::CSlumLordWeenie() { m_bDontClear = true; _nextHeartBeat = Timer::cur_time; //let's tick as soon as possible to establish if we have an owner. } void CSlumLordWeenie::Tick() { if (_nextHeartBeat != -1.0 && _nextHeartBeat <= Timer::cur_time) { if (!_initialized) { _nextHeartBeat = Timer::cur_time + Random::GenUInt(1, 10); CHouseWeenie *house = GetHouse(); if (!house) return; CHouseData *houseData = house->GetHouseData(); houseData->_slumLordId = GetID(); houseData->_position = m_Position; if (houseData->_ownerId) DoForcedMotion(Motion_On); else DoForcedMotion(Motion_Off); //for (auto hook_id : houseData->_hookList) //{ // CWeenieObject *hook = g_pWorld->FindObject(hook_id, true); // if (!hook) // continue; // if(hook->AsHook()) // hook->AsHook()->UpdateHookedObject(); //} _initialized = true; _nextSave = Timer::cur_time + HOUSE_SAVE_INTERVAL; } else { CHouseWeenie *house = GetHouse(); if (!house) return; CHouseData *houseData = house->GetHouseData(); if (houseData->_ownerId) DoForcedMotion(Motion_On); else DoForcedMotion(Motion_Off); //check global data and update if necessary uint32_t now = g_pPhatSDK->GetCurrTimeStamp(); if (g_pHouseManager->_nextHouseMaintenancePeriod < now) { if (!g_pHouseManager->_freeHouseMaintenancePeriod) g_pHouseManager->_currentHouseMaintenancePeriod = now; g_pHouseManager->_freeHouseMaintenancePeriod = false; g_pHouseManager->_nextHouseMaintenancePeriod = now + (30 * 24 * 60 * 60); //in 30 days. } CheckRentPeriod(); _nextHeartBeat = Timer::cur_time + Random::GenUInt(30 * 60, 60 * 60); //check again in 30 to 60 minutes. } } } CHouseWeenie *CSlumLordWeenie::GetHouse() { if (CWeenieObject *house = g_pWorld->FindObject(InqIIDQuality(HOUSE_IID, 0), true)) { return house->AsHouse(); } return NULL; } void CSlumLordWeenie::GetHouseProfile(HouseProfile &prof) { prof._slumlord = GetID(); prof._maintenance_free = 0; prof._bitmask = Active_HouseBitmask; prof._min_level = InqIntQuality(MIN_LEVEL_INT, -1); prof._max_level = InqIntQuality(MAX_LEVEL_INT, -1); prof._min_alleg_rank = 0; if (m_Qualities._create_list) { for (auto &entry : *m_Qualities._create_list) { if (entry.regen_algorithm == HouseBuy_DestinationType) { HousePayment pay; pay.wcid = entry.wcid; pay.num = entry.amount; pay.paid = 0; CWeenieDefaults *weenieDef = g_pWeenieFactory->GetWeenieDefaults(entry.wcid); if (weenieDef) { weenieDef->m_Qualities.InqString(NAME_STRING, pay.name); weenieDef->m_Qualities.InqString(PLURAL_NAME_STRING, pay.pname); } prof._buy.push_back(pay); } else if (entry.regen_algorithm == HouseRent_DestinationType) { HousePayment pay; pay.wcid = entry.wcid; pay.num = entry.amount; pay.paid = 0; CWeenieDefaults *weenieDef = g_pWeenieFactory->GetWeenieDefaults(entry.wcid); if (weenieDef) { weenieDef->m_Qualities.InqString(NAME_STRING, pay.name); weenieDef->m_Qualities.InqString(PLURAL_NAME_STRING, pay.pname); } prof._rent.push_back(pay); } } } if (CHouseWeenie *house = GetHouse()) { prof._name = house->GetHouseOwnerName(); prof._owner = house->GetHouseOwner(); prof._id = house->GetHouseDID(); prof._type = house->GetHouseType(); } } int CSlumLordWeenie::DoUseResponse(CWeenieObject *other) { HouseProfile prof; GetHouseProfile(prof); if (CHouseWeenie *house = GetHouse()) { CHouseData *houseData = house->GetHouseData(); if (houseData && houseData->_ownerId) prof._rent = houseData->_rent; //get the real house rent information that may contain payments uint32_t houseDID = 0; if (other->GetID() == houseData->_ownerId && (!other->m_Qualities.InqDataID(HOUSEID_DID, houseDID) || houseDID == 0)) // player lost link { other->m_Qualities.SetDataID(HOUSEID_DID, houseData->_houseId); } } BinaryWriter profMsg; profMsg.Write(0x21D); prof.Pack(&profMsg); other->SendNetMessage(&profMsg, PRIVATE_MSG, TRUE, FALSE); return CWeenieObject::DoUseResponse(other); } void CSlumLordWeenie::BuyHouse(CPlayerWeenie *player, const PackableList &items) { if (CHouseWeenie *house = GetHouse()) { if (player->GetAccountHouseId()) { player->SendText("You may only own one dwelling at a time.", LTT_DEFAULT); //made up message. return; } if (house->GetHouseOwner()) { player->SendText("That dwelling already has an owner.", LTT_DEFAULT); //made up message. return; } if (int minLevel = InqIntQuality(MIN_LEVEL_INT, 0)) { if (player->InqIntQuality(LEVEL_INT, 1) < minLevel) { player->SendText(csprintf("You must be at least level %d to purchase this dwelling.", minLevel), LTT_DEFAULT); //made up message. return; } } AllegianceTreeNode *allegianceNode = g_pAllegianceManager->GetTreeNode(player->GetID()); if (InqBoolQuality(HOUSE_REQUIRES_MONARCH_BOOL, false)) { if (!allegianceNode || allegianceNode->_patronID != 0) { player->SendText("You must be a monarch to purchase this dwelling.", LTT_DEFAULT); //made up message. return; } } if (int minAllegianceRank = InqIntQuality(ALLEGIANCE_MIN_LEVEL_INT, 0)) { if (!allegianceNode || allegianceNode->_rank < minAllegianceRank) { player->SendText(csprintf("Your allegiance rank must be at least %d to purchase this dwelling.", minAllegianceRank), LTT_DEFAULT); //made up message. return; } } if (house->GetHouseType() != Apartment_HouseType && player->InqIntQuality(HOUSE_PURCHASE_TIMESTAMP_INT, 0) + (30 * 24 * 60) > g_pPhatSDK->GetCurrTimeStamp()) { player->SendText("You cannot buy another landscape house yet. This restriction does not apply to apartments.", LTT_DEFAULT); //made up message. return; } HouseProfile prof; GetHouseProfile(prof); std::map consumeList; for (HousePayment &payment : prof._buy) { for (auto &itemID : items) { CWeenieObject *item = g_pWorld->FindObject(itemID); if (!item || !player->FindContainedItem(itemID)) continue; if (payment.wcid == W_COINSTACK_CLASS) { if (payment.paid < payment.num) { if (item->m_Qualities.id == payment.wcid || item->InqIntQuality(ITEM_TYPE_INT, 0) == ITEM_TYPE::TYPE_PROMISSORY_NOTE) { payment.paid += item->InqIntQuality(VALUE_INT, 0); consumeList[item] = item->InqIntQuality(STACK_SIZE_INT, 1); //add the complete stack, we will sort values later. } } } else { if (item->m_Qualities.id == payment.wcid && payment.paid < payment.num) { int amountToConsume = min(item->InqIntQuality(STACK_SIZE_INT, 1), payment.num - payment.paid); payment.paid += amountToConsume; consumeList[item] = amountToConsume; } } } } for (HousePayment &payment : prof._buy) { if (payment.paid < payment.num) { player->SendText("That's not everything you need to buy this dwelling.", LTT_DEFAULT); //made up message. return; } } for (auto entry : consumeList) { CWeenieObject *item = entry.first; int amount = entry.second; if (item->m_Qualities.id == W_COINSTACK_CLASS || item->InqIntQuality(ITEM_TYPE_INT, 0) == ITEM_TYPE::TYPE_PROMISSORY_NOTE) item->Remove(); //we remove the entire stack of coins/trade notes and then give back the appropriate amount. else { int stackSize = item->InqIntQuality(STACK_SIZE_INT, 1); item->DecrementStackNum(amount); } } int timeStamp = g_pPhatSDK->GetCurrTimeStamp(); player->SendText("Congratulations! You now own this dwelling.", LTT_DEFAULT); //Disabled the cooldown on purchasing houses //if (house->GetHouseType() != 4)//apartments are not affected nor do the affect the house purchase frequency limitation //{ // player->m_Qualities.SetInt(HOUSE_PURCHASE_TIMESTAMP_INT, timeStamp); // player->NotifyIntStatUpdated(HOUSE_PURCHASE_TIMESTAMP_INT); //} player->m_Qualities.SetDataID(HOUSEID_DID, house->GetHouseDID()); player->NotifyDIDStatUpdated(HOUSEID_DID); CHouseData *houseData = house->GetHouseData(); houseData->ClearOwnershipData(); houseData->_ownerId = player->GetID(); houseData->_ownerAccount = player->GetClient()->GetAccountInfo().id; houseData->_purchaseTimestamp = timeStamp; houseData->_currentMaintenancePeriod = g_pHouseManager->_currentHouseMaintenancePeriod; houseData->_rent = prof._rent; houseData->_buy = prof._buy; houseData->_houseType = house->GetHouseType(); houseData->_position = GetPosition(); for (auto hook_id : houseData->_hookList) { CWeenieObject *hook = g_pWorld->FindObject(hook_id, true); if (!hook) continue; hook->m_Qualities.SetInstanceID(HOUSE_OWNER_IID, player->GetID()); hook->NotifyIIDStatUpdated(HOUSE_OWNER_IID, false); hook->Save(); } houseData->SetHookVisibility(true); DoForcedMotion(Motion_On); g_pHouseManager->SendHouseData(player, house->GetHouseDID()); DoUseResponse(player); player->RecalculateCoinAmount(W_COINSTACK_CLASS); player->Save(); house->m_Qualities.SetString(HOUSE_OWNER_NAME_STRING, player->GetName()); house->Save(); houseData->Save(); HOUSE_LOG << "Player:" << player->GetName() << "bought house" << houseData->_houseId; } } void CSlumLordWeenie::CheckRentPeriod() { if (CHouseWeenie *house = GetHouse()) { CHouseData *houseData = house->GetHouseData(); if (!houseData->_ownerId) return; //no owner no rent if (houseData->_currentMaintenancePeriod < g_pHouseManager->_currentHouseMaintenancePeriod) { //check if we still meet the allegiance requirements to own this house. AllegianceTreeNode *allegianceNode = g_pAllegianceManager->GetTreeNode(houseData->_ownerId); if (InqBoolQuality(HOUSE_REQUIRES_MONARCH_BOOL, false)) { if (!allegianceNode || allegianceNode->_patronID) { if (CWeenieObject *player = g_pWorld->FindObject(houseData->_ownerId)) //todo: a way to inform the allegiance and the player if he is offline. player->SendText("You no longer meet the monarch requirement for your dwelling so it has been abandoned.", LTT_DEFAULT); //made up message. houseData->AbandonHouse(); return; } } if (int minAllegianceRank = InqIntQuality(ALLEGIANCE_MIN_LEVEL_INT, 0)) { if (!allegianceNode || allegianceNode->_rank < minAllegianceRank) { if (CWeenieObject *player = g_pWorld->FindObject(houseData->_ownerId)) //todo: a way to inform the allegiance and the player if he is offline. player->SendText(csprintf("You no longer meet the allegiance rank requirement for your dwelling so it has been abandoned.", minAllegianceRank), LTT_DEFAULT); //made up message. houseData->AbandonHouse(); return; } } bool fullyPaid = true; for (HousePayment &payment : houseData->_rent) { if (payment.paid < payment.num) { fullyPaid = false; break; } } if (fullyPaid) { HouseProfile prof; GetHouseProfile(prof); //update rent and buy values from house profile, maybe the prices have changed! houseData->_rent = prof._rent; houseData->_buy = prof._buy; houseData->Save(); if (CWeenieObject *owner = g_pWorld->FindObject(houseData->_ownerId)) g_pHouseManager->SendHouseData(owner->AsPlayer(), house->GetHouseDID()); //update house's owner panel if the owner is online. } else { //houseData->AbandonHouse(); temporarily disable abandon house on rent not paid } } } } void CSlumLordWeenie::RentHouse(CPlayerWeenie *player, const PackableList &items) { if (CHouseWeenie *house = GetHouse()) { CHouseData *houseData = house->GetHouseData(); if (!houseData->_ownerId) { player->SendText("That dwelling has no owner.", LTT_DEFAULT); //made up message. return; } bool requiresRent = false; for (HousePayment &payment : houseData->_rent) { if (payment.paid < payment.num) { requiresRent = true; break; } } if (!requiresRent) { player->SendText("The maintenance has already been paid for this period. You may not prepay the next period's maintenance.", LTT_DEFAULT); //made up message. return; } std::map consumeList; for (HousePayment &payment : houseData->_rent) { for (auto &itemID : items) { CWeenieObject *item = g_pWorld->FindObject(itemID); if (!item || !player->FindContainedItem(itemID)) continue; if (payment.wcid == W_COINSTACK_CLASS) { if (payment.paid < payment.num) { if (item->m_Qualities.id == payment.wcid || item->InqIntQuality(ITEM_TYPE_INT, 0) == ITEM_TYPE::TYPE_PROMISSORY_NOTE) { payment.paid += item->InqIntQuality(VALUE_INT, 0); consumeList[item] = item->InqIntQuality(STACK_SIZE_INT, 1); //add the complete stack, we will sort values later. } } } else { if (item->m_Qualities.id == payment.wcid && payment.paid < payment.num) { int amountToConsume = min(item->InqIntQuality(STACK_SIZE_INT, 1), payment.num - payment.paid); payment.paid += amountToConsume; consumeList[item] = amountToConsume; } } } } for (auto entry : consumeList) { CWeenieObject *item = entry.first; int amount = entry.second; if (item->m_Qualities.id == W_COINSTACK_CLASS || item->InqIntQuality(ITEM_TYPE_INT, 0) == ITEM_TYPE::TYPE_PROMISSORY_NOTE) item->Remove(); //we remove the entire stack of coins/trade notes and then give back the appropriate amount. else { int stackSize = item->InqIntQuality(STACK_SIZE_INT, 1); item->DecrementStackNum(amount); } } DoUseResponse(player); player->RecalculateCoinAmount(W_COINSTACK_CLASS); player->Save(); house->Save(); houseData->Save(); if (CWeenieObject *owner = g_pWorld->FindObject(houseData->_ownerId)) g_pHouseManager->SendHouseData(owner->AsPlayer(), house->GetHouseDID()); //update house's owner panel if the owner is online. HOUSE_LOG << "Player:" << player->GetName() << "paid rent" << houseData->_houseId; } } CHookWeenie::CHookWeenie() { m_bDontClear = true; _nextInitCheck = Timer::cur_time; } void CHookWeenie::Tick() { //if (!_initialized && _nextInitCheck != -1.0 && _nextInitCheck <= Timer::cur_time) //{ // _nextInitCheck = Timer::cur_time + Random::GenUInt(1, 10); // CHouseData *houseData = GetHouseData(); // if (!houseData) // return; // if (houseData->_ownerId) // { // SetHookVisibility(houseData->_hooksVisible); // m_Qualities.SetInstanceID(HOUSE_OWNER_IID, houseData->_ownerId); // NotifyIIDStatUpdated(HOUSE_OWNER_IID, false); // } // else // { // m_Qualities.RemoveInstanceID(HOUSE_OWNER_IID); // NotifyIIDStatUpdated(HOUSE_OWNER_IID, false); // } // UpdateHookedObject(NULL); // SetHookVisibility(houseData->_hooksVisible); // _nextInitCheck = -1.0; // _initialized = true; //} CContainerWeenie::Tick(); } void CHookWeenie::SaveEx(CWeenieSave & save) { //if we don't clear before saving some hooked item's position and rotation changes, not sure what is causing that. ClearHookedObject(false); CContainerWeenie::SaveEx(save); m_bSaveMe = false; UpdateHookedObject(NULL, false); } void CHookWeenie::LoadEx(CWeenieSave & save) { CContainerWeenie::LoadEx(save); _nextInitCheck = Timer::cur_time; } void CHookWeenie::PreSpawnCreate() { CHouseData *houseData = GetHouseData(); if (!houseData) return; if (houseData->_ownerId) { //SetHookVisibility(houseData->_hooksVisible); m_Qualities.SetInstanceID(HOUSE_OWNER_IID, houseData->_ownerId); //NotifyIIDStatUpdated(HOUSE_OWNER_IID, false); } else { m_Qualities.RemoveInstanceID(HOUSE_OWNER_IID); //NotifyIIDStatUpdated(HOUSE_OWNER_IID, false); } if (houseData->_hooksVisible) { m_Qualities.RemoveBool(VISIBILITY_BOOL); } else if (m_Items.empty()) { m_Qualities.SetBool(VISIBILITY_BOOL, false); } UpdateHookedObject(NULL, false); //SetHookVisibility(houseData->_hooksVisible); } int CHookWeenie::DoUseResponse(CWeenieObject *other) { CHouseWeenie *house = GetHouse(); if (!house) return WERROR_NO_HOUSE; CHouseData *houseData = house->GetHouseData(); if (!houseData) return WERROR_NO_HOUSE; if (houseData->_hooksVisible) { if (house->HasStorageAccess(other->AsPlayer())) { int useResponse = CContainerWeenie::DoUseResponse(other); m_bSaveMe = true; Save(); return useResponse; } else { other->SendText("You do not have permission to access that.", LTT_DEFAULT); //made up message. return WERROR_ACTIVATION_NOT_ALLOWED_NO_NAME; } } else { //if house hooks are off this means we're using the hooked item. if (house->HasAccess(other->AsPlayer())) { CWeenieObject *hookedItem; if (m_Items.empty()) return WERROR_OBJECT_GONE; hookedItem = m_Items[0]; if (!hookedItem) return WERROR_OBJECT_GONE; if (hookedItem->InqIntQuality(HOOK_GROUP_INT, 0) == 4 && house->GetHouseType() != Mansion_HouseType) { other->SendText("That can only be activated when hooked in a mansion.", LTT_DEFAULT); //made up message. return WERROR_OBJECT_GONE; } // Prevent hooked items from being usable if recently involved in PK Activity. if (hookedItem->InqIntQuality(HOOK_GROUP_INT, 0) && other->_IsPlayer() && other->AsPlayer()->CheckPKActivity()) { return WERROR_PORTAL_PK_ATTACKED_TOO_RECENTLY; } } else { other->SendText("You do not have permission to access that.", LTT_DEFAULT); //made up message. return WERROR_HOOK_NOT_PERMITED_TO_USE_HOOK; } } return CWeenieObject::DoUseResponse(other); } void CHookWeenie::Identify(CWeenieObject *other, uint32_t overrideId) { CHouseData *houseData = GetHouseData(); if (!houseData || houseData->_hooksVisible) CContainerWeenie::Identify(other, overrideId); else { //if house hooks are off this means we're identifying the hooked item. CWeenieObject *hookedItem; if (m_Items.empty()) return; hookedItem = m_Items[0]; if (!hookedItem) return; hookedItem->Identify(other, GetID()); } } uint32_t CHookWeenie::Container_InsertInventoryItem(uint32_t dwCell, CWeenieObject *pItem, uint32_t slot) { UpdateHookedObject(pItem); return CContainerWeenie::Container_InsertInventoryItem(dwCell, pItem, slot); } void CHookWeenie::ReleaseContainedItemRecursive(CWeenieObject *item) { ClearHookedObject(); CContainerWeenie::ReleaseContainedItemRecursive(item); } void CHookWeenie::OnContainerClosed(CWeenieObject *requestedBy) { CContainerWeenie::OnContainerClosed(requestedBy); m_bSaveMe = true; Save(); } void CHookWeenie::UpdateHookedObject(CWeenieObject *hookedItem, bool sendUpdate) { if (!hookedItem && !m_Items.empty()) hookedItem = m_Items[0]; if (!hookedItem) return; uint32_t value; if (hookedItem->m_Qualities.InqDataID(SETUP_DID, value)) { m_Qualities.SetDataID(SETUP_DID, value); _phys_obj->part_array->SetSetupID(value, TRUE); //NotifyDIDStatUpdated(SETUP_DID, false); //SetPlacementFrame(Placement::Hook, FALSE); } else { m_Qualities.RemoveDataID(SETUP_DID); //NotifyDIDStatUpdated(SETUP_DID, false); } if (hookedItem->m_Qualities.InqDataID(MOTION_TABLE_DID, value)) { m_Qualities.SetDataID(MOTION_TABLE_DID, value); //NotifyDIDStatUpdated(MOTION_TABLE_DID, false); _phys_obj->SetMotionTableID(value); } else { m_Qualities.RemoveDataID(MOTION_TABLE_DID); //NotifyDIDStatUpdated(MOTION_TABLE_DID, false); } if (hookedItem->m_Qualities.InqDataID(SOUND_TABLE_DID, value)) { m_Qualities.SetDataID(SOUND_TABLE_DID, value); //NotifyDIDStatUpdated(SOUND_TABLE_DID, false); } else { m_Qualities.RemoveDataID(SOUND_TABLE_DID); //NotifyDIDStatUpdated(SOUND_TABLE_DID, false); } if (hookedItem->m_Qualities.InqDataID(PHYSICS_EFFECT_TABLE_DID, value)) { m_Qualities.SetDataID(PHYSICS_EFFECT_TABLE_DID, value); //NotifyDIDStatUpdated(PHYSICS_EFFECT_TABLE_DID, false); set_phstable_id(value); } else { m_Qualities.RemoveDataID(PHYSICS_EFFECT_TABLE_DID); //NotifyDIDStatUpdated(PHYSICS_EFFECT_TABLE_DID, false); if (physics_script_table) { PhysicsScriptTable::Release(physics_script_table); physics_script_table = NULL; } } double floatValue; if (hookedItem->m_Qualities.InqFloat(DEFAULT_SCALE_FLOAT, floatValue)) { m_Qualities.SetFloat(DEFAULT_SCALE_FLOAT, floatValue); //NotifyFloatStatUpdated(DEFAULT_SCALE_FLOAT, false); _phys_obj->SetScaleStatic(floatValue); } else { m_Qualities.SetFloat(DEFAULT_SCALE_FLOAT, 1.0); //NotifyFloatStatUpdated(DEFAULT_SCALE_FLOAT, false); _phys_obj->SetScaleStatic(1.0); } if (hookedItem->m_Qualities.InqFloat(TRANSLUCENCY_FLOAT, floatValue)) { m_Qualities.SetFloat(TRANSLUCENCY_FLOAT, floatValue); //NotifyFloatStatUpdated(TRANSLUCENCY_FLOAT, false); _phys_obj->SetTranslucencyInternal(floatValue); } else { m_Qualities.RemoveFloat(TRANSLUCENCY_FLOAT); //NotifyFloatStatUpdated(TRANSLUCENCY_FLOAT, false); _phys_obj->SetTranslucencyInternal(0.0); } set_state(PhysicsState::LIGHTING_ON_PS | PhysicsState::REPORT_COLLISIONS_PS, TRUE); m_bObjDescOverride = true; hookedItem->GetObjDesc(m_ObjDescOverride); int32_t placement = hookedItem->InqIntQuality(HOOK_PLACEMENT_INT, Placement::Resting, TRUE); if (placement != Placement::Resting) SetPlacementFrame(placement, FALSE); if (sendUpdate && InqBoolQuality(VISIBILITY_BOOL, true)) NotifyObjectUpdated(false); } void CHookWeenie::ClearHookedObject(bool sendUpdate) { CWeenieDefaults *defaults = g_pWeenieFactory->GetWeenieDefaults(m_Qualities.id); uint32_t value; if (defaults->m_Qualities.InqDataID(SETUP_DID, value)) { m_Qualities.SetDataID(SETUP_DID, value); //NotifyDIDStatUpdated(SETUP_DID, false); } else { m_Qualities.RemoveDataID(SETUP_DID); //NotifyDIDStatUpdated(SETUP_DID, false); } if (defaults->m_Qualities.InqDataID(MOTION_TABLE_DID, value)) { m_Qualities.SetDataID(MOTION_TABLE_DID, value); //NotifyDIDStatUpdated(MOTION_TABLE_DID, false); _phys_obj->SetMotionTableID(value); } else { m_Qualities.RemoveDataID(MOTION_TABLE_DID); //NotifyDIDStatUpdated(MOTION_TABLE_DID, false); } if (defaults->m_Qualities.InqDataID(SOUND_TABLE_DID, value)) { m_Qualities.SetDataID(SOUND_TABLE_DID, value); //NotifyDIDStatUpdated(SOUND_TABLE_DID, false); } else { m_Qualities.RemoveDataID(SOUND_TABLE_DID); //NotifyDIDStatUpdated(SOUND_TABLE_DID, false); } if (defaults->m_Qualities.InqDataID(PHYSICS_EFFECT_TABLE_DID, value)) { m_Qualities.SetDataID(PHYSICS_EFFECT_TABLE_DID, value); //NotifyDIDStatUpdated(PHYSICS_EFFECT_TABLE_DID, false); set_phstable_id(value); } else { m_Qualities.RemoveDataID(PHYSICS_EFFECT_TABLE_DID); //NotifyDIDStatUpdated(PHYSICS_EFFECT_TABLE_DID, false); if (physics_script_table) { PhysicsScriptTable::Release(physics_script_table); physics_script_table = NULL; } } double floatValue; if (defaults->m_Qualities.InqFloat(DEFAULT_SCALE_FLOAT, floatValue)) { m_Qualities.SetFloat(DEFAULT_SCALE_FLOAT, floatValue); //NotifyFloatStatUpdated(DEFAULT_SCALE_FLOAT, false); _phys_obj->SetScaleStatic(floatValue); } else { m_Qualities.SetFloat(DEFAULT_SCALE_FLOAT, 1.0); //NotifyFloatStatUpdated(DEFAULT_SCALE_FLOAT, false); _phys_obj->SetScaleStatic(1.0); } if (defaults->m_Qualities.InqFloat(TRANSLUCENCY_FLOAT, floatValue)) { m_Qualities.SetFloat(TRANSLUCENCY_FLOAT, floatValue); //NotifyFloatStatUpdated(TRANSLUCENCY_FLOAT, false); _phys_obj->SetTranslucencyInternal(floatValue); } else { m_Qualities.RemoveFloat(TRANSLUCENCY_FLOAT); //NotifyFloatStatUpdated(TRANSLUCENCY_FLOAT, false); _phys_obj->SetTranslucencyInternal(0.0); } set_state(ETHEREAL_PS | IGNORE_COLLISIONS_PS, TRUE); m_bObjDescOverride = false; m_ObjDescOverride.Clear(); SetPlacementFrame(Placement::Resting, FALSE); if (sendUpdate && InqBoolQuality(VISIBILITY_BOOL, true)) NotifyObjectUpdated(false); } void CHookWeenie::SetHookVisibility(bool newSetting) { if (!m_Items.empty()) { CWeenieObject *hookedItem = m_Items[0]; if (!hookedItem) return; m_Qualities.RemoveBool(VISIBILITY_BOOL); //if it's visible we remove the bool altogether. //NotifyBoolStatUpdated(VISIBILITY_BOOL, false); if (newSetting) { //revert to hook's info. CWeenieDefaults *weenieDef = g_pWeenieFactory->GetWeenieDefaults(m_Qualities.id); if (weenieDef) { if (m_Qualities._emote_table) SafeDelete(m_Qualities._emote_table); m_Qualities.SetString(NAME_STRING, weenieDef->m_Qualities.GetString(NAME_STRING, "")); m_Qualities.SetInt(ITEMS_CAPACITY_INT, weenieDef->m_Qualities.GetInt(ITEMS_CAPACITY_INT, 0)); } NotifyObjectUpdated(false); } else { //overwrite hook with hooked item's info. if (hookedItem->m_Qualities._emote_table) { SafeDelete(m_Qualities._emote_table); m_Qualities._emote_table = new CEmoteTable(); BinaryWriter w; hookedItem->m_Qualities._emote_table->Pack(&w); BinaryReader r(w.GetData(), w.GetSize()); m_Qualities._emote_table->UnPack(&r); } else SafeDelete(m_Qualities._emote_table); if (CWeenieObject *hookedItem = m_Items[0]) { m_Qualities.SetString(NAME_STRING, hookedItem->GetName()); m_Qualities.SetInt(ITEMS_CAPACITY_INT, hookedItem->m_Qualities.GetInt(ITEMS_CAPACITY_INT, 0)); } NotifyObjectUpdated(false); } } else { if (newSetting) { m_Qualities.RemoveBool(VISIBILITY_BOOL); //if it's visible we remove the bool altogether. //NotifyBoolStatUpdated(VISIBILITY_BOOL, false); NotifyObjectCreated(false); } else { m_Qualities.SetBool(VISIBILITY_BOOL, false); //NotifyBoolStatUpdated(VISIBILITY_BOOL, false); NotifyObjectRemoved(); } } Save(); } CHouseWeenie *CHookWeenie::GetHouse() { if (CWeenieObject *house = g_pWorld->FindObject(InqIIDQuality(HOUSE_IID, 0), true)) { return house->AsHouse(); } return NULL; } CHouseData *CHookWeenie::GetHouseData() { if (uint32_t hid = InqDIDQuality(HOUSEID_DID, 0)) return g_pHouseManager->GetHouseData(hid); CHouseWeenie *house = GetHouse(); if (house) return house->GetHouseData(); else return NULL; } CDeedWeenie::CDeedWeenie() { } CHouseWeenie *CDeedWeenie::GetHouse() { if (CWeenieObject *house = g_pWorld->FindObject(InqIIDQuality(HOUSE_IID, 0), true)) { return house->AsHouse(); } return NULL; } CBootSpotWeenie::CBootSpotWeenie() { m_bDontClear = true; } CHouseWeenie *CBootSpotWeenie::GetHouse() { if (CWeenieObject *house = g_pWorld->FindObject(InqIIDQuality(HOUSE_IID, 0), true)) { return house->AsHouse(); } return NULL; } CHousePortalWeenie::CHousePortalWeenie() { m_bDontClear = true; } void CHousePortalWeenie::ApplyQualityOverrides() { SetRadarBlipColor(Portal_RadarBlipEnum); } CHouseWeenie *CHousePortalWeenie::GetHouse() { if (CWeenieObject *house = g_pWorld->FindObject(InqIIDQuality(HOUSE_IID, 0), true)) { return house->AsHouse(); } return NULL; } int CHousePortalWeenie::Use(CPlayerWeenie *other) { CHouseWeenie *house = GetHouse(); if (!house) { other->NotifyUseDone(); return WERROR_NO_HOUSE; } if (house->HasAccess(other->AsPlayer())) return CPortal::Use(other); else { other->SendText("You do not have permission to access that.", LTT_DEFAULT); //made up message. other->NotifyUseDone(); return WERROR_NONE; } } bool CHousePortalWeenie::GetDestination(Position &dest) { if (CHouseWeenie *house = GetHouse()) { if (Position *pos = g_pPortalDataEx->GetHousePortalDest(house->GetHouseDID(), m_Position.objcell_id)) { dest = *pos; return true; } } return false; } CStorageWeenie::CStorageWeenie() { m_bDontClear = true; } int CStorageWeenie::DoUseResponse(CWeenieObject *other) { CHouseWeenie *house = GetHouse(); if (!house) return WERROR_NO_HOUSE; if (house->HasStorageAccess(other->AsPlayer())) { int storageUseResponse = CContainerWeenie::DoUseResponse(other); return storageUseResponse; } else { other->SendText("You do not have permission to access that.", LTT_DEFAULT); //made up message. } return CWeenieObject::DoUseResponse(other); } void CStorageWeenie::OnContainerClosed(CWeenieObject *requestedBy) { CChestWeenie::OnContainerClosed(requestedBy); m_bSaveMe = true; Save(); } CHouseWeenie *CStorageWeenie::GetHouse() { if (CWeenieObject *house = g_pWorld->FindObject(InqIIDQuality(HOUSE_IID, 0), true)) { return house->AsHouse(); } return NULL; }