| name | uo-combat-loop |
| description | Fight a single focused target with the headless UO client — pursue it, watch its HP and your own, keep melee range or archery line-of-sight, and handle running out of arrows. Use whenever the user asks to fight, kill, attack, or engage a monster/mobile in combat (as opposed to a one-off `attack` command). |
Running a combat loop against one target
Requires a logged-in character with the map loaded (uo-login). Pursuit uses the same
goto mechanics as uo-navigation — read that first if you haven't already, especially
the part about goto not remembering blocked tiles between calls.
The core idea: pick one target and stick with it. Don't use attacknearest inside
the loop — that re-picks a target every call, which fights whatever's closest instead
of finishing the fight you started. Use attacknearest/an (or a mobiles scan) only
once, to choose the target, then drive the rest of the loop entirely off that one
serial.
1. Pick the target
echo "mobiles 10" >> /tmp/cuocmd
sleep 1
tail -15 /tmp/cuolog
Pick a mobile whose notoriety is CanBeAttacked (or Criminal/Enemy/Murderer —
anything attack will actually accept). Note its serial — call it <target> for the
rest of this loop. Everything below is scoped to that one serial.
Mobile HP always shows HP:0/0 in mobiles output until you explicitly request it —
the client never gets a health bar for free:
echo "hp <target>" >> /tmp/cuocmd
sleep 1
echo "mobiles 10" >> /tmp/cuocmd
sleep 1
tail -10 /tmp/cuolog
2. Engage
echo "attack <target>" >> /tmp/cuocmd
sleep 1
attack auto-enables war mode if it isn't already on. Once the server accepts the
attack, it auto-swings on its own timer as long as you stay in range and LOS — you do
not need to resend attack every swing, only when combat has clearly stalled (see
step 4).
3. The loop
Each iteration, in this order:
a. Check your own status first. A dead player can't do anything else meaningfully:
echo "dead" >> /tmp/cuocmd
sleep 1
tail -3 /tmp/cuolog
If dead, stop the combat loop entirely and hand off to uo-death-recovery — don't keep
sending combat commands to a ghost. If alive but HP is getting low relative to
status's max, that's a judgment call (retreat, keep fighting, use a healing item if
you have one — this skill doesn't cover healing).
b. Check the target's position and HP:
printf 'mobiles 10\nhp <target>\n' >> /tmp/cuocmd
sleep 1
tail -15 /tmp/cuolog
Grep the output for <target>'s line. Two end conditions to watch for:
- HP reaches 0 — it's dead (or about to be).
- The serial disappears from
mobiles entirely — it either died and became a
corpse (removed from the mobile list) or walked out of your scan range. Either way,
the fight with this target is over; don't keep waiting on it.
Don't call hp every single iteration — it's an extra round trip. Once every few
iterations (e.g. every 3rd loop, or whenever you're about to make a decision that
depends on exact HP) is enough; position needs checking more often since a moving
target can walk out of range within a couple of seconds.
c. Pursue if needed. Compare your pos to the target's position from mobiles:
d. Resume the attack if swings have stopped. If several iterations have passed with
no Swing: Player → <target> lines in the log and the target is still alive and in
range/LOS, the server likely dropped combat (you moved out of range, target broke LOS,
etc.) — resend attack <target> to restart it.
Repeat a–d until the target dies (or you decide to disengage).
4. Archery: running out of arrows
Check remaining ammo via inventory — count "arrow" entries in the backpack listing
(or crossbow bolts, if that's the ammo type). If the backpack hasn't been opened
yet this session, inv will show it as empty regardless of what's actually in
there (see uo-equip-items step 1) — don't read that as "out of ammo" and
disengage prematurely:
echo "inv" >> /tmp/cuocmd
sleep 1
echo "use <backpack_serial>" >> /tmp/cuocmd
sleep 2
echo "inv" >> /tmp/cuocmd
sleep 1
tail -10 /tmp/cuolog
When ammo hits 0 (or you notice swings have silently stopped landing and you're at
range with an empty quiver), you have two options:
- Switch to melee, if you already have a melee weapon in the backpack. Unequip the
bow and wear the melee weapon (see
uo-equip-items for the unequip → wear → confirm
pattern). Combat then continues under the melee rules in step 3c (need adjacency).
- Otherwise, go restock. Disengage (the target will likely wander off — that's
fine), navigate back to a vendor that sells the ammo (
uo-navigation to get there,
uo-buy-items for the <VendorName> buy → shop → buy flow), then return and
re-engage. Re-run step 1's mobiles scan when you get back — your original target
may have moved, died to something else, or no longer be around, in which case pick a
new target rather than chasing a serial that isn't there anymore.
Gotchas
attacknearest picks whatever's closest right now — never call it mid-loop or
you'll abandon your focused target for something that wandered closer.
- HP shown in
mobiles is a snapshot from the last hp request, not live — always
treat it as "as of my last check," especially for a target that might already be
dead by the time you read the output.
- A target's own wandering (most town/field mobiles patrol even out of combat) means
melee pursuit can look like constant micro-adjustment — that's normal, not a bug.
- Being in range isn't enough for archery — you need line of sight too. A target
can be well within bow/crossbow range and swings will still not land (or won't start)
if a wall, fence, or other obstruction sits between you and it. Don't just check
distance — check
los (step 3c) before assuming a stalled ranged attack means the
target is out of range; it may just be blocked, and the fix is repositioning, not
necessarily closing distance.