commit
0e25cc9a73
@ -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.
|
After Width: | Height: | Size: 998 B |
Loading…
Reference in new issue