diff --git a/addons/addons.xml b/addons/addons.xml
index 6286a66f4..bee570b84 100644
--- a/addons/addons.xml
+++ b/addons/addons.xml
@@ -819,8 +819,15 @@
IndiNope
Lili
- Block graphical effects from Geomancer's Indi- spells.
+ Blocks graphical effects from Geomancer's Indi- spells.
https://github.com/Windower/Lua/issues
https://github.com/lili-ffxi
+
+ position_manager
+ Lili
+ Allows you to set a screen position per character name. Each character will be moved to that screen position on login. Requires the WinControl plugin to be installed.
+ https://github.com/Windower/Lua/issues
+ https://github.com/lili-ffxi
+
diff --git a/addons/position_manager/README.md b/addons/position_manager/README.md
new file mode 100644
index 000000000..541d0c4e4
--- /dev/null
+++ b/addons/position_manager/README.md
@@ -0,0 +1,31 @@
+# Position Manager
+
+Set and save screen position per-character.
+
+Command: `//pm set [name]`
+
+`pos_x` and `pos_y` are obligatory and must be numbers.
+`name` is optional. If no name is provided, settings will be saved for the current character.
+`:all` is a special name that can be used to set the default position.
+
+**Note**: Characters are only moved after they're logged in. The `:all` position will be used for the character login screen as well.
+
+### Examples:
+`//pm set 0 0`
+Will set your _current_ character to the position X: 0, Y: 0.
+
+`//pm set 0 60 :all`
+Will set the default positioning for all characters to X: 0 and Y: 60 (the height of the Windows 10 taskbar with 150% UI scaling.), and delete all other character-specific settings.
+
+`//pm set 1920 0 Yourname`
+Will set the default position for the character called "Yourname" to X: 1920 and Y: 0.
+This will make the character appear on the secondary screen that is to the right of the main screen - useful for multi-screen setups.
+
+`//pm set Yourmain 0 40`
+`//pm set Youralt 800 40`
+Will set your main to X: 0, Y: 40, and your alt to the X: 800, Y: 40.
+If your laptop screen is 1600px wide, and your instances are both set at 800x600, this will put them side by side.
+
+**Warning:** the `all` name will delete every other character-specific settings that are already saved! It's best to use it only once after you install the addon, to set default position for non-specified characters.
+
+Enjoy.
diff --git a/addons/position_manager/position_manager.lua b/addons/position_manager/position_manager.lua
new file mode 100644
index 000000000..3aa3d2b27
--- /dev/null
+++ b/addons/position_manager/position_manager.lua
@@ -0,0 +1,91 @@
+--Copyright © 2020, Lili
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of position_manager nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL Lili BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+_addon.name = 'position_manager'
+_addon.author = 'Lili'
+_addon.version = '1.0.0'
+_addon.command = 'pm'
+
+if not windower.file_exists(windower.windower_path .. '\\plugins\\WinControl.dll') then
+ print('position_manager: error - Please install the WinControl plugin in the launcher.')
+ windower.send_command('lua u position_manager')
+ return
+else
+ print('position_manager: loading WinControl...')
+ windower.send_command('load wincontrol')
+end
+
+config = require('config')
+
+default = {
+ x = 0,
+ y = 0,
+}
+
+settings = config.load(default)
+
+function move(settings)
+ windower.send_command('wincontrol move %s %s':format(settings.x, settings.y))
+end
+
+function handle_commands(cmd, pos_x, pos_y, name)
+ cmd = cmd:lower() or 'help'
+
+ if cmd == 'r' then
+ windower.send_command('lua r position_manager')
+ elseif cmd == 'set' and type(pos_x) == 'number' and type(pos_y) == 'number' then
+ if name ~= nil and type(name) ~= 'string' then
+ windower.add_to_chat(207, 'plugin_manager: ERROR - invalid name provided.')
+ windower.send_command('pm help')
+ return
+ elseif not name then
+ name = windower.ffxi.get_player().name
+ elseif name == ':all' then
+ name = 'all'
+ end
+
+ settings.x = tonumber(pos_x)
+ settings.y = tonumber(pos_y)
+ config.save(settings, name)
+
+ -- TODO: possibly add IPC
+ if windower.ffxi.get_info().logged_in then
+ player_name = windower.ffxi.get_player().name
+ if name:lower() == player_name:lower() then
+ move(settings)
+ end
+ end
+ elseif cmd == 'help' then
+ windower.add_to_chat(207, 'position_manager: Usage: //pm set [name]')
+ windower.add_to_chat(207, 'position_manager: See the readme for details.')
+ else
+ windower.add_to_chat(207, 'position_manager: %s command not found.':format(cmd))
+ windower.send_command('pm help')
+ end
+end
+
+config.register(settings, move)
+windower.register_event('addon command', handle_commands)