diff --git a/code/ryzom/client/src/commands.cpp b/code/ryzom/client/src/commands.cpp index 07d84461c..074079780 100644 --- a/code/ryzom/client/src/commands.cpp +++ b/code/ryzom/client/src/commands.cpp @@ -4493,6 +4493,13 @@ NLMISC_COMMAND(debugItemInfoWaiters, "log ItemInfoWaiters", "") return true; } +NLMISC_COMMAND(debugItemInfoCache, "log ItemInfoCache", "") +{ + getInventory().debugItemInfoCache(); + + return true; +} + NLMISC_COMMAND(debugInfoWindows, "log info windows sheetId", "") { CInterfaceHelp::debugOpenedInfoWindows(); diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp index 1b9a7d720..f94a7f180 100644 --- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp +++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp @@ -75,52 +75,52 @@ const uint64 NOTIFY_ANIM_MS_DURATION = 1000; // *************************************************************************** - - -// ********************************************************************************************************** -class CControlSheetTooltipInfoWaiter : public IItemInfoWaiter +void CControlSheetInfoWaiter::sendRequest() { -public: - // The item used to open this window - CDBCtrlSheet* CtrlSheet; - string LuaMethodName; -public: - ucstring infoValidated(CDBCtrlSheet* ctrlSheet, string luaMethodName); - virtual void infoReceived(); -}; -static CControlSheetTooltipInfoWaiter ControlSheetTooltipUpdater; + Requesting = true; + getInventory().addItemInfoWaiter(this); +} -void CControlSheetTooltipInfoWaiter::infoReceived() +void CControlSheetInfoWaiter::infoReceived() { - getInventory().removeItemInfoWaiter(&ControlSheetTooltipUpdater); - infoValidated(CtrlSheet, LuaMethodName); + if (!Requesting) { + return; + } + + getInventory().removeItemInfoWaiter(this); + infoValidated(); + CtrlSheet->infoReceived(); + Requesting = false; } -ucstring CControlSheetTooltipInfoWaiter::infoValidated(CDBCtrlSheet* ctrlSheet, string luaMethodName) +ucstring CControlSheetInfoWaiter::infoValidated() const { ucstring help; - // delegate setup of context he help ( & window ) to lua - CInterfaceManager *im = CInterfaceManager::getInstance(); - CLuaState *ls= CLuaManager::getInstance().getLuaState(); + if (CtrlSheet && !LuaMethodName.empty()) { - CLuaStackRestorer lsr(ls, 0); + // delegate setup of context he help ( & window ) to lua + CInterfaceManager *im = CInterfaceManager::getInstance(); + CLuaState *ls= CLuaManager::getInstance().getLuaState(); + { + CLuaStackRestorer lsr(ls, 0); - CLuaIHM::pushReflectableOnStack(*ls, (CReflectableRefPtrTarget *)ctrlSheet); - ls->pushGlobalTable(); - CLuaObject game(*ls); - game = game["game"]; - game.callMethodByNameNoThrow(luaMethodName.c_str(), 1, 1); + CLuaIHM::pushReflectableOnStack(*ls, (CReflectableRefPtrTarget *)CtrlSheet); + ls->pushGlobalTable(); + CLuaObject game(*ls); + game = game["game"]; + game.callMethodByNameNoThrow(LuaMethodName.c_str(), 1, 1); - // retrieve result from stack - if (!ls->empty()) - { - CLuaIHM::pop(*ls, help); - } - else - { - nlwarning(toString("Ucstring result expected when calling '%s', possible script error", luaMethodName.c_str()).c_str()); + // retrieve result from stack + if (!ls->empty()) + { + CLuaIHM::pop(*ls, help); + } + else + { + nlwarning(toString("Ucstring result expected when calling '%s', possible script error", LuaMethodName.c_str()).c_str()); + } } } @@ -137,7 +137,7 @@ int CDBCtrlSheet::luaGetDraggedSheet(CLuaState &ls) // *************************************************************************** int CDBCtrlSheet::luaGetItemInfo(CLuaState &ls) { - CDBCtrlSheet *ctrlSheet = const_cast(this); + CDBCtrlSheet *ctrlSheet = const_cast(this); uint32 itemSlotId = getInventory().getItemSlotId(ctrlSheet); CClientItemInfo itemInfo = getInventory().getItemInfo(itemSlotId); @@ -203,7 +203,7 @@ int CDBCtrlSheet::luaGetCreatorName(CLuaState &ls) ucstring creatorName; STRING_MANAGER::CStringManagerClient::instance()->getString(itemInfo.CreatorName, creatorName); CLuaIHM::push(ls, creatorName); - + return 1; } @@ -236,7 +236,6 @@ int CDBCtrlSheet::luaWaitInfo(CLuaState &ls) // *************************************************************************** int CDBCtrlSheet::luaBuildCrystallizedSpellListBrick(CLuaState &ls) { - CDBCtrlSheet *ctrlSheet = const_cast(this); uint32 itemSlotId= getInventory().getItemSlotId(ctrlSheet); @@ -257,7 +256,7 @@ int CDBCtrlSheet::luaBuildCrystallizedSpellListBrick(CLuaState &ls) } } - + // Reset other to 0. for(;;currentBrick++) { @@ -505,6 +504,7 @@ CCtrlDraggable(param) _Useable= true; _GrayedLink= NULL; _NeedSetup= true; + _ItemInfoChanged = true; _IconW = 0; _IconH = 0; _SetupInit= false; @@ -528,6 +528,11 @@ CCtrlDraggable(param) _ItemRMClassType= NULL; _ItemRMFaberStatType= NULL; _NotifyAnimEndTime = 0; + + _HpBuffIcon = "ico_heal.tga"; + _SapBuffIcon = "ico_sap.tga"; + _StaBuffIcon = "ico_stamina.tga"; + _FocusBuffIcon = "ico_focus.tga"; } // ---------------------------------------------------------------------------- @@ -535,6 +540,11 @@ CDBCtrlSheet::~CDBCtrlSheet() { NL3D::UDriver *Driver = CViewRenderer::getInstance()->getDriver(); + if (_ItemInfoWaiter.Requesting) + { + getInventory().removeItemInfoWaiter(&_ItemInfoWaiter); + } + if (_GuildBack) { if (Driver) @@ -613,6 +623,22 @@ bool CDBCtrlSheet::parse(xmlNodePtr cur, CInterfaceGroup * parentGroup) prop = (char*) xmlGetProp( cur, (xmlChar*)"slot" ); if(prop) _DrawSlot= CInterfaceElement::convertBool(prop); + // + _HpBuffIcon = "ico_heal.tga"; + prop = (char*) xmlGetProp( cur, (xmlChar*)"hp_buff_icon" ); + if (prop) _HpBuffIcon = string((const char *)prop); + + _SapBuffIcon = "ico_sap.tga"; + prop = (char*) xmlGetProp( cur, (xmlChar*)"sap_buff_icon" ); + if (prop) _SapBuffIcon = string((const char *)prop); + + _StaBuffIcon = "ico_stamina.tga"; + prop = (char*) xmlGetProp( cur, (xmlChar*)"sta_buff_icon" ); + if (prop) _StaBuffIcon = string((const char *)prop); + + _FocusBuffIcon = "ico_focus.tga"; + prop = (char*) xmlGetProp( cur, (xmlChar*)"focus_buff_icon" ); + if (prop) _FocusBuffIcon = string((const char *)prop); updateActualType(); // Init size for Type @@ -1040,6 +1066,64 @@ void CDBCtrlSheet::updateIconSize() } } +// *************************************************************************** +void CDBCtrlSheet::infoReceived() +{ + if (!_ItemSheet) + { + return; + } + + const CClientItemInfo *itemInfo = getInventory().getItemInfoCache(getItemSerial(), getItemCreateTime()); + if (itemInfo == NULL) + { + // schedule for recheck on next draw() + _ItemInfoChanged = true; + return; + } + + // crystallized spell + { + _EnchantIcons.clear(); + CViewRenderer &rVR = *CViewRenderer::getInstance(); + CSBrickManager *pBM= CSBrickManager::getInstance(); + for(uint i=0; iEnchantment.Bricks.size(); ++i) + { + const CSBrickSheet *brick = pBM->getBrick(itemInfo->Enchantment.Bricks[i]); + if (brick) + { + if (!brick->isRoot() && !brick->isCredit() && !brick->isParameter()) + { + _EnchantIcons.push_back(SBuffIcon(rVR.getTextureIdFromName(brick->getIcon()), brick->IconColor)); + rVR.getTextureSizeFromId(_EnchantIcons.back().TextureId, _EnchantIcons.back().IconW, _EnchantIcons.back().IconH); + } + else if (brick->isRoot()) + { + // there should be single root icon and it should be first one + _EnchantIcons.push_back(SBuffIcon(rVR.getTextureIdFromName(brick->getIconBack()), brick->IconBackColor)); + rVR.getTextureSizeFromId(_EnchantIcons.back().TextureId, _EnchantIcons.back().IconW, _EnchantIcons.back().IconH); + } + } + } + } + + // buff icons + { + _BuffIcons.clear(); + CViewRenderer &rVR = *CViewRenderer::getInstance(); + + if (itemInfo->HpBuff > 0) _BuffIcons.push_back(SBuffIcon(rVR.getTextureIdFromName(_HpBuffIcon))); + if (itemInfo->SapBuff > 0) _BuffIcons.push_back(SBuffIcon(rVR.getTextureIdFromName(_SapBuffIcon))); + if (itemInfo->StaBuff > 0) _BuffIcons.push_back(SBuffIcon(rVR.getTextureIdFromName(_StaBuffIcon))); + if (itemInfo->FocusBuff > 0) _BuffIcons.push_back(SBuffIcon(rVR.getTextureIdFromName(_FocusBuffIcon))); + + // update sizes + for(uint i = 0; i < _BuffIcons.size(); ++i) + { + rVR.getTextureSizeFromId(_BuffIcons[i].TextureId, _BuffIcons[i].IconW, _BuffIcons[i].IconH); + } + } +} // *************************************************************************** void CDBCtrlSheet::setupPact() @@ -1103,6 +1187,7 @@ void CDBCtrlSheet::setupItem () CInterfaceManager *pIM= CInterfaceManager::getInstance(); sint32 sheet = _SheetId.getSInt32(); + // If this is the same sheet, need to resetup if (_LastSheetId != sheet || _NeedSetup) { @@ -1203,6 +1288,9 @@ void CDBCtrlSheet::setupItem () // Special Item requirement updateItemCharacRequirement(_LastSheetId); + + // update item info markers + _ItemInfoChanged = true; } else { @@ -1277,6 +1365,13 @@ void CDBCtrlSheet::setupItem () _Useable = CSkillManager::getInstance()->checkBaseSkillMetRequirement(_ItemSheet->RequiredSkill, _ItemSheet->RequiredSkillLevel); } */ + + // at each frame, update item info icon if changed + if (_ItemInfoChanged) + { + _ItemInfoChanged = false; + setupItemInfoWaiter(); + } } @@ -1857,7 +1952,7 @@ void CDBCtrlSheet::draw() CInterfaceManager *pIM = CInterfaceManager::getInstance(); CViewRenderer &rVR = *CViewRenderer::getInstance(); - + if (_Type != SheetType_Macro) { if (_LastSheetId != _SheetId.getSInt32()) @@ -1949,13 +2044,13 @@ void CDBCtrlSheet::draw() rVR.getTextureSizeFromId(frontTex, texWidth, texHeight); CQuadUV regenTris[5]; - uint numTris = buildPie(regenTris, 0.f, amount, texWidth); + uint numTris = buildPie(regenTris, 0.f, amount, texWidth); nlassert(numTris <= sizeofarray(regenTris)); for (uint tri = 0; tri < numTris; ++tri) { rVR.drawQuad(_RenderLayer + 1, regenTris[tri], frontTex, CRGBA::White, false); } - numTris = buildPie(regenTris, amount, 1.f, texWidth); + numTris = buildPie(regenTris, amount, 1.f, texWidth); nlassert(numTris <= sizeofarray(regenTris)); for (uint tri = 0; tri < numTris; ++tri) { @@ -1977,7 +2072,7 @@ void CDBCtrlSheet::draw() if (texId != -1) { sint32 texWidth, texHeight; - rVR.getTextureSizeFromId(texId, texWidth, texHeight); + rVR.getTextureSizeFromId(texId, texWidth, texHeight); const float freq0 = 1.f; const float phase0 = 0.f; const float freq1 = -1.f; @@ -1994,20 +2089,20 @@ void CDBCtrlSheet::draw() // ---------------------------------------------------------------------------- void CDBCtrlSheet::drawRotatedQuad(CViewRenderer &vr, float angle, float scale, uint renderLayer, uint32 texId, sint32 texWidth, sint32 texHeight) { - NLMISC::CQuadUV quv; + NLMISC::CQuadUV quv; float cosA = cosf(angle); float sinA = sinf(angle); // - quv.V0.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (- cosA + sinA), + quv.V0.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (- cosA + sinA), _YReal + 0.5f * _HReal + 0.5f * scale * texHeight * (- sinA - cosA), 0.5f); // - quv.V1.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (cosA + sinA), + quv.V1.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (cosA + sinA), _YReal + 0.5f * _HReal + 0.5f * scale * texHeight * (sinA - cosA), 0.5f); // - quv.V2.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (cosA - sinA), - _YReal + 0.5f * _HReal + 0.5f * scale * texHeight * (sinA + cosA), 0.5f); + quv.V2.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (cosA - sinA), + _YReal + 0.5f * _HReal + 0.5f * scale * texHeight * (sinA + cosA), 0.5f); // - quv.V3.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (- cosA - sinA), + quv.V3.set(_XReal + 0.5f * _WReal + 0.5f * scale * texWidth * (- cosA - sinA), _YReal + 0.5f * _HReal + 0.5f * scale * texHeight * (- sinA + cosA), 0.5f); // quv.Uv0.set(0.f, 0.f); @@ -2022,13 +2117,13 @@ void CDBCtrlSheet::drawRotatedQuad(CViewRenderer &vr, float angle, float scale, // ---------------------------------------------------------------------------- inline void CDBCtrlSheet::uvToScreen(float x, float y, CVector &screenPos, uint texSize) const { - screenPos.set(_XReal + texSize * x, _YReal + texSize * (1.f - y), 0.5f); + screenPos.set(_XReal + texSize * x, _YReal + texSize * (1.f - y), 0.5f); } // ---------------------------------------------------------------------------- void CDBCtrlSheet::buildPieCorner(float angle, CUV &uv, CVector &pos, uint texSize) const -{ +{ float radAngle = angle * 2.f * float(NLMISC::Pi); // angle origin is at 12'o'clock float x = cosf(0.5f * float(NLMISC::Pi) - radAngle); @@ -2061,33 +2156,33 @@ void CDBCtrlSheet::buildPieCorner(float angle, CUV &uv, CVector &pos, uint texSi y /= -x; x = -1.f; } - } + } // remap to unit quad // (well we could have worked with tan() too, I find it simpler this way ....) uv.set(0.5f * x + 0.5f, 0.5f - 0.5f * y); - uvToScreen(uv.U, uv.V, pos, texSize); + uvToScreen(uv.U, uv.V, pos, texSize); } // ---------------------------------------------------------------------------- uint CDBCtrlSheet::buildPie(CQuadUV *triPtr, float startAngle, float endAngle, uint texSize) -{ +{ static volatile bool exit1 = false; nlassert(startAngle <= endAngle); - const sint32 factor = 65536; - const float invFactor = 1.f / factor; + const sint32 factor = 65536; + const float invFactor = 1.f / factor; sint32 iCurr = (uint32) (startAngle * factor) ; sint32 iEnd = (uint32) (endAngle * factor); clamp(iCurr, 0, factor); clamp(iEnd, 0, factor); uint triCount = 0; CVector quadCenter; - uvToScreen(0.5f, 0.5f, quadCenter, texSize); + uvToScreen(0.5f, 0.5f, quadCenter, texSize); while (iCurr != iEnd) { sint32 iNext = iCurr + (factor / 4); - iNext -= ((iNext - factor / 8) % (factor / 4)); // snap to nearest corner - iNext = std::min(iNext, iEnd); + iNext -= ((iNext - factor / 8) % (factor / 4)); // snap to nearest corner + iNext = std::min(iNext, iEnd); // well, not really a quad, but we don't have yet simple triangles rendering in the ui triPtr->Uv0.set(0.5f, 0.5f); triPtr->V0 = quadCenter; @@ -2220,6 +2315,69 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti rVR.draw11RotFlipBitmap (_RenderLayer+1, x, y, 0, false, _DispOver2BmpId, fastMulRGB(curSheetColor, _IconOver2Color)); } + if (!_BuffIcons.empty()) + { + // there is max 4 icons + sint32 hArea = (hSheet / 4); + sint32 xIcon = x; + sint32 yIcon = y; + for (uint i = 0; i < _BuffIcons.size(); ++i) + { + sint32 wIcon = _BuffIcons[i].IconW; + sint32 hIcon = _BuffIcons[i].IconH; + if (hIcon > hArea) + { + wIcon = wIcon * ((float)hArea / hIcon); + hIcon = hArea; + } + rVR.drawRotFlipBitmap (_RenderLayer+1, xIcon, yIcon, wIcon, hIcon, 0, false, _BuffIcons[i].TextureId, fastMulRGB(curSheetColor, _BuffIcons[i].Color)); + xIcon += wIcon; + // move up the row for 3rd/4th icon + if (i % 3 == 1) { + xIcon = x; + yIcon += hIcon; + } + } + } + + // Is the item enchanted ? + sint32 enchant = _Enchant.getSInt32(); + if (enchant > 0) + { + // Yes draw the additionnal bitmap and the charge (number of enchanted spell we can launch with the enchanted item) + enchant--; + rVR.draw11RotFlipBitmap (_RenderLayer+1, x, y, 0, false, rVR.getSystemTextureId(CViewRenderer::ItemEnchantedTexture), curSheetColor); + drawNumber(x+1, y-2+hSheet-rVR.getFigurTextureH(), wSheet, hSheet, numberColor, enchant, false); + } + + if (!_EnchantIcons.empty()) + { + // should only only 2 icons at most + // draw them in single line, top-right + sint32 hArea = (hSheet / 3); + sint32 xIcon = x + wSheet - 1; + sint32 yIcon = y + hSheet - 1/* - hArea*/; + // 0 is expected to be background + for (uint i = 1; i < _EnchantIcons.size(); ++i) + { + sint32 wIcon = _EnchantIcons[i].IconW; + sint32 hIcon = _EnchantIcons[i].IconH; + if (hIcon > hArea) + { + wIcon = wIcon * ((float)hArea / hIcon); + hIcon = hArea; + } + // need to move x before draw because of right aligned + if (i == 1) + { + xIcon -= wIcon; + } + yIcon -= hIcon; + rVR.drawRotFlipBitmap(_RenderLayer + 1, xIcon, yIcon, wIcon, hIcon, 0, false, _EnchantIcons[0].TextureId, fastMulRGB(curSheetColor, _EnchantIcons[0].Color)); + rVR.drawRotFlipBitmap(_RenderLayer+1, xIcon, yIcon, wIcon, hIcon, 0, false, _EnchantIcons[i].TextureId, fastMulRGB(curSheetColor, _EnchantIcons[i].Color)); + } + } + // Draw Quality. -1 for lookandfeel. Draw it with global color if (_DispQuality != -1) { @@ -2242,15 +2400,6 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti } drawNumber(x+1+crossW, y+1, wSheet, hSheet, curSheetColor, quantity, false); } - // Is the item enchanted ? - sint32 enchant = _Enchant.getSInt32(); - if (enchant > 0) - { - // Yes draw the additionnal bitmap and the charge (number of enchanted spell we can launch with the enchanted item) - enchant--; - rVR.draw11RotFlipBitmap (_RenderLayer+2, x, y, 0, false, rVR.getSystemTextureId(CViewRenderer::ItemEnchantedTexture), curSheetColor); - drawNumber(x+1, y-2+hSheet-rVR.getFigurTextureH(), wSheet, hSheet, numberColor, enchant, false); - } // if a raw material for example, must add special icon text. displayCharBitmaps(_RenderLayer+2, x, y, curSheetColor); @@ -2348,8 +2497,8 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti case SheetType_SPhrase : setupSPhrase(); break; default: nlassert(true); break; } - - + + bool showOutOfRangeSymbol = false; bool forceGrayed = false; @@ -2361,15 +2510,15 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti { CSPhraseManager *pPM= CSPhraseManager::getInstance(); const CSPhraseCom &phrase= pPM->getPhrase(phraseId); - // get the phrase Data version, to check if it had changed. + // get the phrase Data version, to check if it had changed. uint32 totalActionMalus = pPM->getTotalActionMalus(phrase); uint8 targetSlot = UserEntity->targetSlot(); if (targetSlot < CLFECOMMON::INVALID_SLOT) { CEntityCL *target = EntitiesMngr.entity(targetSlot); if (target && UserEntity) - { - double dist2 = (target->pos() - UserEntity->pos()).sqrnorm(); + { + double dist2 = (target->pos() - UserEntity->pos()).sqrnorm(); CSBrickManager *pBM= CSBrickManager::getInstance(); CSBrickSheet *rootBrick= NULL; if(phrase.Bricks.size()) @@ -2396,7 +2545,7 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti break; } } - + if (!isPrimalMagic) { forceGrayed = !(target->isPlayer() && target->isFriend()); @@ -2405,21 +2554,21 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti { forceGrayed = false; } - + } - + if (!forceGrayed) { sint phraseRange; sint rangeMalus; pPM->getPhraseMagicRange(phrase, totalActionMalus, phraseRange, rangeMalus); - double rangeDist = (float) (phraseRange + rangeMalus); + double rangeDist = (float) (phraseRange + rangeMalus); if (phraseRange != 0) // if range is '0' then it is a 'self' action { - rangeDist += 0.5 + target->getSheetScale() * target->getSheetColRadius(); // player radius + rangeDist += 0.5 + target->getSheetScale() * target->getSheetColRadius(); // player radius if (dist2 > rangeDist * rangeDist) { - showOutOfRangeSymbol = true; + showOutOfRangeSymbol = true; } } } @@ -2451,11 +2600,11 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti if (isTaunt && !forceGrayed) { - double rangeDist = (float) maxRange; - rangeDist += 0.5 + target->getSheetScale() * target->getSheetColRadius(); // player radius + double rangeDist = (float) maxRange; + rangeDist += 0.5 + target->getSheetScale() * target->getSheetColRadius(); // player radius if (dist2 > rangeDist * rangeDist) { - showOutOfRangeSymbol = true; + showOutOfRangeSymbol = true; } } } @@ -2522,15 +2671,15 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti // if the pointer is over the button if (_Over) // Draw -1,-1 because the slot over is 26x26 - rVR.draw11RotFlipBitmap (_RenderLayer+1, x-1, y-1, 0, false, _TextureIdOver, curSheetColor ); + rVR.draw11RotFlipBitmap (_RenderLayer+1, x-1, y-1, 0, false, _TextureIdOver, curSheetColor ); } if (showOutOfRangeSymbol) - { + { rVR.draw11RotFlipBitmap (_RenderLayer+1, x, y, 0, false, rVR.getSystemTextureId(CViewRenderer::OutOfRangeTexture), CRGBA::White); - } + } } - break; + break; case CCtrlSheetInfo::SheetType_OutpostBuilding: setupOutpostBuilding(); if (_DispBackBmpId != -1) @@ -3114,6 +3263,53 @@ const COutpostBuildingSheet *CDBCtrlSheet::asOutpostBuildingSheet() const return NULL; } +// *************************************************************************** +void CDBCtrlSheet::setupItemInfoWaiter() +{ + const CItemSheet *item = asItemSheet(); + if(!item) + return; + + if (!useItemInfoForFamily(item->Family)) + return; + + if (getItemSerial() == 0 || getItemCreateTime() == 0) + return; + + string luaMethodName = ((item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) ? "updateCrystallizedSpellTooltip" : "updateBuffItemTooltip"); + CDBCtrlSheet *ctrlSheet = const_cast(this); + uint itemSlotId = getInventory().getItemSlotId(ctrlSheet); + + // Prepare the waiter for tooltips + _ItemInfoWaiter.ItemSheet= ctrlSheet->getSheetId(); + _ItemInfoWaiter.LuaMethodName = luaMethodName; + _ItemInfoWaiter.ItemSlotId= itemSlotId; + _ItemInfoWaiter.CtrlSheet = ctrlSheet; + + // send out request only if cache is not set + const CClientItemInfo *itemInfo = getInventory().getItemInfoCache(getItemSerial(), getItemCreateTime()); + if (itemInfo) + { + infoReceived(); + } + else + { + // Using isInventoryPresent/Available() will fail for packers when out of range + // Getting server item however will work correctly for packer/room/guild + const CItemImage *itemImage = getInventory().getServerItem(itemSlotId); + if (itemImage) + { + _ItemInfoWaiter.sendRequest(); + } + else + { + // schedule for next draw() - if inventory should not be available (ie guild), + // but user opens it anyway, then this will loop back here on every draw() + _ItemInfoChanged = true; + } + } +} + // *************************************************************************** void CDBCtrlSheet::getContextHelp(ucstring &help) const { @@ -3177,34 +3373,17 @@ void CDBCtrlSheet::getContextHelp(ucstring &help) const { if (useItemInfoForFamily(item->Family)) { - string luaMethodName = ( (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) ? "updateCrystallizedSpellTooltip" : "updateBuffItemTooltip"); - CDBCtrlSheet *ctrlSheet = const_cast(this); - CCtrlBase *ctrlBase = const_cast(this); - if ( ! getInventory().isItemInfoUpToDate(getInventory().getItemSlotId(ctrlSheet))) - { - // Prepare the waiter - ControlSheetTooltipUpdater.ItemSheet= ctrlSheet->getSheetId(); - ControlSheetTooltipUpdater.LuaMethodName = luaMethodName; - ControlSheetTooltipUpdater.ItemSlotId= getInventory().getItemSlotId(ctrlSheet); - ControlSheetTooltipUpdater.CtrlSheet = ctrlSheet; - - // Add the waiter - getInventory().addItemInfoWaiter(&ControlSheetTooltipUpdater); - } - - if (!_ContextHelp.empty()) - { - help= _ContextHelp; - ctrlBase->setDefaultContextHelp(ucstring()); - } - else - help = ControlSheetTooltipUpdater.infoValidated(ctrlSheet, luaMethodName); + // call lua function to update tooltip window + help = _ItemInfoWaiter.infoValidated(); + } + else if (!_ContextHelp.empty()) + { + help = _ContextHelp; } else - if (!_ContextHelp.empty()) - help= _ContextHelp; - else - help= getItemActualName(); + { + help = getItemActualName(); + } } else help= _ContextHelp; @@ -3247,18 +3426,18 @@ void CDBCtrlSheet::getContextHelp(ucstring &help) const } else { - // delegate setup of context he help ( & window ) to lua + // delegate setup of context he help ( & window ) to lua CInterfaceManager *im = CInterfaceManager::getInstance(); CLuaState *ls= CLuaManager::getInstance().getLuaState(); { CLuaStackRestorer lsr(ls, 0); - CSPhraseManager *pPM= CSPhraseManager::getInstance(); + CSPhraseManager *pPM= CSPhraseManager::getInstance(); _PhraseAdapter = new CSPhraseComAdpater; _PhraseAdapter->Phrase = pPM->getPhrase(phraseId); CLuaIHM::pushReflectableOnStack(*ls, _PhraseAdapter); - ls->pushGlobalTable(); + ls->pushGlobalTable(); CLuaObject game(*ls); - game = game["game"]; + game = game["game"]; game.callMethodByNameNoThrow("updatePhraseTooltip", 1, 1); // retrieve result from stack help = ucstring(); @@ -3322,21 +3501,7 @@ void CDBCtrlSheet::getContextHelpToolTip(ucstring &help) const { if (useItemInfoForFamily(item->Family)) { - string luaMethodName = (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) ? "updateCrystallizedSpellTooltip" : "updateBuffItemTooltip"; - CDBCtrlSheet *ctrlSheet = const_cast(this); - if ( ! getInventory().isItemInfoUpToDate(getInventory().getItemSlotId(ctrlSheet))) - { - // Prepare the waiter - ControlSheetTooltipUpdater.ItemSheet= ctrlSheet->getSheetId(); - ControlSheetTooltipUpdater.LuaMethodName = luaMethodName; - ControlSheetTooltipUpdater.ItemSlotId= getInventory().getItemSlotId(ctrlSheet); - ControlSheetTooltipUpdater.CtrlSheet = ctrlSheet; - - // Add the waiter - getInventory().addItemInfoWaiter(&ControlSheetTooltipUpdater); - } - - help = ControlSheetTooltipUpdater.infoValidated(ctrlSheet, luaMethodName); + help = _ItemInfoWaiter.infoValidated(); return; } } @@ -3551,6 +3716,10 @@ void CDBCtrlSheet::resetAllTexIDs() _Stackable= 1; _IconW = 0; _IconH = 0; + + _ItemInfoChanged = true; + _EnchantIcons.clear(); + _BuffIcons.clear(); } diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.h b/code/ryzom/client/src/interface_v3/dbctrl_sheet.h index ac2f2c3aa..1d185d9f4 100644 --- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.h +++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.h @@ -37,7 +37,7 @@ #include "game_share/item_family.h" // #include "../time_client.h" - +#include "item_info_waiter.h" class CItemSheet; class CPactSheet; @@ -53,6 +53,25 @@ namespace NLGUI class CViewRenderer; } +class CDBCtrlSheet; + +// *************************************************************************** +// Item info request from server +class CControlSheetInfoWaiter : public IItemInfoWaiter +{ +public: + CDBCtrlSheet* CtrlSheet; + string LuaMethodName; + bool Requesting; + CControlSheetInfoWaiter() + : IItemInfoWaiter(), Requesting(false) + { } +public: + ucstring infoValidated() const; + void sendRequest(); + virtual void infoReceived(); +}; + // *************************************************************************** /** Common info for CDBCtrlSheet and CDBGroupListSheet @@ -576,12 +595,14 @@ public: // For auras, powers, etc. set the range of ticks during which regen occurs void setRegenTickRange(const CTickRange &tickRange); const CTickRange &getRegenTickRange() const { return _RegenTickRange; } - + // start notify anim (at the end of regen usually) void startNotifyAnim(); -protected: + // callback from info waiter + void infoReceived(); +protected: inline bool useItemInfoForFamily(ITEMFAMILY::EItemFamily family) const; void setupItem(); @@ -622,6 +643,7 @@ protected: NLMISC::CCDBNodeLeaf *_ItemRMFaberStatType; mutable sint32 _LastSheetId; + bool _ItemInfoChanged; /// Display sint32 _DispSlotBmpId; // Display slot bitmap id @@ -632,6 +654,26 @@ protected: sint32 _DispOverBmpId; // Over Icon sint32 _DispOver2BmpId; // Over Icon N0 2 for bricks / items. Useful for items when _DispOverBmpId is used to paint user color on the item. + std::string _HpBuffIcon; + std::string _SapBuffIcon; + std::string _StaBuffIcon; + std::string _FocusBuffIcon; + + // texture ids to show + struct SBuffIcon + { + SBuffIcon(sint32 txid, NLMISC::CRGBA col=NLMISC::CRGBA::White) + :TextureId(txid), Color(col), IconW(0), IconH(0) + { } + + sint32 TextureId; + NLMISC::CRGBA Color; + sint32 IconW; + sint32 IconH; + }; + std::vector _BuffIcons; + std::vector _EnchantIcons; + // Level Brick or Quality union { @@ -751,6 +793,7 @@ protected: sint64 _NotifyAnimEndTime; + CControlSheetInfoWaiter _ItemInfoWaiter; private: mutable TSheetType _ActualType; @@ -769,17 +812,20 @@ private: // special for items void updateItemCharacRequirement(sint32 sheetId); + // Send ITEM_INFO:GET request to server to fetch Buffs, Enchant info + void setupItemInfoWaiter(); + // update armour color, and cache void updateArmourColor(sint8 col); // setup sheet DB. _DbBranchName must be ok, and _SecondIndexInDb and _IndexInDb as well void setupSheetDbLinks (); - + // 'regen' rendering // convert from uv coordinates in the [0, 1] x [0, 1] range to screen coords inline void uvToScreen(float x, float y, NLMISC::CVector &screenPos, uint texSize) const; - // from an angle in the [0, 1] range, return both uv & screen coords for that angle + // from an angle in the [0, 1] range, return both uv & screen coords for that angle // (angle is projected on the side of rectangle of the 'regen' texture) void buildPieCorner(float angle, NLMISC::CUV &uv, NLMISC::CVector &pos, uint texSize) const; // from a start and end angle in the [0, 1] range, build the set of uv mapped triangles necessary diff --git a/code/ryzom/client/src/interface_v3/inventory_manager.cpp b/code/ryzom/client/src/interface_v3/inventory_manager.cpp index e4ce2b6e9..78966338a 100644 --- a/code/ryzom/client/src/interface_v3/inventory_manager.cpp +++ b/code/ryzom/client/src/interface_v3/inventory_manager.cpp @@ -58,6 +58,8 @@ using namespace std; using namespace NLMISC; +extern TSessionId CharacterHomeSessionId; + extern NLMISC::CLog g_log; // Context help extern void contextHelp (const std::string &help); @@ -127,6 +129,8 @@ CItemImage::CItemImage() Sheet = NULL; Quality = NULL; Quantity = NULL; + CreateTime = NULL; + Serial = NULL; UserColor = NULL; Price = NULL; Weight= NULL; @@ -141,6 +145,8 @@ void CItemImage::build(CCDBNodeBranch *branch) Sheet = dynamic_cast(branch->getNode(ICDBNode::CTextId("SHEET"), false)); Quality = dynamic_cast(branch->getNode(ICDBNode::CTextId("QUALITY"), false)); Quantity = dynamic_cast(branch->getNode(ICDBNode::CTextId("QUANTITY"), false)); + CreateTime = dynamic_cast(branch->getNode(ICDBNode::CTextId("CREATE_TIME"), false)); + Serial = dynamic_cast(branch->getNode(ICDBNode::CTextId("SERIAL"), false)); UserColor = dynamic_cast(branch->getNode(ICDBNode::CTextId("USER_COLOR"), false)); Price = dynamic_cast(branch->getNode(ICDBNode::CTextId("PRICE"), false)); Weight = dynamic_cast(branch->getNode(ICDBNode::CTextId("WEIGHT"), false)); @@ -149,7 +155,145 @@ void CItemImage::build(CCDBNodeBranch *branch) ResaleFlag = dynamic_cast(branch->getNode(ICDBNode::CTextId("RESALE_FLAG"), false)); // Should always have at least those one:(ie all but Price) - nlassert(Sheet && Quality && Quantity && UserColor && Weight && NameId && InfoVersion); + nlassert(Sheet && Quality && Quantity && CreateTime && Serial && UserColor && Weight && NameId && InfoVersion); +} + +uint64 CItemImage::getItemId() const +{ + return ((uint64)getSerial() << 32) | getCreateTime(); +} + +// ************************************************************************************************* +void CItemInfoCache::load(const std::string &filename) +{ + try + { + CIFile f; + if (f.open(filename)) + { + serial(f); + } + } catch(...) + { } +} + +void CItemInfoCache::save(const std::string &filename) +{ + try + { + COFile f; + if (f.open(filename)) + { + serial(f); + } + }catch(...) + { } +} + +void CItemInfoCache::serial(NLMISC::IStream &s) +{ + s.serialCheck(NELID("METI")); + uint ver = 1; + s.serialVersion(ver); + + uint8 byte = 1; + if (s.isReading()) + { + _ItemInfoCacheMap.clear(); + while(true) + { + uint64 key; + + s.serial(byte); + if (byte == 0) + { + break; + } + s.serial(key); + s.serial(_ItemInfoCacheMap[key].CacheCycle); + _ItemInfoCacheMap[key].serial(s); + + // these are not used in item info cache + _ItemInfoCacheMap[key].InfoVersionFromMsg = 0; + _ItemInfoCacheMap[key].InfoVersionFromSlot = 0; + _ItemInfoCacheMap[key].InfoVersionSlotServerWaiting = 0; + } + } + else + { + byte = 1; + TItemInfoCacheMap::iterator it = _ItemInfoCacheMap.begin(); + while (it != _ItemInfoCacheMap.end()) + { + // purge item from cache if not encountered in X save + if (it->second.CacheCycle < 10000) + { + // 'record exists' byte + s.serial(byte); + + // item id (serial << 32 | createTime) + uint64 key = it->first; + s.serial(key); + + uint32 cycle = it->second.CacheCycle+1; + s.serial(cycle); + + // item info + it->second.serial(s); + } + + ++it; + } + // eof of records byte + byte = 0; + s.serial(byte); + } +} + +const CClientItemInfo *CItemInfoCache::getItemInfo(uint32 serial, uint32 createTime) const +{ + if (serial > 0 && createTime > 0) + { + uint64 itemId = ((uint64)serial << 32) | createTime; + return getItemInfo(itemId); + } + + return NULL; +} + +const CClientItemInfo *CItemInfoCache::getItemInfo(uint64 itemId) const +{ + if (itemId > 0) + { + TItemInfoCacheMap::const_iterator it = _ItemInfoCacheMap.find(itemId); + if (it != _ItemInfoCacheMap.end()) + return &(it->second); + } + + return NULL; +} + +void CItemInfoCache::readFromImpulse(uint64 itemId, CItemInfos itemInfo) +{ + if (itemId > 0) + { + _ItemInfoCacheMap[itemId].readFromImpulse(itemInfo); + _ItemInfoCacheMap[itemId].CacheCycle = 0; + } +} + +void CItemInfoCache::debugItemInfoCache() const +{ + nlinfo("ItemInfoCache: %d entries", _ItemInfoCacheMap.size()); + uint count = 0; + for (auto it = _ItemInfoCacheMap.begin(); it != _ItemInfoCacheMap.end(); ++it) + { + uint32 serial = (it->first >> 32) & 0xFFFFFFFF; + uint32 created = it->first & 0xFFFFFFFF; + nlinfo("[%-4d] cacheCycle:%d, serial:%d, createTime:%d", count++, it->second.CacheCycle, serial, created); + } + CInterfaceManager *pIM= CInterfaceManager::getInstance(); + pIM->displaySystemInfo(toString("ItemInfoCache: %d entries written to client.log", _ItemInfoCacheMap.size())); } // ************************************************************************************************* @@ -180,12 +324,16 @@ CInventoryManager::CInventoryManager() BagItemEquipped[i]= false; } + _ItemInfoCacheFilename = toString("save/item_infos_%d.cache", CharacterHomeSessionId.asInt()); + _ItemInfoCache.load(_ItemInfoCacheFilename); + nlctassert(NumInventories== sizeof(InventoryIndexes)/sizeof(InventoryIndexes[0])); } // *************************************************************************** CInventoryManager::~CInventoryManager() { + _ItemInfoCache.save(_ItemInfoCacheFilename); } // ************************************************************************************************* @@ -257,6 +405,11 @@ CItemImage &CInventoryManager::getServerBagItem(uint index) nlassert(index < MAX_BAGINV_ENTRIES); return ServerBag[index]; } +const CItemImage &CInventoryManager::getServerBagItem(uint index) const +{ + nlassert(index < MAX_BAGINV_ENTRIES); + return ServerBag[index]; +} // ************************************************************************************************* CItemImage &CInventoryManager::getServerTempItem(uint index) @@ -264,6 +417,11 @@ CItemImage &CInventoryManager::getServerTempItem(uint index) nlassert(index < MAX_TEMPINV_ENTRIES); return ServerTempInv[index]; } +const CItemImage &CInventoryManager::getServerTempItem(uint index) const +{ + nlassert(index < MAX_TEMPINV_ENTRIES); + return ServerTempInv[index]; +} // ************************************************************************************************* CItemImage *CInventoryManager::getServerHandItem(uint index) @@ -1736,12 +1894,12 @@ void CTempInvManager::update() // show/hide weight info depending on temp inventory mode bool displayWeight = (_Mode == TEMP_INV_MODE::Craft); - CViewBase *weightText = dynamic_cast(pGC->getView("weight_txt")); - if (weightText != NULL) + CViewBase *weightText = dynamic_cast(pGC->getView("weight_txt")); + if (weightText != NULL) weightText->setActive(displayWeight); CViewBase *weightImg = dynamic_cast(pGC->getView("weight")); if (weightImg != NULL) - weightImg->setActive(displayWeight); + weightImg->setActive(displayWeight); if (_Mode == TEMP_INV_MODE::Forage) { @@ -2178,24 +2336,24 @@ bool SBagOptions::canDisplay(CDBCtrlSheet *pCS) const return false; // Armor - if ((pIS->Family == ITEMFAMILY::ARMOR) || + if ((pIS->Family == ITEMFAMILY::ARMOR) || (pIS->Family == ITEMFAMILY::JEWELRY)) if (!bFilterArmor) bDisplay = false; // Weapon - if ((pIS->Family == ITEMFAMILY::SHIELD) || + if ((pIS->Family == ITEMFAMILY::SHIELD) || (pIS->Family == ITEMFAMILY::MELEE_WEAPON) || - (pIS->Family == ITEMFAMILY::RANGE_WEAPON) || + (pIS->Family == ITEMFAMILY::RANGE_WEAPON) || (pIS->Family == ITEMFAMILY::AMMO) || - (pIS->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) || + (pIS->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) || (pIS->Family == ITEMFAMILY::ITEM_SAP_RECHARGE) || (pIS->Family == ITEMFAMILY::BRICK) ) if (!bFilterWeapon) bDisplay = false; // Tool - if ((pIS->Family == ITEMFAMILY::CRAFTING_TOOL) || + if ((pIS->Family == ITEMFAMILY::CRAFTING_TOOL) || (pIS->Family == ITEMFAMILY::HARVEST_TOOL) || - (pIS->Family == ITEMFAMILY::TAMING_TOOL) || + (pIS->Family == ITEMFAMILY::TAMING_TOOL) || (pIS->Family == ITEMFAMILY::TRAINING_TOOL) || (pIS->Family == ITEMFAMILY::BAG)) if (!bFilterTool) bDisplay = false; @@ -3262,22 +3420,53 @@ uint CInventoryManager::getItemSheetForSlotId(uint slotId) const return 0; } +// *************************************************************************** +const CClientItemInfo *CInventoryManager::getItemInfoCache(uint32 serial, uint32 createTime) const +{ + return _ItemInfoCache.getItemInfo(serial, createTime); +} + // *************************************************************************** const CClientItemInfo &CInventoryManager::getItemInfo(uint slotId) const { TItemInfoMap::const_iterator it= _ItemInfoMap.find(slotId); static CClientItemInfo empty; - if(it==_ItemInfoMap.end()) + if (it == _ItemInfoMap.end() || !isItemInfoUpToDate(slotId)) + { + // if slot has not been populated yet or out of date, then return info from cache if possible + const CItemImage *item = getServerItem(slotId); + if (item && item->getItemId() > 0) { + const CClientItemInfo *ret = _ItemInfoCache.getItemInfo(item->getItemId()); + if (ret != NULL) + { + return *ret; + } + } + } + + if (it == _ItemInfoMap.end()) + { return empty; - else - return it->second; + } + + return it->second; } // *************************************************************************** -bool CInventoryManager::isItemInfoUpToDate(uint slotId) +bool CInventoryManager::isItemInfoAvailable(uint slotId) const +{ + TItemInfoMap::const_iterator it= _ItemInfoMap.find(slotId); + return it != _ItemInfoMap.end(); +} +// *************************************************************************** +bool CInventoryManager::isItemInfoUpToDate(uint slotId) const { + TItemInfoMap::const_iterator it= _ItemInfoMap.find(slotId); + if (it == _ItemInfoMap.end()) + return true; + // true if the version already matches - return getItemInfo(slotId).InfoVersionFromMsg == getItemInfo(slotId).InfoVersionFromSlot; + return it->second.InfoVersionFromMsg == it->second.InfoVersionFromSlot; } // *************************************************************************** @@ -3314,8 +3503,10 @@ void CInventoryManager::removeItemInfoWaiter(IItemInfoWaiter *waiter) void CInventoryManager::updateItemInfoWaiters(uint itemSlotId) { // First verify if the versions matches. If differ, no need to update waiters since not good. - if(getItemInfo(itemSlotId).InfoVersionFromMsg != getItemInfo(itemSlotId).InfoVersionFromSlot) + if (!isItemInfoUpToDate(itemSlotId)) + { return; + } bool isItemFromTrading= (itemSlotId>>CItemInfos::SlotIdIndexBitSize)==INVENTORIES::trading; @@ -3409,10 +3600,15 @@ void CInventoryManager::onReceiveItemSheet(ICDBNode* node) // *************************************************************************** void CInventoryManager::onReceiveItemInfo(const CItemInfos &itemInfo) { - uint itemSlotId; - // update the Info - itemSlotId= itemInfo.slotId; + uint itemSlotId = itemInfo.slotId; + + const CItemImage *item = getServerItem(itemSlotId); + if (item && item->getItemId() > 0) + { + _ItemInfoCache.readFromImpulse(item->getItemId(), itemInfo); + } + // write in map, from DB. _ItemInfoMap[itemSlotId].readFromImpulse(itemInfo); @@ -3426,7 +3622,7 @@ void CInventoryManager::onReceiveItemInfo(const CItemInfos &itemInfo) // *************************************************************************** void CInventoryManager::onRefreshItemInfoVersion(uint16 slotId, uint8 infoVersion) { - _ItemInfoMap[slotId].refreshInfoVersion( infoVersion ); + _ItemInfoMap[slotId].refreshInfoVersion(infoVersion); } // *************************************************************************** @@ -3522,13 +3718,19 @@ void CInventoryManager::debugItemInfoWaiters() } } +// *************************************************************************** +void CInventoryManager::debugItemInfoCache() const +{ + _ItemInfoCache.debugItemInfoCache(); +} + // *************************************************************************** void CInventoryManager::sortBag() { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CDBGroupIconListBag *pIconList; CDBGroupListSheetBag *pList; - + pIconList = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(LIST_BAG_ICONS)); if (pIconList != NULL) pIconList->needToSort(); pList = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(LIST_BAG_TEXT)); @@ -3677,6 +3879,14 @@ CItemImage &CInventoryManager::getServerPAItem(uint beastIndex, uint index) return ServerPAInv[beastIndex][index]; } +const CItemImage &CInventoryManager::getServerPAItem(uint beastIndex, uint index) const +{ + nlassert(beastIndex < MAX_INVENTORY_ANIMAL); + nlassert(index < MAX_ANIMALINV_ENTRIES); + return ServerPAInv[beastIndex][index]; +} + + // *************************************************************************** CItemImage &CInventoryManager::getLocalItem(uint inv, uint index) { @@ -3703,6 +3913,93 @@ CItemImage &CInventoryManager::getServerItem(uint inv, uint index) return dummy; } +// *************************************************************************** +const CItemImage *CInventoryManager::getServerItem(uint slotId) const +{ + uint inv, index; + getSlotInvIndex(slotId, inv, index); + + if (inv == INVENTORIES::bag) + { + return &getServerBagItem(index); + } + else if (inv >= INVENTORIES::pet_animal && inv getDbBranch("SERVER:GUILD:INVENTORY:" + toString(index)); + if (itemBranch) + { + static CItemImage image; + image.build(itemBranch); + return ℑ + } + } + return NULL; + } + else if (inv == INVENTORIES::player_room) + { + // player is in their room + if (getInventory().isInventoryAvailable(INVENTORIES::player_room)) + { + CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch(SERVER_INVENTORY ":ROOM:" + toString(index)); + if (itemBranch) + { + static CItemImage image; + image.build(itemBranch); + return ℑ + } + } + return NULL; + } + else if (inv == INVENTORIES::trading) + { + CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:TRADING:" + toString(index)); + if (itemBranch) + { + static CItemImage image; + image.build(itemBranch); + return ℑ + } + } + else if (inv == INVENTORIES::exchange) + { + CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:EXCHANGE:GIVE:" + toString(index)); + if (itemBranch) + { + static CItemImage image; + image.build(itemBranch); + return ℑ + } + } + else if (inv == INVENTORIES::exchange_proposition) + { + CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:EXCHANGE:RECEIVE:" + toString(index)); + if (itemBranch) + { + static CItemImage image; + image.build(itemBranch); + return ℑ + } + } + else + { + nlwarning("getServerItem: invalid inventory %d for slotId %d", inv, slotId); + } + + // invalid inventory + return NULL; +} + // *************************************************************************** CInventoryManager::TInvType CInventoryManager::invTypeFromString(const string &str) { @@ -3719,3 +4016,11 @@ CInventoryManager::TInvType CInventoryManager::invTypeFromString(const string &s if (sTmp == "inv_room") return InvRoom; return InvUnknown; } + +// *************************************************************************** +void CInventoryManager::getSlotInvIndex(uint slotId, uint &inv, uint &index) const +{ + inv = slotId >> CItemInfos::SlotIdIndexBitSize; + index = slotId & CItemInfos::SlotIdIndexBitMask; +} + diff --git a/code/ryzom/client/src/interface_v3/inventory_manager.h b/code/ryzom/client/src/interface_v3/inventory_manager.h index 0fa6fd2ee..8cc9f85d2 100644 --- a/code/ryzom/client/src/interface_v3/inventory_manager.h +++ b/code/ryzom/client/src/interface_v3/inventory_manager.h @@ -31,7 +31,7 @@ namespace NLMISC{ class CCDBNodeBranch; } class CDBCtrlSheet; - +class IItermInfoWaiter; const uint MAX_TEMPINV_ENTRIES = INVENTORIES::NbTempInvSlots; const uint MAX_BAGINV_ENTRIES = INVENTORIES::NbBagSlots; @@ -60,6 +60,8 @@ public: NLMISC::CCDBNodeLeaf *Sheet; NLMISC::CCDBNodeLeaf *Quality; NLMISC::CCDBNodeLeaf *Quantity; + NLMISC::CCDBNodeLeaf *CreateTime; + NLMISC::CCDBNodeLeaf *Serial; NLMISC::CCDBNodeLeaf *UserColor; NLMISC::CCDBNodeLeaf *Price; NLMISC::CCDBNodeLeaf *Weight; @@ -72,10 +74,13 @@ public: CItemImage(); // build from a branch void build(NLMISC::CCDBNodeBranch *branch); + uint64 getItemId() const; // shortcuts to avoid NULL pointer tests uint32 getSheetID() const { return (uint32) (Sheet ? Sheet->getValue32() : 0); } uint16 getQuality() const { return (uint16) (Quality ? Quality->getValue16() : 0); } uint16 getQuantity() const { return (uint16) (Quantity ? Quantity->getValue16() : 0); } + uint32 getCreateTime() const { return (uint32) (CreateTime ? CreateTime->getValue32() : 0); } + uint32 getSerial() const { return (uint32) (Serial ? Serial->getValue32() : 0); } uint8 getUserColor() const { return (uint8) (UserColor ? UserColor->getValue16() : 0); } uint32 getPrice() const { return (uint32) (Price ? Price->getValue32() : 0); } uint32 getWeight() const { return (uint32) (Weight ? Weight->getValue32() : 0); } @@ -87,6 +92,8 @@ public: void setSheetID(uint32 si) { if (Sheet) Sheet->setValue32((sint32) si); } void setQuality(uint16 quality) { if (Quality) Quality->setValue16((sint16) quality); } void setQuantity(uint16 quantity) { if (Quantity) Quantity->setValue16((sint16) quantity); } + void setCreateTime(uint32 create_time) { if (CreateTime) CreateTime->setValue32((sint32) create_time); } + void setSerial(uint32 serial) { if (Serial) Serial->setValue32((sint32) serial); } void setUserColor(uint8 uc) { if (UserColor) UserColor->setValue8((sint8) uc); } void setPrice(uint32 price) { if (Price) Price->setValue32((sint32) price); } void setWeight(uint32 wgt) { if (Weight) Weight->setValue32((sint32) wgt); } @@ -108,11 +115,15 @@ public: // This is the InfoVersionFromSlot when last request was sent to server uint16 InfoVersionSlotServerWaiting; + // Used to track cache age (reset on use, +1 on every save) + uint32 CacheCycle; + CClientItemInfo() { InfoVersionFromMsg= 0; InfoVersionFromSlot= 0; InfoVersionSlotServerWaiting= 0; + CacheCycle= 0; } /// Set InfoVersion from Info message (info requested by the player) @@ -122,21 +133,25 @@ public: void refreshInfoVersion(uint8 infoVersion) { InfoVersionFromMsg= infoVersion; } }; - -class IItemInfoWaiter +class CItemInfoCache { public: - IItemInfoWaiter() {ItemSlotId= 0; ItemSheet= 0;} - virtual ~IItemInfoWaiter() {} - // The item SheetId. If differ from current sheet in the SlotId, the infos are not updated / requested - uint ItemSheet; - // The item SlotId to retrieve info. - uint ItemSlotId; - - // Called when the info is received for this slot. - virtual void infoReceived() =0; -}; + void load(const std::string &filename); + void save(const std::string &filename); + void serial(NLMISC::IStream &s); + + // retrieve pointer to item info or null if error + const CClientItemInfo *getItemInfo(uint32 serial, uint32 createTime) const; + const CClientItemInfo *getItemInfo(uint64 itemId) const; + // set/update item info in cache + void readFromImpulse(uint64 itemId, CItemInfos itemInfo); + void debugItemInfoCache() const; + +private: + typedef std::map TItemInfoCacheMap; + TItemInfoCacheMap _ItemInfoCacheMap; +}; // *************************************************************************** /** This manager gives direct access to inventory slots (bag, temporary inventory, hands, and equip inventory) @@ -189,8 +204,10 @@ public: // SERVER INVENTORY // get item of bag (local inventory) CItemImage &getServerBagItem(uint index); + const CItemImage &getServerBagItem(uint index) const; // get temporary item (local inventory) CItemImage &getServerTempItem(uint index); + const CItemImage &getServerTempItem(uint index) const; // get hand item (local inventory) CItemImage *getServerHandItem(uint index); // get equip item (local inventory) @@ -200,8 +217,11 @@ public: void setServerMoney(uint64 value); // get item of pack animal (server inventory). beastIndex ranges from 0 to MAX_INVENTORY_ANIMAL-1 CItemImage &getServerPAItem(uint beastIndex, uint index); + const CItemImage &getServerPAItem(uint beastIndex, uint index) const; // get the item Image for the given inventory. assert if bad inventory CItemImage &getServerItem(uint inv, uint index); + // get the item Image for the given slotId or NULL if bad + const CItemImage *getServerItem(uint slotId) const; // Drag'n'Drop Management enum TFrom { Slot, TextList, IconList, Nowhere }; @@ -267,9 +287,13 @@ public: uint16 getItemSlotId(CDBCtrlSheet *ctrl); uint16 getItemSlotId(const std::string &itemDb, uint slotIndex); const CClientItemInfo &getItemInfo(uint slotId) const; + // get item info from cache + const CClientItemInfo *getItemInfoCache(uint32 serial, uint32 createTime) const; uint getItemSheetForSlotId(uint slotId) const; + // Returns true if the item info is already in slot cache + bool isItemInfoAvailable(uint slotId) const; // Returns true if the item info version already matches - bool isItemInfoUpToDate(uint slotId); + bool isItemInfoUpToDate(uint slotId) const; // Add a Waiter on ItemInfo (ItemHelp opening). no-op if here, but reorder (returns true if the version already matches or if waiter is NULL) void addItemInfoWaiter(IItemInfoWaiter *waiter); // remove a Waiter on ItemInfo (ItemHelp closing). no-op if not here. NB: no delete @@ -279,6 +303,7 @@ public: void onRefreshItemInfoVersion(uint16 slotId, uint8 infoVersion); // Log for debug void debugItemInfoWaiters(); + void debugItemInfoCache() const; void sortBag(); @@ -294,6 +319,9 @@ public: enum TInvType { InvBag, InvPA0, InvPA1, InvPA2, InvPA3, InvPA4, InvPA5, InvPA6, InvGuild, InvRoom, InvUnknown }; static TInvType invTypeFromString(const std::string &str); + // inventory and slot from slotId + void getSlotInvIndex(uint slotId, uint &inv, uint &index) const; + private: // LOCAL INVENTORY @@ -318,7 +346,9 @@ private: CDBCtrlSheet *DNDCurrentItem; // ItemExtraInfo management. - typedef std::map TItemInfoMap; + std::string _ItemInfoCacheFilename; + CItemInfoCache _ItemInfoCache; + typedef std::map TItemInfoMap; TItemInfoMap _ItemInfoMap; typedef std::list TItemInfoWaiters; TItemInfoWaiters _ItemInfoWaiters; diff --git a/code/ryzom/client/src/interface_v3/item_info_waiter.h b/code/ryzom/client/src/interface_v3/item_info_waiter.h new file mode 100644 index 000000000..7976f94f2 --- /dev/null +++ b/code/ryzom/client/src/interface_v3/item_info_waiter.h @@ -0,0 +1,39 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#ifndef RZ_ITEM_INFO_WAITER_H +#define RZ_ITEM_INFO_WAITER_H + +class IItemInfoWaiter +{ +public: + IItemInfoWaiter() {ItemSlotId= 0; ItemSheet= 0;} + virtual ~IItemInfoWaiter() {} + // The item SheetId. If differ from current sheet in the SlotId, the infos are not updated / requested + uint ItemSheet; + // The item SlotId to retrieve info. + uint ItemSlotId; + + // Called when the info is received for this slot. + virtual void infoReceived() =0; +}; + +#endif // RZ_DBCTRL_SHEET_INFO_WAITER_H + +/* End of item_info_waiter.h */ + diff --git a/code/ryzom/client/src/interface_v3/obs_huge_list.cpp b/code/ryzom/client/src/interface_v3/obs_huge_list.cpp index 76506ed57..03c2d973a 100644 --- a/code/ryzom/client/src/interface_v3/obs_huge_list.cpp +++ b/code/ryzom/client/src/interface_v3/obs_huge_list.cpp @@ -161,6 +161,8 @@ bool CHugeListObs::init() case Trading: _Items[k].SlotType = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:SLOT_TYPE").c_str(), (int) k), false); _Items[k].Quality = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:QUALITY").c_str(), (int) k), false); + _Items[k].Serial = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:SERIAL").c_str(), (int) k), false); + _Items[k].CreateTime = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:CREATE_TIME").c_str(), (int) k), false); _Items[k].SheetIDOrSkill = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:SHEET").c_str(), (int) k), false); _Items[k].Price = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:PRICE").c_str(), (int) k), false); _Items[k].Weight = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:WEIGHT").c_str(), (int) k), false); @@ -181,6 +183,7 @@ bool CHugeListObs::init() if ((_Items[k].SlotType == NULL) || (_Items[k].Quality == NULL) || (_Items[k].SheetIDOrSkill == NULL) || (_Items[k].Price == NULL) || (_Items[k].Weight==NULL) || (_Items[k].InfoVersion==NULL) || (_Items[k].UserColor==NULL) || (_Items[k].NameId==NULL) || (_Items[k].Quantity==NULL) || + (_Items[k].Serial == NULL) || (_Items[k].CreateTime == NULL) || (_Items[k].PriceRetire==NULL) || (_Items[k].SellerType==NULL) || (_Items[k].ResaleTimeLeft==NULL) || (_Items[k].VendorNameId==NULL) || (_Items[k].Enchant ==NULL) || (_Items[k].RMClassType == NULL) || (_Items[k].RMFaberStatType == NULL) || (_Items[k].PrerequisitValid == NULL) || @@ -191,6 +194,8 @@ bool CHugeListObs::init() case ItemForMissions: _Items[k].SlotType = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:SLOT_TYPE").c_str(), (int) k), false); _Items[k].Quality = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:QUALITY").c_str(), (int) k), false); + _Items[k].Serial = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:SERIAL").c_str(), (int) k), false); + _Items[k].CreateTime = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:CREATE_TIME").c_str(), (int) k), false); _Items[k].SheetIDOrSkill = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:SHEET").c_str(), (int) k), false); _Items[k].LogicTextID = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:LOGIC_TEXT_ID").c_str(), (int) k), false); _Items[k].DescTextID = NLGUI::CDBManager::getInstance()->getDbProp(toString((dbPath + ":%d:DESC_TEXT_ID").c_str(), (int) k), false); @@ -205,7 +210,7 @@ bool CHugeListObs::init() (_Items[k].LogicTextID == NULL) || (_Items[k].DescTextID == NULL) || (_Items[k].Weight==NULL) || (_Items[k].InfoVersion==NULL) || (_Items[k].UserColor==NULL) || (_Items[k].Enchant ==NULL) || (_Items[k].RMClassType == NULL) || (_Items[k].RMFaberStatType == NULL) || - (_Items[k].NameId==NULL) + (_Items[k].NameId==NULL) || (_Items[k].Serial == NULL) || (_Items[k].CreateTime == NULL) ) return false; break; @@ -383,6 +388,8 @@ void CHugeListObs::update(ICDBNode * /* node */) { page.Items[k].SlotType = (TRADE_SLOT_TYPE::TTradeSlotType) _Items[k].SlotType->getValue32(); page.Items[k].Quality = (uint16) _Items[k].Quality->getValue16(); + page.Items[k].Serial = (uint32) _Items[k].Serial->getValue32(); + page.Items[k].CreateTime = (uint32) _Items[k].CreateTime->getValue32(); page.Items[k].SheetIDOrSkill = (uint32) _Items[k].SheetIDOrSkill->getValue32(); page.Items[k].Price = (uint32) _Items[k].Price->getValue32(); page.Items[k].Weight= (uint16) _Items[k].Weight->getValue16(); @@ -420,6 +427,8 @@ void CHugeListObs::update(ICDBNode * /* node */) case ItemForMissions: page.Items[k].SlotType = (TRADE_SLOT_TYPE::TTradeSlotType) _Items[k].SlotType->getValue32(); page.Items[k].Quality = (uint16) _Items[k].Quality->getValue16(); + page.Items[k].Serial = (uint32) _Items[k].Serial->getValue32(); + page.Items[k].CreateTime = (uint32) _Items[k].CreateTime->getValue32(); page.Items[k].SheetIDOrSkill = (uint32) _Items[k].SheetIDOrSkill->getValue32(); page.Items[k].LogicTextID = (uint32) _Items[k].LogicTextID->getValue32(); page.Items[k].DescTextID = (uint32) _Items[k].DescTextID->getValue32(); @@ -569,6 +578,10 @@ void CHugeListObs::updateUIItemPage(uint index) case Trading: leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":QUALITY", false); if (leaf) leaf->setValue32(currItem.Quality); + leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":SERIAL", false); + if (leaf) leaf->setValue32(currItem.Serial); + leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":CREATE_TIME", false); + if (leaf) leaf->setValue32(currItem.CreateTime); leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":SLOT_TYPE", false); if (leaf) leaf->setValue32(currItem.SlotType); leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":SHEET", false); @@ -610,6 +623,10 @@ void CHugeListObs::updateUIItemPage(uint index) case ItemForMissions: leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":QUALITY", false); if (leaf) leaf->setValue32(currItem.Quality); + leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":SERIAL", false); + if (leaf) leaf->setValue32(currItem.Serial); + leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":CREATE_TIME", false); + if (leaf) leaf->setValue32(currItem.CreateTime); leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":SLOT_TYPE", false); if (leaf) leaf->setValue32(currItem.SlotType); leaf = NLGUI::CDBManager::getInstance()->getDbProp(dbPath + toString(k + index * TRADE_PAGE_NUM_ITEMS) + ":SHEET", false); diff --git a/code/ryzom/client/src/interface_v3/obs_huge_list.h b/code/ryzom/client/src/interface_v3/obs_huge_list.h index 64ef38252..480d31540 100644 --- a/code/ryzom/client/src/interface_v3/obs_huge_list.h +++ b/code/ryzom/client/src/interface_v3/obs_huge_list.h @@ -124,6 +124,8 @@ private: // NLMISC::CCDBNodeLeaf *SlotType; NLMISC::CCDBNodeLeaf *Quality; + NLMISC::CCDBNodeLeaf *Serial; + NLMISC::CCDBNodeLeaf *CreateTime; NLMISC::CCDBNodeLeaf *SheetIDOrSkill; // NLMISC::CCDBNodeLeaf *LogicTextID; // valid if the item is to be obtained for a mission @@ -157,6 +159,8 @@ private: GuildName(NULL), SlotType(NULL), Quality(NULL), + Serial(NULL), + CreateTime(NULL), SheetIDOrSkill(NULL), LogicTextID(NULL), DescTextID(NULL), @@ -216,6 +220,8 @@ private: TRADE_SLOT_TYPE::TTradeSlotType SlotType; uint16 Quality; + uint32 Serial; + uint32 CreateTime; uint32 SheetIDOrSkill; uint32 LogicTextID; // Valid if the item is to be obtained as a mission reward uint32 DescTextID; // Valid if the item is to be obtained as a mission reward