master
Rauru 6 years ago
commit 0e25cc9a73

2
.gitignore vendored

@ -0,0 +1,2 @@
*.db
/db_backups/

@ -0,0 +1,78 @@
# Mobber
A mob database plugin for campaigns and global quests.
## Instructions
### Prerequisites
```
Mushclient version 5.06+
Aardmush r1916 snapshot+
```
### Installing
- [x] File -> Plugins -> Add -> Rauru_Mobber.xml
- [ ] File -> Plugins -> Add -> Rauru_Mobber_Miniwindow.xml (optional)
## New Installation Setup
A few commands are suggested to start on the right foot.
```
1. Enter 'mobber developer' to enable developer mode.
2. Enter 'mobber update areas' to add the default areas to mobber.
3. Enter 'mobber update rooms' to import your rooms from Aardwolf.db to mobber.
4. Enter 'mobber developer' to disable developer mode.
5. Enter 'mobber show areas' to verify areas and rooms were added.
6. Check 'mobber help' for all of the commands and to start logging mobs.
```
## Frequently Asked Questions
1. How do I upgrade to a new version of mobber?
- Download the new version of mobber.
- Drag and drop the mobber database and backups folder into the new version folder.
- Delete the old folder.
- Put the new version folder into your plugins folder.
2. Why are some mobs in cp/gq check different colors?
- Red: The mob is currently dead.
- Yellow: The area or room has not been logged.
- Gray: The matching mob or room is outside of your level range.
- Blue: A matching area was found.
- Light Blue: A matching mob or room was found within your level range.
3. Why am I getting error prompts when trying to add a mob to a room?
- You are likely trying to add a mob to a room or zone that you have not
added to the mobber database yet.
## Developer Commands - Further Explained
1. mobber backup
- Forces a manual backup of the database into /mobber/db_backups/
2. mobber vacuum
- Defragments and frees up unused space from the database.
3. mobber remove zone
- Removes the current zone and any rooms and mobs that belong to it.
- Usually 'mobber removemob zone' command is sufficient enough.
4. mobber update keywords
- Explicitly generates and adds mob keywords to the database.
- This command must be used before manually assigning keywords.
# Author
* **Rauru** - *Plugin Author*
## Acknowledgments
* Anymouse - *Tester*
* Castiel - *Tester*
* Crowley - *Tester*
* Gizmmo - *Tester*
![help](https://i.imgur.com/IywOEBw.png)
![menu1](https://i.imgur.com/AlbjeXo.png)
![menu2](https://i.imgur.com/GEf13X8.png)
![areas](https://i.imgur.com/VG0sAaZ.png)

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
name="Rauru_Mobber"
author="Rauru"
id="252b8dd0a1b4b0966f590d6f"
language="Lua"
purpose="A mob database plugin for campaigns and global quests."
save_state="y"
requires="5.06"
version="2.45"
>
<description trim="y">
<![CDATA[
]]>
</description>
</plugin>
<script>
<![CDATA[
local path = string.match(GetPluginInfo(GetPluginID(), 6), "(.*)\\.*$") .. "\\lua\\";
package.path = path .. "?;" .. path .. "?.lua;" .. package.path;
require("mobber");
]]>
</script>
</muclient>

@ -0,0 +1,772 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
name="Rauru_Mobber_Miniwindow"
author="Rauru"
id="22c840004727cceeb20f4fee"
language="Lua"
purpose="Mobber: Miniwindow."
save_state="y"
requires="5.06"
version="2.45"
>
</plugin>
<aliases>
<alias
match="^mobber btncolor (?<colour>.+?) (?:win(dow)?|mw|miniwin)$"
enabled="y"
regexp="y"
script="mobberMw.changeBtnColour"
>
</alias>
<alias
match="^mobber (?<toggle>hide|show) (?:win(dow)?|mw|miniwin)$"
enabled="y"
regexp="y"
script="mobberMw.hideShowWindow"
>
</alias>
<alias
match="^mobber reset (?:win(dow)?|mw|miniwin)$"
enabled="y"
regexp="y"
script="mobberMw.reset"
>
</alias>
</aliases>
<triggers>
<trigger
match="^You cannot take another campaign for .+?$"
name="trigger_campaign_text1"
enabled="y"
regexp="y"
script="mobberMw.setWindowMessage"
>
</trigger>
<trigger
match="^(?:You may take another campaign\.|You may take a campaign at this level\.|## You may now take another campaign\.)$"
name="trigger_campaign_text2"
enabled="y"
regexp="y"
script="mobberMw.setWindowMessage"
>
</trigger>
<trigger
match="^You will have to level before you can go on another campaign\.$"
name="trigger_campaign_text3"
enabled="y"
regexp="y"
script="mobberMw.setWindowMessage"
>
</trigger>
<trigger
match="^Campaign cleared\.$"
name="trigger_campaign_text4"
enabled="y"
regexp="y"
script="mobberMw.clear"
>
</trigger>
<trigger
match="^You are no longer part of Global Quest # \d+? and will be unable to rejoin\.$"
name="trigger_global_quest_text1"
enabled="y"
regexp="y"
script="mobberMw.clear"
>
</trigger>
<trigger
match="^Global Quest # \d+? has not yet started\.$"
name="trigger_global_quest_text2"
enabled="y"
regexp="y"
script="mobberMw.setWindowMessage"
>
</trigger>
<trigger
match="^You have already completed this global quest\.$"
name="trigger_global_quest_text3"
enabled="y"
regexp="y"
script="mobberMw.setWindowMessage"
>
</trigger>
</triggers>
<script>
<![CDATA[
----------------------------------
-- Mobber Miniwindow
----------------------------------
local pluginFolder = string.match(GetPluginInfo(GetPluginID(), 6), "(.*)\\.*$");
local path = pluginFolder .. "\\lua\\";
package.path = path .. "?.lua;" .. package.path;
require("gmcphelper");
require("mw_theme_base");
require("stringutils");
require("var");
mobberMw = {};
mobberMw.win = "mobberMw_main_" .. GetPluginID();
mobberMw.mobberPluginId = "252b8dd0a1b4b0966f590d6f";
mobberMw.isHidingButtons = var.isHidingButtons == "true" and true or false;
mobberMw.default = {};
mobberMw.default.width = 350;
mobberMw.default.height = mobberMw.isHidingButtons and 87 or 84;
---------------------------------------
-- Install movewindow
---------------------------------------
mobberMw.startx = "";
mobberMw.starty = "";
mobberMw.posx = "";
mobberMw.posy = "";
mobberMw.lastRefresh = 0;
mobberMw.MIN_WIDTH = 350;
mobberMw.MAX_WIDTH = 350;
mobberMw.MIN_HEIGHT = mobberMw.isHidingButtons and 87 or 84;
mobberMw.MAX_HEIGHT = mobberMw.isHidingButtons and 258 or 283;
mobberMw.isWindowCollapsed = var.isWindowCollapsed == "true" and true or false;
function mobberMw.preprocess()
mobberMw.windowInfo = movewindow.install(
mobberMw.win, miniwin.pos_center_all, miniwin.create_absolute_location,
false, nil,
{
mouseup = mobberMw.mouseUp, mousedown = mobberMw.leftClickOnly,
dragmove = mobberMw.leftClickOnly, dragrelease = mobberMw.leftClickOnly
},
nil
);
end
function mobberMw.mouseUp(flags, hotspotId, win)
if (bit.band(flags, miniwin.hotspot_got_rh_mouse) ~= 0) then
mobberMw.rightClickMenu();
return true;
end
if (hotspotId == "btnSelect" or tonumber(hotspotId)) then
if (mobberMw.questType == "Global Quest") then
Execute("xgq " .. (tonumber(hotspotId) or 1));
else
Execute("xcp " .. (tonumber(hotspotId) or 1));
end
elseif (hotspotId == "btnCheck") then
if (mobberMw.questType == "Global Quest") then
Execute("gq check");
else
Execute("cp check");
end
elseif (hotspotId == "btnGo") then
Execute("xgo");
elseif (hotspotId == "btnNext") then
Execute("xnext");
elseif (hotspotId == "btnPrev") then
Execute("xprev");
elseif (hotspotId == "btnKill") then
Execute("ak");
end
return false;
end
function mobberMw.mouseDown(flags, hotspotId)
if (hotspotId == "resize") then
mobberMw.startx, mobberMw.starty = WindowInfo(mobberMw.win, 17), WindowInfo(mobberMw.win, 18);
end
end
function mobberMw.mouseDrag(flags, hotspotId)
mobberMw.posx, mobberMw.posy = WindowInfo(mobberMw.win, 17), WindowInfo(mobberMw.win, 18);
mobberMw.width = mobberMw.width + mobberMw.posx - mobberMw.startx;
mobberMw.startx = mobberMw.posx;
if (mobberMw.width < mobberMw.MIN_WIDTH) then
mobberMw.width = mobberMw.MIN_WIDTH;
mobberMw.startx = mobberMw.windowInfo.window_left + mobberMw.width;
elseif (mobberMw.width > mobberMw.MAX_WIDTH) then
mobberMw.width = mobberMw.MAX_WIDTH;
mobberMw.startx = mobberMw.windowInfo.window_left + mobberMw.width;
elseif (mobberMw.windowInfo.window_left + mobberMw.width > GetInfo(281)) then
mobberMw.width = GetInfo(281) - mobberMw.windowInfo.window_left;
mobberMw.startx = GetInfo(281);
end
mobberMw.height = mobberMw.height + mobberMw.posy - mobberMw.starty;
mobberMw.starty = mobberMw.posy;
if (mobberMw.height < mobberMw.MIN_HEIGHT) then
mobberMw.height = mobberMw.MIN_HEIGHT;
mobberMw.starty = mobberMw.windowInfo.window_top + mobberMw.height;
elseif (mobberMw.height > mobberMw.MAX_HEIGHT) then
mobberMw.height = mobberMw.MAX_HEIGHT;
mobberMw.starty = mobberMw.windowInfo.window_top + mobberMw.height;
elseif (mobberMw.windowInfo.window_top + mobberMw.height > GetInfo(280)) then
mobberMw.height = GetInfo(280) - mobberMw.windowInfo.window_top;
mobberMw.starty = GetInfo(280);
end
if (utils.timer() - mobberMw.lastRefresh > 0.0333) then
mobberMw.mouseRelease();
mobberMw.lastRefresh = utils.timer();
end
end
function mobberMw.mouseRelease(flags, hotspotId)
WindowResize(mobberMw.win, mobberMw.width, mobberMw.height, Theme.PRIMARY_BODY);
mobberMw.drawContent();
end
function mobberMw.leftClickOnly(flags, hotspotId, win)
if (bit.band(flags, miniwin.hotspot_got_rh_mouse) ~= 0) then
return true;
end
return false;
end
function mobberMw.rightClickMenu()
local menustring = "Collapse/Expand|Toggle Autosize|Hide/Show Buttons|-|Bring To Front|Send To Back|-|^Bot|-|Clear Window|Reset Window";
local result = WindowMenu(
mobberMw.win,
WindowInfo(mobberMw.win, 14),
WindowInfo(mobberMw.win, 15),
menustring
);
if (result == "Collapse/Expand") then
mobberMw.collapseExpandWindow();
elseif (result == "Toggle Autosize") then
mobberMw.toggleAutosizing();
elseif (result == "Hide/Show Buttons") then
mobberMw.hideShowButtons();
elseif (result == "Clear Window") then
mobberMw.clear();
elseif (result == "Bring To Front") then
CallPlugin("462b665ecb569efbf261422f","boostMe", mobberMw.win);
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow raised.", "magenta");
elseif (result == "Send To Back") then
CallPlugin("462b665ecb569efbf261422f","dropMe", mobberMw.win);
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow lowered.", "magenta");
elseif (result == "Reset Window") then
mobberMw.reset();
end
end
---------------------
-- Create miniwindow
---------------------
mobberMw.width = var.width or mobberMw.default.width;
mobberMw.height = var.height or mobberMw.default.height;
function mobberMw.createWindow()
WindowCreate(
mobberMw.win,
mobberMw.windowInfo.window_left,
mobberMw.windowInfo.window_top,
mobberMw.width,
mobberMw.height,
mobberMw.windowInfo.window_mode, -- ignored
mobberMw.windowInfo.window_flags, -- absolute
Theme.PRIMARY_BODY
);
CallPlugin("462b665ecb569efbf261422f","boostMe", mobberMw.win);
end
------------------------
-- Load font and images
------------------------
mobberMw.font = {};
mobberMw.font.id = "mobberMwFont";
mobberMw.font.name = "Dina";
mobberMw.font.size = 6;
mobberMw.font.width = nil;
mobberMw.font.height = nil;
function mobberMw.loadFont()
local fonts = utils.getfontfamilies();
if (not fonts.Dina) then
AddFont(GetInfo (66) .. "\\Dina.fon");
fonts = utils.getfontfamilies();
end
WindowFont(
mobberMw.win,
mobberMw.font.id,
mobberMw.font.name,
mobberMw.font.size,
false, false, false, false, 0,
miniwin.font_family_modern + miniwin.font_pitch_monospaced
);
mobberMw.font.width = WindowFontInfo(mobberMw.win, mobberMw.font.id, 6);
mobberMw.font.height = WindowFontInfo(mobberMw.win, mobberMw.font.id, 1);
mobberMw.textTop = (Theme.TITLE_PADDING * 2) + mobberMw.font.height + mobberMw.topPadding;
mobberMw.textBottom = mobberMw.textTop + mobberMw.font.height;
end
function mobberMw.loadImages()
WindowLoadImage(mobberMw.win, "bgImg", pluginFolder .. "\\resources\\bg.png");
end
------------------------
-- Draw window
------------------------
mobberMw.left = nil;
mobberMw.top = nil;
mobberMw.right = nil;
mobberMw.bottom = nil;
mobberMw.textTop = nil;
mobberMw.textBottom = nil;
mobberMw.questType = nil;
mobberMw.targets = nil;
mobberMw.leftPadding = 5;
mobberMw.topPadding = mobberMw.isHidingButtons and 1 or 25;
mobberMw.btnColour = var.btnColour or ColourNameToRGB("white");
mobberMw.isWindowAutosized = var.isWindowAutosized == "true" and true or false;
mobberMw.buttons = {
{btnName = "btnKill", btnTxt = "K"},
{btnName = "btnSelect", btnTxt = "S"},
{btnName = "btnGo", btnTxt = "G"},
{btnName = "btnNext", btnTxt = "N"},
{btnName = "btnPrev", btnTxt = "P"},
};
mobberMw.targetHotspots = {};
function mobberMw.drawContent(windowMessage)
mobberMw.deleteTargetHotspots();
mobberMw.drawWindowOutline();
if (not mobberMw.isWindowCollapsed) then
mobberMw.drawBgImage();
if (mobberMw.targets and next(mobberMw.targets)) then
mobberMw.drawQuest();
mobberMw.drawWindowOutline(); -- so targets don't overlap border
else
mobberMw.drawMessage(windowMessage);
end
if (not mobberMw.isHidingButtons) then
mobberMw.drawButtons();
end
mobberMw.drawResizeTag();
end
WindowShow(mobberMw.win, true);
end
function mobberMw.drawWindowOutline()
local title = "Mobber - ";
title = title .. (mobberMw.questType or "Made by Rauru");
mobberMw.left, mobberMw.top, mobberMw.right, mobberMw.bottom = Theme.DressWindow(mobberMw.win, mobberMw.font.id, title, "center");
end
function mobberMw.drawButtons()
local left = mobberMw.leftPadding - mobberMw.left;
local top = (Theme.TITLE_PADDING * 2) + mobberMw.font.height + 1;
local bottom = mobberMw.textTop - 1;
local right = bottom - top + left;
for k,v in ipairs(mobberMw.buttons) do
mobberMw.drawButton(mobberMw.win, v.btnName, v.btnTxt, left, top, right, bottom);
right = right + 24;
left = left + 24;
end
left = mobberMw.right - (right - left);
right = mobberMw.right;
mobberMw.drawButton(mobberMw.win, "btnCheck", "C", left, top, right, bottom);
end
function mobberMw.drawButton(win, btnName, btnTxt, left, top, right, bottom)
Theme.Draw3DRect(win, left, top, right, bottom, false);
WindowAddHotspot(win, btnName, left, top, right, bottom, "", "", "", "", "mobberMw.mouseUp", "", miniwin.cursor_hand, 0);
Theme.OutlinedText(
win, mobberMw.font.id, btnTxt,
left + ((right - left - mobberMw.font.width) / 2), top + ((bottom - top - mobberMw.font.height) / 2),
right, bottom,
mobberMw.btnColour, ColourNameToRGB("black"), false, 1
);
end
function mobberMw.drawBgImage()
WindowRectOp(mobberMw.win, 2, mobberMw.left, mobberMw.top, mobberMw.right+1, mobberMw.bottom+1, Theme.PRIMARY_BODY);
WindowDrawImageAlpha(mobberMw.win, "bgImg", mobberMw.left, mobberMw.top, mobberMw.right, mobberMw.bottom, 0.25, 0, 0);
end
function mobberMw.drawQuest()
local textTop = mobberMw.textTop;
local textBottom = mobberMw.textBottom;
if (mobberMw.isWindowAutosized) then
local listBottom = mobberMw.textBottom + mobberMw.font.height * #mobberMw.targets - 7;
mobberMw.height = math.max(listBottom, mobberMw.MIN_HEIGHT);
WindowResize(mobberMw.win, mobberMw.width, mobberMw.height, Theme.PRIMARY_BODY);
end
for i = 1, #mobberMw.targets do
local target = mobberMw.targets[i];
local mobName = toPascalCase(target.name);
local zoneName = toPascalCase(target.rooms[1].zone_name);
local amount = target.amount > 1 and target.amount or " ";
local lookupType = target.lookupType;
local colour;
if (target.isDead) then
colour = ColourNameToRGB("red");
elseif (lookupType == "mobMatchInLvlRange" or lookupType == "roomMatchInLvlRange") then
colour = ColourNameToRGB("paleturquoise");
elseif (lookupType == "areaMatch") then
colour = ColourNameToRGB("deepskyblue");
elseif (lookupType == "mobMatchOutOfLvlRange" or lookupType == "roomMatchOutOfLvlRange") then
colour = ColourNameToRGB("darkgray");
else
colour = ColourNameToRGB("yellow");
mobName = zoneName;
zoneName = "Missing";
end
local targetLine = string.format("%-3.3s %-29.28s%s %-13.12s", i .. ".", mobName, amount, "(" .. zoneName .. ")");
local targetLineLength = WindowTextWidth(mobberMw.win, mobberMw.font.id, targetLine);
Theme.DrawTextBox(mobberMw.win, mobberMw.font.id, mobberMw.leftPadding, textTop, targetLine, false, true, Theme.PRIMARY_BODY, colour);
WindowAddHotspot(mobberMw.win, i, mobberMw.leftPadding, textTop, targetLineLength, textBottom, "", "", "", "", "mobberMw.mouseUp", "", miniwin.cursor_hand, 0);
table.insert(mobberMw.targetHotspots, i);
textTop = textBottom;
textBottom = textBottom + mobberMw.font.height;
end
end
function mobberMw.drawMessage(windowMessage)
if (not windowMessage) then
if (mobberMw.questType == "Campaign") then
windowMessage = "No campaign active.";
elseif (mobberMw.questType == "Global Quest") then
windowMessage = "No global quest active.";
else
windowMessage = "No campaign/global quest active.";
end
end
local textLength = WindowTextWidth(mobberMw.win, mobberMw.font.id, windowMessage);
WindowText(mobberMw.win, mobberMw.font.id, windowMessage, (mobberMw.width - textLength) / 2, mobberMw.height / 2, 0, 0, Theme.BODY_TEXT);
end
function mobberMw.drawResizeTag()
Theme.AddResizeTag(mobberMw.win, 1, nil, nil, "mobberMw.mouseDown", "mobberMw.mouseDrag", "mobberMw.mouseRelease");
end
----------------------------------
-- Utilities
----------------------------------
mobberMw.isWindowHidden = false;
function mobberMw.setWindowMessage(name, line, wildcards)
if (mobberMw.isWindowCollapsed or mobberMw.isWindowHidden) then
return;
end
local windowMessage;
if (name == "trigger_campaign_text1") then
if (mobberMw.targets and next(mobberMw.targets)) then
return;
end
local hour = tonumber(line:match("^.+ (%d+) hour.+$")) or 0;
local minute = tonumber(line:match("^.+ (%d+) minute.+$")) or 0;
local second = tonumber(line:match("^.+ (%d+) second.+$")) or 0;
AddTimer(
"mw_campaign_timer",
hour, minute, second, "echo Campaign Timer Deleted.",
timer_flag.Enabled + timer_flag.OneShot + timer_flag.Replace + timer_flag.Temporary,
""
);
EnableTimer("mw_campaign_timer_report", true);
elseif (name == "trigger_campaign_text2") then
windowMessage = "You may take another campaign.";
elseif (name == "trigger_campaign_text3") then
windowMessage = "Level up for another campaign.";
elseif (name == "trigger_global_quest_text2") then
windowMessage = "Waiting for global quest to start.";
elseif (name == "trigger_global_quest_text3") then
windowMessage = "You completed this global quest.";
end
mobberMw.drawContent(windowMessage);
end
function mobberMw.reportCampaignTimer()
if (IsTimer("mw_campaign_timer")) then
if (mobberMw.questType == "Campaign" and not mobberMw.isWindowCollapsed
and not mobberMw.isWindowHidden and (not mobberMw.targets or not next(mobberMw.targets))) then
local secondsRemaining = GetTimerInfo("mw_campaign_timer", 13);
local windowMessage;
if (secondsRemaining and math.floor(secondsRemaining) > 0) then
windowMessage = "Time Remaining: " .. formatSeconds(math.floor(secondsRemaining));
else
EnableTimer("mw_campaign_timer_report", false);
windowMessage = "You may take another campaign.";
end
mobberMw.drawContent(windowMessage);
else
EnableTimer("mw_campaign_timer_report", false);
DeleteTimer("mw_campaign_timer");
end
else
EnableTimer("mw_campaign_timer_report", false);
end
end
function mobberMw.deleteTargetHotspots()
for k,v in ipairs(mobberMw.targetHotspots) do
WindowDeleteHotspot(mobberMw.win, v);
end
mobberMw.targetHotspots = {};
end
function mobberMw.changeBtnColour(name, line, wildcards)
local colour = ColourNameToRGB(wildcards.colour);
local msg;
if (colour ~= -1) then
msg = "Button color set to: " .. wildcards.colour;
mobberMw.btnColour = colour;
else
msg = "Button color " .. wildcards.colour .. " is invalid.";
mobberMw.btnColour = ColourNameToRGB("white");
end
if (not mobberMw.isWindowHidden) then
mobberMw.drawContent("Button color changed.");
end
CallPlugin(mobberMw.mobberPluginId, "utility.print", msg, "magenta");
end
function mobberMw.hideShowWindow(name, line, wildcards)
if (wildcards.toggle == "show") then
mobberMw.isWindowHidden = false;
mobberMw.drawContent();
WindowShow(mobberMw.win, true);
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow visible.", "magenta");
else
mobberMw.isWindowHidden = true;
WindowShow(mobberMw.win, false);
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow hidden.", "magenta");
end
end
function mobberMw.hideShowButtons()
SaveState();
if (mobberMw.isHidingButtons) then
mobberMw.isHidingButtons = false;
mobberMw.default.height = 84;
mobberMw.MIN_HEIGHT = 84;
mobberMw.MAX_HEIGHT = 283;
mobberMw.topPadding = 25;
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow buttons on.", "magenta");
else
mobberMw.isHidingButtons = true;
mobberMw.default.height = 87;
mobberMw.MIN_HEIGHT = 87;
mobberMw.MAX_HEIGHT = 258;
mobberMw.topPadding = 1;
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow buttons off.", "magenta");
end
if (not mobberMw.isWindowCollapsed) then
mobberMw.width = mobberMw.default.width;
mobberMw.height = mobberMw.default.height;
end
WindowDelete(mobberMw.win);
mobberMw.preprocess();
mobberMw.createWindow();
mobberMw.loadFont();
mobberMw.loadImages();
mobberMw.drawContent();
end
function mobberMw.collapseExpandWindow()
local collapsedHeight = mobberMw.textTop - mobberMw.topPadding;
if (mobberMw.isWindowCollapsed) then
mobberMw.isWindowCollapsed = false;
mobberMw.height = mobberMw.MAX_HEIGHT;
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow expanded.", "magenta");
else
mobberMw.isWindowCollapsed = true;
mobberMw.height = collapsedHeight;
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow collapsed.", "magenta");
end
mobberMw.createWindow();
mobberMw.drawContent();
end
function mobberMw.toggleAutosizing()
if (mobberMw.isWindowAutosized) then
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow autosizing off.", "magenta");
else
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow autosizing on.", "magenta");
end
mobberMw.isWindowAutosized = not mobberMw.isWindowAutosized;
end
function mobberMw.clear()
mobberMw.questType = nil;
mobberMw.targets = nil;
mobberMw.createWindow();
if (not mobberMw.isWindowHidden) then
mobberMw.drawContent();
end
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow cleared.", "magenta");
end
function mobberMw.reset()
mobberMw.questType = nil;
mobberMw.targets = nil;
mobberMw.isWindowHidden = false;
mobberMw.isWindowAutosized = false;
mobberMw.isWindowCollapsed = false;
mobberMw.width = mobberMw.default.width;
mobberMw.height = mobberMw.default.height;
mobberMw.windowInfo.window_left = 0;
mobberMw.windowInfo.window_top = 0;
mobberMw.createWindow();
mobberMw.drawContent();
CallPlugin(mobberMw.mobberPluginId, "utility.print", "Miniwindow reset.", "magenta");
end
----------------------------------
-- Mushclient Plugin Callbacks
----------------------------------
function OnPluginInstall()
mobberMw.preprocess();
mobberMw.createWindow();
mobberMw.loadFont();
mobberMw.loadImages();
mobberMw.drawContent();
AddTimer(
"mw_campaign_timer_report",
0, 0, 1, "",
timer_flag.Temporary,
"mobberMw.reportCampaignTimer"
);
end
function OnPluginSaveState()
movewindow.save_state(mobberMw.win);
var.width = mobberMw.width;
var.height = mobberMw.height;
var.isWindowAutosized = mobberMw.isWindowAutosized;
var.isWindowCollapsed = mobberMw.isWindowCollapsed;
var.btnColour = mobberMw.btnColour;
var.isHidingButtons = mobberMw.isHidingButtons;
end
function OnPluginEnable()
WindowShow(mobberMw.win, true);
SaveState();
end
function OnPluginDisable()
WindowShow(mobberMw.win, false);
SaveState();
end
function OnPluginClose()
OnPluginDisable();
WindowDelete(mobberMw.win);
end
function OnPluginBroadcast(msg, id, name, text)
if (id == mobberMw.mobberPluginId and msg == 420 or msg == 69) then
if (mobberMw.isWindowCollapsed or mobberMw.isWindowHidden) then
return;
end
local targets = loadstring("return " .. text)();
mobberMw.targets = type(targets) == "table" and targets or nil;
if (msg == 420) then
mobberMw.questType = "Campaign";
else
mobberMw.questType = "Global Quest";
end
mobberMw.drawContent();
end
end
]]>
</script>
</muclient>

@ -0,0 +1,71 @@
Version 2.4.5:
--------------
*Miniwindow buttons will now default to on
*Autokill can now have stacked commands delimited by a double semicolon ;;
Version 2.4.4:
--------------
*Fixed a bug where mw was defaulting to collapsed (Castiel)
Version 2.4.3:
--------------
*Added 'mobber quest' command to the help file
*Added 'mobber noexp <expthreshold>' command to auto-handle noexp on campaigns
Version 2.4.2:
--------------
*Toggle ak state will now save. (Crowley/Zexe)
*Added newline to end of scan highlighting
*Merged addmob redundant functions
*Added 'mobber quest' command to show quest target/rooms (Crowley)
Version 2.4.1:
--------------
*Miniwindow fixed resize issue when autoresize was enabled and the height of window increased
*Miniwindow fixed save state of buttons enabled/disabled
Version 2.4:
--------------
*Plugin ids were changed to wipe state files for compatibility
*Mobber find commands (mf, mf <mob>, mfa <mob>) now have condensed outputs
*Added lookups for out-of-level-range campaigns
**If a mob or room match is found out-of-level-range the mob will be darkgray
**Changed lookup order for matching mobs, should be slightly more accurate
*Re-wrote miniwindow plugin for better control
**Miniwindow now has more descriptive messages for campaigns and global quest status
**Miniwindow now has a countdown timer for next campaign available
**Miniwindow now has buttons for sending cp/gq commands
**Miniwindow button colors can be changed with 'mobber btn <color> mw' command
**Miniwindow right-click options now include: collapse/expand, autoresize, hide/show btns
Version 2.3.3:
--------------
*Global quest alert sound will only play if 'mobber mute quest' is set to off. (Castiel)
Version 2.3.2:
--------------
*Fixed global quest complete triggers (Gizmmo)
*Added immhomes to default zone list and changed duplicate roomid of uplanes/lplanes
Version 2.3.1:
--------------
*Fixed quest fail message (Castiel)
Version 2.3:
------------
*Fixed an nil error in miniwin uncaught due to state files
*Changed miniwin bg
*Changed a column in area table to unique, should have no affect on existing db's
Version 2.2:
------------
*Added default target to quick-where
*Right-click popup on miniwindow targets
*Fixed miniwindow reset/hotspot deletion
Version 2.1:
------------
*Added hyperlinks to most outputs
*Added "s&d" alias names
*Added a miniwindow plugin
*Fixed level range searches

File diff suppressed because it is too large Load Diff

@ -0,0 +1,500 @@
--[[
This file contains code that that allows many different miniwindow plugins
to maintain the same UI theme, as long as they follow certain guidelines.
We can use this to get a base set of colors, standard display elements,
and title fonts that get used everywhere in order to unify the visual style
and provide a way to customize that style easily.
Steps for use: (also see https://github.com/fiendish/aardwolfclientpackage/wiki/Miniwindow-Color-Themes )
1) Inside your plugins, require this file at the very beginning of your script section.
2) Use variable names as spelled out in lua/mw_themes/Charcoal.lua for colorization within the "Theme." namespace, e.g. Theme.BODY_TEXT etc.
3) Use the shared graphics functions defined below for drawing various elements with the current color theme.
Some of the functions are not for drawing things. You can ignore them.
4) Four things make a miniwindow fit the theme: The border, the titlebar, the resize widget, and the general colors being used.
The border, titlebar, resize widget, and 3D boxes all have special draw functions included below. The colors are defined in the theme files.
4) Optional: Make your own themes (clone one of the files in lua/mw_themes and customize your colors).
5) Optional: If your plugin wants to preserve state between theme changes (changing theme reloads the plugin),
you can detect whether the plugin is closing because of a theme change with GetVariable(Theme.reloading_variable).
--]]
require "checkplugin"
require "movewindow"
dofile(GetInfo(60) .. "aardwolf_colors.lua");
module ("Theme", package.seeall)
function b9315e040989d3f81f4328d6()
-- used for theme system detection
return true
end
theme_dir = GetInfo(66).."lua\\mw_themes\\"
theme_file = "Charcoal.lua"
function get_theme()
return theme_file
end
reloading_variable = "aard_theme_just_reloading"
function just_reloading()
SetVariable(reloading_variable, 1)
end
local default_theme = {
LOGO_OPACITY = 0.02,
PRIMARY_BODY = 0x0c0c0c,
SECONDARY_BODY = 0x777777,
BODY_TEXT = 0xe8e8e8,
CLICKABLE = 0x666666,
CLICKABLE_HOVER = 0x444444,
CLICKABLE_HOT = 0x40406b,
CLICKABLE_TEXT = 0xc8c8c8,
CLICKABLE_HOVER_TEXT = 0xdddddd,
CLICKABLE_HOT_TEXT = 0xcfc5df,
TITLE_PADDING = 2,
THREE_D_HIGHLIGHT = 0xe8e8e8,
THREE_D_GRADIENT = miniwin.gradient_vertical,
THREE_D_GRADIENT_FIRST = 0xcdced1,
THREE_D_GRADIENT_SECOND = 0x8c8c8c,
THREE_D_GRADIENT_ONLY_IN_TITLE = false,
THREE_D_SOFTSHADOW = 0x606060,
THREE_D_HARDSHADOW = 0x303030,
THREE_D_SURFACE_DETAIL = 0x050505,
SCROLL_TRACK_COLOR1 = 0x444444,
SCROLL_TRACK_COLOR2 = 0x888888,
VERTICAL_TRACK_BRUSH = miniwin.brush_hatch_forwards_diagonal,
DYNAMIC_BUTTON_PADDING = 20,
RESIZER_SIZE = 16
}
function load_theme(file)
local file_loaded, data_from_file = pcall(dofile, theme_dir..file)
-- init defaults
for k,v in pairs(default_theme) do
Theme[k] = v
end
if file_loaded then
if type(data_from_file) == "table" then
for k,v in pairs(data_from_file) do
Theme[k] = v
end
theme_file = file
else
print("Error loading theme file: ", theme_dir..file)
print("This theme file is invalid. Please delete it and select a different theme.")
print()
end
else
print("Error loading theme file: ", theme_dir..file)
print(data_from_file) -- error message
end
end
-- junk files that should not have existed in the first place
local bad_shas = {
["default.lua"] = {
["d189be39efb49730537c8ada65fbd5382d3e44ad"] = true,
["39d9b7fc4d571ac62d1005b52f8da43c4b7827e1"] = true,
["774bdd2f098f1c4fe81d9a6b8eb5bfad6bd79c51"] = true,
["b7fd919be678f0c7b1c52bea644f183a7b9397f1"] = true,
["acc1d615901050b2faa50e1b0497ee30b72f5118"] = true,
["ac993b349691eb51e472ca5b1b0ae18a03943bf2"] = true,
["0cd5bcca0723a57eabedd08a9cfa6f4ec93ea469"] = true
},
["dark_pony.lua"] = {
["d189be39efb49730537c8ada65fbd5382d3e44ad"] = true
},
["Joker.lua"] = {
["17fa722746df781326b100744dd790c66721b749"] = true,
["b2b83804ce87eb889127658cb80c9d23d76082b4"] = true
}
}
require "gitsha"
function theme_has_bad_sha(filename)
return bad_shas[filename] and bad_shas[filename][gitsha(theme_dir..filename)]
end
local theme_controller_ID = "b9315e040989d3f81f4328d6"
local theme_controller_name = "aard_Theme_Controller"
if (GetPluginID() ~= theme_controller_ID) then
if not IsPluginInstalled(theme_controller_ID) then
local inner_action = [[DoAfterSpecial(0.1, 'require \'checkplugin\';do_plugin_check_now(\']]..theme_controller_ID..[[\', \']]..theme_controller_name..[[\')', sendto.script)]]
-- execute_in_global_space(inner_action)
local prefix = GetAlphaOption("script_prefix")
local action = [[
SetAlphaOption("script_prefix", "/")
Execute("/]] .. inner_action:gsub("\\", "\\\\") .. [[")
SetAlphaOption("script_prefix", "]] .. prefix:gsub("\\", "\\\\") .. [[")
]]
DoAfterSpecial(0.1, action, sendto.script)
end
local maybe_theme_file = GetPluginVariable(theme_controller_ID, "theme_file") or theme_file
if not theme_has_bad_sha(maybe_theme_file) then
theme_file = maybe_theme_file
end
load_theme(theme_file)
end
-- Replacement for WindowRectOp action 5, which allows for a 3D look while maintaining color theme.
function Draw3DRect (win, left, top, right, bottom, depressed)
local gradient = (not THREE_D_GRADIENT_ONLY_IN_TITLE or __theme_istitle) and THREE_D_GRADIENT or false
__theme_istitle = false
if right > 0 then
right = right + 1
end
if bottom > 0 then
bottom = bottom + 1
end
if gradient and (THREE_D_GRADIENT_FIRST == THREE_D_GRADIENT_SECOND) then
gradient = false
end
if (gradient == 1) or (gradient == 2) or (gradient == 3) then
WindowGradient(win, left, top, right, bottom,
THREE_D_GRADIENT_FIRST,
THREE_D_GRADIENT_SECOND,
gradient)
else
WindowRectOp(win, 2, left, top, right, bottom, THREE_D_GRADIENT_FIRST)
end
if not depressed then
WindowLine(win, left+1, top+1, right, top+1, THREE_D_HIGHLIGHT, 0x0200, 1)
WindowLine(win, left+1, top+1, left+1, bottom, THREE_D_HIGHLIGHT, 0x0200, 1)
WindowLine(win, left, bottom-2, right, bottom-2, THREE_D_SOFTSHADOW, 0x0200, 1)
WindowLine(win, right-2, top, right-2, bottom-2, THREE_D_SOFTSHADOW, 0x0200, 1)
WindowLine(win, left, bottom-1, right, bottom-1, THREE_D_HARDSHADOW, 0x0200, 1)
WindowLine(win, right-1, top, right-1, bottom-1, THREE_D_HARDSHADOW, 0x0200, 1)
else
WindowLine(win, left, top+1, right, top+1, THREE_D_HARDSHADOW, 0x0200, 1)
WindowLine(win, left+1, top, left+1, bottom, THREE_D_HARDSHADOW, 0x0200, 1)
WindowLine(win, left, top, right, top, THREE_D_HARDSHADOW, 0x0200, 1)
WindowLine(win, left, top, left, bottom, THREE_D_HARDSHADOW, 0x0200, 1)
end
end
function Draw3DTextBox(win, font, left, top, text, utf8)
local right = left + WindowTextWidth(win, font, text)
local bottom = top + TextHeight(win, font)
Draw3DRect(win, left, top, right+3, bottom+3)
WindowText(win, font, text, left+2, top+2, right+1, bottom+1, THREE_D_SURFACE_DETAIL, utf8)
return right-left
end
function DrawTextBox(win, font, left, top, text, utf8, outlined, bgcolor, textcolor)
if nil == bgcolor then
bgcolor = CLICKABLE
end
if nil == textcolor then
textcolor = CLICKABLE_TEXT
end
local right = left + WindowTextWidth(win, font, text) + 4
local bottom = top + TextHeight(win, font)
WindowRectOp(win, 2, left, top+1, right, bottom+2, bgcolor)
if outlined then
WindowRectOp(win, 1, left-1, top, right+1, bottom+3, textcolor)
end
WindowText(win, font, text, left+2, top+1, right, bottom+1, textcolor, utf8)
return right-left
end
function AddResizeTag(win, type, x1, y1, mousedown_callback, dragmove_callback, dragrelease_callback)
local x1, y1 = DrawResizeTag(win, type, x1, y1)
-- Add handler hotspots
if WindowMoveHotspot(win, "resize", x1, y1, 0, 0) ~= 0 then
WindowAddHotspot(win, "resize", x1, y1, 0, 0, nil, nil, mousedown_callback, nil, nil, "", 6, 0)
WindowDragHandler(win, "resize", dragmove_callback, dragrelease_callback, 0)
end
return x1, y1
end
function DrawResizeTag(win, type, x1, y1)
local x2, y2
if not (x1 and y1) then
x2 = WindowInfo(win, 3) - 3
y2 = WindowInfo(win, 4) - 3
x1 = x2 - RESIZER_SIZE + 1
y1 = y2 - RESIZER_SIZE + 1
else
x2 = x1 + RESIZER_SIZE
y2 = y1 + RESIZER_SIZE
end
local m = 2
local n = 2
if (type == 2) or (type == "full") then
Draw3DRect(win, x1, y1, x2, y2, false)
while (x1+m < x2 and y1+n+1 < y2) do
WindowLine(win, x1+m-1, y2-2, x2-1, y1+n-2, THREE_D_HIGHLIGHT, 0, 1)
WindowLine(win, x1+m, y2-2, x2-1, y1+n-1, THREE_D_HARDSHADOW, 0, 1)
WindowLine(win, x1+m+1, y2-2, x2-1, y1+n, THREE_D_GRADIENT_FIRST, 0, 1)
m = m+3
n = n+3
end
WindowSetPixel(win, x2-2, y2-2, THREE_D_HIGHLIGHT)
else
while (x1+m < x2 and y1+n+1 < y2) do
WindowLine(win, x1+m, y2-1, x2, y1+n-1, THREE_D_HIGHLIGHT, 0, 1)
WindowLine(win, x1+m+1, y2-1, x2, y1+n, THREE_D_HARDSHADOW, 0, 1)
WindowLine(win, x1+m+2, y2-1, x2, y1+n+1, THREE_D_GRADIENT_FIRST, 0, 1)
m = m+3
n = n+3
end
WindowLine(win, x1, y2, x2, y2, THREE_D_HARDSHADOW, 0, 1)
WindowLine(win, x2, y1, x2, y2+1, THREE_D_HARDSHADOW, 0, 1)
WindowSetPixel(win, x2-1, y2-1, THREE_D_HIGHLIGHT)
end
return x1, y1
end
function TextHeight(win, font)
return WindowFontInfo(win, font, 1)
end
-- title_alignment can be "left", "right", or "center" (the default)
function DressWindow(win, font, title, title_alignment)
local l, t, r, b = DrawBorder(win)
if title and (title ~= "") then
t = DrawTitleBar(win, font, title, title_alignment)
if t > 1 then
movewindow.add_drag_handler(win, 0, 0, 0, t)
end
end
return l, t, r, b
end
function DrawTitleBar(win, font, title, text_alignment)
local title_lines
if type(title) == "string" then
title_lines = utils.split(title, "\n")
else
title_lines = title
end
local line_height = 0
local title_height = 0
if font and title_lines then
line_height = TextHeight(win, font)
if line_height == nil then
return (2*TITLE_PADDING)
end
title_height = (2*TITLE_PADDING) + (line_height * #title_lines)
end
__theme_istitle = true
Draw3DRect(
win,
-1,
-1,
WindowInfo(win, 3)-1,
title_height,
false
)
local first_color = nil
local txt = nil
for i,v in ipairs(title_lines) do
if type(v) == "table" then
txt = strip_colours_from_styles(v)
else
txt = v
end
local width = WindowTextWidth(win, font, txt)
local text_left = (WindowInfo(win, 3) - width) / 2 -- default text align center
if text_alignment == "left" then
text_left = TITLE_PADDING + 2
elseif text_alignment == "right" then
text_left = WindowInfo(win, 3) - width - TITLE_PADDING
end
local text_right = math.min(text_left + width, WindowInfo(win, 3) - TITLE_PADDING - 2)
local text_top = (line_height * (i-1)) + TITLE_PADDING
if type(v) == "string" then
WindowText(win, font, v, text_left, text_top, text_right, title_height, THREE_D_SURFACE_DETAIL)
else
-- The colors of all styles matching the first style color get stripped out and replaced with the default title color
for i,w in ipairs(v) do
first_color = first_color or w.textcolour
if w.textcolour == first_color then
w.textcolour = THREE_D_SURFACE_DETAIL
end
end
WindowTextFromStyles(win, font, v, text_left, text_top, text_right, title_height, true)
end
end
return title_height
end
function DrawBorder(win)
local r = WindowInfo(win, 3)-3
local b = WindowInfo(win, 4)-3
WindowRectOp(win, 1, 0, 0, 0, 0, THREE_D_HIGHLIGHT)
WindowRectOp(win, 1, 1, 1, -1, -1, THREE_D_SOFTSHADOW)
return 2, 2, r, b
end
function OutlinedText(win, font, text, startx, starty, endx, endy, color, outline_color, utf8, thickness)
if thickness == nil then
thickness = 1
end
if outline_color == nil then
outline_color = THREE_D_HARDSHADOW
end
local right = nil
for xi = -thickness,thickness do
for yi = -thickness,thickness do
right = WindowText(win, font, text, startx+xi, starty+yi, endx+1, endy+1, outline_color, utf8)
end
end
local right = WindowText(win, font, text, startx+1, starty+1, endx, endy, outline_color, utf8)
WindowText(win, font, text, startx, starty, endx, endy, color, utf8)
return right
end
function WindowTextFromStyles(win, font, styles, left, top, right, bottom, utf8)
for i,v in ipairs(styles) do
left = left + WindowText(win, font, v.text, left, top, right, bottom, v.textcolour or BODY_TEXT, utf8)
end
return left
end
-- text with a black outline
function OutlinedTextFromStyles(win, font, styles, startx, starty, endx, endy, outline_color, utf8, thickness)
if thickness == nil then
thickness = 1
end
if outline_color == nil then
outline_color = THREE_D_HARDSHADOW
end
local text = strip_colours_from_styles(styles)
local right = nil
for xi = -thickness,thickness do
for yi = -thickness,thickness do
right = WindowText(win, font, text, startx+xi, starty+yi, endx+1, endy+1, outline_color, utf8)
end
end
WindowTextFromStyles(win, font, styles, startx, starty, endx, endy, utf8)
return right
end
-- Based on mw.lua's popup function, but with theme colors
function Popup(win, -- window name to use
font_id, -- font to use for each body line
info, -- table of lines to show (plain text or styles)
left, top, -- preferred location
stay_left_of, -- guidance for keeping the popup visible
stay_right_of) -- guidance for keeping the popup visible
local BORDER_WIDTH = 2
assert(WindowInfo (win, 1), "Window " .. win .. " must already exist")
assert(WindowFontInfo (win, font_id, 1), "No font " .. font_id .. " in " .. win)
local font_height = WindowFontInfo (win, font_id, 1)
local font_leading = WindowFontInfo (win, font_id, 4) + WindowFontInfo (win, font_id, 5)
-- find text width - minus colour codes
local infowidth = 0
local infoheight = 0
-- calculate remaining width and height
for _, v in ipairs (info) do
if type(v) == "table" then
txt = strip_colours_from_styles(v)
else
txt = strip_colours(v)
end
infowidth = math.max (infowidth, WindowTextWidth (win, font_id, txt))
infoheight = infoheight + font_height
end -- for
infowidth = infowidth + (2 * BORDER_WIDTH) + -- leave room for border
WindowFontInfo (win, font_id, 6) -- one character width extra
infoheight = infoheight + (2 * BORDER_WIDTH) + -- leave room for border
font_leading + -- plus leading below bottom line,
10 -- and 5 pixels top and bottom
-- if align_right then
-- left = left - infowidth
-- end -- if align_right
-- if align_bottom then
-- top = top - infoheight
-- end -- if align_bottom
top = math.min(top, GetInfo(280) - infoheight)
top = math.max(0, top)
if left < stay_left_of then
if left+infowidth > stay_left_of then
left = stay_left_of - infowidth
end
if left < 0 then
left = stay_right_of
end
else
if left < stay_right_of then
left = stay_right_of
end
if (left + infowidth) > GetInfo(281) then
left = stay_left_of - infowidth
end
end
WindowCreate(win,
left, top, -- where
infowidth, -- width (gap of 5 pixels per side)
infoheight, -- height
miniwin.pos_top_left, -- position mode: can't be 0 to 3
miniwin.create_absolute_location + miniwin.create_transparent,
SECONDARY_BODY)
WindowCircleOp(win, miniwin.circle_round_rectangle,
BORDER_WIDTH, BORDER_WIDTH, -BORDER_WIDTH, -BORDER_WIDTH, -- border inset
THREE_D_HIGHLIGHT, miniwin.pen_solid, BORDER_WIDTH, -- line
PRIMARY_BODY, miniwin.brush_solid, -- fill
5, 5) -- diameter of ellipse
local x = BORDER_WIDTH + WindowFontInfo (win, font_id, 6) / 2 -- start 1/2 character in
local y = BORDER_WIDTH + 5 -- skip border, and leave 5 pixel gap
-- show each line
for _, line in ipairs(info) do
if type(line) == "string" then
WindowText(win, font_id, line, x, y, 0, 0, BODY_TEXT)
else
WindowTextFromStyles(win, font_id, line, x, y, 0, 0)
end
y = y + font_height
end -- for
-- display popup window
WindowShow(win, true)
end -- popup

@ -0,0 +1,177 @@
----------------------------------
-- sqlite3 Helper Module
----------------------------------
sqlitedb = {
path = GetPluginInfo(GetPluginID(), 20),
name = "sqlite.db",
db = nil
};
function sqlitedb:new(db)
db = db or {};
setmetatable(db, self);
self.__index = self;
return db;
end
function sqlitedb:open()
if (self.db == nil or not self.db:isopen()) then
self.db = assert(sqlite3.open(self.path .. self.name));
self:pragma();
end
end
function sqlitedb:close()
if (self.db) then
if (self.db:isopen()) then
local code = self.db:close();
self:check(code);
end
self.db = nil;
end
end
function sqlitedb:check(code)
if (code ~= sqlite3.OK and
code ~= sqlite3.ROW and
code ~= sqlite3.DONE) then
local err = self.db:errmsg();
self.db:execute("ROLLBACK;");
error("REPORT THIS ERROR TO RAURU:\r\n" .. err, 2);
end
end
function sqlitedb:exec(sql, checkCode, callback)
assert(sqlite3.complete(sql), "Not an SQL statement: " .. sql);
local code = self.db:execute(sql, callback);
if (checkCode) then
self:check(code);
end
return code;
end
function sqlitedb:pragma()
self:exec("PRAGMA foreign_keys=ON;", true);
end
function sqlitedb:gettable(sql)
assert(sqlite3.complete(sql), "Not an SQL statement:\r\n" .. sql);
local results = {};
for row in self.db:nrows(sql) do
table.insert(results, row);
end
return results;
end
function sqlitedb:changes()
return self.db:changes();
end
function sqlitedb:backup()
self:open();
local isDatabaseBackedUp;
print("\r\n\r\nPERFORMING", self.name, "DATABASE BACKUP.");
print("CHECKING INTEGRITY...");
BroadcastPlugin(999, "repaint");
local integrityCheck = true;
self:exec("PRAGMA wal_checkpoint;", true);
for row in self.db:nrows("PRAGMA integrity_check;") do
if (row.integrity_check ~= "ok") then
integrityCheck = false;
end
end
self:close();
if (not integrityCheck) then
isDatabaseBackedUp = false;
print("INTEGRITY CHECK FAILED. CLOSE MUSHCLIENT AND RESTORE A KNOWN GOOD DATABASE.");
print("BACKUP ABORTED.\r\n\r\n");
else
print("INTEGRITY CHECK PASSED.");
BroadcastPlugin (999, "repaint");
local backupDir = self.path .. "db_backups\\";
local makeDirCmd = "mkdir " .. addQuotes(backupDir);
os.execute(makeDirCmd);
local copyCmd = "copy /Y " .. addQuotes(self.path .. self.name) .. " " .. addQuotes(backupDir .. self.name .. "." .. "backup");
os.execute(copyCmd);
print("FINISHED DATABASE BACKUP.\r\n\r\n");
self:open();
isDatabaseBackedUp = true;
end
return isDatabaseBackedUp;
end
function sqlitedb:vacuum()
local fileSizeBefore;
local fileSizeAfter;
self:open();
local file = io.open(self.path .. self.name);
fileSizeBefore = file and fsize(file) or nil;
print("BEGIN VACUUM ON", self.name);
local code = self:exec("VACUUM;");
if (code ~= sqlite3.OK) then
print("END VACUUM ON", self.name);
local err = self.db:errmsg();
self.db:execute("ROLLBACK;");
file:close();
error(err);
else
fileSizeAfter = file and fsize(file) or nil;
file:close();
if (fileSizeBefore and fileSizeAfter) then
fileSizeBefore = fileSizeBefore / 1024;
fileSizeAfter = fileSizeAfter / 1024;
local fileSizeDif = fileSizeBefore - fileSizeAfter;
print("DISK SPACE RECOVERED: " .. fileSizeDif .. " KB");
end
print("END VACUUM ON", self.name);
end
end
function fsize(file)
local current = file:seek(); -- get current position
local size = file:seek("end"); -- get file size
file:seek("set", current); -- restore position
return size;
end
function addQuotes(str)
return "\"" .. str .. "\"";
end

@ -0,0 +1,113 @@
--[[
String Functions
functions in this module
fixsql - usage: fixsql(sql, likeOperator)
change quotes to double single quotes
stripColors - usage: stripColors(str)
remove color codes from string
toPascalCase - usage toPascalCase(str)
captialize the beginning of each word
formatSeconds - usage formatSeconds(seconds)
convert seconds to hrs mins secs
--]]
function fixsql(sql, likeOperator)
if (sql) then
if (likeOperator) then
if (likeOperator == "left") then
sql = "'%" .. string.gsub(sql, "'", "''") .. "'";
elseif (likeOperator == "right") then
sql = "'" .. string.gsub(sql, "'", "''") .. "%'";
else
sql = "'%" .. string.gsub(sql, "'", "''") .. "%'";
end
else
sql = "'" .. string.gsub(sql, "'", "''") .. "'";
end
else
sql = "NULL;"
end
return sql;
end
function stripColors(str)
str = str:gsub("@@", "\0"); -- change @@ to 0x00
str = str:gsub("@%-", "~"); -- fix tildes (historical)
str = str:gsub("@x%d?%d?%d?", ""); -- strip valid and invalid xterm color codes
str = str:gsub("@.([^@]*)", "%1"); -- strip normal color codes and hidden garbage
return (str:gsub("%z", "@")); -- put @ back (has parentheses on purpose)
end
function toPascalCase(str)
str = string.gsub(str, "(%a)([%w_']*)",
function (first, rest)
return first:upper()..rest:lower();
end
);
return str;
end
function wrap(line, length)
local lines = {};
length = length or 10;
while (#line > length) do
-- find a space not followed by a space, or a , closest to the end of the line
local col = string.find(line:sub(1, length), "[%s,][^%s,]*$");
if (col and col > 2) then
-- col = col - 1 -- use the space to indent
else
col = length -- just cut off at wrap_column
end -- if
table.insert(lines, line:sub(1, col));
line = line:sub(col + 1);
end
table.insert(lines, line);
return lines;
end
function formatSeconds(seconds)
if (not tonumber(seconds)) then
return seconds;
end
if (seconds < 1) then
return string.format("%.2fs", seconds);
end
local hours = math.floor(seconds / 3600);
seconds = seconds % 3600
local mins = math.floor(seconds / 60);
seconds = math.floor(seconds % 60);
local duration = "";
if (hours > 0) then
duration = hours .. "h ";
end
if (mins > 0) then
duration = duration .. mins .. "m ";
end
if (seconds > 0) then
duration = duration .. seconds .. "s";
end
return duration;
end

@ -0,0 +1,5 @@
Customize Background
--------------------
Replace the bg.png image with a png of your own, same file name.
Image should be at least 350 x 270px.

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Loading…
Cancel
Save