From 20b68c6a4244ce9df5db8d87b2604b6257c7f445 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 14 Jan 2014 23:15:01 +0100 Subject: [PATCH] Vip functionality, closes #83 --- data/images/game/viplist/icons.png | Bin 0 -> 2795 bytes data/images/game/viplist/vipcheckbox.png | Bin 0 -> 349 bytes modules/game_viplist/editvip.otui | 138 ++++++++++++++++ modules/game_viplist/viplist.lua | 190 +++++++++++++++++++++-- modules/game_viplist/viplist.otui | 4 + modules/gamelib/const.lua | 5 + src/client/const.h | 1 + src/client/game.cpp | 27 +++- src/client/game.h | 5 +- src/client/luafunctions.cpp | 1 + src/client/protocolcodes.h | 1 + src/client/protocolgame.h | 1 + src/client/protocolgameparse.cpp | 6 +- src/client/protocolgamesend.cpp | 11 ++ 14 files changed, 373 insertions(+), 17 deletions(-) create mode 100644 data/images/game/viplist/icons.png create mode 100644 data/images/game/viplist/vipcheckbox.png create mode 100644 modules/game_viplist/editvip.otui diff --git a/data/images/game/viplist/icons.png b/data/images/game/viplist/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d67ccc598272ad937022b025065228b98b8b9a GIT binary patch literal 2795 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 literal 0 HcmV?d00001 diff --git a/data/images/game/viplist/vipcheckbox.png b/data/images/game/viplist/vipcheckbox.png new file mode 100644 index 0000000000000000000000000000000000000000..b479c4049ccc014cfb9ac13aacc8d379d5d4a2f2 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^89*$>!3HFwze#2TDYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oVGw3ym^DWND9B#o>FdgVhl^WS%{*|{r7)n7Y-UJAiF1B#Zfaf$kjuc}T$Gwv zlA5AWo>`Ki;O^-gkfN8$4irD=>EalYaqsOdMZQA@5^WF57#kHK_j-dT zJDlDJ=`{fPx737%1-cd`3Vod1^*wa 0 or iconId > 0 then + vipInfo[id] = {description = description, iconId = iconId, notifyLogin = notify} + else + vipInfo[id] = nil + end + end + + widget:destroy() + onAddVip(id, name, state, description, iconId, notify) + + editVipWindow:destroy() + iconRadioGroup:destroy() + editVipWindow = nil + end + + cancelButton.onClick = cancelFunction + okButton.onClick = saveFunction + + editVipWindow.onEscape = cancelFunction + editVipWindow.onEnter = saveFunction +end + function destroyAddWindow() addVipWindow:destroy() addVipWindow = nil @@ -72,6 +177,38 @@ function addVip() destroyAddWindow() end +function removeVip(widgetOrName) + if not widgetOrName then + return + end + + local widget + local vipList = vipWindow:getChildById('contentsPanel') + if type(widgetOrName) == 'string' then + local entries = vipList:getChildren() + for i = 1, #entries do + if entries[i]:getText():lower() == widgetOrName:lower() then + widget = entries[i] + break + end + end + if not widget then + return + end + else + widget = widgetOrName + end + + if widget then + local id = widget:getId():sub(4) + g_game.removeVip(id) + vipList:removeChild(widget) + if vipInfo[id] and g_game.getFeature(GameAdditionalVipInfo) then + vipInfo[id] = nil + end + end +end + function hideOffline(state) settings = {} settings['hideOffline'] = state @@ -90,7 +227,7 @@ end function getSortedBy() local settings = g_settings.getNode('VipList') - if not settings then + if not settings or not settings['sortedBy'] then return 'status' end return settings['sortedBy'] @@ -104,8 +241,7 @@ function sortBy(state) refresh() end - -function onAddVip(id, name, state) +function onAddVip(id, name, state, description, iconId, notify) local vipList = vipWindow:getChildById('contentsPanel') local label = g_ui.createWidget('VipListLabel') @@ -113,6 +249,27 @@ function onAddVip(id, name, state) label:setId('vip' .. id) label:setText(name) + if not g_game.getFeature(GameAdditionalVipInfo) then + local tmpVipInfo = vipInfo[tostring(id)] + label.iconId = 0 + label.notifyLogin = false + if tmpVipInfo then + if tmpVipInfo.iconId then + label:setImageClip(torect((tmpVipInfo.iconId * 12) .. ' 0 12 12')) + label.iconId = tmpVipInfo.iconId + end + if tmpVipInfo.description then + label:setTooltip(tmpVipInfo.description) + end + label.notifyLogin = tmpVipInfo.notifyLogin or false + end + else + label:setTooltip(description) + label:setImageClip(torect((iconId * 12) .. ' 0 12 12')) + label.iconId = iconId + label.notifyLogin = notify + end + if state == VipState.Online then label:setColor('#00ff00') elseif state == VipState.Pending then @@ -135,13 +292,14 @@ function onAddVip(id, name, state) for i=1,childrenCount do local child = vipList:getChildByIndex(i) - if state == VipState.Online and child.vipState ~= VipState.Online and getSortedBy() == 'status' then + if (state == VipState.Online and child.vipState ~= VipState.Online and getSortedBy() == 'status') + or (label.iconId > child.iconId and getSortedBy() == 'type') then vipList:insertChild(i, label) return end - if ((state ~= VipState.Online and child.vipState ~= VipState.Online) - or (state == VipState.Online and child.vipState == VipState.Online)) or getSortedBy() == 'name' then + if (((state ~= VipState.Online and child.vipState ~= VipState.Online) or (state == VipState.Online and child.vipState == VipState.Online)) and getSortedBy() == 'status') + or (label.iconId == child.iconId and getSortedBy() == 'type') or getSortedBy() == 'name' then local childText = child:getText():lower() local length = math.min(childText:len(), nameLower:len()) @@ -154,6 +312,7 @@ function onAddVip(id, name, state) break elseif j == nameLower:len() then -- We are at the end of nameLower, and its shorter than childText, thus insert before vipList:insertChild(i, label) + return end end end @@ -165,10 +324,17 @@ end function onVipStateChange(id, state) local vipList = vipWindow:getChildById('contentsPanel') local label = vipList:getChildById('vip' .. id) - local text = label:getText() + local name = label:getText() + local description = label:getTooltip() + local iconId = label.iconId + local notify = label.notifyLogin label:destroy() - onAddVip(id, text, state) + onAddVip(id, name, state, description, iconId, notify) + + if notify and state ~= VipState.Pending then + modules.game_textmessage.displayFailureMessage(tr('%s has logged %s.', name, (state == VipState.Online and 'in' or 'out'))) + end end function onVipListMousePress(widget, mousePos, mouseButton) @@ -179,6 +345,7 @@ function onVipListMousePress(widget, mousePos, mouseButton) local menu = g_ui.createWidget('PopupMenu') menu:addOption(tr('Add new VIP'), function() createAddWindow() end) + menu:addSeparator() if not isHiddingOffline() then menu:addOption(tr('Hide Offline'), function() hideOffline(true) end) else @@ -193,6 +360,10 @@ function onVipListMousePress(widget, mousePos, mouseButton) menu:addOption(tr('Sort by status'), function() sortBy('status') end) end + if not(getSortedBy() == 'type') then + menu:addOption(tr('Sort by type'), function() sortBy('type') end) + end + menu:display(mousePos) return true @@ -206,7 +377,8 @@ function onVipListLabelMousePress(widget, mousePos, mouseButton) local menu = g_ui.createWidget('PopupMenu') menu:addOption(tr('Send Message'), function() g_game.openPrivateChannel(widget:getText()) end) menu:addOption(tr('Add new VIP'), function() createAddWindow() end) - menu:addOption(tr('Remove %s', widget:getText()), function() if widget then g_game.removeVip(widget:getId():sub(4)) vipList:removeChild(widget) end end) + menu:addOption(tr('Edit %s', widget:getText()), function() if widget then createEditWindow(widget) end end) + menu:addOption(tr('Remove %s', widget:getText()), function() if widget then removeVip(widget) end end) menu:addSeparator() menu:addOption(tr('Copy Name'), function() g_window.setClipboardText(widget:getText()) end) diff --git a/modules/game_viplist/viplist.otui b/modules/game_viplist/viplist.otui index 2cd0a4a6..38fdea54 100644 --- a/modules/game_viplist/viplist.otui +++ b/modules/game_viplist/viplist.otui @@ -1,5 +1,9 @@ VipListLabel < GameLabel margin-top: 2 + text-offset: 16 0 + image-rect: 0 0 12 12 + image-clip: 0 0 12 12 + image-source: /images/game/viplist/icons font: verdana-11px-monochrome phantom: false diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index 67e7afcd..f4d4b8b7 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -31,6 +31,9 @@ EmblemBlue = 3 EmblemMember = 4 EmblemOther = 5 +VipIconFirst = 0 +VipIconLast = 10 + North = 0 East = 1 South = 2 @@ -100,6 +103,8 @@ GameNewFluids = 47 GamePlayerStateU16 = 48 GameNewOutfitProtocol = 49 GamePVPMode = 50 +GameWritableDate = 51 +GameAdditionalVipInfo = 52 TextColors = { red = '#f55e5e', --'#c83200' diff --git a/src/client/const.h b/src/client/const.h index bd87b4e4..3e94ecfc 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -375,6 +375,7 @@ namespace Otc GameNewOutfitProtocol = 49, GamePVPMode = 50, GameWritableDate = 51, + GameAdditionalVipInfo = 52, LastGameFeature = 101 }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 59c405e3..551ac8f9 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -402,10 +402,10 @@ void Game::processRuleViolationLock() g_lua.callGlobalField("g_game", "onRuleViolationLock"); } -void Game::processVipAdd(uint id, const std::string& name, uint status, int iconId, bool notifyLogin) +void Game::processVipAdd(uint id, const std::string& name, uint status, const std::string& description, int iconId, bool notifyLogin) { - m_vips[id] = Vip(name, status); - g_lua.callGlobalField("g_game", "onAddVip", id, name, status, iconId, notifyLogin); + m_vips[id] = Vip(name, status, description, iconId, notifyLogin); + g_lua.callGlobalField("g_game", "onAddVip", id, name, status, description, iconId, notifyLogin); } void Game::processVipStateChange(uint id, uint status) @@ -1192,6 +1192,23 @@ void Game::removeVip(int playerId) m_protocolGame->sendRemoveVip(playerId); } +void Game::editVip(int playerId, const std::string& description, int iconId, bool notifyLogin) +{ + if(!canPerformGameAction()) + return; + + auto it = m_vips.find(playerId); + if(it == m_vips.end()) + return; + + std::get<2>(m_vips[playerId]) = description; + std::get<3>(m_vips[playerId]) = iconId; + std::get<4>(m_vips[playerId]) = notifyLogin; + + if(getFeature(Otc::GameAdditionalVipInfo)) + m_protocolGame->sendEditVip(playerId, description, iconId, notifyLogin); +} + void Game::setChaseMode(Otc::ChaseModes chaseMode) { if(!canPerformGameAction()) @@ -1521,6 +1538,10 @@ void Game::setProtocolVersion(int version) enableFeature(Otc::GameOfflineTrainingTime); } + if(version >= 963) { + enableFeature(Otc::GameAdditionalVipInfo); + } + if(version >= 973) { enableFeature(Otc::GameLoginPending); enableFeature(Otc::GameNewSpeedLaw); diff --git a/src/client/game.h b/src/client/game.h index d5e17064..f4f330e1 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -36,7 +36,7 @@ #include -typedef std::tuple Vip; +typedef std::tuple Vip; //@bindsingleton g_game class Game @@ -101,7 +101,7 @@ protected: void processRuleViolationLock(); // vip related - void processVipAdd(uint id, const std::string& name, uint status, int iconId, bool notifyLogin); + void processVipAdd(uint id, const std::string& name, uint status, const std::string& description, int iconId, bool notifyLogin); void processVipStateChange(uint id, uint status); // tutorial hint @@ -206,6 +206,7 @@ public: // vip related void addVip(const std::string& name); void removeVip(int playerId); + void editVip(int playerId, const std::string& description, int iconId, bool notifyLogin); // fight modes related void setChaseMode(Otc::ChaseModes chaseMode); diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 7a39ea9c..42636904 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -225,6 +225,7 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "changeOutfit", &Game::changeOutfit, &g_game); g_lua.bindSingletonFunction("g_game", "addVip", &Game::addVip, &g_game); g_lua.bindSingletonFunction("g_game", "removeVip", &Game::removeVip, &g_game); + g_lua.bindSingletonFunction("g_game", "editVip", &Game::editVip, &g_game); g_lua.bindSingletonFunction("g_game", "setChaseMode", &Game::setChaseMode, &g_game); g_lua.bindSingletonFunction("g_game", "setFightMode", &Game::setFightMode, &g_game); g_lua.bindSingletonFunction("g_game", "setPVPMode", &Game::setPVPMode, &g_game); diff --git a/src/client/protocolcodes.h b/src/client/protocolcodes.h index ad45de36..67f2ec4e 100644 --- a/src/client/protocolcodes.h +++ b/src/client/protocolcodes.h @@ -232,6 +232,7 @@ namespace Proto { ClientMount = 212, // 870 ClientAddVip = 220, ClientRemoveVip = 221, + ClientEditVip = 222, ClientBugReport = 230, ClientRuleViolation = 231, ClientDebugReport = 232, diff --git a/src/client/protocolgame.h b/src/client/protocolgame.h index d7d2df21..fd26ba30 100644 --- a/src/client/protocolgame.h +++ b/src/client/protocolgame.h @@ -102,6 +102,7 @@ public: void sendMountStatus(bool mount); void sendAddVip(const std::string& name); void sendRemoveVip(uint playerId); + void sendEditVip(uint playerId, const std::string& description, int iconId, bool notifyLogin); void sendBugReport(const std::string& comment); void sendRuleViolation(const std::string& target, int reason, int action, const std::string& comment, const std::string& statement, int statementId, bool ipBanishment); void sendDebugReport(const std::string& a, const std::string& b, const std::string& c, const std::string& d); diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index ab61063b..f38d6422 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -1492,19 +1492,19 @@ void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg) void ProtocolGame::parseVipAdd(const InputMessagePtr& msg) { uint id, iconId = 0, status; - std::string name, desc; + std::string name, desc = ""; bool notifyLogin = false; id = msg->getU32(); name = g_game.formatCreatureName(msg->getString()); - if(g_game.getProtocolVersion() >= 963) { + if(g_game.getFeature(Otc::GameAdditionalVipInfo)) { desc = msg->getString(); iconId = msg->getU32(); notifyLogin = msg->getU8(); } status = msg->getU8(); - g_game.processVipAdd(id, name, status, iconId, notifyLogin); + g_game.processVipAdd(id, name, status, desc, iconId, notifyLogin); } void ProtocolGame::parseVipState(const InputMessagePtr& msg) diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 163f6a27..85b103b5 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -740,6 +740,17 @@ void ProtocolGame::sendRemoveVip(uint playerId) send(msg); } +void ProtocolGame::sendEditVip(uint playerId, const std::string& description, int iconId, bool notifyLogin) +{ + OutputMessagePtr msg(new OutputMessage); + msg->addU8(Proto::ClientEditVip); + msg->addU32(playerId); + msg->addString(description); + msg->addU32(iconId); + msg->addU8(notifyLogin); + send(msg); +} + void ProtocolGame::sendBugReport(const std::string& comment) { OutputMessagePtr msg(new OutputMessage);