From b575dfcc10c9e0e2ff4b29ecdeaff4636ea59dfd Mon Sep 17 00:00:00 2001 From: iLPdev Date: Thu, 16 Feb 2023 20:39:37 -0800 Subject: [PATCH] Prepare for v1.0.0 package release (#37) * Made modifications adapting to new PR functionality. * Fixing speedwalking. * Making the change called for in https://forums.mudlet.org/viewtopic.php?p=46822#p46822 and https://github.com/Mudlet/Mudlet/issues/6019. * Adding processing of gmcp messages and events for the updated values. Adapting mapper to use the new events. Updating stats to use the updated gmcp data and also to make the xp bar reflect score. * fix to typos in stamina bar variables Changings players to player --------- Co-authored-by: Dalem --- prs-core.lua | 143 +++++++++++++++++++++++++++++++++++++ prs-mapper.lua | 190 +++++++++++++++++++++++++++++++++++-------------- prs-stats.lua | 73 ++++++++++++------- 3 files changed, 326 insertions(+), 80 deletions(-) create mode 100644 prs-core.lua diff --git a/prs-core.lua b/prs-core.lua new file mode 100644 index 0000000..665fd2b --- /dev/null +++ b/prs-core.lua @@ -0,0 +1,143 @@ +core = core or {} +core.events = core.events or {} + +local function is_array(t) + local i = 0 + for _ in pairs(t) do + i = i + 1 + if t[i] == nil then return false end + end + return true +end + +local function remove_key(table, key) + local element = table[key] + table[key] = nil + return element +end + +local function update_state_from_diff(old, diff, prefix) + local events = {} + for k in pairs(diff) do + if type(diff[k]) == "table" and not is_array(diff[k]) then + if next(diff[k]) == nil then + old[k] = {} + else + old[k] = old[k] or {} + + update_state_from_diff(old[k], remove_key(diff, k), prefix.."."..k) + end + else + old[k] = remove_key(diff, k) + end + table.insert(events, prefix.."."..k) + end + + for _, ev in ipairs(events) do + raiseEvent(ev) + end +end + +local function get_dropped_and_added_affects(old_affects, new_affects) + local added_affects = {} + local dropped_affects = {} + + if new_affects then table.sort(new_affects, function(left, right) + return left.name < right.name + end) end + if old_affects then table.sort(old_affects, function(left, right) + return left.name < right.name + end) end + + local old_aff_idx = 1 + local new_aff_idx = 1 + local old_aff_n = table.getn(old_affects) + local new_aff_n = table.getn(new_affects) + while true do + if new_aff_idx > new_aff_n and old_aff_idx > old_aff_n then + break + elseif old_aff_idx > old_aff_n then + table.insert(added_affects, 1, new_affects[new_aff_idx]) + new_aff_idx = new_aff_idx + 1 + elseif new_aff_idx > new_aff_n then + table.insert(dropped_affects, 1, old_affects[old_aff_idx]) + old_aff_idx = old_aff_idx + 1 + elseif old_affects[old_aff_idx].name == new_affects[new_aff_idx].name then + old_aff_idx = old_aff_idx + 1 + new_aff_idx = new_aff_idx + 1 + elseif old_affects[old_aff_idx].name > new_affects[new_aff_idx].name then + table.insert(added_affects, 1, new_affects[new_aff_idx]) + new_aff_idx = new_aff_idx + 1 + elseif old_affects[old_aff_idx].name < new_affects[new_aff_idx].name then + table.insert(dropped_affects, 1, old_affects[old_aff_idx]) + old_aff_idx = old_aff_idx + 1 + end + end + + return dropped_affects, added_affects +end + +function core.on_gmcp_char(event, args) + local diff_table = gmcp.Char.State.update + + local dropped = {} + local added = {} + if diff_table.affects then + if not gmcp.Char.affects then gmcp.Char.affects = {} end + dropped, added = get_dropped_and_added_affects(gmcp.Char.affects, diff_table.affects) + end + + update_state_from_diff(gmcp.Char, diff_table, "gmcp.Char") + + if table.getn(dropped) > 0 then + for i, affect in ipairs(dropped) do + raiseEvent("affectDropped", affect.name) + end + end + if table.getn(added) > 0 then + for i, affect in ipairs(added) do + raiseEvent("affectAdded", affect.name) + end + end +end + +function core.on_gmcp_char_state(event, args) + local diff_table = gmcp.Char.State.update + + local dropped = {} + local added = {} + if diff_table.affects then + if not gmcp.Char.affects then gmcp.Char.affects = {} end + dropped, added = get_dropped_and_added_affects(gmcp.Char.affects, diff_table.affects) + end + + update_state_from_diff(gmcp.Char, diff_table, "gmcp.Char") + + if table.getn(dropped) > 0 then + for i, affect in ipairs(dropped) do + raiseEvent("affectDropped", affect.name) + end + end + if table.getn(added) > 0 then + for i, affect in ipairs(added) do + raiseEvent("affectAdded", affect.name) + end + end +end + + +function core.initialize() + for _, handlerID in pairs(core.events) do + killAnonymousEventHandler(handlerID) + end + core.events.ongmcpchar = registerAnonymousEventHandler("gmcp.Char", "core.on_gmcp_char") + core.events.ongmcpcharstate = registerAnonymousEventHandler("gmcp.Char.State", "core.on_gmcp_char_state") +end + +function core.stop() + for _, handlerID in pairs(core.events) do + killAnonymousEventHandler(handlerID) + end +end + +core.initialize() \ No newline at end of file diff --git a/prs-mapper.lua b/prs-mapper.lua index 9111d4f..38d6c61 100644 --- a/prs-mapper.lua +++ b/prs-mapper.lua @@ -4,12 +4,16 @@ -- forums in the generic mapper thread with pieces from Jor'Mox's generic mapper -- script and the mmpkg mapper by breakone9r. +mudlet = mudlet or {}; mudlet.mapper_script = true + map = map or {} map.room_info = map.room_info or {} map.prev_info = map.prev_info or {} map.aliases = map.aliases or {} +map.events = map.events or {} map.configs = map.configs or {} -map.configs.speedwalk_delay = 0 +map.configs.speedwalk_delay = .2 + local defaults = { -- using Geyser to handle the mapper in this, since this is a totally new script @@ -67,66 +71,119 @@ local stubmap = { [17] = "eastup", [18] = "westdown", [19] = "westup", [20] = "eastdown", } +local reverse_dir = { + n = "s", + e = "w", + s = "n", + w = "e", + ne = "sw", + nw = "se", + se = "nw", + sw = "ne", + d = "u", + u = "d" +} + local short = {} for k,v in pairs(exitmap) do short[v] = k end +local function get_room_id_by_coordinates(area_name, x, y, z) + local result = getRoomIDbyHash(area_name..":"..x..","..y..","..z) + if result == -1 then + return nil + else + return result + end +end + local function make_room() - local info = map.room_info - local coords = {info.x,-info.y,0} - addRoom(info.vnum) - setRoomName(info.vnum, info.name) - local areas = getAreaTable() - local areaID = areas[info.area] - if not areaID then - areaID = addAreaName(info.area) - setGridMode(areaID, true) - end - setRoomArea(info.vnum, areaID) - setRoomCoordinates(info.vnum, coords[1], coords[2], coords[3]) - if terrain_types[info.terrain] then - setRoomEnv(info.vnum, terrain_types[info.terrain].id) + local info = map.room_info + local coords = {info.x,-info.y,0} + local room_id = createRoomID() + if not addRoom(room_id) then + echo("Error: call to addRoom failed.\n") + end + + setRoomIDbyHash(room_id, info.area..":"..coords[1]..","..coords[2]..","..coords[3]) + + setRoomName(room_id, info.name) + + local areas = getAreaTable() + + local area_id + if areas[info.area] == nil then + local areaID, err = addAreaName(info.area) + if areaID == nil or areaID < 1 or err then + echo("Error: new area name could not be added - error is: ".. err.."\n") + return + else + setGridMode(areaID, true) + area_id = areaID end - for dir, id in pairs(info.exits) do - -- need to see how special exits are represented to handle those properly here - if getRoomName(id) then - setExit(info.vnum, id, dir) - else - setExitStub(info.vnum, dir, true) - end + else + area_id = areas[info.area] + end + + if not area_id == getRoomArea(room_id) then + echo("Error: room area was not set successfully.\n") + end + setRoomArea(room_id, area_id) + setRoomCoordinates(room_id, coords[1], coords[2], coords[3]) + if terrain_types[info.terrain] then + setRoomEnv(room_id, terrain_types[info.terrain].id) + end + + for dir, _ in pairs(info.exits) do + setExitStub(room_id, dir, true) + + local exit_coords_delta = move_vectors[dir] + local exit_room_id = get_room_id_by_coordinates(info.area, coords[1] + exit_coords_delta[1], coords[2] + exit_coords_delta[2], coords[3] + exit_coords_delta[3]) + if exit_room_id ~= nill then + connectExitStub(room_id, dir) end + end end local function shift_room(dir) - local ID = map.room_info.vnum - local x,y,z = getRoomCoordinates(ID) - local x1,y1,z1 = table.unpack(move_vectors[dir]) - x = x + x1 - y = y + y1 - z = z + z1 - setRoomCoordinates(ID,x,y,z) - updateMap() + local ID = get_room_id_by_coordinates(map.room_info.area, map.room_info.x, -map.room_info.y, 0) + local x,y,z = getRoomCoordinates(ID) + local x1,y1,z1 = table.unpack(move_vectors[dir]) + x = x + x1 + y = y + y1 + z = z + z1 + setRoomCoordinates(ID,x,y,z) + updateMap() end local function handle_move() - local info = map.room_info - if not getRoomName(info.vnum) then - make_room() - else - local stubs = getExitStubs1(info.vnum) - if stubs then - for _, n in ipairs(stubs) do - local dir = stubmap[n] - local id = info.exits[dir] - -- need to see how special exits are represented to handle those properly here - if id and getRoomName(id) then - setExit(info.vnum, id, dir) - end - end - end + local info = map.room_info + local room_id = get_room_id_by_coordinates(info.area, info.x, -info.y, 0) + + if not room_id then + make_room() + else + local stubs = getExitStubs1(room_id) + if stubs == nil then return end + + local areas = getAreaTable() + local area_id = areas[info.area] + if area_id == nil then + echo("Error: found an existant room with an unmapped area id.\n") + return + end + + local coords = {info.x,-info.y,0} + + for _, v in pairs(stubs) do + local exit_coords_delta = move_vectors[stubmap[v]] + local exit_room_id = get_room_id_by_coordinates(info.area, coords[1] + exit_coords_delta[1], coords[2] + exit_coords_delta[2], coords[3] + exit_coords_delta[3]) + if exit_room_id ~= nill then + connectExitStub(room_id, dir) + end end - centerview(map.room_info.vnum) + end end local function config() @@ -145,7 +202,7 @@ local function config() map.aliases = {} -- making an alias to let the user shift a room around via command line table.insert(map.aliases,tempAlias([[^shift (\w+)$]],[[raiseEvent("shiftRoom",matches[2])]])) - table.insert(map.aliases,tempAlias([[^make_room$]],[[make_room()]])) + table.insert(map.aliases,tempAlias([[^make_room$]],[[make_room()]])) end local function check_doors(roomID,exits) @@ -199,14 +256,26 @@ end function map.speedwalk(roomID, walkPath, walkDirs) roomID = roomID or speedWalkPath[#speedWalkPath] - getPath(map.room_info.vnum, roomID) + local areas = getAreaTable() + local area_id = areas[gmcp.Char.room.area] + if area_id == nil then + echo("Error: could not identify the current area.\n") + return + end + local current_room_id = get_room_id_by_coordinates(gmcp.Char.room.area, gmcp.Char.room.x, -gmcp.Char.room.y, 0) + if current_room_id == nil then + echo("Error: could not find the current room in the map.\n") + return + end + + getPath(current_room_id, roomID) walkPath = speedWalkPath walkDirs = speedWalkDir if #speedWalkPath == 0 then echo("No path to chosen room found.",false,true) return end - table.insert(walkPath, 1, map.room_info.vnum) + table.insert(walkPath, 1, current_room_id) -- go through dirs to find doors that need opened, etc -- add in necessary extra commands to walkDirs table local k = 1 @@ -261,13 +330,13 @@ function map.eventHandler(event,...) if event == "gmcp.room.info" then map.prev_info = map.room_info map.room_info = { - vnum = tonumber(gmcp.room.info.num), + vnum = gmcp.room.info.num, area = gmcp.room.info.zone, x = tonumber(gmcp.room.info.x), y = tonumber(gmcp.room.info.y), name = gmcp.room.info.name, terrain = gmcp.room.info.terrain, - exits = gmcp.room.info.exits + exits = table.deepcopy(gmcp.room.info.exits) } for k,v in pairs(map.room_info.exits) do map.room_info.exits[k] = tonumber(v) @@ -285,6 +354,19 @@ function map.eventHandler(event,...) end end -registerAnonymousEventHandler("gmcp.room.info","map.eventHandler") -registerAnonymousEventHandler("shiftRoom","map.eventHandler") -registerAnonymousEventHandler("sysConnectionEvent", "map.eventHandler") \ No newline at end of file +if map.events.room_info_id then killAnonymousEventHandler(map.events.room_info_id) end -- clean up any already registered handlers for this function +map.events.room_info_id = registerAnonymousEventHandler("gmcp.room.info","map.eventHandler") +if map.events.shift_room_id then killAnonymousEventHandler(map.events.shift_room_id) end -- clean up any already registered handlers for this function +map.events.shift_room_id = registerAnonymousEventHandler("shiftRoom","map.eventHandler") +if map.events.connect_id then killAnonymousEventHandler(map.events.connect_id) end -- clean up any already registered handlers for this function +map.events.connect_id = registerAnonymousEventHandler("sysConnectionEvent", "map.eventHandler") +if map.events.centering_id then killAnonymousEventHandler(map.events.centering_id) end -- clean up any already registered handlers for this function +map.events.centering_id = registerAnonymousEventHandler("gmcp.Char.room", function(event, args) + + if gmcp.Char.room.area == "Battlefield" then return end + + local room_id = get_room_id_by_coordinates(gmcp.Char.room.area, gmcp.Char.room.x, -gmcp.Char.room.y, 0) + if room_id ~= nil then + centerview(room_id) + end +end) \ No newline at end of file diff --git a/prs-stats.lua b/prs-stats.lua index 2a6bd9d..774da05 100644 --- a/prs-stats.lua +++ b/prs-stats.lua @@ -1,4 +1,9 @@ PRSstats = PRSstats or {} +PRSstats.events = PRSstats.events or {} +PRSstats.xp = PRSstats.xp or {} +PRSstats.xp.current = PRSstats.xp.current or 50 +PRSstats.xp.tnl = PRSstats.xp.tnl or 100 + function PRSstats.stats() local SUG = require("PRS.sug") PRSstats.UW = Geyser.UserWindow:new({name = "Stats", titleText ="Vitals", x = "75%", y = "50%", height="50%", docked = true}) @@ -6,12 +11,12 @@ function PRSstats.stats() -- Hit Points Gauge HPbar = SUG:new({ name = "HP", - height = 50, + height = 25, width = "95%", -- everything up to here is standard Geyser.Gauge updateTime = 250, -- this timer will update every 250ms, or 4 times a second textTemplate = "  HP: |c / |m (|p%)", -- gauge will show "HP: 500/1000 (50%)" as the text if you had 500 current and 1000 max hp - currentVariable = "gmcp.Char.Vitals.hp", --if gmcp.Char.Vitals.hp is nil or unreachable, it will use the defaultCurrent of 50 - maxVariable = "gmcp.Char.Vitals.maxhp", --if gmcp.Char.Vitals.maxhp is nil or unreachable, it will use the defaultMax of 100 + currentVariable = "gmcp.Char.player.hp", --if gmcp.Char.Vitals.hp is nil or unreachable, it will use the defaultCurrent of 50 + maxVariable = "gmcp.Char.player.maxHp", --if gmcp.Char.Vitals.maxhp is nil or unreachable, it will use the defaultMax of 100 }, PRSstats.UW) HPbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #98f041, stop: 0.1 #8cf029, stop: 0.49 #66cc00, stop: 0.5 #52a300, stop: 1 #66cc00); border-top: 1px black solid; @@ -34,13 +39,13 @@ function PRSstats.stats() -- Energy Points Gauge ENbar = SUG:new({ name = "EN", - y = 70, - height = 50, + y = 45, + height = 25, width = "95%", updateTime = 250, textTemplate = "  EN: |c / |m (|p%)", - currentVariable = "gmcp.Char.Vitals.en", - maxVariable = "gmcp.Char.Vitals.maxen", + currentVariable = "gmcp.Char.player.energy", + maxVariable = "gmcp.Char.player.maxEnergy", }, PRSstats.UW) ENbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffffff, stop: 0.1 #eeeeee, stop: 0.49 #cccccc, stop: 0.5 #aaaaaa, stop: 1 #cccccc); border-top: 1px black solid; @@ -63,13 +68,13 @@ function PRSstats.stats() -- Stamina Points Gauge STbar = SUG:new({ name = "ST", - y = 130, - height = 50, + y = 80, + height = 25, width = "95%", updateTime = 250, textTemplate = "  ST: |c / |m (|p%)", - currentVariable = "gmcp.Char.Vitals.st", - maxVariable = "gmcp.Char.Vitals.maxst", + currentVariable = "gmcp.Char.player.stamina", + maxVariable = "gmcp.Char.player.maxStamina", }, PRSstats.UW) STbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffff50, stop: 0.1 #ffe200, stop: 0.49 #c1c100, stop: 0.5 #a4a40c, stop: 1 #c1c100); border-top: 1px black solid; @@ -90,13 +95,13 @@ function PRSstats.stats() -- Food Points Gauge HPbar = SUG:new({ name = "FP", - y = 190, + y = 115, height = 25, width = "95%", updateTime = 250, textTemplate = "  Food: |c / |m (|p%)", - currentVariable = "gmcp.Char.Vitals.food", - maxVariable = "gmcp.Char.Vitals.maxfood", + currentVariable = "gmcp.Char.player.food", + maxVariable = "gmcp.Char.player.maxFood", }, PRSstats.UW) HPbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #98f041, stop: 0.1 #8cf029, stop: 0.49 #66cc00, stop: 0.5 #52a300, stop: 1 #66cc00); border-top: 1px black solid; @@ -119,13 +124,13 @@ function PRSstats.stats() -- Rage Points Gauge RPbar = SUG:new({ name ="RP", - y = 225, + y = 150, height = 25, width = "95%", updateTime = 250, textTemplate = "  Rage: |c", - currentVariable = "gmcp.Char.Vitals.rage", - maxVariable = "gmcp.Char.Vitals.maxrage", + currentVariable = "gmcp.Char.player.rage", + maxVariable = "gmcp.Char.player.maxRage", }, PRSstats.UW) RPbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f04141, stop: 0.1 #ef2929, stop: 0.49 #cc0000, stop: 0.5 #a40000, stop: 1 #cc0000); border-top: 1px black solid; @@ -146,13 +151,13 @@ function PRSstats.stats() -- Combo Points Gauge CPbar = SUG:new({ name = "CP", - y = 260, + y = 185, height = 25, width = "95%", updateTime = 250, textTemplate = "  Combo: |c", - currentVariable = "gmcp.Char.Vitals.combo", - maxVariable = "gmcp.Char.Vitals.maxcombo", + currentVariable = "gmcp.Char.player.combo", + maxVariable = "gmcp.Char.player.maxCombo", }, PRSstats.UW) CPbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f04141, stop: 0.1 #ef2929, stop: 0.49 #cc0000, stop: 0.5 #a40000, stop: 1 #cc0000); border-top: 1px black solid; @@ -173,13 +178,13 @@ function PRSstats.stats() -- Experience Points Gauge XPbar = SUG:new({ name = "XP", - y = 295, - height = 50, + y = 220, + height = 25, width = "95%", updateTime = 250, - textTemplate = "  XP: |c / |m", - currentVariable = "gmcp.Char.Vitals.xp", - maxVariable = "gmcp.Char.Vitals.nl", + textTemplate = "  XP: |c / |m (|p%)", + currentVariable = "PRSstats.xp.current", + maxVariable = "PRSstats.xp.max" }, PRSstats.UW) XPbar.front:setStyleSheet([[background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #3399ff, stop: 0.1 #0080ff, stop: 0.49 #0000ff, stop: 0.5 #0000cc, stop: 1 #0000ff); border-top: 1px black solid; @@ -198,4 +203,20 @@ function PRSstats.stats() XPbar.text:setStyleSheet([[ font-weight: bold; ]]) -end \ No newline at end of file +end + +if PRSstats.events.xp_id then killAnonymousEventHandler(PRSstats.events.xp_id) end +PRSstats.events.xp_id = registerAnonymousEventHandler("gmcp.Char.player.xp", function() + PRSstats.xp.current = gmcp.Char.player.xp - gmcp.Char.player.xpForCurrentLevel +end) + +if PRSstats.events.xpForCurrentLevel_id then killAnonymousEventHandler(PRSstats.events.xpForCurrentLevel_id) end +PRSstats.events.xpForCurrentLevel_id = registerAnonymousEventHandler("gmcp.Char.player.xpForCurrentLevel", function() + PRSstats.xp.current = gmcp.Char.player.xp - gmcp.Char.player.xpForCurrentLevel + PRSstats.xp.max = gmcp.Char.player.xpForNextLevel - gmcp.Char.player.xpForCurrentLevel +end) + +if PRSstats.events.xpForNextLevel_id then killAnonymousEventHandler(PRSstats.events.xpForNextLevel_id) end +PRSstats.events.xpForNextLevel_id = registerAnonymousEventHandler("gmcp.Char.player.xpForNextLevel", function() + PRSstats.xp.max = gmcp.Char.player.xpForNextLevel - gmcp.Char.player.xpForCurrentLevel +end)