Skip to content

Repository files navigation

Those Damn Retainers

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.

Commands

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.

Building

dotnet build ThoseDamnRetainers.sln -c Release

The 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.Tests

runs the planner tests. They link the pure sources rather than referencing the plugin assembly, so they need neither Dalamud nor the game.

How it decides what to send

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.

Putting things up for sale

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.

Repricing market listings

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.

Deliberately not handled

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

Layout

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

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages