@its-not-rocket-science/ananke 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/LICENSE +21 -0
- package/README.md +2199 -0
- package/STABLE_API.md +266 -0
- package/dist/src/anatomy/anatomy-compiler.d.ts +14 -0
- package/dist/src/anatomy/anatomy-compiler.js +277 -0
- package/dist/src/anatomy/anatomy-contracts.d.ts +94 -0
- package/dist/src/anatomy/anatomy-contracts.js +1 -0
- package/dist/src/anatomy/anatomy-helpers.d.ts +82 -0
- package/dist/src/anatomy/anatomy-helpers.js +233 -0
- package/dist/src/anatomy/anatomy-schema.d.ts +28 -0
- package/dist/src/anatomy/anatomy-schema.js +388 -0
- package/dist/src/anatomy/index.d.ts +4 -0
- package/dist/src/anatomy/index.js +4 -0
- package/dist/src/archetypes.d.ts +87 -0
- package/dist/src/archetypes.js +285 -0
- package/dist/src/arena.d.ts +173 -0
- package/dist/src/arena.js +695 -0
- package/dist/src/bridge/bridge-engine.d.ts +46 -0
- package/dist/src/bridge/bridge-engine.js +252 -0
- package/dist/src/bridge/index.d.ts +4 -0
- package/dist/src/bridge/index.js +5 -0
- package/dist/src/bridge/interpolation.d.ts +64 -0
- package/dist/src/bridge/interpolation.js +130 -0
- package/dist/src/bridge/mapping.d.ts +33 -0
- package/dist/src/bridge/mapping.js +54 -0
- package/dist/src/bridge/types.d.ts +94 -0
- package/dist/src/bridge/types.js +2 -0
- package/dist/src/campaign.d.ts +141 -0
- package/dist/src/campaign.js +235 -0
- package/dist/src/channels.d.ts +15 -0
- package/dist/src/channels.js +20 -0
- package/dist/src/chronicle.d.ts +124 -0
- package/dist/src/chronicle.js +232 -0
- package/dist/src/collective-activities.d.ts +154 -0
- package/dist/src/collective-activities.js +247 -0
- package/dist/src/competence/acoustic.d.ts +101 -0
- package/dist/src/competence/acoustic.js +242 -0
- package/dist/src/competence/catalogue.d.ts +30 -0
- package/dist/src/competence/catalogue.js +241 -0
- package/dist/src/competence/crafting.d.ts +35 -0
- package/dist/src/competence/crafting.js +88 -0
- package/dist/src/competence/engineering.d.ts +53 -0
- package/dist/src/competence/engineering.js +108 -0
- package/dist/src/competence/framework.d.ts +68 -0
- package/dist/src/competence/framework.js +694 -0
- package/dist/src/competence/index.d.ts +12 -0
- package/dist/src/competence/index.js +13 -0
- package/dist/src/competence/interspecies.d.ts +81 -0
- package/dist/src/competence/interspecies.js +108 -0
- package/dist/src/competence/language.d.ts +79 -0
- package/dist/src/competence/language.js +115 -0
- package/dist/src/competence/naturalist.d.ts +97 -0
- package/dist/src/competence/naturalist.js +187 -0
- package/dist/src/competence/navigation.d.ts +24 -0
- package/dist/src/competence/navigation.js +48 -0
- package/dist/src/competence/performance.d.ts +125 -0
- package/dist/src/competence/performance.js +210 -0
- package/dist/src/competence/teaching.d.ts +64 -0
- package/dist/src/competence/teaching.js +121 -0
- package/dist/src/competence/willpower.d.ts +74 -0
- package/dist/src/competence/willpower.js +114 -0
- package/dist/src/crafting/index.d.ts +55 -0
- package/dist/src/crafting/index.js +229 -0
- package/dist/src/crafting/manufacturing.d.ts +83 -0
- package/dist/src/crafting/manufacturing.js +165 -0
- package/dist/src/crafting/materials.d.ts +53 -0
- package/dist/src/crafting/materials.js +120 -0
- package/dist/src/crafting/recipes.d.ts +75 -0
- package/dist/src/crafting/recipes.js +233 -0
- package/dist/src/crafting/workshops.d.ts +61 -0
- package/dist/src/crafting/workshops.js +170 -0
- package/dist/src/debug.d.ts +86 -0
- package/dist/src/debug.js +76 -0
- package/dist/src/derive.d.ts +21 -0
- package/dist/src/derive.js +88 -0
- package/dist/src/describe.d.ts +29 -0
- package/dist/src/describe.js +276 -0
- package/dist/src/dialogue.d.ts +122 -0
- package/dist/src/dialogue.js +266 -0
- package/dist/src/dist.d.ts +20 -0
- package/dist/src/dist.js +39 -0
- package/dist/src/downtime.d.ts +89 -0
- package/dist/src/downtime.js +391 -0
- package/dist/src/economy.d.ts +116 -0
- package/dist/src/economy.js +182 -0
- package/dist/src/emotional-contagion.d.ts +142 -0
- package/dist/src/emotional-contagion.js +274 -0
- package/dist/src/equipment.d.ts +206 -0
- package/dist/src/equipment.js +598 -0
- package/dist/src/faction.d.ts +102 -0
- package/dist/src/faction.js +237 -0
- package/dist/src/generate.d.ts +35 -0
- package/dist/src/generate.js +166 -0
- package/dist/src/index.d.ts +42 -0
- package/dist/src/index.js +54 -0
- package/dist/src/inheritance.d.ts +69 -0
- package/dist/src/inheritance.js +136 -0
- package/dist/src/inventory.d.ts +194 -0
- package/dist/src/inventory.js +637 -0
- package/dist/src/item-durability.d.ts +69 -0
- package/dist/src/item-durability.js +308 -0
- package/dist/src/legend.d.ts +97 -0
- package/dist/src/legend.js +269 -0
- package/dist/src/lod.d.ts +9 -0
- package/dist/src/lod.js +84 -0
- package/dist/src/metrics.d.ts +51 -0
- package/dist/src/metrics.js +91 -0
- package/dist/src/model3d.d.ts +138 -0
- package/dist/src/model3d.js +214 -0
- package/dist/src/mythology.d.ts +101 -0
- package/dist/src/mythology.js +308 -0
- package/dist/src/narrative-render.d.ts +42 -0
- package/dist/src/narrative-render.js +194 -0
- package/dist/src/narrative-stress.d.ts +123 -0
- package/dist/src/narrative-stress.js +183 -0
- package/dist/src/narrative.d.ts +44 -0
- package/dist/src/narrative.js +257 -0
- package/dist/src/party.d.ts +70 -0
- package/dist/src/party.js +226 -0
- package/dist/src/polity.d.ts +262 -0
- package/dist/src/polity.js +398 -0
- package/dist/src/presets.d.ts +42 -0
- package/dist/src/presets.js +170 -0
- package/dist/src/progression.d.ts +170 -0
- package/dist/src/progression.js +256 -0
- package/dist/src/quest-generators.d.ts +76 -0
- package/dist/src/quest-generators.js +534 -0
- package/dist/src/quest.d.ts +239 -0
- package/dist/src/quest.js +520 -0
- package/dist/src/relationships-effects.d.ts +75 -0
- package/dist/src/relationships-effects.js +219 -0
- package/dist/src/relationships.d.ts +104 -0
- package/dist/src/relationships.js +347 -0
- package/dist/src/replay.d.ts +47 -0
- package/dist/src/replay.js +82 -0
- package/dist/src/rng.d.ts +9 -0
- package/dist/src/rng.js +37 -0
- package/dist/src/settlement-services.d.ts +67 -0
- package/dist/src/settlement-services.js +267 -0
- package/dist/src/settlement.d.ts +143 -0
- package/dist/src/settlement.js +419 -0
- package/dist/src/sim/action.d.ts +28 -0
- package/dist/src/sim/action.js +12 -0
- package/dist/src/sim/aging.d.ts +95 -0
- package/dist/src/sim/aging.js +243 -0
- package/dist/src/sim/ai/decide.d.ts +10 -0
- package/dist/src/sim/ai/decide.js +267 -0
- package/dist/src/sim/ai/perception.d.ts +12 -0
- package/dist/src/sim/ai/perception.js +54 -0
- package/dist/src/sim/ai/personality.d.ts +54 -0
- package/dist/src/sim/ai/personality.js +202 -0
- package/dist/src/sim/ai/presets.d.ts +2 -0
- package/dist/src/sim/ai/presets.js +28 -0
- package/dist/src/sim/ai/system.d.ts +6 -0
- package/dist/src/sim/ai/system.js +13 -0
- package/dist/src/sim/ai/targeting.d.ts +8 -0
- package/dist/src/sim/ai/targeting.js +42 -0
- package/dist/src/sim/ai/types.d.ts +14 -0
- package/dist/src/sim/ai/types.js +1 -0
- package/dist/src/sim/body.d.ts +9 -0
- package/dist/src/sim/body.js +32 -0
- package/dist/src/sim/bodyplan.d.ts +161 -0
- package/dist/src/sim/bodyplan.js +677 -0
- package/dist/src/sim/capability.d.ts +135 -0
- package/dist/src/sim/capability.js +8 -0
- package/dist/src/sim/combat.d.ts +21 -0
- package/dist/src/sim/combat.js +77 -0
- package/dist/src/sim/commandBuilders.d.ts +11 -0
- package/dist/src/sim/commandBuilders.js +39 -0
- package/dist/src/sim/commands.d.ts +71 -0
- package/dist/src/sim/commands.js +8 -0
- package/dist/src/sim/condition.d.ts +35 -0
- package/dist/src/sim/condition.js +21 -0
- package/dist/src/sim/cone.d.ts +40 -0
- package/dist/src/sim/cone.js +44 -0
- package/dist/src/sim/context.d.ts +68 -0
- package/dist/src/sim/context.js +1 -0
- package/dist/src/sim/density.d.ts +14 -0
- package/dist/src/sim/density.js +33 -0
- package/dist/src/sim/disease.d.ts +141 -0
- package/dist/src/sim/disease.js +353 -0
- package/dist/src/sim/entity.d.ts +251 -0
- package/dist/src/sim/entity.js +19 -0
- package/dist/src/sim/events.d.ts +25 -0
- package/dist/src/sim/events.js +5 -0
- package/dist/src/sim/explosion.d.ts +40 -0
- package/dist/src/sim/explosion.js +40 -0
- package/dist/src/sim/formation-unit.d.ts +138 -0
- package/dist/src/sim/formation-unit.js +197 -0
- package/dist/src/sim/formation.d.ts +12 -0
- package/dist/src/sim/formation.js +54 -0
- package/dist/src/sim/frontage.d.ts +30 -0
- package/dist/src/sim/frontage.js +84 -0
- package/dist/src/sim/grapple.d.ts +100 -0
- package/dist/src/sim/grapple.js +480 -0
- package/dist/src/sim/hazard.d.ts +104 -0
- package/dist/src/sim/hazard.js +201 -0
- package/dist/src/sim/hydrostatic.d.ts +58 -0
- package/dist/src/sim/hydrostatic.js +117 -0
- package/dist/src/sim/impairment.d.ts +20 -0
- package/dist/src/sim/impairment.js +162 -0
- package/dist/src/sim/indexing.d.ts +7 -0
- package/dist/src/sim/indexing.js +7 -0
- package/dist/src/sim/injury.d.ts +54 -0
- package/dist/src/sim/injury.js +66 -0
- package/dist/src/sim/intent.d.ts +26 -0
- package/dist/src/sim/intent.js +7 -0
- package/dist/src/sim/kernel.d.ts +45 -0
- package/dist/src/sim/kernel.js +1992 -0
- package/dist/src/sim/kinds.d.ts +64 -0
- package/dist/src/sim/kinds.js +56 -0
- package/dist/src/sim/knockback.d.ts +50 -0
- package/dist/src/sim/knockback.js +82 -0
- package/dist/src/sim/limb.d.ts +48 -0
- package/dist/src/sim/limb.js +78 -0
- package/dist/src/sim/medical.d.ts +32 -0
- package/dist/src/sim/medical.js +33 -0
- package/dist/src/sim/morale.d.ts +69 -0
- package/dist/src/sim/morale.js +92 -0
- package/dist/src/sim/mount.d.ts +150 -0
- package/dist/src/sim/mount.js +225 -0
- package/dist/src/sim/nutrition.d.ts +74 -0
- package/dist/src/sim/nutrition.js +168 -0
- package/dist/src/sim/occlusion.d.ts +8 -0
- package/dist/src/sim/occlusion.js +71 -0
- package/dist/src/sim/push.d.ts +11 -0
- package/dist/src/sim/push.js +79 -0
- package/dist/src/sim/ranged.d.ts +44 -0
- package/dist/src/sim/ranged.js +69 -0
- package/dist/src/sim/seeds.d.ts +3 -0
- package/dist/src/sim/seeds.js +16 -0
- package/dist/src/sim/sensory-extended.d.ts +103 -0
- package/dist/src/sim/sensory-extended.js +181 -0
- package/dist/src/sim/sensory.d.ts +38 -0
- package/dist/src/sim/sensory.js +109 -0
- package/dist/src/sim/skills.d.ts +70 -0
- package/dist/src/sim/skills.js +69 -0
- package/dist/src/sim/sleep.d.ts +107 -0
- package/dist/src/sim/sleep.js +215 -0
- package/dist/src/sim/spatial.d.ts +8 -0
- package/dist/src/sim/spatial.js +59 -0
- package/dist/src/sim/step/capability.d.ts +8 -0
- package/dist/src/sim/step/capability.js +77 -0
- package/dist/src/sim/step/concentration.d.ts +9 -0
- package/dist/src/sim/step/concentration.js +25 -0
- package/dist/src/sim/step/effects.d.ts +17 -0
- package/dist/src/sim/step/effects.js +96 -0
- package/dist/src/sim/step/energy.d.ts +3 -0
- package/dist/src/sim/step/energy.js +31 -0
- package/dist/src/sim/step/hazards.d.ts +4 -0
- package/dist/src/sim/step/hazards.js +19 -0
- package/dist/src/sim/step/injury.d.ts +10 -0
- package/dist/src/sim/step/injury.js +353 -0
- package/dist/src/sim/step/morale.d.ts +11 -0
- package/dist/src/sim/step/morale.js +130 -0
- package/dist/src/sim/step/movement.d.ts +5 -0
- package/dist/src/sim/step/movement.js +172 -0
- package/dist/src/sim/step/push.d.ts +11 -0
- package/dist/src/sim/step/push.js +79 -0
- package/dist/src/sim/step/substances.d.ts +3 -0
- package/dist/src/sim/step/substances.js +75 -0
- package/dist/src/sim/substance.d.ts +38 -0
- package/dist/src/sim/substance.js +57 -0
- package/dist/src/sim/systemic-toxicology.d.ts +109 -0
- package/dist/src/sim/systemic-toxicology.js +263 -0
- package/dist/src/sim/team.d.ts +9 -0
- package/dist/src/sim/team.js +37 -0
- package/dist/src/sim/tech.d.ts +36 -0
- package/dist/src/sim/tech.js +46 -0
- package/dist/src/sim/terrain.d.ts +121 -0
- package/dist/src/sim/terrain.js +141 -0
- package/dist/src/sim/testing.d.ts +13 -0
- package/dist/src/sim/testing.js +100 -0
- package/dist/src/sim/thermoregulation.d.ts +77 -0
- package/dist/src/sim/thermoregulation.js +161 -0
- package/dist/src/sim/tick.d.ts +3 -0
- package/dist/src/sim/tick.js +3 -0
- package/dist/src/sim/toxicology.d.ts +52 -0
- package/dist/src/sim/toxicology.js +104 -0
- package/dist/src/sim/trace.d.ts +141 -0
- package/dist/src/sim/trace.js +1 -0
- package/dist/src/sim/tuning.d.ts +16 -0
- package/dist/src/sim/tuning.js +42 -0
- package/dist/src/sim/vec3.d.ts +14 -0
- package/dist/src/sim/vec3.js +31 -0
- package/dist/src/sim/weapon_dynamics.d.ts +102 -0
- package/dist/src/sim/weapon_dynamics.js +142 -0
- package/dist/src/sim/weather.d.ts +95 -0
- package/dist/src/sim/weather.js +105 -0
- package/dist/src/sim/world.d.ts +52 -0
- package/dist/src/sim/world.js +1 -0
- package/dist/src/sim/wound-aging.d.ts +120 -0
- package/dist/src/sim/wound-aging.js +223 -0
- package/dist/src/species.d.ts +106 -0
- package/dist/src/species.js +664 -0
- package/dist/src/story-arcs.d.ts +17 -0
- package/dist/src/story-arcs.js +276 -0
- package/dist/src/tech-diffusion.d.ts +80 -0
- package/dist/src/tech-diffusion.js +185 -0
- package/dist/src/traits.d.ts +25 -0
- package/dist/src/traits.js +178 -0
- package/dist/src/types.d.ts +117 -0
- package/dist/src/types.js +1 -0
- package/dist/src/units.d.ts +41 -0
- package/dist/src/units.js +64 -0
- package/dist/src/weapons.d.ts +20 -0
- package/dist/src/weapons.js +824 -0
- package/dist/src/world-generation.d.ts +52 -0
- package/dist/src/world-generation.js +301 -0
- package/package.json +74 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// src/sim/ai/personality.ts — Phase 47: Advanced AI Personalities
|
|
2
|
+
//
|
|
3
|
+
// Four orthogonal personality axes modulate decision-making on top of the base AIPolicy:
|
|
4
|
+
//
|
|
5
|
+
// aggression → retreat range (less retreat) + hesitation override
|
|
6
|
+
// caution → defence intensity boost
|
|
7
|
+
// loyalty → switches focus target to protect a distressed ally
|
|
8
|
+
// opportunism → switches focus target to the most-wounded enemy
|
|
9
|
+
//
|
|
10
|
+
// All modifiers are delta-based around q(0.50): neutral personality produces
|
|
11
|
+
// identical behaviour to absent personality.
|
|
12
|
+
import { SCALE, q, clampQ, mulDiv } from "../../units.js";
|
|
13
|
+
import { computeCompanionLoyalty } from "../../party.js";
|
|
14
|
+
import { eventSeed } from "../seeds.js";
|
|
15
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Neutral personality: q(0.50) on all axes.
|
|
18
|
+
* Produces identical behaviour to an entity with no personality set.
|
|
19
|
+
*/
|
|
20
|
+
export const NEUTRAL_PERSONALITY = {
|
|
21
|
+
aggression: q(0.50),
|
|
22
|
+
caution: q(0.50),
|
|
23
|
+
loyalty: q(0.50),
|
|
24
|
+
opportunism: q(0.50),
|
|
25
|
+
};
|
|
26
|
+
/** Named predefined personalities. */
|
|
27
|
+
export const PERSONALITIES = {
|
|
28
|
+
/** Charges into melee, ignores pain and fear, rarely retreats. */
|
|
29
|
+
berserker: {
|
|
30
|
+
aggression: q(0.90),
|
|
31
|
+
caution: q(0.10),
|
|
32
|
+
loyalty: q(0.30),
|
|
33
|
+
opportunism: q(0.20),
|
|
34
|
+
},
|
|
35
|
+
/** Avoids engagement, maximises defence and retreat distance. */
|
|
36
|
+
coward: {
|
|
37
|
+
aggression: q(0.10),
|
|
38
|
+
caution: q(0.90),
|
|
39
|
+
loyalty: q(0.20),
|
|
40
|
+
opportunism: q(0.70),
|
|
41
|
+
},
|
|
42
|
+
/** Shields allies at personal cost; steadfast defender. */
|
|
43
|
+
guardian: {
|
|
44
|
+
aggression: q(0.55),
|
|
45
|
+
caution: q(0.55),
|
|
46
|
+
loyalty: q(0.90),
|
|
47
|
+
opportunism: q(0.15),
|
|
48
|
+
},
|
|
49
|
+
/** Targets the weak; disloyal; always looks for an advantage. */
|
|
50
|
+
schemer: {
|
|
51
|
+
aggression: q(0.40),
|
|
52
|
+
caution: q(0.70),
|
|
53
|
+
loyalty: q(0.10),
|
|
54
|
+
opportunism: q(0.90),
|
|
55
|
+
},
|
|
56
|
+
/** Disciplined, reliable, protects unit; balanced aggression. */
|
|
57
|
+
soldier: {
|
|
58
|
+
aggression: q(0.65),
|
|
59
|
+
caution: q(0.50),
|
|
60
|
+
loyalty: q(0.70),
|
|
61
|
+
opportunism: q(0.35),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
// ── Derivation from cognitive profile ────────────────────────────────────────
|
|
65
|
+
/**
|
|
66
|
+
* Derive a personality from existing cognitive and resilience attributes.
|
|
67
|
+
*
|
|
68
|
+
* Mapping rationale:
|
|
69
|
+
* aggression ← distressTolerance (pain tolerance → willing to keep fighting)
|
|
70
|
+
* caution ← intrapersonal (self-awareness → more careful and defensive)
|
|
71
|
+
* loyalty ← interpersonal (social empathy → protects allies)
|
|
72
|
+
* opportunism ← logicalMathematical (planning → targets strategically)
|
|
73
|
+
*/
|
|
74
|
+
export function derivePersonalityFromCognition(attrs) {
|
|
75
|
+
return {
|
|
76
|
+
aggression: attrs.resilience.distressTolerance,
|
|
77
|
+
caution: (attrs.cognition?.intrapersonal ?? q(0.50)),
|
|
78
|
+
loyalty: (attrs.cognition?.interpersonal ?? q(0.50)),
|
|
79
|
+
opportunism: (attrs.cognition?.logicalMathematical ?? q(0.50)),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// ── Loyalty combination ──────────────────────────────────────────────────────
|
|
83
|
+
/**
|
|
84
|
+
* Compute effective loyalty combining personality loyalty and companion loyalty to party leader.
|
|
85
|
+
* If entity belongs to a party and has a relationship with the leader, companion loyalty is used.
|
|
86
|
+
* Otherwise falls back to personality loyalty (or neutral q(0.50) if no personality).
|
|
87
|
+
*/
|
|
88
|
+
export function computeEffectiveLoyalty(self, world) {
|
|
89
|
+
const personality = self.personality;
|
|
90
|
+
const baseLoyalty = personality?.loyalty ?? q(0.50);
|
|
91
|
+
const partyRegistry = world.__partyRegistry;
|
|
92
|
+
if (!partyRegistry)
|
|
93
|
+
return baseLoyalty;
|
|
94
|
+
const partyId = self.party;
|
|
95
|
+
if (!partyId)
|
|
96
|
+
return baseLoyalty;
|
|
97
|
+
const party = partyRegistry.parties.get(partyId);
|
|
98
|
+
if (!party)
|
|
99
|
+
return baseLoyalty;
|
|
100
|
+
const leaderId = party.leaderId;
|
|
101
|
+
if (leaderId === self.id)
|
|
102
|
+
return baseLoyalty; // leader is self
|
|
103
|
+
const relationshipGraph = world.__relationshipGraph;
|
|
104
|
+
if (!relationshipGraph)
|
|
105
|
+
return baseLoyalty;
|
|
106
|
+
const companionLoyalty = computeCompanionLoyalty(self, leaderId, relationshipGraph);
|
|
107
|
+
// Use the higher of base loyalty or companion loyalty (max loyalty)
|
|
108
|
+
return companionLoyalty > baseLoyalty ? companionLoyalty : baseLoyalty;
|
|
109
|
+
}
|
|
110
|
+
// ── Pure formula helpers (exported for unit testing) ──────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Effective retreat range after aggression bias.
|
|
113
|
+
*
|
|
114
|
+
* aggression q(0.90) → range reduced by ~0.20m (fights more aggressively)
|
|
115
|
+
* aggression q(0.50) → unchanged
|
|
116
|
+
* aggression q(0.10) → range increased by ~0.20m (retreats sooner)
|
|
117
|
+
*/
|
|
118
|
+
export function computeEffectiveRetreatRange(baseRange_m, aggression) {
|
|
119
|
+
const delta = mulDiv(aggression - q(0.50), Math.trunc(0.40 * SCALE.m), SCALE.Q);
|
|
120
|
+
return Math.max(0, baseRange_m - delta);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Effective defence intensity after caution bias.
|
|
124
|
+
*
|
|
125
|
+
* caution q(0.90) → +q(0.20) max boost
|
|
126
|
+
* caution q(0.50) → unchanged
|
|
127
|
+
* caution q(0.10) → −q(0.20) max reduction
|
|
128
|
+
*/
|
|
129
|
+
export function computeDefenceIntensityBoost(baseIntensity, caution) {
|
|
130
|
+
const delta = mulDiv(caution - q(0.50), q(0.40), SCALE.Q);
|
|
131
|
+
return clampQ((baseIntensity + delta), q(0), q(1.0));
|
|
132
|
+
}
|
|
133
|
+
// ── Target bias functions ─────────────────────────────────────────────────────
|
|
134
|
+
/** Ally shock/fluid-loss above this Q value counts as "in distress". */
|
|
135
|
+
const ALLY_DISTRESS_Q = q(0.20);
|
|
136
|
+
/** Enemy must be within this many SCALE.m units of the ally to count as a threat to them. */
|
|
137
|
+
const ALLY_THREAT_RANGE_m = Math.trunc(2.5 * SCALE.m);
|
|
138
|
+
const ALLY_THREAT_RANGE_m2 = ALLY_THREAT_RANGE_m * ALLY_THREAT_RANGE_m;
|
|
139
|
+
/**
|
|
140
|
+
* Loyalty override: if an ally is in distress and has an enemy nearby, switch target to
|
|
141
|
+
* that enemy. Only triggers when loyalty > q(0.50); roll probability = loyaltyQ / SCALE.Q.
|
|
142
|
+
*/
|
|
143
|
+
export function applyLoyaltyBias(self, world, currentTarget, loyaltyQ) {
|
|
144
|
+
if (loyaltyQ <= q(0.50))
|
|
145
|
+
return currentTarget;
|
|
146
|
+
for (const ally of world.entities) {
|
|
147
|
+
if (ally.teamId !== self.teamId || ally.id === self.id || ally.injury.dead)
|
|
148
|
+
continue;
|
|
149
|
+
const distressed = ally.injury.shock > ALLY_DISTRESS_Q
|
|
150
|
+
|| ally.injury.fluidLoss > ALLY_DISTRESS_Q;
|
|
151
|
+
if (!distressed)
|
|
152
|
+
continue;
|
|
153
|
+
// Find the enemy nearest to this distressed ally
|
|
154
|
+
let threatEnemy;
|
|
155
|
+
let bestD2 = Infinity;
|
|
156
|
+
for (const e of world.entities) {
|
|
157
|
+
if (e.teamId === self.teamId || e.injury.dead)
|
|
158
|
+
continue;
|
|
159
|
+
const adx = e.position_m.x - ally.position_m.x;
|
|
160
|
+
const ady = e.position_m.y - ally.position_m.y;
|
|
161
|
+
const d2 = adx * adx + ady * ady;
|
|
162
|
+
if (d2 < ALLY_THREAT_RANGE_m2 && d2 < bestD2) {
|
|
163
|
+
bestD2 = d2;
|
|
164
|
+
threatEnemy = e;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!threatEnemy)
|
|
168
|
+
continue;
|
|
169
|
+
const seed = eventSeed(world.seed, world.tick, self.id, ally.id, 0x10A1B);
|
|
170
|
+
if ((seed % SCALE.Q) < loyaltyQ)
|
|
171
|
+
return threatEnemy;
|
|
172
|
+
}
|
|
173
|
+
return currentTarget;
|
|
174
|
+
}
|
|
175
|
+
/** Minimum consciousness difference for the weakest enemy to qualify for an opportunism switch. */
|
|
176
|
+
const OPPORTUNISM_GAP = q(0.30);
|
|
177
|
+
/**
|
|
178
|
+
* Opportunism override: if a significantly more-wounded enemy is present, switch target.
|
|
179
|
+
* Only triggers when opportunism > q(0.50); roll probability = opportunismQ / SCALE.Q.
|
|
180
|
+
*/
|
|
181
|
+
export function applyOpportunismBias(self, world, currentTarget, opportunismQ) {
|
|
182
|
+
if (opportunismQ <= q(0.50))
|
|
183
|
+
return currentTarget;
|
|
184
|
+
let weakest;
|
|
185
|
+
let lowestConsc = SCALE.Q + 1;
|
|
186
|
+
for (const e of world.entities) {
|
|
187
|
+
if (e.teamId === self.teamId || e.injury.dead)
|
|
188
|
+
continue;
|
|
189
|
+
if (e.injury.consciousness < lowestConsc) {
|
|
190
|
+
lowestConsc = e.injury.consciousness;
|
|
191
|
+
weakest = e;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!weakest || !currentTarget || weakest.id === currentTarget.id)
|
|
195
|
+
return currentTarget;
|
|
196
|
+
if (currentTarget.injury.consciousness - lowestConsc < OPPORTUNISM_GAP)
|
|
197
|
+
return currentTarget;
|
|
198
|
+
const seed = eventSeed(world.seed, world.tick, self.id, weakest.id, 0x06670);
|
|
199
|
+
if ((seed % SCALE.Q) < opportunismQ)
|
|
200
|
+
return weakest;
|
|
201
|
+
return currentTarget;
|
|
202
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { q } from "../../units.js";
|
|
2
|
+
import { SCALE } from "../../units.js";
|
|
3
|
+
export const AI_PRESETS = {
|
|
4
|
+
lineInfantry: {
|
|
5
|
+
archetype: "lineInfantry",
|
|
6
|
+
desiredRange_m: Math.trunc(0.9 * SCALE.m),
|
|
7
|
+
engageRange_m: Math.trunc(1.2 * SCALE.m),
|
|
8
|
+
retreatRange_m: Math.trunc(0.35 * SCALE.m),
|
|
9
|
+
threatRange_m: Math.trunc(1.6 * SCALE.m),
|
|
10
|
+
defendWhenThreatenedQ: q(0.35),
|
|
11
|
+
parryBiasQ: q(0.55),
|
|
12
|
+
dodgeBiasQ: q(0.10),
|
|
13
|
+
retargetCooldownTicks: 15,
|
|
14
|
+
focusStickinessQ: q(0.75),
|
|
15
|
+
},
|
|
16
|
+
skirmisher: {
|
|
17
|
+
archetype: "skirmisher",
|
|
18
|
+
desiredRange_m: Math.trunc(1.4 * SCALE.m),
|
|
19
|
+
engageRange_m: Math.trunc(1.6 * SCALE.m),
|
|
20
|
+
retreatRange_m: Math.trunc(0.50 * SCALE.m),
|
|
21
|
+
threatRange_m: Math.trunc(1.6 * SCALE.m),
|
|
22
|
+
defendWhenThreatenedQ: q(0.25),
|
|
23
|
+
parryBiasQ: q(0.35),
|
|
24
|
+
dodgeBiasQ: q(0.45),
|
|
25
|
+
retargetCooldownTicks: 10,
|
|
26
|
+
focusStickinessQ: q(0.55),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WorldState } from "../world.js";
|
|
2
|
+
import type { CommandMap } from "../commands.js";
|
|
3
|
+
import type { WorldIndex } from "../indexing.js";
|
|
4
|
+
import type { SpatialIndex } from "../spatial.js";
|
|
5
|
+
import type { AIPolicy } from "./types.js";
|
|
6
|
+
export declare function buildAICommands(world: WorldState, index: WorldIndex, spatial: SpatialIndex, policyFor: (eId: number) => AIPolicy | undefined): CommandMap;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { decideCommandsForEntity } from "./decide.js";
|
|
2
|
+
export function buildAICommands(world, index, spatial, policyFor) {
|
|
3
|
+
const out = new Map();
|
|
4
|
+
for (const e of world.entities) {
|
|
5
|
+
const policy = policyFor(e.id);
|
|
6
|
+
if (!policy)
|
|
7
|
+
continue;
|
|
8
|
+
const cmds = decideCommandsForEntity(world, index, spatial, e, policy);
|
|
9
|
+
if (cmds.length > 0)
|
|
10
|
+
out.set(e.id, cmds);
|
|
11
|
+
}
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Entity } from "../entity.js";
|
|
2
|
+
import type { WorldState } from "../world.js";
|
|
3
|
+
import type { WorldIndex } from "../indexing.js";
|
|
4
|
+
import type { SpatialIndex } from "../spatial.js";
|
|
5
|
+
import type { AIPolicy } from "./types.js";
|
|
6
|
+
import { type SensoryEnvironment } from "../sensory.js";
|
|
7
|
+
export declare function pickTarget(world: WorldState, self: Entity, index: WorldIndex, spatial: SpatialIndex, policy: AIPolicy, env?: SensoryEnvironment): Entity | undefined;
|
|
8
|
+
export declare function updateFocus(self: Entity, target: Entity | undefined, policy: AIPolicy): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { perceiveLocal } from "./perception.js";
|
|
2
|
+
import { SCALE, mulDiv } from "../../units.js";
|
|
3
|
+
import { eventSeed } from "../seeds.js";
|
|
4
|
+
import { DEFAULT_PERCEPTION, DEFAULT_SENSORY_ENV } from "../sensory.js";
|
|
5
|
+
export function pickTarget(world, self, index, spatial, policy, env = DEFAULT_SENSORY_ENV) {
|
|
6
|
+
const ai = self.ai ?? { focusTargetId: 0, retargetCooldownTicks: 0, decisionCooldownTicks: 0 };
|
|
7
|
+
const focused = ai.focusTargetId !== 0 ? index.byId.get(ai.focusTargetId) : undefined;
|
|
8
|
+
// keep focus if still valid and cooldown active
|
|
9
|
+
if (focused && !focused.injury.dead && ai.retargetCooldownTicks > 0)
|
|
10
|
+
return focused;
|
|
11
|
+
// Phase 4: use entity's own threat horizon as perception radius
|
|
12
|
+
// Phase 33: spatial intelligence scales the effective horizon
|
|
13
|
+
// Formula: base × (0.40 + cognSpatial) — human baseline (0.60) → no change (×1.0)
|
|
14
|
+
const perc = (self.attributes).perception ?? DEFAULT_PERCEPTION;
|
|
15
|
+
const cognSpatial = self.attributes.cognition?.spatial ?? 0;
|
|
16
|
+
const perceptionRadius = cognSpatial
|
|
17
|
+
? Math.trunc(mulDiv(perc.threatHorizon_m, (4000 + cognSpatial), SCALE.Q))
|
|
18
|
+
: perc.threatHorizon_m;
|
|
19
|
+
const p = perceiveLocal(world, self, index, spatial, perceptionRadius, perc.attentionDepth, env);
|
|
20
|
+
if (p.enemies.length === 0)
|
|
21
|
+
return undefined;
|
|
22
|
+
// Stickiness: prefer keeping previous target if present and alive
|
|
23
|
+
if (focused && !focused.injury.dead) {
|
|
24
|
+
const seed = eventSeed(world.seed, world.tick, self.id, ai.focusTargetId, 0xF0C05);
|
|
25
|
+
const rollQ = (seed % SCALE.Q);
|
|
26
|
+
if (rollQ < policy.focusStickinessQ)
|
|
27
|
+
return focused;
|
|
28
|
+
}
|
|
29
|
+
// Otherwise choose nearest (already sorted)
|
|
30
|
+
return p.enemies[0];
|
|
31
|
+
}
|
|
32
|
+
export function updateFocus(self, target, policy) {
|
|
33
|
+
if (!self.ai)
|
|
34
|
+
self.ai = { focusTargetId: 0, retargetCooldownTicks: 0, decisionCooldownTicks: 0 };
|
|
35
|
+
if (!target) {
|
|
36
|
+
self.ai.focusTargetId = 0;
|
|
37
|
+
self.ai.retargetCooldownTicks = 0;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
self.ai.focusTargetId = target.id;
|
|
41
|
+
self.ai.retargetCooldownTicks = policy.retargetCooldownTicks;
|
|
42
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Q } from "../../units.js";
|
|
2
|
+
export type AIArchetype = "skirmisher" | "lineInfantry" | "berserker" | "defender";
|
|
3
|
+
export interface AIPolicy {
|
|
4
|
+
archetype: AIArchetype;
|
|
5
|
+
desiredRange_m: number;
|
|
6
|
+
engageRange_m: number;
|
|
7
|
+
retreatRange_m: number;
|
|
8
|
+
threatRange_m: number;
|
|
9
|
+
defendWhenThreatenedQ: Q;
|
|
10
|
+
parryBiasQ: Q;
|
|
11
|
+
dodgeBiasQ: Q;
|
|
12
|
+
retargetCooldownTicks: number;
|
|
13
|
+
focusStickinessQ: Q;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
import type { HitArea } from "./combat.js";
|
|
3
|
+
export type BodyRegion = "head" | "torso" | "leftArm" | "rightArm" | "leftLeg" | "rightLeg";
|
|
4
|
+
export declare const ALL_REGIONS: readonly BodyRegion[];
|
|
5
|
+
export type MajorOrgan = "brain" | "lung" | "liver" | "spleen";
|
|
6
|
+
export type BoneRegion = "bone" | "skull" | "femur" | "tibia";
|
|
7
|
+
export declare const DEFAULT_REGION_WEIGHTS: Record<BodyRegion, Q>;
|
|
8
|
+
export declare function regionFromHit(area: HitArea, sideBit: 0 | 1): BodyRegion;
|
|
9
|
+
export declare function weightedMean01(values: Record<BodyRegion, Q>, weights?: Record<BodyRegion, Q>): Q;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { q, SCALE } from "../units.js";
|
|
2
|
+
export const ALL_REGIONS = [
|
|
3
|
+
"head",
|
|
4
|
+
"torso",
|
|
5
|
+
"leftArm",
|
|
6
|
+
"rightArm",
|
|
7
|
+
"leftLeg",
|
|
8
|
+
"rightLeg",
|
|
9
|
+
];
|
|
10
|
+
export const DEFAULT_REGION_WEIGHTS = {
|
|
11
|
+
head: q(0.12),
|
|
12
|
+
torso: q(0.50),
|
|
13
|
+
leftArm: q(0.095),
|
|
14
|
+
rightArm: q(0.095),
|
|
15
|
+
leftLeg: q(0.095),
|
|
16
|
+
rightLeg: q(0.095),
|
|
17
|
+
};
|
|
18
|
+
export function regionFromHit(area, sideBit) {
|
|
19
|
+
if (area === "head")
|
|
20
|
+
return "head";
|
|
21
|
+
if (area === "torso")
|
|
22
|
+
return "torso";
|
|
23
|
+
if (area === "arm")
|
|
24
|
+
return sideBit === 0 ? "leftArm" : "rightArm";
|
|
25
|
+
return sideBit === 0 ? "leftLeg" : "rightLeg";
|
|
26
|
+
}
|
|
27
|
+
export function weightedMean01(values, weights = DEFAULT_REGION_WEIGHTS) {
|
|
28
|
+
let acc = 0;
|
|
29
|
+
for (const k of ALL_REGIONS)
|
|
30
|
+
acc += Math.trunc((values[k] * weights[k]) / SCALE.Q);
|
|
31
|
+
return acc;
|
|
32
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { Q, I32 } from "../units.js";
|
|
2
|
+
import { DamageChannel } from "../channels.js";
|
|
3
|
+
import { SegmentCoverageProfile } from "../anatomy/anatomy-contracts.js";
|
|
4
|
+
export interface LocomotionModel {
|
|
5
|
+
/** Primary locomotion mechanism. */
|
|
6
|
+
type: "biped" | "quadruped" | "hexapod" | "undulation" | "flight" | "distributed";
|
|
7
|
+
/**
|
|
8
|
+
* Phase 8B: flight capability. Present only for winged body plans.
|
|
9
|
+
* Wings are segments; liftCapacity_kg is the total mass the creature can sustain aloft.
|
|
10
|
+
*/
|
|
11
|
+
flight?: {
|
|
12
|
+
/** Segment IDs used for lift (must match BodySegment.id values in the same plan). */
|
|
13
|
+
wingSegments: string[];
|
|
14
|
+
/** Maximum liftable mass (SCALE.kg units; e.g. 3000 = 3 kg). */
|
|
15
|
+
liftCapacity_kg: I32;
|
|
16
|
+
/** Energy cost multiplier relative to ground movement (Q; q(2.0) = double cost). */
|
|
17
|
+
flightStaminaCost: Q;
|
|
18
|
+
/** Mobility reduction per unit of average wing structural damage (Q multiplier). */
|
|
19
|
+
wingDamagePenalty: Q;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface CNSLayout {
|
|
23
|
+
/** Whether the nervous system is concentrated or spread through the body. */
|
|
24
|
+
type: "centralized" | "distributed";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One functional segment in a body plan.
|
|
28
|
+
* The `id` is also the key in InjuryState.byRegion.
|
|
29
|
+
*/
|
|
30
|
+
export interface BodySegment {
|
|
31
|
+
/** Unique within the plan; used as the injury region key. */
|
|
32
|
+
id: string;
|
|
33
|
+
/** Parent segment id (for anatomical hierarchy); null for the root segment. */
|
|
34
|
+
parent: string | null;
|
|
35
|
+
/** Mass in fixed-point kg units (SCALE.kg = 1000, so 5 kg = 5000). */
|
|
36
|
+
mass_kg: I32;
|
|
37
|
+
/**
|
|
38
|
+
* Hit probability weight per damage channel (Q, 0..SCALE.Q).
|
|
39
|
+
* Omitted channels fall back to the Kinetic weight.
|
|
40
|
+
* The kinetic weights should sum to approximately SCALE.Q across all segments.
|
|
41
|
+
*/
|
|
42
|
+
exposureWeight: Partial<Record<DamageChannel, Q>>;
|
|
43
|
+
/** Contribution to locomotion capability. */
|
|
44
|
+
locomotionRole?: "primary" | "secondary" | "none";
|
|
45
|
+
/** Contribution to manipulation capability. */
|
|
46
|
+
manipulationRole?: "primary" | "secondary" | "none";
|
|
47
|
+
/** Central nervous system contribution. */
|
|
48
|
+
cnsRole?: "central" | "ganglionic" | "none";
|
|
49
|
+
tags?: string[];
|
|
50
|
+
/** Structural biology of this segment. Absent = endoskeleton default. */
|
|
51
|
+
structureType?: "endoskeleton" | "exoskeleton" | "hydrostatic" | "gelatinous";
|
|
52
|
+
/**
|
|
53
|
+
* For exoskeletons: structural damage level (Q) at which the shell is breached.
|
|
54
|
+
* Below breach: all incoming damage routes to structuralDamage only.
|
|
55
|
+
* At or above breach: normal surface / internal / structural split applies.
|
|
56
|
+
*/
|
|
57
|
+
breachThreshold?: Q;
|
|
58
|
+
/**
|
|
59
|
+
* Fluid transport system type.
|
|
60
|
+
* "open" = arthropod hemolymph; "closed" = vertebrate blood; "none" = dry.
|
|
61
|
+
*/
|
|
62
|
+
fluidSystem?: "closed" | "open" | "none";
|
|
63
|
+
/**
|
|
64
|
+
* Hemolymph loss rate (Q) per tick when an open-fluid segment is breached.
|
|
65
|
+
* Feeds InjuryState.hemolymphLoss (parallel to the vertebrate fluidLoss model).
|
|
66
|
+
*/
|
|
67
|
+
hemolymphLossRate?: Q;
|
|
68
|
+
/**
|
|
69
|
+
* Is this segment a joint (articulation between hardened plates)?
|
|
70
|
+
* Joints take extra structural damage from kinetic impacts.
|
|
71
|
+
*/
|
|
72
|
+
isJoint?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Structural damage multiplier applied to strInc when isJoint = true.
|
|
75
|
+
* e.g. q(1.5) = joints take 50% more structural damage than adjacent plates.
|
|
76
|
+
*/
|
|
77
|
+
jointDamageMultiplier?: Q;
|
|
78
|
+
/**
|
|
79
|
+
* Can this segment regenerate structural integrity via a molt event?
|
|
80
|
+
* When true, completing a molt cycle reduces structuralDamage by q(0.10).
|
|
81
|
+
*/
|
|
82
|
+
regeneratesViaMolting?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Intrinsic structural armor resist (joules) — energy absorbed by the shell
|
|
85
|
+
* before damage channels are allocated. Distinct from worn equipment armour.
|
|
86
|
+
* Absent or 0 = no intrinsic resistance.
|
|
87
|
+
*/
|
|
88
|
+
intrinsicArmor_J?: number;
|
|
89
|
+
}
|
|
90
|
+
export type BodySegmentId = BodySegment["id"];
|
|
91
|
+
export interface BodyPlan {
|
|
92
|
+
id: string;
|
|
93
|
+
segments: BodySegment[];
|
|
94
|
+
locomotion: LocomotionModel;
|
|
95
|
+
cnsLayout: CNSLayout;
|
|
96
|
+
coverageProfiles?: readonly SegmentCoverageProfile[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the exposure weight for a segment on a given damage channel.
|
|
100
|
+
* Falls back to the kinetic weight when the channel is not explicitly specified.
|
|
101
|
+
*/
|
|
102
|
+
export declare function getExposureWeight(seg: BodySegment, channel: DamageChannel): Q;
|
|
103
|
+
/**
|
|
104
|
+
* Resolve which segment is struck, given a uniform [0, SCALE.Q) random value.
|
|
105
|
+
* Uses kinetic exposure weights for physical-impact hits.
|
|
106
|
+
*/
|
|
107
|
+
export declare function resolveHitSegment(plan: BodyPlan, r01: Q): BodySegmentId;
|
|
108
|
+
/** Return all segment ids from a body plan. */
|
|
109
|
+
export declare function segmentIds(plan: BodyPlan): readonly string[];
|
|
110
|
+
/**
|
|
111
|
+
* Standard humanoid (human, elf, robot):
|
|
112
|
+
* bilateral upright, 4 limbs, centralized CNS.
|
|
113
|
+
*/
|
|
114
|
+
export declare const HUMANOID_PLAN: BodyPlan;
|
|
115
|
+
/**
|
|
116
|
+
* Quadruped (dog, horse, bear):
|
|
117
|
+
* 4 locomotion limbs, lower centre of gravity, no dedicated manipulation.
|
|
118
|
+
*/
|
|
119
|
+
export declare const QUADRUPED_PLAN: BodyPlan;
|
|
120
|
+
/**
|
|
121
|
+
* Theropod (large bipedal predator, fantasy drake):
|
|
122
|
+
* bipedal, heavy tail counterbalance, vestigial forelimbs.
|
|
123
|
+
*/
|
|
124
|
+
export declare const THEROPOD_PLAN: BodyPlan;
|
|
125
|
+
/**
|
|
126
|
+
* Sauropod (long neck and tail, 4 locomotion limbs):
|
|
127
|
+
* brachiosaurus-type, massive torso, slow-moving.
|
|
128
|
+
*/
|
|
129
|
+
export declare const SAUROPOD_PLAN: BodyPlan;
|
|
130
|
+
/**
|
|
131
|
+
* Avian (bird, winged creature):
|
|
132
|
+
* hollow bones, wings as forelimbs, legs for perching/walking.
|
|
133
|
+
*/
|
|
134
|
+
export declare const AVIAN_PLAN: BodyPlan;
|
|
135
|
+
/**
|
|
136
|
+
* Vermiform (snake, worm):
|
|
137
|
+
* no discrete limbs, lateral undulation locomotion.
|
|
138
|
+
*/
|
|
139
|
+
export declare const VERMIFORM_PLAN: BodyPlan;
|
|
140
|
+
/**
|
|
141
|
+
* Centaur (combined horse and humanoid upper body):
|
|
142
|
+
* 4 locomotion limbs + 2 manipulation limbs.
|
|
143
|
+
*/
|
|
144
|
+
export declare const CENTAUR_PLAN: BodyPlan;
|
|
145
|
+
/**
|
|
146
|
+
* Octopoid (octopus-type):
|
|
147
|
+
* distributed manipulation, all arms contribute to locomotion.
|
|
148
|
+
*/
|
|
149
|
+
export declare const OCTOPOID_PLAN: BodyPlan;
|
|
150
|
+
/**
|
|
151
|
+
* Phase 8B reference plan: giant grasshopper (arthropod exoskeleton).
|
|
152
|
+
* Demonstrates all Phase 8B fields:
|
|
153
|
+
* - All segments have structureType: "exoskeleton"
|
|
154
|
+
* - Thorax has fluidSystem: "open" + hemolymphLossRate
|
|
155
|
+
* - Wings have breachThreshold, isJoint, jointDamageMultiplier
|
|
156
|
+
* - Legs have regeneratesViaMolting: true
|
|
157
|
+
* - locomotion.flight wired to wing segment IDs
|
|
158
|
+
*
|
|
159
|
+
* Kinetic exposure weights sum to exactly 10000 (SCALE.Q).
|
|
160
|
+
*/
|
|
161
|
+
export declare const GRASSHOPPER_PLAN: BodyPlan;
|