diff --git a/scripts/zones/Windurst_Walls/npcs/Arbitrix.lua b/scripts/zones/Windurst_Walls/npcs/Arbitrix.lua index e35529bf36b..a812ed2aa96 100644 --- a/scripts/zones/Windurst_Walls/npcs/Arbitrix.lua +++ b/scripts/zones/Windurst_Walls/npcs/Arbitrix.lua @@ -7,28 +7,39 @@ local ID = require("scripts/zones/Windurst_Walls/IDs") require("scripts/globals/settings") ----------------------------------- +local adoulinOptionOff = 0x80 +local pictlogicaOptionOff = 0x100 +local wantedOptionOff = 0x1000 +local hideOptionFlags = adoulinOptionOff + pictlogicaOptionOff + wantedOptionOff +local costs = +{ + [1] = 10, + [2] = 10, + [3] = 10, + [4] = 10, + [5] = 10, + [6] = 50 +} function onTrigger(player, npc) local playerAgeDays = (os.time() - player:getTimeCreated()) / 86400 - local dailyTallyPoints = player:getCurrency("daily_tally") local firstVisit = dailyTallyPoints == -1 - local gobbieBoxUsed = player:getCharVar("gobbieBoxUsed") - local specialDialUsed = 1 - local adoulinDialUsed = 1 - local pictlogicaDialUsed = 1 - local wantedDialUsed = 1 - - local adoulinOptionOff = 0x80 - local pictlogicaOptionOff = 0x100 - local wantedOptionOff = 0x1000 - local hideOptionFlags = adoulinOptionOff + pictlogicaOptionOff + wantedOptionOff + local specialDialUsed = player:getMaskBit(gobbieBoxUsed, 0) and 1 or 0 + local adoulinDialUsed = player:getMaskBit(gobbieBoxUsed, 1) and 1 or 0 + local pictlogicaDialUsed = player:getMaskBit(gobbieBoxUsed, 2) and 1 or 0 + local wantedDialUsed = player:getMaskBit(gobbieBoxUsed, 3) and 1 or 0 + local holdingItem = player:getCharVar("gobbieBoxHoldingItem") if playerAgeDays >= GOBBIE_BOX_MIN_AGE and firstVisit then player:startEvent(519) elseif playerAgeDays >= GOBBIE_BOX_MIN_AGE then - player:startEvent(520, specialDialUsed, adoulinDialUsed, pictlogicaDialUsed, wantedDialUsed, 0, 0, hideOptionFlags, dailyTallyPoints) + if holdingItem ~= 0 then + player:startEvent(521) + else + player:startEvent(520, specialDialUsed, adoulinDialUsed, pictlogicaDialUsed, wantedDialUsed, 0, 0, hideOptionFlags, dailyTallyPoints) + end else player:messageSpecial(ID.text.YOU_MUST_WAIT_ANOTHER_N_DAYS, GOBBIE_BOX_MIN_AGE - playerAgeDays) end @@ -36,18 +47,69 @@ end function onEventUpdate(player, csid, option) local dailyTallyPoints = player:getCurrency("daily_tally") + local holdingItem = player:getCharVar("gobbieBoxHoldingItem") + local gobbieBoxUsed = player:getCharVar("gobbieBoxUsed") + local specialDialUsed = player:getMaskBit(gobbieBoxUsed, 0) and 1 or 0 + local adoulinDialUsed = player:getMaskBit(gobbieBoxUsed, 1) and 1 or 0 + local pictlogicaDialUsed = player:getMaskBit(gobbieBoxUsed, 2) and 1 or 0 + local wantedDialUsed = player:getMaskBit(gobbieBoxUsed, 3) and 1 or 0 + local itemid = 0 - if csid == 520 and option == 4 then -- Peek items - player:updateEvent(200, 200, 200) - elseif csid == 520 and option == 9 then - player:updateEvent(1, 1, (dailyTallyPoints >= 10) or 1) - elseif csid == 520 and option == 10 then - -- Deliver Dial 1 + if csid == 520 then + if option == 4 then + player:updateEvent(SelectDailyItem(player, 6), SelectDailyItem(player, 6), SelectDailyItem(player, 6), 0, 0, 0, 0, dailyTallyPoints) -- peek + else + local dial = math.floor(option / 8) + local option_type = option % 8 + local dial_used = false + local dial_cost = costs[dial] + local dial_mask = false + if dial >= 6 then + dial_mask = dial - 6 + dial_used = player:getMaskBit(gobbieBoxUsed, dial_mask) + end + switch (option_type): caseof + { + [1] = function() + if dial_used then + player:updateEvent(1, dial, 2) -- already used this dial + elseif dailyTallyPoints >= dial_cost then + itemid = SelectDailyItem(player, dial) + player:setCharVar("gobbieBoxHoldingItem", itemid) + player:setCurrency("daily_tally", dailyTallyPoints - dial_cost) + if dial_mask then + player:setMaskBit(gobbieBoxUsed, "gobbieBoxUsed", dial_mask, true) + end + player:updateEvent(itemid, dial, 0) + else + player:updateEvent(1, dial, 1) -- not enough points + end + end, + [2] = function() + if player:getFreeSlotsCount() == 0 then + player:updateEvent(holdingItem, 0, 0, 1) -- inventory full, exit event + player:messageSpecial(ID.text.ITEM_CANNOT_BE_OBTAINED + 2) -- generic "Cannot obtain the item." + end + end, + [5] = function() + if holdingItem > 0 and npcUtil.giveItem(player, holdingItem) then + player:setCharVar("gobbieBoxHoldingItem", 0) + end + player:updateEvent(specialDialUsed, adoulinDialUsed, pictlogicaDialUsed, wantedDialUsed, 0, 0, hideOptionFlags, dailyTallyPoints) + end, + } + end end end function onEventFinish(player, csid, option) if csid == 519 then player:setCurrency("daily_tally", 50) + elseif csid == 521 then + if player:getFreeSlotsCount() == 0 then + player:messageSpecial(ID.text.ITEM_CANNOT_BE_OBTAINED + 2) -- generic "Cannot obtain the item." + elseif npcUtil.giveItem(player, player:getCharVar("gobbieBoxHoldingItem")) then + player:setCharVar("gobbieBoxHoldingItem", 0) + end end end diff --git a/src/map/daily_system.cpp b/src/map/daily_system.cpp index 65239457399..64fa19a4896 100644 --- a/src/map/daily_system.cpp +++ b/src/map/daily_system.cpp @@ -1,34 +1,180 @@ #include "daily_system.h" - -#include "map.h" +#include "items/item.h" +#include "utils/charutils.h" +#include "utils/itemutils.h" namespace daily { -void UpdateDailyTallyPoints() -{ - uint16 dailyTallyLimit = map_config.daily_tally_limit; - uint16 dailyTallyAmount = map_config.daily_tally_amount; - - const char* fmtQuery = "UPDATE char_points \ - SET char_points.daily_tally = LEAST(%u, char_points.daily_tally + %u) \ - WHERE char_points.daily_tally > -1;"; + std::vector materialsDialItems; + std::vector foodDialItems; + std::vector medicineDialItems; + std::vector sundries1DialItems; + std::vector sundries2DialItems; + std::vector specialDialItems; - int32 ret = Sql_Query(SqlHandle, fmtQuery, dailyTallyLimit, dailyTallyAmount); - - if (ret == SQL_ERROR) + uint16 SelectItem(CCharEntity* player, uint8 dial) { - ShowError("Failed to update daily tally points\n"); + int selection; + std::vector dialItems; + switch (dial) + { + case 1: + { + dialItems = materialsDialItems; + break; + } + case 2: + { + dialItems = foodDialItems; + break; + } + case 3: + { + dialItems = medicineDialItems; + break; + } + case 4: + { + dialItems = sundries1DialItems; + break; + } + case 5: + { + dialItems = sundries2DialItems; + break; + } + case 6: + { + dialItems = specialDialItems; + break; + } + } + do + { + selection = std::rand() % dialItems.size(); + } while ((itemutils::GetItem(dialItems[selection])->getFlag() & ITEM_FLAG_RARE) > 0 && charutils::HasItem(player, dialItems[selection])); + return dialItems[selection]; } - else + + void LoadDailyItems() { - ShowDebug("Distributed daily tally points\n"); - } + int32 ret = Sql_Query(SqlHandle, "SELECT itemid, aH, flags FROM item_basic WHERE flags & 4 > 0"); + uint16 itemid, aH, flags; - fmtQuery = "DELETE FROM char_vars WHERE varname = 'gobbieBoxUsed';"; + if (ret != SQL_ERROR && Sql_NumRows(SqlHandle) != 0) + { + while (Sql_NextRow(SqlHandle) == SQL_SUCCESS) + { + itemid = Sql_GetUIntData(SqlHandle,0); + aH = Sql_GetUIntData(SqlHandle,1); + flags = Sql_GetUIntData(SqlHandle,2); + specialDialItems.push_back(itemid); + switch (aH) + { + /* Dial 1 (Materials) */ + case 38: // Smithing + case 39: // Goldsmithing + case 40: // Clothcrafting + case 41: // Leathercrafting + case 42: // Bonecrafting + case 43: // Woodworking + case 44: // Alchemy + case 50: // Beast-Made + { + materialsDialItems.push_back(itemid); + break; + } + /* Dial 2 (Food) */ + case 52: // Meat & Eggs + case 53: // Seafood + case 54: // Vegetables + case 55: // Soups + case 56: // Breads & Rice + case 57: // Sweets + case 58: // Drinks + { + foodDialItems.push_back(itemid); + break; + } + /* Dial 3 (Medicine) */ + case 33: // Medicine + { + medicineDialItems.push_back(itemid); + break; + } + /* Dial 4 (Sundries 1) */ + case 15: // Ammunition + case 36: // Cards + case 49: // Ninja Tools + { + if ((flags & ITEM_FLAG_CANUSE) > 0) // only usable (pouch, case, quiver, etc) + { + sundries1DialItems.push_back(itemid); + } + break; + } + /* Dial 5 (Sundries 2) */ + case 47: // Fishing Gear + case 51: // Fish + { + if (itemid == 489 || itemid == 17386) // Lu Shang is probably only special dial + { + break; + } + sundries2DialItems.push_back(itemid); + break; + } + default: + { + switch (itemid) + { + case 605: // pickaxe + case 1020: // sickle + case 1021: // hatchet + case 1022: // thief's tools + case 1023: // living key + case 15453: // lugworm belt + case 15454: // little worm belt + { + sundries2DialItems.push_back(itemid); + break; + } + } + } + } + } + } + else + { + ShowError("Failed to load daily tally items\n"); + } + } - if (Sql_Query(SqlHandle, fmtQuery, dailyTallyAmount) == SQL_ERROR) + void UpdateDailyTallyPoints() { - ShowError("Failed to delete daily tally char_vars entries"); + uint16 dailyTallyLimit = map_config.daily_tally_limit; + uint16 dailyTallyAmount = map_config.daily_tally_amount; + + const char* fmtQuery = "UPDATE char_points \ + SET char_points.daily_tally = LEAST(%u, char_points.daily_tally + %u) \ + WHERE char_points.daily_tally > -1;"; + + int32 ret = Sql_Query(SqlHandle, fmtQuery, dailyTallyLimit, dailyTallyAmount); + + if (ret == SQL_ERROR) + { + ShowError("Failed to update daily tally points\n"); + } + else + { + ShowDebug("Distributed daily tally points\n"); + } + + fmtQuery = "DELETE FROM char_vars WHERE varname = 'gobbieBoxUsed';"; + + if (Sql_Query(SqlHandle, fmtQuery, dailyTallyAmount) == SQL_ERROR) + { + ShowError("Failed to delete daily tally char_vars entries"); + } } -} } \ No newline at end of file diff --git a/src/map/daily_system.h b/src/map/daily_system.h index a38cf51477d..64bc7a776a1 100644 --- a/src/map/daily_system.h +++ b/src/map/daily_system.h @@ -1,9 +1,12 @@ #ifndef _DAILYSYSTEM_H #define _DAILYSYSTEM_H +#include "map.h" namespace daily { -void UpdateDailyTallyPoints(); + uint16 SelectItem(CCharEntity* player, uint8 dial); + void LoadDailyItems(); + void UpdateDailyTallyPoints(); } #endif //_DAILYSYSTEM_H diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index af0b5c466fe..270d25f9ba4 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -38,6 +38,7 @@ #include "../ability.h" #include "../alliance.h" #include "../battlefield.h" +#include "../daily_system.h" #include "../enmity_container.h" #include "../guild.h" #include "../instance.h" diff --git a/src/map/lua/luautils.cpp b/src/map/lua/luautils.cpp index f58f4df7e98..c1e8cffdf20 100644 --- a/src/map/lua/luautils.cpp +++ b/src/map/lua/luautils.cpp @@ -80,6 +80,7 @@ #include "../ai/states/magic_state.h" #include #include "../battlefield.h" +#include "../daily_system.h" namespace luautils { @@ -148,6 +149,7 @@ namespace luautils lua_register(LuaHandle, "getAbility", luautils::getAbility); lua_register(LuaHandle, "getSpell", luautils::getSpell); + lua_register(LuaHandle, "SelectDailyItem", luautils::SelectDailyItem); Lunar::Register(LuaHandle); Lunar::Register(LuaHandle); @@ -4509,4 +4511,14 @@ namespace luautils } } + int32 SelectDailyItem(lua_State* L) + { + TPZ_DEBUG_BREAK_IF(lua_isnil(L, 1) || !lua_isuserdata(L, 1)); + TPZ_DEBUG_BREAK_IF(lua_isnil(L, 2) || !lua_isnumber(L, 2)); + CLuaBaseEntity* PLuaBaseEntity = Lunar::check(L, 1); + CCharEntity* player = (CCharEntity*)PLuaBaseEntity->GetBaseEntity(); + lua_pushinteger(L, daily::SelectItem(player, (uint8)lua_tointeger(L, 2))); + return 1; + } + }; // namespace luautils diff --git a/src/map/lua/luautils.h b/src/map/lua/luautils.h index 1c10e389534..47d0f34d75e 100644 --- a/src/map/lua/luautils.h +++ b/src/map/lua/luautils.h @@ -285,6 +285,8 @@ namespace luautils void OnFurniturePlaced(CCharEntity* PChar, CItemFurnishing* itemId); void OnFurnitureRemoved(CCharEntity* PChar, CItemFurnishing* itemId); + + int32 SelectDailyItem(lua_State* L); }; #endif //- _LUAUTILS_H - diff --git a/src/map/map.cpp b/src/map/map.cpp index 0674dbfea11..deb5f31c640 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -54,6 +54,7 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "status_effect_container.h" #include "utils/zoneutils.h" #include "conquest_system.h" +#include "daily_system.h" #include "utils/mobutils.h" #include "ai/controllers/automaton_controller.h" @@ -218,6 +219,7 @@ int32 do_init(int32 argc, char** argv) battleutils::LoadSkillChainDamageModifiers(); petutils::LoadPetList(); mobutils::LoadCustomMods(); + daily::LoadDailyItems(); ShowStatus("do_init: loading zones"); zoneutils::LoadZoneList();