A panel that clips onto the retainer inventory window and moves items in bulk. Type an amount per item, press OK, and the whole batch is carried across for you.
- Lists everything in the open retainer, and everything in your own four bags
- Works both ways: Retrieve (retainer to bags) and Stow (bags to retainer)
- Saved lists, either "move exactly this many" or "top me up to this many"
- Tells you up front what will not fit, instead of stranding a batch halfway
- Every move is throttled and verified before the next one is sent
You open the retainer yourself. The plugin only does the carrying — there is no walking, no talking to NPCs, and no clicking through menus on your behalf.
| Command | What it does |
|---|---|
/tdr |
Open the settings window |
/tdr <list name> |
Apply a saved list and run it at the open retainer |
The panel itself has no command: it appears when a retainer's inventory opens and goes when it closes.
dotnet build ThoseDamnRetainers.sln -c ReleaseThe loadable plugin ends up in ThoseDamnRetainers/bin/Release/. Add that folder as a dev plugin
location through Dalamud's settings inside the game — editing dalamudConfig.json while FFXIV
is running does not stick, because Dalamud holds it in memory and writes it back on exit.
dotnet run --project ThoseDamnRetainers.Testsruns the planner tests. They link the pure sources rather than referencing the plugin assembly, so they need neither Dalamud nor the game.
The game gives us two primitives and no more:
InventoryManager.MoveItemSlot(srcContainer, srcSlot, dstContainer, dstSlot)— moves a whole slot, with no way to say how many.InventoryManager.SplitItem(container, slot, quantity)— peels an amount off a stack inside one container, and picks the landing slot itself.
So retrieving "5 of a stack of 99" is two requests: split 5 off inside the retainer, then move that
new stack across. TransferPlanner works out one operation at a time from the live contents rather
than building a script up front — partly because the landing slot of a split is not knowable in
advance, and partly because a plan written thirty seconds ago may no longer match what is in the
bags.
TransferQueue sends one request, watches for the specific change that request should cause —
the source slot emptying, or one more stack of the item appearing — waits out a short pause, and
only then asks for the next operation. Looking for that one change rather than for any change at
all is what stops a venture returning or another plugin tidying a bag from being mistaken for our
own move landing.
Progress is measured by how much the receiving side has gained since the batch started, not by counting requests, which keeps it honest no matter which slot the game chose. If that total ever goes down, the game did something other than what was asked and the batch stops rather than shuffle items around blindly.
The Sell tab lists what is in the retainer's bags, tick what you want gone, and it goes up undercutting the competition.
Anything the market board will not take is shown greyed out with the reason rather than quietly
left off the list. Two flags decide it: IsUntradable, and whether the item has a market board
search category at all — an item with none has nowhere to be listed under, which is the game's own
way of saying no.
Prices come from universalis.app, because the game will not tell us what things are going for unless you send it to look, item by item. That data is uploaded by other players' clients, so every price carries the time it was last reported and anything older than your threshold is flagged. Your own retainers' listings are excluded from the comparison — undercutting yourself in a loop is the obvious way for a tool like this to quietly cost you money.
The rule is yours: match the cheapest, go a fixed number of gil under, or a percentage under. There is a floor under everything it computes, so a stale or crashed market cannot turn a valuable stack into pocket change.
Some things can only be listed one at a time, so ten of them means ten separate listings and ten trips through the sell dialog to change a price. The Market tab gathers listings per item: set one price, see exactly which listings would move and to what, and apply the lot.
This part is more cautious than the rest, for two reasons. InventoryManager.SetRetainerMarketPrice
is the game's own function for it, but reading a price back afterwards may only prove the client
accepted it, not the server. And a wrong number on the market board is real gil.
So the first batch you ever run sends one change and then stops, naming the listing and asking you to look at it in game. Say it worked and the rest goes through; say it did not and nothing else is touched. That answer is remembered, so it only asks once.
Every change is verified before the next is sent, and a listing whose item no longer matches what the preview showed — because it sold in the meantime — is skipped rather than repriced blindly.
| Why | |
|---|---|
| Gil | Needs a different game function entirely |
| Crystals and shards | Fixed slots per crystal type, so splitting an amount out is meaningless |
| Items listed on the market board | Would have to be pulled off sale first |
| Gear the retainer is wearing | Same |
ThoseDamnRetainers/
Models/ Bag, snapshots, wish lines, operations, presets — no game references
Services/ TransferPlanner (pure), plus the scanner, queue, config and item lookups
Windows/ DockedWindow (clips onto a game addon), the panel, the settings window
ThoseDamnRetainers.Tests/
Program.cs A plain runner: 60 checks over the planner, no test framework needed