Initial Release. Version 1.1.
This commit is contained in:
commit
cefa04983e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
docs/*
|
||||||
|
build/*
|
47
.woodpecker.yml
Normal file
47
.woodpecker.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
steps:
|
||||||
|
- name: build-BadPVP
|
||||||
|
image: git.vertinext.com/roryejinn/alpine_muddler
|
||||||
|
when:
|
||||||
|
- path:
|
||||||
|
include: [ 'src/*' ]
|
||||||
|
event: [push, pull_request]
|
||||||
|
environment:
|
||||||
|
GITEATKN:
|
||||||
|
from_secret: GITEA_TOKEN
|
||||||
|
VERSION: 1.1
|
||||||
|
commands:
|
||||||
|
- apk add tea
|
||||||
|
- cd BadPVP
|
||||||
|
- java -jar /muddle/muddle.jar
|
||||||
|
- tea login add --name=temp --url=https://git.vertinext.com/ --token=$GITEATKN
|
||||||
|
- tea releases create --asset "build/BadPVP.mpackage" --asset "build/BadPVP.xml" --tag $VERSION --title "Latest Compiled Release"
|
||||||
|
|
||||||
|
- name: notify-Failure
|
||||||
|
image: codeberg.org/l-x/woodpecker-ntfy
|
||||||
|
when:
|
||||||
|
- status: [ failure ]
|
||||||
|
event: [push, pull_request]
|
||||||
|
settings:
|
||||||
|
url: https://gotify.vertinext.com/woodpecker
|
||||||
|
token:
|
||||||
|
from_secret: GOTIFY_TOKEN
|
||||||
|
title: ${CI} - ${CI_REPO_NAME}
|
||||||
|
actions: "http, Open Build, ${CI_PIPELINE_URL}, clear=true"
|
||||||
|
tags: warning
|
||||||
|
icon: https://woodpecker-ci.org/img/logo.svg
|
||||||
|
message: Failed to build Achaea Plugins
|
||||||
|
|
||||||
|
- name: notify-Success
|
||||||
|
image: codeberg.org/l-x/woodpecker-ntfy
|
||||||
|
when:
|
||||||
|
- status: [ success ]
|
||||||
|
event: [push, pull_request]
|
||||||
|
settings:
|
||||||
|
url: https://gotify.vertinext.com/woodpecker
|
||||||
|
token:
|
||||||
|
from_secret: GOTIFY_TOKEN
|
||||||
|
title: ${CI} - ${CI_REPO_NAME}
|
||||||
|
actions: "http, Open Build, ${CI_PIPELINE_URL}, clear=true"
|
||||||
|
tags: white_check_mark
|
||||||
|
icon: https://woodpecker-ci.org/img/logo.svg
|
||||||
|
message: Built new release packages for Achaea Plugins
|
7
LICENSE
Normal file
7
LICENSE
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2024 Charles Click
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
README.md
Normal file
23
README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# BadPVP
|
||||||
|
BadPVP is an extension of the [Fighter](https://git.vertinext.com/roryejinn/Fighter) combat system made for Achaea. This system includes target management and group combat features and is made to work alongside Atul's Group Combat Package. It can technically work alongside any other Group Combat Package as well as long as it follows the same formatting method as Atul's package.
|
||||||
|
|
||||||
|
## Atul's Formatting Expectations
|
||||||
|
In order for BadPVP to properly handle group combat and target calling, Targets must be called using these formats. On both formats, it does not matter if they feature a trailing period. The system works the same way regardless.
|
||||||
|
|
||||||
|
### Single Target
|
||||||
|
```
|
||||||
|
(Party) Name Say/Says, "Target: Name"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Targets
|
||||||
|
```
|
||||||
|
(Party) Name Say/Says, "I have # targets: Name, Name, Name, Name"
|
||||||
|
```
|
||||||
|
|
||||||
|
The number is important here. We use that to validate the presented data. It must match the number of names.
|
||||||
|
|
||||||
|
## Adding New Classes
|
||||||
|
If you want to add new Class Engines, please see the readme for [Fighter](https://git.vertinext.com/roryejinn/Fighter), the Combat Engine being used in this system. This readme has detailed instructions for adding your own Class Engines and using them in combat.
|
||||||
|
|
||||||
|
## Curing Systems Support
|
||||||
|
Currently we only support Legacy, however work is being made on an Orion port.
|
6
mfile
Normal file
6
mfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"package": "BadPVP",
|
||||||
|
"version": "1.1",
|
||||||
|
"description": "A pvp package that is made to work alongside Atul's Group Combat Scripts. It includes target management.",
|
||||||
|
"author": "Charles Click"
|
||||||
|
}
|
34
src/aliases/BadPVP/addLeader.lua
Normal file
34
src/aliases/BadPVP/addLeader.lua
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
local operation = matches[2]
|
||||||
|
local name = matches[3]
|
||||||
|
local ndbData = nil
|
||||||
|
local cityColor = nil
|
||||||
|
local ourCity = string.split(gmcp.Char.Status.city, " ")[1]
|
||||||
|
|
||||||
|
if table.contains(Legacy.NDB.db, name) then
|
||||||
|
ndbData = Legacy.NDB.db[name]
|
||||||
|
cityColor = Legacy.NDB.color(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if ndbData == nil then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Ignoring request to add " .. name .. " to your group leaders because they aren't in the NDB. Add " .. name .. " first and try again.")
|
||||||
|
elseif operation == "+" then
|
||||||
|
table.insert(badpvp.groups.leaders, name)
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Added " .. cityColor .. name .. "<firebrick> to your list of group leaders.")
|
||||||
|
if ndbData.city ~= ourCity then cecho("\n<firebrick>COMBAT NOTICE: Did you really mean to add " .. cityColor .. name .. "<firebrick> from " .. cityColor .. ndbData.city .. "<firebrick> to your group leaders?") end
|
||||||
|
else
|
||||||
|
local isInTable = table.index_of(badpvp.groups.leaders, name)
|
||||||
|
if isInTable == nil then
|
||||||
|
if ndbData ~= nil then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: " .. cityColor .. name .. "<firebrick> isn't a group leader.")
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: " .. name .. " isn't a group leader.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.remove(badpvp.groups.leaders, isInTable)
|
||||||
|
if ndbData ~= nil then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Removed " .. cityColor .. name .. "<firebrick> from list of group leaders.")
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Removed " .. name .. " from list of group leaders.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
32
src/aliases/BadPVP/aliases.json
Normal file
32
src/aliases/BadPVP/aliases.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "callAction",
|
||||||
|
"isActive": "yes",
|
||||||
|
"regex": "^pvp$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nextTarget",
|
||||||
|
"isActive": "yes",
|
||||||
|
"regex": "^pvpnt$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "resetSystem",
|
||||||
|
"isActive": "yes",
|
||||||
|
"regex": "^pvpreset$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addLeader",
|
||||||
|
"isActive": "yes",
|
||||||
|
"regex": "^pvpleaders (-|\\+)(\\w+)$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "listLeaders",
|
||||||
|
"isActive": "yes",
|
||||||
|
"regex": "^pvpleaders$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "displayHelp",
|
||||||
|
"isActive": "yes",
|
||||||
|
"regex": "^pvphelp$"
|
||||||
|
}
|
||||||
|
]
|
7
src/aliases/BadPVP/callAction.lua
Normal file
7
src/aliases/BadPVP/callAction.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
local ourClass = gmcp.Char.Status.class
|
||||||
|
|
||||||
|
if table.contains(badpvp.classes.implemented, ourClass) then
|
||||||
|
badpvp.classes.implementations[ourClass].decideAction()
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: No implementation for " .. ourClass)
|
||||||
|
end
|
1
src/aliases/BadPVP/displayHelp.lua
Normal file
1
src/aliases/BadPVP/displayHelp.lua
Normal file
@ -0,0 +1 @@
|
|||||||
|
badpvp.generateHelp()
|
42
src/aliases/BadPVP/listLeaders.lua
Normal file
42
src/aliases/BadPVP/listLeaders.lua
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
local tablemaker = require("BadPVP.MDK.ftext").TableMaker
|
||||||
|
|
||||||
|
leaderTable = tablemaker:new({
|
||||||
|
title = "Group Leaders",
|
||||||
|
printTitle = true,
|
||||||
|
printHeaders = false,
|
||||||
|
frameColor = "<steel_blue>",
|
||||||
|
separateRows = false,
|
||||||
|
titleColor = "<white>"
|
||||||
|
})
|
||||||
|
leaderTable:addColumn({
|
||||||
|
name = "Name",
|
||||||
|
width = 40
|
||||||
|
})
|
||||||
|
leaderTable:addColumn({
|
||||||
|
name = "City",
|
||||||
|
width = 40
|
||||||
|
})
|
||||||
|
|
||||||
|
for k, v in pairs(badpvp.groups.leaders) do
|
||||||
|
local getCity = nil
|
||||||
|
local cityColor = nil
|
||||||
|
|
||||||
|
if table.contains(Legacy.NDB.db, v) then
|
||||||
|
getCity = Legacy.NDB.db[v].city
|
||||||
|
cityColor = Legacy.NDB.color(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
if getCity then
|
||||||
|
leaderTable:addRow({
|
||||||
|
cityColor .. v .. "<reset>",
|
||||||
|
cityColor .. getCity .. "<reset>"
|
||||||
|
})
|
||||||
|
else
|
||||||
|
leaderTable:addRow({
|
||||||
|
v,
|
||||||
|
getCity or "Not in NDB"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cecho(leaderTable:assemble())
|
30
src/aliases/BadPVP/nextTarget.lua
Normal file
30
src/aliases/BadPVP/nextTarget.lua
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
local ourIndex = badpvp.groups.targetIndex
|
||||||
|
|
||||||
|
if #badpvp.groups.targetList == 0 then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Target List is empty.")
|
||||||
|
return
|
||||||
|
elseif (ourIndex + 1) > #badpvp.groups.targetList then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Reached end of target list. Rolling back to first entry.")
|
||||||
|
ourIndex = 1
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Moving to next target in list.")
|
||||||
|
ourIndex = ourIndex + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local findTarget = true
|
||||||
|
while findTarget do
|
||||||
|
local ourTarget = badpvp.groups.targetList[ourIndex]
|
||||||
|
if table.contains(badpvp.groups.deadTargets, ourTarget) then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Target was listed as dead so skipping one time.")
|
||||||
|
table.remove(badpvp.groups.deadTargets, table.index_of(badpvp.groups.deadTargets, ourTarget))
|
||||||
|
ourIndex = ourIndex + 1
|
||||||
|
elseif badpvp.groups.targetVisible and not table.contains(Legacy.Room.players, ourTarget) then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Set to only target visible enemies and we cannot see " .. ourTarget .. ". Skipping.")
|
||||||
|
ourIndex = ourIndex + 1
|
||||||
|
else
|
||||||
|
findTarget = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
badpvp.groups.targetIndex = ourIndex
|
||||||
|
expandAlias("st " .. badpvp.groups.targetList[ourIndex])
|
2
src/aliases/BadPVP/resetSystem.lua
Normal file
2
src/aliases/BadPVP/resetSystem.lua
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Hot reloading BadPVP.")
|
||||||
|
badpvp.Setup()
|
48
src/resources/MDK/LICENSE.lua
Normal file
48
src/resources/MDK/LICENSE.lua
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
--[===[
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020 Damian Monogue
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
--]===]
|
||||||
|
|
||||||
|
-- schema validation provided by schema.lua, license below
|
||||||
|
--[[
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Sebastian Schoener
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
]]
|
91
src/resources/MDK/README.md
Normal file
91
src/resources/MDK/README.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# the demonnic MDK and You
|
||||||
|
|
||||||
|
This is a collection of Lua 'classes' and modules I wrote for Mudlet. It is largely targeted at scripters, and comes packaged in two ways depending on how you intend to use/distribute your work. Please see [Installation](#installation) for more details
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The [MDK wiki](https://github.com/demonnic/MDK/wiki) contains an entry for each module or class, as well as examples.
|
||||||
|
|
||||||
|
Starting with alpha2 of the MDK, the ldocs generated from code are included in the zipped releases. The current release's ldocs can always be viewed at <https://demonnic.github.io/mdk/current/>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
How you 'install' the MDK depends on how you intend to use it.
|
||||||
|
|
||||||
|
### I just want to install the MDK for my own personal use
|
||||||
|
|
||||||
|
You just want to get your hands on the goods, and aren't looking to use any MDK items in an exported package for sharing or anything like that.
|
||||||
|
Well, you are who the mdk mpackage is for! Download the MDK.mpackage from your desired release on the [Releases](https://github.com/demonnic/MDK/releases) page and install it in the package manager. The examples in the [wiki](https://demonnic.github.io/mdk/current/) are written with this in mind, and you would require the items you need as `local EMCO = require("MDK.emco")`
|
||||||
|
|
||||||
|
### I am a package author looking to include/use one of the MDK modules or classes in my package
|
||||||
|
|
||||||
|
You should download the `demonnic-MDK-<version>.zip` file for your desired release on the [Releases](https://github.com/demonnic/MDK/releases) page.
|
||||||
|
Inside are the individual .lua files for the modules and classes described in the [wiki](https://demonnic.github.io/mdk/current/) and [API docs](https://demonnic.github.io/mdk/current/).
|
||||||
|
You can include all of them if you wish, or only the ones you actually make use of. I ask that you include the LICENSE.lua or LICENSE-MDK.lua file (depending on the release) file in addition.
|
||||||
|
They should go in the root of your package, so that when your package is installed the files can be found at `getMudletHomeDir() .. "/<packagename>/emco.lua"`. You would then use `local EMCO = require("<mypackagename>.emco")`
|
||||||
|
So for example if your package name is "MySuperCoolPackage" and it installs to `getMudletHomeDir() .. "/MySuperCoolPackage/"` then you use `local EMCO = require("MySuperCoolPackage.emco")` and the emco.lua file should be at `getMudletHomeDir() .. "/MySuperCoolPackage/emco.lua"`
|
||||||
|
|
||||||
|
## Files (Modules/Classes)
|
||||||
|
|
||||||
|
These files contain the modules in the MDK. You only need to include those files which you intend to use, except as noted in the descriptions below.
|
||||||
|
If you include any of the modules from the MDK, you should also include LICENSE.lua or LICENSE-MDK.lua. It contains the licenses for my modules and for luaunit and lua-schema which are not my original works.
|
||||||
|
You should maybe also include demontools.lua, as it notes below several other of the MDK modules make use of items within it.
|
||||||
|
|
||||||
|
* aliasmgr.ua
|
||||||
|
* Object to manage tempAliases programmatically. <https://github.com/demonnic/MDK/wiki/AliasMgr>
|
||||||
|
|
||||||
|
* chyron.lua
|
||||||
|
* Label which moves a message across its face from right to left, like a stock ticker or the news chyrons. Documentation at <https://github.com/demonnic/MDK/wiki/Chyron>
|
||||||
|
|
||||||
|
* demontools.lua
|
||||||
|
* Collection of miscellaneous useful functions. You should include this file if you use the MDK, as several other modules make use of it. Include functions for converting c/d/hecho, html, and ansi colored strings between each other, mkdir_p, and some others. <https://github.com/demonnic/MDK/wiki/DemonTools>
|
||||||
|
|
||||||
|
* emco.lua
|
||||||
|
* EMCO. Documentation at <https://github.com/demonnic/MDK/wiki/EMCO> Will make use of LoggingConsole if loggingconsole.lua and demontools.lua are included
|
||||||
|
|
||||||
|
* figlet.lua
|
||||||
|
* Creates FIGlets from strings
|
||||||
|
* Reference package with multiple fonts and color gradients at <https://github.com/demonnic/figinator>
|
||||||
|
|
||||||
|
* ftext.lua
|
||||||
|
* basic fText. Documentation at <https://github.com/demonnic/MDK/wiki/fText>
|
||||||
|
* now includes TextFormatter and TableMaker as ftext.TextFormatter and ftext.TableMaker
|
||||||
|
|
||||||
|
* gradientmaker.lua
|
||||||
|
* Functions for creating color gradients for use with c/d/hecho. Documentation at <https://github.com/demonnic/MDK/wiki/GradientMaker>
|
||||||
|
|
||||||
|
* loggingconsole.lua
|
||||||
|
* Self logging extension to the mini console. Works just like a Geyser.MiniConsole but adds a templated path and fileName constraint, as well as logFormat so it can log what is echod or appended to it. Requires demontools.lua in order to work.
|
||||||
|
|
||||||
|
* loginator.lua
|
||||||
|
* Creates objects for logging messages to disk. <https://github.com/demonnic/MDK/wiki/Loginator>
|
||||||
|
|
||||||
|
* mastermindsolver.lua
|
||||||
|
* A class which will help you solve Master Mind puzzles. <https://github.com/demonnic/MDK/wiki/MasterMindSolver>
|
||||||
|
|
||||||
|
* revisionator.lua
|
||||||
|
* A class which aims to make upgrading between package versions easier by storing and running patch functions. <https://github.com/demonnic/MDK/wiki/Revisionator>
|
||||||
|
|
||||||
|
* sortbox.lua
|
||||||
|
* SortBox, an alternative to H/VBox which can be either, and also provides options for sorting its contents. Overview at <https://github.com/demonnic/MDK/wiki/SortBox>
|
||||||
|
|
||||||
|
* spinbox.lua
|
||||||
|
* SpinBox, a Geyser element for adjusting numbers with your mouse. Overview at <https://github.com/demonnic/MDK/wiki/SpinBox>
|
||||||
|
|
||||||
|
* sug.lua
|
||||||
|
* Self Updating Gauges, will watch a set of variables and update itself on a timer based on what values those variables hold. Documentation at <https://github.com/demonnic/MDK/wiki/SelfUpdatingGauge>
|
||||||
|
|
||||||
|
* textgauge.lua
|
||||||
|
* TextGauges, what it says on the tin. Documentation at <https://github.com/demonnic/MDK/wiki/TextGauge>
|
||||||
|
|
||||||
|
* timergauge.lua
|
||||||
|
* TimerGauge, an extension of Geyser.Gauge which serves as an animated countdown timer. Overview at <https://github.com/demonnic/MDK/wiki/TimerGauge>
|
||||||
|
|
||||||
|
## Others people's work I depend upon
|
||||||
|
|
||||||
|
* schema.lua
|
||||||
|
* lua-schema, for defining table schema. Documentation at <https://github.com/sschoener/lua-schema>
|
||||||
|
* will be used by Archon for ensuring configuration tables are as they should be.
|
||||||
|
|
||||||
|
* LICENSE.lua
|
||||||
|
* Contains the license information for MDK, as well as lua-schema and luaunit which have been included.
|
1697
src/resources/MDK/ftext.lua
Normal file
1697
src/resources/MDK/ftext.lua
Normal file
File diff suppressed because it is too large
Load Diff
447
src/resources/MDK/ftext_spec.lua
Normal file
447
src/resources/MDK/ftext_spec.lua
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
local ftext = require("MDK.ftext")
|
||||||
|
|
||||||
|
describe("ftext:", function()
|
||||||
|
describe("ftext.fText:", function()
|
||||||
|
local fText = ftext.fText
|
||||||
|
|
||||||
|
it("Should properly center text", function()
|
||||||
|
local expected = " some text "
|
||||||
|
local actual = fText("some text", {width = 20})
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should properly pad left aligned text", function()
|
||||||
|
local expected = "some text "
|
||||||
|
local actual = fText("some text", {width = 20, alignment = "left"})
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should properly pad right aligned text", function()
|
||||||
|
local expected = " some text"
|
||||||
|
local actual = fText("some text", {width = 20, alignment = "right"})
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should wrap lines to the correct length", function()
|
||||||
|
local str = "This is a test of the emergency broadcast system. This is only a test"
|
||||||
|
local options = {width = 10, alignment = "centered"}
|
||||||
|
local actual = fText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(line:len(), 10)
|
||||||
|
end
|
||||||
|
options.width = 15
|
||||||
|
actual = fText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(line:len(), 15)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("non-space spacer character:", function()
|
||||||
|
local str = "some text"
|
||||||
|
local options = {width = "20", alignment = "left", spacer = "="}
|
||||||
|
it("Should work with left align", function()
|
||||||
|
local expected = "some text =========="
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should work with right align", function()
|
||||||
|
local expected = "========== some text"
|
||||||
|
options.alignment = "right"
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should work with center align", function()
|
||||||
|
local expected = ("==== some text =====")
|
||||||
|
options.alignment = "center"
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("nogap option:", function()
|
||||||
|
local str = "some text"
|
||||||
|
local options = {width = "20", alignment = "left", spacer = "=", nogap = true}
|
||||||
|
|
||||||
|
it("Should work with left align", function()
|
||||||
|
local expected = "some text==========="
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should work with right align", function()
|
||||||
|
local expected = "===========some text"
|
||||||
|
options.alignment = "right"
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should work with center align", function()
|
||||||
|
local expected = "=====some text======"
|
||||||
|
options.alignment = "center"
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("cap functionality", function()
|
||||||
|
local str = "some text"
|
||||||
|
local options = {width = 20, spacer = "=", cap = "|"}
|
||||||
|
|
||||||
|
it("Should place the spacer outside the cap by default", function()
|
||||||
|
local expected = "===| some text |===="
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should place it inside the cap if inside option is true", function()
|
||||||
|
local expected = "|=== some text ====|"
|
||||||
|
options.inside = true
|
||||||
|
local actual = fText(str, options)
|
||||||
|
options.inside = nil
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(20, actual:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should mirror certain characters with their opposites", function()
|
||||||
|
local expected = "===[ some text ]===="
|
||||||
|
options.mirror = true
|
||||||
|
options.cap = "["
|
||||||
|
local actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
options.inside = true
|
||||||
|
expected = "[=== some text ====]"
|
||||||
|
actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
options.inside = nil
|
||||||
|
options.cap = "<"
|
||||||
|
expected = "===< some text >===="
|
||||||
|
actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
options.cap = "{"
|
||||||
|
expected = "==={ some text }===="
|
||||||
|
actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
options.cap = "("
|
||||||
|
expected = "===( some text )===="
|
||||||
|
actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
options.cap = "|"
|
||||||
|
expected = "===| some text |===="
|
||||||
|
actual = fText(str, options)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("ftext.cfText", function()
|
||||||
|
local cfText = ftext.cfText
|
||||||
|
local str = "some text"
|
||||||
|
local options = {
|
||||||
|
width = 20,
|
||||||
|
spacer = "=",
|
||||||
|
cap = "[",
|
||||||
|
inside = true,
|
||||||
|
mirror = true,
|
||||||
|
capColor = "<purple>",
|
||||||
|
spacerColor = "<green>",
|
||||||
|
textColor = "<red>",
|
||||||
|
}
|
||||||
|
it("Should handle cecho colored text", function()
|
||||||
|
local expectedStripped = "[=== some text ====]"
|
||||||
|
local expected = "<purple>[<reset><green>===<reset><red> some text <reset><green>====<reset><purple>]<reset>"
|
||||||
|
local actual = cfText(str, options)
|
||||||
|
local actualStripped = cecho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
expectedStripped = "===[ some text ]===="
|
||||||
|
expected = "<green>===<reset><purple>[<reset><red> some text <reset><purple>]<reset><green>====<reset>"
|
||||||
|
options.inside = false
|
||||||
|
actual = cfText(str, options)
|
||||||
|
actualStripped = cecho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should wrap cecho lines to the correct length", function()
|
||||||
|
local str = "This is a test of the emergency broadcast system. This is only a test"
|
||||||
|
local options = {width = 10, alignment = "centered"}
|
||||||
|
local actual = cfText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(cecho2string(line):len(), 10)
|
||||||
|
end
|
||||||
|
options.width = 15
|
||||||
|
actual = cfText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(cecho2string(line):len(), 15)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("ftext.dfText", function()
|
||||||
|
local dfText = ftext.dfText
|
||||||
|
local str = "some text"
|
||||||
|
local options = {
|
||||||
|
width = 20,
|
||||||
|
spacer = "=",
|
||||||
|
cap = "[",
|
||||||
|
inside = true,
|
||||||
|
mirror = true,
|
||||||
|
capColor = "<160,32,240>",
|
||||||
|
spacerColor = "<0,255,0>",
|
||||||
|
textColor = "<255,0,0>",
|
||||||
|
}
|
||||||
|
it("Should handle decho colored text", function()
|
||||||
|
local expectedStripped = "[=== some text ====]"
|
||||||
|
local expected = "<160,32,240>[<r><0,255,0>===<r><255,0,0> some text <r><0,255,0>====<r><160,32,240>]<r>"
|
||||||
|
local actual = dfText(str, options)
|
||||||
|
local actualStripped = decho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
expectedStripped = "===[ some text ]===="
|
||||||
|
expected = "<0,255,0>===<r><160,32,240>[<r><255,0,0> some text <r><160,32,240>]<r><0,255,0>====<r>"
|
||||||
|
options.inside = false
|
||||||
|
actual = dfText(str, options)
|
||||||
|
actualStripped = decho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should wrap decho lines to the correct length", function()
|
||||||
|
local str = "This is a test of the emergency broadcast system. This is only a test"
|
||||||
|
local options = {width = 10, alignment = "centered"}
|
||||||
|
local actual = dfText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(decho2string(line):len(), 10)
|
||||||
|
end
|
||||||
|
options.width = 15
|
||||||
|
actual = dfText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(decho2string(line):len(), 15)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("ftext.hfText", function()
|
||||||
|
local hfText = ftext.hfText
|
||||||
|
local str = "some text"
|
||||||
|
local options = {
|
||||||
|
width = 20,
|
||||||
|
spacer = "=",
|
||||||
|
cap = "[",
|
||||||
|
inside = true,
|
||||||
|
mirror = true,
|
||||||
|
capColor = "#a020f0",
|
||||||
|
spacerColor = "#00ff00",
|
||||||
|
textColor = "#ff0000",
|
||||||
|
}
|
||||||
|
it("Should handle hecho colored text", function()
|
||||||
|
local expectedStripped = "[=== some text ====]"
|
||||||
|
local expected = "#a020f0[#r#00ff00===#r#ff0000 some text #r#00ff00====#r#a020f0]#r"
|
||||||
|
local actual = hfText(str, options)
|
||||||
|
local actualStripped = hecho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
expectedStripped = "===[ some text ]===="
|
||||||
|
expected = "#00ff00===#r#a020f0[#r#ff0000 some text #r#a020f0]#r#00ff00====#r"
|
||||||
|
options.inside = false
|
||||||
|
actual = hfText(str, options)
|
||||||
|
actualStripped = hecho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should wrap hecho lines to the correct length", function()
|
||||||
|
local str = "This is a test of the emergency broadcast system. This is only a test"
|
||||||
|
local options = {width = 10, alignment = "centered"}
|
||||||
|
local actual = hfText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(hecho2string(line):len(), 10)
|
||||||
|
end
|
||||||
|
options.width = 15
|
||||||
|
actual = hfText(str, options)
|
||||||
|
for _, line in ipairs(actual:split("\n")) do
|
||||||
|
assert.equals(hecho2string(line):len(), 15)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("ftext.TextFormatter", function()
|
||||||
|
local tf = ftext.TextFormatter
|
||||||
|
local str = "some text"
|
||||||
|
local formatter
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
formatter = tf:new({width = 20})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should let you change width using :setWidth", function()
|
||||||
|
formatter:setWidth(80)
|
||||||
|
local expected =
|
||||||
|
"<white><reset><white> <reset><white> some text <reset><white> <reset><white><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(80, cecho2string(actual):len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should format for cecho by default", function()
|
||||||
|
local expected = "<white><reset><white> <reset><white> some text <reset><white> <reset><white><reset>"
|
||||||
|
local expectedStripped = " some text "
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
local actualStripped = cecho2string(actual)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
assert.equals(expectedStripped, actualStripped)
|
||||||
|
assert.equals(20, actualStripped:len())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should produce the same line as cfText given the same options", function()
|
||||||
|
local expected = ftext.cfText(str, formatter.options)
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should let you change type using :setType", function()
|
||||||
|
formatter:setType("h")
|
||||||
|
local expected = ftext.hfText(str, formatter.options)
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
formatter:setType("d")
|
||||||
|
expected = ftext.dfText(str, formatter.options)
|
||||||
|
actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
formatter:setType("")
|
||||||
|
expected = ftext.fText(str, formatter.options)
|
||||||
|
actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should default to word wrapping, and let you change it with :setWrap", function()
|
||||||
|
formatter:setWidth(10)
|
||||||
|
local expected =
|
||||||
|
"<white><reset><white> <reset><white> some <reset><white> <reset><white><reset>\n<white><reset><white> <reset><white> text <reset><white> <reset><white><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
expected = "<white><reset><white><reset><white> some text <reset><white><reset><white><reset>"
|
||||||
|
formatter:setWrap(false)
|
||||||
|
actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the cap using :setCap", function()
|
||||||
|
formatter:setCap('|')
|
||||||
|
local expected = "<white>|<reset><white> <reset><white> some text <reset><white> <reset><white>|<reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the capColor using :setCapColor", function()
|
||||||
|
formatter:setCapColor('<red>')
|
||||||
|
local expected = "<red><reset><white> <reset><white> some text <reset><white> <reset><red><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the spacer color using :setSpacerColor", function()
|
||||||
|
formatter:setSpacerColor("<red>")
|
||||||
|
local expected = "<white><reset><red> <reset><white> some text <reset><red> <reset><white><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the text color using :setTextColor", function()
|
||||||
|
formatter:setTextColor("<red>")
|
||||||
|
local expected = "<white><reset><white> <reset><red> some text <reset><white> <reset><white><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the spacer using :setSpacer", function()
|
||||||
|
formatter:setSpacer("=")
|
||||||
|
-- local expected = "<white><reset><white> <reset><white> some text <reset><white> <reset><white><reset>"
|
||||||
|
local expected = "<white><reset><white>====<reset><white> some text <reset><white>=====<reset><white><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to set the alignment using :setAlignment", function()
|
||||||
|
formatter:setAlignment("left")
|
||||||
|
local expected = "<white><reset><white><reset><white>some text <reset><white> <reset><white><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
formatter:setAlignment("right")
|
||||||
|
expected = "<white><reset><white> <reset><white> some text<reset><white><reset><white><reset>"
|
||||||
|
actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the 'inside' option using :setInside", function()
|
||||||
|
formatter:setInside(false)
|
||||||
|
local expected = "<white> <reset><white><reset><white> some text <reset><white><reset><white> <reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should allow you to change the mirror option using :setMirror", function()
|
||||||
|
formatter:setCap('<')
|
||||||
|
formatter:setMirror(true)
|
||||||
|
local expected = "<white><<reset><white> <reset><white> some text <reset><white> <reset><white>><reset>"
|
||||||
|
local actual = formatter:format(str)
|
||||||
|
assert.equal(expected, actual)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("ftext.TableMaker", function()
|
||||||
|
local TableMaker = ftext.TableMaker
|
||||||
|
local tm
|
||||||
|
before_each(function()
|
||||||
|
tm = TableMaker:new()
|
||||||
|
tm:addColumn({name = "col1", width = 15, textColor = "<red>"})
|
||||||
|
tm:addColumn({name = "col2", width = 15, textColor = "<blue>"})
|
||||||
|
tm:addColumn({name = "col3", width = 15, textColor = "<green>"})
|
||||||
|
tm:addRow({"some text", "more text", "other text"})
|
||||||
|
tm:addRow({"little text", "bigger text", "text"})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Should assemble a formatted table given default options", function()
|
||||||
|
local expected = [[<white>*************************************************<reset>
|
||||||
|
<white>*<reset><white><reset><white> <reset><red> col1 <reset><white> <reset><white><reset><white>|<reset><white><reset><white> <reset><blue> col2 <reset><white> <reset><white><reset><white>|<reset><white><reset><white> <reset><green> col3 <reset><white> <reset><white><reset><white>*<reset>
|
||||||
|
<white>*<reset><white>---------------<reset><white>|<reset><white>---------------<reset><white>|<reset><white>---------------<reset><white>*<reset>
|
||||||
|
<white>*<reset><white><reset><white> <reset><red> some text <reset><white> <reset><white><reset><white>|<reset><white><reset><white> <reset><blue> more text <reset><white> <reset><white><reset><white>|<reset><white><reset><white> <reset><green> other text <reset><white> <reset><white><reset><white>*<reset>
|
||||||
|
<white>*<reset><white>---------------<reset><white>|<reset><white>---------------<reset><white>|<reset><white>---------------<reset><white>*<reset>
|
||||||
|
<white>*<reset><white><reset><white> <reset><red> little text <reset><white> <reset><white><reset><white>|<reset><white><reset><white> <reset><blue> bigger text <reset><white> <reset><white><reset><white>|<reset><white><reset><white> <reset><green> text <reset><white> <reset><white><reset><white>*<reset>
|
||||||
|
<white>*************************************************<reset>
|
||||||
|
]]
|
||||||
|
local actual = tm:assemble()
|
||||||
|
assert.equals(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("TableMaker:getCell should return the text and formatter for a specific cell", function()
|
||||||
|
local expectedText = "more text"
|
||||||
|
local expectedFormatter = tm.columns[2]
|
||||||
|
local actualText, actualFormatter = tm:getCell(1, 2)
|
||||||
|
assert.equals(expectedText, actualText)
|
||||||
|
assert.equals(expectedFormatter, actualFormatter)
|
||||||
|
local expectedFormatted = "<white><reset><white> <reset><blue> more text <reset><white> <reset><white><reset>"
|
||||||
|
local actualFormatted = actualFormatter:format(actualText)
|
||||||
|
assert.equals(expectedFormatted, actualFormatted)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
1
src/resources/MDK/mdkversion.txt
Normal file
1
src/resources/MDK/mdkversion.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
2.10.0
|
200
src/scripts/BadPVP/alchemist.lua
Normal file
200
src/scripts/BadPVP/alchemist.lua
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
-- The alchemist implementation variables
|
||||||
|
alchemist = alchemist or {}
|
||||||
|
alchemist.specialty = nil
|
||||||
|
alchemist.balances = alchemist.balances or {}
|
||||||
|
alchemist.balances.homunculus = true
|
||||||
|
alchemist.balances.humor = true
|
||||||
|
alchemist.humors = alchemist.humors or {}
|
||||||
|
alchemist.humors.table = {
|
||||||
|
"Sanguine",
|
||||||
|
"Sanguine",
|
||||||
|
"Choleric",
|
||||||
|
"Sanguine",
|
||||||
|
"Melancholic",
|
||||||
|
"Sanguine",
|
||||||
|
"Phlegmatic"
|
||||||
|
}
|
||||||
|
alchemist.humors.index = 1
|
||||||
|
alchemist.wrackCount = 0
|
||||||
|
alchemist.inundate = false
|
||||||
|
|
||||||
|
-- The primary function to build pvp actions for the alchemist class
|
||||||
|
-- NOTE: This script was written for a Sublimation user and primarily pushes keeping class specific weaknesses and using bleeding/aurify as a killpath
|
||||||
|
function alchemist.decideAction()
|
||||||
|
if gmcp.Char.Status.class ~= "Alchemist" then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Called Alchemist decision engine when not a Alchemist.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not target or target == nil or target == "None" or target == "Dude" then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Called decision engine without a target.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set alchemist specialty if blank
|
||||||
|
if gmcp.Char.Status.class == "Alchemist" and alchemist.specialty == nil then
|
||||||
|
alchemist.setSpecialty()
|
||||||
|
end
|
||||||
|
|
||||||
|
local commandString = { "setalias murder stand" }
|
||||||
|
local affsReport = {}
|
||||||
|
local enemyClass = nil
|
||||||
|
local humors = {
|
||||||
|
Choleric = ak.alchemist.humour.choleric or 0,
|
||||||
|
Melancholic = ak.alchemist.humour.melancholic or 0,
|
||||||
|
Phlegmatic = ak.alchemist.humour.phlegmatic or 0,
|
||||||
|
Sanguine = ak.alchemist.humour.sanguine or 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if table.contains(Legacy.CT.Enemies, target) then
|
||||||
|
enemyClass = Legacy.CT.Enemies[target]
|
||||||
|
elseif table.contains(Legacy.NDB.db, target) then
|
||||||
|
enemyClass = Legacy.NDB.db[target].class
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Order: Homunculus Attack, Temper/Inundate, Educe/Wrack OR Truewrack <- That or a Killpath
|
||||||
|
-- Killpath Reave: At least two tempered targets, can attempt to reave (not used)
|
||||||
|
-- Killpath aurify: If HP/MP both < 60%, turn target to gold
|
||||||
|
if (tonumber(ak.manapercent) < 60) and (tonumber(ak.healthpercent) < 60) then
|
||||||
|
-- Unleash the Aurify!
|
||||||
|
-- This one should process immediately
|
||||||
|
table.insert(commandString, "aurify " .. target)
|
||||||
|
send(table.concat(commandString, "/"))
|
||||||
|
send("queue addclearfull eqbal murder")
|
||||||
|
send("pt Starting Aurify attempt on " .. target)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Homunculus - Homunculus Balance (Skip if not on balance)
|
||||||
|
-- homunculus attack target (do damage) (3 seconds)
|
||||||
|
-- homunculus shriek target (remove focus balance) (12 seconds) (unused)
|
||||||
|
-- homunculus corrupt target (flips bleed/clot. Bleed now causes mana loss, and clotting causes damage) (12 seconds)
|
||||||
|
if alchemist.balances.homunculus then
|
||||||
|
local hpOver = tonumber(ak.healthpercent) > 60
|
||||||
|
local mpOver = tonumber(ak.manapercent) > 60
|
||||||
|
if (hpOver and not mpOver) or (mpOver and not hpOver) then
|
||||||
|
-- One resource is high, the other is low. Corrupt to flip usage.
|
||||||
|
table.insert(commandString, "homunculus corrupt " .. target)
|
||||||
|
table.insert(affsReport, "homunculus corruption")
|
||||||
|
alchemist.balances.homunculus = false
|
||||||
|
tempTimer(12, function() alchemist.resetBalance("homunculus") end)
|
||||||
|
else
|
||||||
|
-- Just attack
|
||||||
|
table.insert(commandString, "homunculus attack " .. target)
|
||||||
|
alchemist.balances.homunculus = false
|
||||||
|
tempTimer(3, function() alchemist.resetBalance("homunculus") end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Temper
|
||||||
|
-- temper target humour (1.7 seconds Humor)
|
||||||
|
-- inundate target humor (1.7 seconds humor)
|
||||||
|
-- This will rotate through the alchemist.humors.table and temper those humors in order until it is time to inundate
|
||||||
|
if humors.Sanguine > 2 then
|
||||||
|
-- push inundate at 3+ sanguine to push bleeding
|
||||||
|
table.insert(commandString, "inundate " .. target .. " sanguine")
|
||||||
|
table.insert(affsReport, "bleeding")
|
||||||
|
tempTimer(1.7, function() alchemist.resetBalance("humor") end)
|
||||||
|
alchemist.inundate = true
|
||||||
|
elseif humors.Phlegmatic >= 4 then
|
||||||
|
-- For some reason, they allowed us to push phlegmatic very high so inundate this for extra affs
|
||||||
|
table.insert(commandString, "inundate " .. target .. " phlegmatic")
|
||||||
|
table.insert(affsReport, "lethargy")
|
||||||
|
if humors.Phlegmatic >= 4 then table.insert(affsReport, "anorexia") end
|
||||||
|
if humors.Phlegmatic >= 6 then table.insert(affsReport, "slickness") end
|
||||||
|
if humors.Phlegmatic >= 8 then table.insert(affsReport, "weariness") end
|
||||||
|
tempTimer(1.7, function() alchemist.resetBalance("humor") end)
|
||||||
|
else
|
||||||
|
-- Temper whatever humor we have
|
||||||
|
local ourHumor = alchemist.humors.table[alchemist.humors.index]
|
||||||
|
table.insert(commandString, "temper " .. target .. " " .. ourHumor)
|
||||||
|
table.insert(affsReport, "tempered" .. ourHumor .. "(" .. tostring(humors[ourHumor] + 1) .. ")")
|
||||||
|
alchemist.humors.index = alchemist.humors.index + 1
|
||||||
|
if alchemist.humors.index > #alchemist.humors.table then alchemist.humors.index = 1 end
|
||||||
|
tempTimer(1.7, function() alchemist.resetBalance("humor") end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Educe
|
||||||
|
local hpPercent = ((gmcp.Char.Vitals.hp / gmcp.Char.Vitals.maxhp) * 100)
|
||||||
|
if alchemist.wrackCount ~= 0 and alchemist.wrackCount % 3 ~= 0 then
|
||||||
|
-- Educe here as we aren't truewracking
|
||||||
|
if ak.defs.shield then
|
||||||
|
-- Break Shield
|
||||||
|
table.insert(commandString, "educe copper " .. target)
|
||||||
|
elseif alchemist.inundate then
|
||||||
|
-- We inundated so we want to educe iron to push damage
|
||||||
|
-- We should only not do that when the target is shielded
|
||||||
|
table.insert(commandString, "educe iron" .. target)
|
||||||
|
alchemist.inundate = false
|
||||||
|
elseif hpPercent <= 40 then
|
||||||
|
-- Educe tin to give us a break
|
||||||
|
table.insert(commandString, "educe tin")
|
||||||
|
else
|
||||||
|
-- Just do damage
|
||||||
|
table.insert(commandString, "educe iron " .. target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wrack
|
||||||
|
-- wrack target humor/affliction (2 seconds Bal)
|
||||||
|
-- truewrack target humor/affliction humor/affliction (2.8 Seconds Bal)
|
||||||
|
if alchemist.wrackCount ~= 0 and alchemist.wrackCount % 3 ~= 0 then
|
||||||
|
-- Simple Wrack
|
||||||
|
table.insert(commandString, "wrack " .. target .. " impatience")
|
||||||
|
table.insert(affsReport, "impatience")
|
||||||
|
else
|
||||||
|
-- Truewrack
|
||||||
|
local ourSecondAff = nil
|
||||||
|
if table.contains(badpvp.classes.clumsiness, enemyClass) then ourSecondAff = "clumsiness" end
|
||||||
|
if table.contains(badpvp.classes.weariness, enemyClass) then ourSecondAff = "weariness" end
|
||||||
|
if table.contains(badpvp.classes.stupidity, enemyClass) then ourSecondAff = "stupidity" end
|
||||||
|
if table.contains(badpvp.classes.haemophilia, enemyClass) then ourSecondAff = "haemophilia" end
|
||||||
|
if ourSecondAff == nil then ourSecondAff = "haemophilia" end
|
||||||
|
table.insert(commandString, "truewrack " .. target .. " impatience " .. ourSecondAff)
|
||||||
|
table.insert(affsReport, "impatience")
|
||||||
|
table.insert(affsReport, ourSecondAff)
|
||||||
|
end
|
||||||
|
alchemist.wrackCount = alchemist.wrackCount + 1
|
||||||
|
|
||||||
|
-- Send final command, Report afflictions
|
||||||
|
send(table.concat(commandString, "/"))
|
||||||
|
send("queue addclearfull eqbal murder")
|
||||||
|
send("pt " .. target .. " hit with " .. table.concat(affsReport, " "))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Functioned used to reset special class balances for Alchemist
|
||||||
|
function alchemist.resetBalance(which)
|
||||||
|
if which == "homunculus" then
|
||||||
|
alchemist.balances.homunculus = true
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Homunculus balance restored.")
|
||||||
|
elseif which == "humor" then
|
||||||
|
alchemist.balances.humor = true
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Humor balance restored.")
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: We were asked to reset the Alchemist balance " .. which .. " but that isn't a valid balance.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- A function that sets our alchemy specialty so we can refer to this much faster later
|
||||||
|
function alchemist.setSpecialty()
|
||||||
|
local ourSpecialty = nil
|
||||||
|
for k, v in pairs(gmcp.Char.Skills.Groups) do
|
||||||
|
if v.name == "Sublimation" then
|
||||||
|
ourSpecialty = "Sublimation"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if v.name == "Formulation" then
|
||||||
|
ourSpecialty = "Formulation"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ourSpecialty == nil then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: No alchemy specialty found. If you have not yet embraced your class or you are not currently an Alchemist this is normal. Otherwise, this is an error.")
|
||||||
|
else
|
||||||
|
alchemist.specialty = ourSpecialty
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Set alchemy specialty to " .. ourSpecialty)
|
||||||
|
end
|
||||||
|
end
|
238
src/scripts/BadPVP/badpvp.lua
Normal file
238
src/scripts/BadPVP/badpvp.lua
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
badpvp = badpvp or {}
|
||||||
|
|
||||||
|
function badpvp.Setup()
|
||||||
|
badpvp.classes = {}
|
||||||
|
|
||||||
|
-- To add a new class, put the name here
|
||||||
|
badpvp.classes.implemented = {
|
||||||
|
"Psion",
|
||||||
|
"Alchemist"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Then in here, set the class name from above equal to the main object in that classes pvp script
|
||||||
|
-- Important Note: You always need a name.decideAction function as this is called to generate pvp actions
|
||||||
|
badpvp.classes.implementations = {
|
||||||
|
Psion = psion,
|
||||||
|
Alchemist = alchemist
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Classes where we want to push stupidity
|
||||||
|
badpvp.classes.stupidity = {
|
||||||
|
"Alchemist"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Classes where we want to push clumsiness
|
||||||
|
badpvp.classes.clumsiness = {
|
||||||
|
"Blademaster",
|
||||||
|
"Druid",
|
||||||
|
"Infernal",
|
||||||
|
"Monk",
|
||||||
|
"Paladin",
|
||||||
|
"Runewarden",
|
||||||
|
"Priest",
|
||||||
|
"Sentinel",
|
||||||
|
"Serpent",
|
||||||
|
"Shaman",
|
||||||
|
"Psion",
|
||||||
|
"Depthswalker"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- classes where we want to push weariness
|
||||||
|
badpvp.classes.weariness = {
|
||||||
|
"Blademaster",
|
||||||
|
"Druid",
|
||||||
|
"Monk",
|
||||||
|
"Priest",
|
||||||
|
"Pariah",
|
||||||
|
"Occultist",
|
||||||
|
"Sentinel",
|
||||||
|
"Serpent"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- classes where we want to push confusion
|
||||||
|
badpvp.classes.confusion = {
|
||||||
|
"Psion"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- classes where we want to push haemophilia
|
||||||
|
badpvp.classes.haemophilia = {
|
||||||
|
"Magi"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Don't change this section
|
||||||
|
badpvp.groupsSetup()
|
||||||
|
-- End Don't change
|
||||||
|
|
||||||
|
-- Here is an example of adding additional setup for your pvp systems
|
||||||
|
-- This will set the variable alchemist.specialty to Sublimation or Formulation depending
|
||||||
|
alchemist.setSpecialty()
|
||||||
|
end
|
||||||
|
|
||||||
|
registerAnonymousEventHandler("sysLoadEvent", "badpvp.Setup")
|
||||||
|
|
||||||
|
function badpvp.groupsSetup()
|
||||||
|
badpvp.groups = {}
|
||||||
|
|
||||||
|
badpvp.groups.overrideAllies = false
|
||||||
|
|
||||||
|
badpvp.groups.leaders = {
|
||||||
|
"Bulwer",
|
||||||
|
"Nezaya",
|
||||||
|
"Belaziel"
|
||||||
|
}
|
||||||
|
|
||||||
|
badpvp.groups.trustedCity = "Hashan"
|
||||||
|
|
||||||
|
badpvp.groups.allies = {
|
||||||
|
"Cyrene"
|
||||||
|
}
|
||||||
|
|
||||||
|
badpvp.groups.alwaysTarget = {
|
||||||
|
"Archaeon"
|
||||||
|
}
|
||||||
|
|
||||||
|
badpvp.groups.targetList = {}
|
||||||
|
|
||||||
|
badpvp.groups.targetIndex = 1
|
||||||
|
|
||||||
|
badpvp.groups.deadTargets = {}
|
||||||
|
|
||||||
|
badpvp.groups.targetVisible = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function badpvp.verifyInstall(_, name)
|
||||||
|
if name ~= "BadPVP" then return end
|
||||||
|
badpvp.Setup()
|
||||||
|
end
|
||||||
|
|
||||||
|
registerAnonymousEventHandler("sysInstall", "badpvp.verifyInstall")
|
||||||
|
|
||||||
|
function badpvp.verifyUninstall(_, name)
|
||||||
|
if name ~= "BadPVP" then return end
|
||||||
|
badpvp = nil
|
||||||
|
alchemist = nil
|
||||||
|
psion = nil
|
||||||
|
end
|
||||||
|
registerAnonymousEventHandler("sysUninstall", "badpvp.verifyUninstall")
|
||||||
|
|
||||||
|
function badpvp.verifyTarget(name)
|
||||||
|
if table.contains(Legacy.NDB.db, name) then
|
||||||
|
if table.contains(badpvp.groups.alwaysTarget, name) or table.contains(Legacy.CT.Enemies, name) then
|
||||||
|
-- Always target enemies or those listed in the override table
|
||||||
|
return true
|
||||||
|
elseif Legacy.NDB.db[name].city == badpvp.groups.trustedCity then
|
||||||
|
-- Always ignore targetting anyone from Hashan
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Ignoring target call for " .. name .. " as they are Hashani.")
|
||||||
|
return false
|
||||||
|
elseif table.contains(badpvp.groups.allies, Legacy.NDB.db[name].city) and not badpvp.groups.overrideAllies then
|
||||||
|
-- Don't target those from an allied city unless overrideAllies is true
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Ignoring target call for " .. name .. " as they are from an allied city.")
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Ignoring target call for " .. name .. " as they don't exist in NDB.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function badpvp.verifyCaller(name)
|
||||||
|
return table.contains(badpvp.groups.leaders, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function badpvp.processTargetList(numberOfTargets, listOfTargets)
|
||||||
|
local ourTable = string.split(string.trim(listOfTargets), ", ")
|
||||||
|
if numberOfTargets ~= tostring(#ourTable) then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Ignoring target list as it has more targets than expected")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
send("unenemy all")
|
||||||
|
|
||||||
|
badpvp.groups.targetList = {}
|
||||||
|
badpvp.groups.deadTargets = {}
|
||||||
|
badpvp.groups.targetIndex = 1
|
||||||
|
|
||||||
|
for k, name in pairs(ourTable) do
|
||||||
|
if badpvp.verifyTarget(name) then
|
||||||
|
table.insert(badpvp.groups.targetList, name)
|
||||||
|
send("enemy " .. name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expandAlias("st " .. badpvp.groups.targetList[1])
|
||||||
|
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Targetlist now set to " ..
|
||||||
|
tostring(#badpvp.groups.targetList) .. " targets: " .. table.concat(badpvp.groups.targetList, ", "))
|
||||||
|
end
|
||||||
|
|
||||||
|
function badpvp.generateHelp()
|
||||||
|
local tablemaker = require("BadPVP.MDK.ftext").TableMaker
|
||||||
|
local formatter = require ("BadPVP.MDK.ftext").TextFormatter
|
||||||
|
|
||||||
|
headerFormat = formatter:new({
|
||||||
|
width = 80,
|
||||||
|
cap = "<|",
|
||||||
|
capColor = "<DarkSlateBlue>",
|
||||||
|
textColor = "<white>",
|
||||||
|
spacerColor = "<steel_blue>",
|
||||||
|
inside = false,
|
||||||
|
spacer = " ",
|
||||||
|
alignment = "center",
|
||||||
|
mirror = true
|
||||||
|
})
|
||||||
|
|
||||||
|
helpTable = tablemaker:new({
|
||||||
|
title = "BadPVP Help",
|
||||||
|
printTitle = false,
|
||||||
|
printHeaders = true,
|
||||||
|
frameColor = "<steel_blue>",
|
||||||
|
separateRows = false,
|
||||||
|
forceHeaderSeparator = true
|
||||||
|
})
|
||||||
|
helpTable:addColumn({
|
||||||
|
name = "Command",
|
||||||
|
width = 30,
|
||||||
|
textColor = "<DarkSlateBlue>"
|
||||||
|
})
|
||||||
|
helpTable:addColumn({
|
||||||
|
name = "Description",
|
||||||
|
width = 50,
|
||||||
|
textColor = "<DarkSlateGrey>"
|
||||||
|
})
|
||||||
|
--pvp
|
||||||
|
helpTable:addRow({
|
||||||
|
"pvp",
|
||||||
|
"Generate your next pvp action."
|
||||||
|
})
|
||||||
|
--pvpnt
|
||||||
|
helpTable:addRow({
|
||||||
|
"<LightSlateBlue>pvpnt",
|
||||||
|
"<LightSlateGrey>Manually move to the next target in the list."
|
||||||
|
})
|
||||||
|
--pvpreset
|
||||||
|
helpTable:addRow({
|
||||||
|
"pvpreset",
|
||||||
|
"Reload the Engine and pick up new changes."
|
||||||
|
})
|
||||||
|
--pvpleaders (-|+)name
|
||||||
|
helpTable:addRow({
|
||||||
|
"<LightSlateBlue>pvpLeaders (-|+)name",
|
||||||
|
"<LightSlateGrey>Add(+) or Remove(-) a group Leader"
|
||||||
|
})
|
||||||
|
--pvpleaders
|
||||||
|
helpTable:addRow({
|
||||||
|
"pvpleaders",
|
||||||
|
"List all current group Leaders."
|
||||||
|
})
|
||||||
|
--pvphelp
|
||||||
|
helpTable:addRow({
|
||||||
|
"<LightSlateBlue>pvphelp",
|
||||||
|
"<LightSlateGrey>Display this menu."
|
||||||
|
})
|
||||||
|
|
||||||
|
cecho(headerFormat:format("BadPVP Help Docs"))
|
||||||
|
cecho(helpTable:assemble())
|
||||||
|
|
||||||
|
end
|
188
src/scripts/BadPVP/psion.lua
Normal file
188
src/scripts/BadPVP/psion.lua
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
-- Psion Implementation Variables
|
||||||
|
psion = psion or {}
|
||||||
|
psion.timeToFlurry = false
|
||||||
|
|
||||||
|
-- The primary function to build pvp actions for the psion class
|
||||||
|
-- NOTE: This script primarily pushes for keeping specific class weaknesses applied and stacking unweaved body/mind until it can be inverted to spirit and flurried against
|
||||||
|
function psion.decideAction()
|
||||||
|
if gmcp.Char.Status.class ~= "Psion" then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Called Psion decision engine when not a Psion.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not target or target == nil or target == "None" or target == "Dude" then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Called decision engine without a target.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local commandString = { "setalias murder stand" }
|
||||||
|
local affsReport = {}
|
||||||
|
local limbs = nil
|
||||||
|
local enemyClass = nil
|
||||||
|
local unwoven = {}
|
||||||
|
local psionFlags = {}
|
||||||
|
local preferredLeg = nil
|
||||||
|
local preferredArm = nil
|
||||||
|
|
||||||
|
-- Preset flags
|
||||||
|
psionFlags.clumsiness = false
|
||||||
|
psionFlags.weariness = false
|
||||||
|
psionFlags.stupidity = false
|
||||||
|
|
||||||
|
-- Check enemy class
|
||||||
|
if table.contains(Legacy.CT.Enemies, target) then
|
||||||
|
enemyClass = Legacy.CT.Enemies[target]
|
||||||
|
elseif table.contains(Legacy.NDB.db, target) then
|
||||||
|
enemyClass = Legacy.NDB.db[target].class
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check limb damage
|
||||||
|
if table.contains(lb, target) then limbs = lb[target] end
|
||||||
|
|
||||||
|
-- Set preferred limbs
|
||||||
|
if limbs ~= nil then
|
||||||
|
local limbTable = { "right", "left" }
|
||||||
|
|
||||||
|
if limbs.hits['left leg'] > limbs.hits['right leg'] then
|
||||||
|
preferredLeg = 'left'
|
||||||
|
elseif limbs.hits['left leg'] == limbs.hits['right leg'] then
|
||||||
|
preferredLeg = limbTable[math.random(#limbTable)]
|
||||||
|
else
|
||||||
|
preferredLeg = 'right'
|
||||||
|
end
|
||||||
|
|
||||||
|
if limbs.hits['left arm'] > limbs.hits['right arm'] then
|
||||||
|
preferredArm = 'left'
|
||||||
|
elseif limbs.hits['left arm'] == limbs.hits['right arm'] then
|
||||||
|
preferredArm = limbTable[math.random(#limbTable)]
|
||||||
|
else
|
||||||
|
preferredArm = 'right'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set unweavings
|
||||||
|
if table.contains(ak.psion.unweaving, 'body') then unwoven.body = ak.psion.unweaving.body else unwoven.body = 0 end
|
||||||
|
if table.contains(ak.psion.unweaving, 'mind') then unwoven.mind = ak.psion.unweaving.mind else unwoven.mind = 0 end
|
||||||
|
if table.contains(ak.psion.unweaving, 'spirit') then unwoven.spirit = ak.psion.unweaving.spirit else unwoven.spirit = 0 end
|
||||||
|
|
||||||
|
-- Weave Prepare
|
||||||
|
-- Disruption = paralysis
|
||||||
|
-- Laceration = haemophilia
|
||||||
|
-- Dazzle = clumsiness
|
||||||
|
-- Rattle = epilepsy
|
||||||
|
-- Vapours = asthma
|
||||||
|
-- Incisive = beats clumsiness
|
||||||
|
-- Order: Incisive for Clumsiness, Paralysis, Class Specific, Asthma, Haemophilia, Epilepsy, Clumsy
|
||||||
|
if Legacy.Curing.Affs['clumsiness'] then
|
||||||
|
-- We have clumsiness so use incisive
|
||||||
|
table.insert(commandString, "weave prepare incisive")
|
||||||
|
elseif not ak.check('paralysis', 100) then
|
||||||
|
table.insert(commandString, "weave prepare disruption")
|
||||||
|
table.insert(affsReport, "paralysis")
|
||||||
|
elseif enemyClass ~= nil and table.contains(badpvp.classes.clumsiness, enemyClass) and not ak.check('clumsiness', 100) then
|
||||||
|
table.insert(commandString, "weave prepare dazzle")
|
||||||
|
table.insert(affsReport, "clumsiness")
|
||||||
|
psionFlags.clumsiness = true
|
||||||
|
elseif enemyClass ~= nil and table.contains(badpvp.classes.haemophilia, enemyClass) and not ak.check('haemophilia', 100) then
|
||||||
|
table.insert(commandString, "weave prepare laceration")
|
||||||
|
table.insert(affsReport, "haemophilia")
|
||||||
|
elseif not ak.check('asthma', 100) then
|
||||||
|
table.insert(commandString, "weave prepare vapours")
|
||||||
|
table.insert(affsReport, "asthma")
|
||||||
|
elseif not ak.check('haemophilia', 100) then
|
||||||
|
table.insert(commandString, "weave prepare laceration")
|
||||||
|
table.insert(affsReport, "haemophilia")
|
||||||
|
elseif not ak.check('epilepsy', 100) then
|
||||||
|
table.insert(commandString, "weave prepare rattle")
|
||||||
|
table.insert(affsReport, "epilepsy")
|
||||||
|
elseif not ak.check('clumsiness', 100) then
|
||||||
|
table.insert(commandString, "weave prepare dazzle")
|
||||||
|
table.insert(affsReport, "clumsiness")
|
||||||
|
psionFlags.clumsiness = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We do not have transcend, but once we do decide psionics action here
|
||||||
|
-- examples:
|
||||||
|
-- If paralized, we can psi manipulate touch tree
|
||||||
|
-- we can excise if mana less than 30% on enemy
|
||||||
|
-- we can use psi splinter to break shields
|
||||||
|
-- we can use psi combustion to push bleeding
|
||||||
|
-- we can use psi expunge to cure impatience
|
||||||
|
-- we can use psi muddle on stupidity inflicted enemies
|
||||||
|
-- we can just straight up psi shatter them
|
||||||
|
|
||||||
|
-- Weaving
|
||||||
|
if ak.defs.shield then
|
||||||
|
table.insert(commandString, "weave cleave " .. target)
|
||||||
|
elseif unwoven.mind == 0 then
|
||||||
|
table.insert(commandString, "weave unweave " .. target .. " mind")
|
||||||
|
table.insert(affsReport, "unweavingmind(1)")
|
||||||
|
elseif unwoven.body == 0 then
|
||||||
|
table.insert(commandString, "weave unweave " .. target .. " body")
|
||||||
|
table.insert(affsReport, "unweavingbody(1)")
|
||||||
|
elseif not psionFlags.clumsiness and enemyClass ~= nil and table.contains(badpvp.classes.clumsiness, enemyClass) and not ak.check("clumsiness", 100) then
|
||||||
|
table.insert(commandString, "weave sever " .. target .. " " .. preferredArm)
|
||||||
|
table.insert(affsReport, "clumsiness")
|
||||||
|
psionFlags.clumsiness = true
|
||||||
|
elseif not psionFlags.weariness and enemyClass ~= nil and table.contains(badpvp.classes.weariness, enemyClass) and not ak.check("weariness", 100) then
|
||||||
|
table.insert(commandString, "weave puncture " .. target .. " " .. preferredArm)
|
||||||
|
table.insert(affsReport, "weariness")
|
||||||
|
psionFlags.weariness = true
|
||||||
|
elseif not psionFlags.stupidity and enemyClass ~= nil and table.contains(badpvp.classes.stupidity, enemyClass) and not ak.check("stupidity", 100) then
|
||||||
|
table.insert(commandString, "weave backhand " .. target)
|
||||||
|
table.insert(affsReport, "stupidity")
|
||||||
|
table.insert(affsReport, "dizziness")
|
||||||
|
psionFlags.stupidity = true
|
||||||
|
elseif not ak.check("nausea", 100) then
|
||||||
|
-- exsanguinate for nausea
|
||||||
|
table.insert(commandString, "weave exsanguinate " .. target)
|
||||||
|
table.insert(affsReport, "nausea")
|
||||||
|
if ak.bleeding >= 150 then table.insert(affsReport, "anorexia") end
|
||||||
|
elseif not ak.check("asthma", 100) then
|
||||||
|
-- deathblow for asthma
|
||||||
|
table.insert(commandString, "weave deathblow " .. target)
|
||||||
|
table.insert(affsReport, "asthma")
|
||||||
|
table.insert(affsReport, "bleeding")
|
||||||
|
elseif not ak.check("stupidity", 100) or not ak.check("dizziness", 100) then
|
||||||
|
-- backhand for stupidity and dizziness
|
||||||
|
table.insert(commandString, "weave backhand " .. target)
|
||||||
|
table.insert(affsReport, "stupidity")
|
||||||
|
table.insert(affsReport, "dizziness")
|
||||||
|
psionFlags.stupidity = true
|
||||||
|
elseif not ak.check("blackout", 100) and ak.check("prone", 100) then
|
||||||
|
-- lightsteal for blackout
|
||||||
|
table.insert(commandString, "weave lightsteal " .. target)
|
||||||
|
table.insert(affsReport, "trueblind")
|
||||||
|
table.insert(affsReport, "blackout")
|
||||||
|
elseif unwoven.body == 5 then
|
||||||
|
-- invert!
|
||||||
|
table.insert(commandString, "weave invert " .. target .. " body spirit")
|
||||||
|
table.insert(affsReport, "unweavingspirit(5)")
|
||||||
|
psion.timeToFlurry = true
|
||||||
|
elseif unwoven.mind == 5 then
|
||||||
|
-- invert!
|
||||||
|
table.insert(commandString, "weave invert " .. target .. " mind spirit")
|
||||||
|
table.insert(affsReport, "unweavingspirit(5)")
|
||||||
|
psion.timeToFlurry = true
|
||||||
|
elseif psion.timeToFlurry then
|
||||||
|
-- Unleash Flurry!
|
||||||
|
table.insert(commandString, "weave flurry " .. target)
|
||||||
|
psion.timeToFlurry = false
|
||||||
|
elseif unwoven.body < 5 then
|
||||||
|
table.insert(commandString, "weave unweave " .. target .. " body")
|
||||||
|
table.insert(affsReport, "unweavingbody(" .. tostring(unwoven.body + 1) .. ")")
|
||||||
|
elseif unwoven.mind < 5 then
|
||||||
|
table.insert(commandString, "weave unweave " .. target .. " mind")
|
||||||
|
table.insert(affsReport, "unweavingmind(" .. tostring(unwoven.mind + 1) .. ")")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Send final command, Report afflictions
|
||||||
|
send(table.concat(commandString, "/"))
|
||||||
|
send("queue addclearfull eqbal murder")
|
||||||
|
send("pt " .. target .. " hit with " .. table.concat(affsReport, " "))
|
||||||
|
|
||||||
|
-- If we are automating, start automation
|
||||||
|
if fighter.automate then
|
||||||
|
psion.pid = registerAnonymousEventHandler("gmcp.Char.Vitals", "psion.isReady")
|
||||||
|
end
|
||||||
|
end
|
14
src/scripts/BadPVP/scripts.json
Normal file
14
src/scripts/BadPVP/scripts.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "badpvp",
|
||||||
|
"isActive": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psion",
|
||||||
|
"isActive": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "alchemist",
|
||||||
|
"isActive": "yes"
|
||||||
|
}
|
||||||
|
]
|
5
src/triggers/BadPVP/targetList.lua
Normal file
5
src/triggers/BadPVP/targetList.lua
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
if badpvp.verifyCaller(matches[2]) then
|
||||||
|
badpvp.processTargetList(matches[4], matches[5])
|
||||||
|
else
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Ignoring target list as caller is not a leader.")
|
||||||
|
end
|
4
src/triggers/BadPVP/targetSpecific.lua
Normal file
4
src/triggers/BadPVP/targetSpecific.lua
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
if badpvp.verifyCaller(matches[2]) and badpvp.verifyTarget(matches[4]) then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Setting target to " .. matches[4])
|
||||||
|
expandAlias("st " .. matches[4])
|
||||||
|
end
|
54
src/triggers/BadPVP/triggers.json
Normal file
54
src/triggers/BadPVP/triggers.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "targetList",
|
||||||
|
"isActive": "yes",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^\\(Party\\): (.*) (say|says), \"I have (\\d{1,3}) targets: (.*?)\\.?\"$",
|
||||||
|
"type": "regex"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "targetSpecific",
|
||||||
|
"isActive": "yes",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^\\(Party\\): (.*) (say|says), \"Target: (.*?)\\.?\"$",
|
||||||
|
"type": "regex"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wasSlain",
|
||||||
|
"isActive": "yes",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^You have slain (\\w+)\\.$",
|
||||||
|
"type": "regex"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "^(\\w+) has been slain by \\w+\\.$",
|
||||||
|
"type": "regex"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "^Unable to withstand the arrow piercing \\w+ chest, (\\w+) stumbles to the ground, collapsing in a pool ",
|
||||||
|
"type": "regex"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "^(\\w+)\\'s body disintegrates and a ghostly spirit rises from the ashes\\. The ghost cries out in pain as a body forms around \\w+ that has .*\\.$",
|
||||||
|
"type": "regex"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wasKilled",
|
||||||
|
"isActive": "yes",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^You have been slain by (\\w+)\\.$",
|
||||||
|
"type": "regex"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
2
src/triggers/BadPVP/wasKilled.lua
Normal file
2
src/triggers/BadPVP/wasKilled.lua
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cecho("\n<firebrick>COMBAT NOTICE: We died. Resetting engine.")
|
||||||
|
badpvp.Setup()
|
11
src/triggers/BadPVP/wasSlain.lua
Normal file
11
src/triggers/BadPVP/wasSlain.lua
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
local deadTarget = matches[2]
|
||||||
|
|
||||||
|
if deadTarget == target then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: Target died. Moving to next target.")
|
||||||
|
expandAlias("nt")
|
||||||
|
end
|
||||||
|
|
||||||
|
if table.contains(badpvp.groups.targetList, deadTarget) then
|
||||||
|
cecho("\n<firebrick>COMBAT NOTICE: A target in the list died so we've set them to be skipped one time.")
|
||||||
|
table.insert(badpvp.groups.deadTargets, deadTarget)
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user