A list that can chain anything
This linked list doesn't wrap its elements in node structs. Instead, any struct can be a node: the list header just records where inside each element the "next" pointer lives, as a byte offset. One list type, reused for every kind of object in the game — at the price of some pointer arithmetic on every operation.
That arithmetic is the new read. The offset isn't a constant baked into a load — it's data, loaded from the header at runtime, then added to a node pointer. Here's setHp, which uses the same trick to store a stat into an animal at a per-species offset:
lh t6, 2(a0) # sp->hpOffset — the offset is DATA, loaded like any field
addu t7, a1, t6 # animal + offset = the address OF the hp field
sw a2, 0(t7) # store through it, displacement 0
jr ra
nop
lh, addu, then a memory op at 0(...) — that triple is the fingerprint of a runtime-computed field address. In C it's a cast dance: *(s32 *)((u32)animal + sp->hpOffset). The list header's version of that expression is wrapped in a macro for you in the context, exactly as the original source has it.
The target is func_800cb060: push a node on the front. Four memory writes' worth of bookkeeping — study which value each store carries. Note the scheduler hoisted the offset load to the very top, above the head swap it belongs after; by now that shouldn't slow you down.
Your task
Write func_800cb060 to reproduce the target assembly.