-
Notifications
You must be signed in to change notification settings - Fork 0
Memory and Roles
Two smaller, optional packages: AgentMemory for state that should outlive a single tick but isn't quite core
simulation state, and the role packages for soft, advisory group-role assignment.
Unlike Blackboard (per-tick AI state), AgentMemory entries carry a timestamp so callers can decide for themselves
how stale a remembered value is allowed to be before it's worth refreshing.
public static final MemoryKey<BlockPos> NEAREST_WATER = MemoryKey.of("nearest_water", BlockPos.class);
var memory = new AgentMemory();
memory.remember(NEAREST_WATER, waterPos, currentTick);
var entry = memory.recall(NEAREST_WATER); // MemoryEntry<BlockPos>, or null
var fresh = memory.recallIfFresh(NEAREST_WATER, currentTick, 200); // value only if <=200 ticks old, else null
memory.forget(NEAREST_WATER);
memory.has(NEAREST_WATER);AgentMemory also generalizes the "find nearest position matching a condition, remember it, skip re-scanning next
time" pattern:
memory.findAndRemember(level, origin, radius, candidatePos -> isSuitable(candidatePos), NEAREST_WATER, currentTick);This is a plain brute-force cube scan. For large radii or hot-path use, maintain your own spatial index (as
Ovomorphosis's ResinWebRegistry does) and remember that result here rather than calling this helper repeatedly.
MemoryKey<T> mirrors BlackboardKey<T>, a stable string id paired with a Class<T> for the safe cast on read.
A "role" is a soft, advisory intent label a planner assigns an agent for a while (the motivating example: Ovomorphosis's
XenoRole, hunter, guard, scout). Roles are meant to bias GOAP goal scoring, not to gate what an agent is
mechanically allowed to do.
public enum MyRole implements AgentRole {
HUNTER,
GUARD,
SCOUT,
}RoleSelector<E, R> handles the mechanical bookkeeping, who currently holds which role, and honoring a minimum hold
duration so roles don't flicker every tick. It has no opinion on how roles should be scored or distributed; you
supply that as a scorer function.
var roleSelector = new RoleSelector<MyEntity, MyRole>(200); // minHoldDuration in ticks
// One-off, ignoring hold duration:
roleSelector.assign(agent, MyRole.GUARD, currentTick);
// Respecting hold duration, no-ops if not yet eligible:
boolean assigned = roleSelector.tryAssign(agent, MyRole.HUNTER, currentTick);
// Re-score a whole group at once, only touching agents eligible for reassignment:
roleSelector.reassignEligible(hiveMembers, member -> scoreBestRoleFor(member), currentTick);
var current = roleSelector.currentRole(agent); // null if never assigned
roleSelector.clear(agent);RoleAssignment<R> is the underlying immutable record (role, assignedTick) with canReassign(currentTick, minHoldDuration).
Store the chosen role on the agent's blackboard under a mod-defined key if actions/tree nodes need to read it
(AzureCortex doesn't reserve a CommonBlackboardKeys entry for this, since role concepts are entirely mod-specific).
-
GOAP Planning, where role-based score biasing typically plugs in (inside your
GoalPlanner). -
Core Concepts, how
Blackboard(per-tick) differs fromAgentMemory(cross-tick, timestamped).