@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,141 @@
|
|
|
1
|
+
import type { Vec3 } from "./vec3.js";
|
|
2
|
+
import type { Q, I32 } from "../units.js";
|
|
3
|
+
import { TraceKinds, type HitArea } from "./kinds.js";
|
|
4
|
+
import type { MedicalAction } from "./medical.js";
|
|
5
|
+
import { BodyRegion } from "./body.js";
|
|
6
|
+
import { BodySegmentId } from "./bodyplan.js";
|
|
7
|
+
export type TraceEvent = {
|
|
8
|
+
kind: typeof TraceKinds.TickStart;
|
|
9
|
+
tick: number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: typeof TraceKinds.TickEnd;
|
|
12
|
+
tick: number;
|
|
13
|
+
} | {
|
|
14
|
+
kind: typeof TraceKinds.Intent;
|
|
15
|
+
tick: number;
|
|
16
|
+
entityId: number;
|
|
17
|
+
intent: unknown;
|
|
18
|
+
} | {
|
|
19
|
+
kind: typeof TraceKinds.Move;
|
|
20
|
+
tick: number;
|
|
21
|
+
entityId: number;
|
|
22
|
+
pos: Vec3;
|
|
23
|
+
vel: Vec3;
|
|
24
|
+
} | {
|
|
25
|
+
kind: typeof TraceKinds.Injury;
|
|
26
|
+
tick: number;
|
|
27
|
+
entityId: number;
|
|
28
|
+
dead: boolean;
|
|
29
|
+
shockQ: Q;
|
|
30
|
+
fluidLossQ: Q;
|
|
31
|
+
consciousnessQ: Q;
|
|
32
|
+
} | {
|
|
33
|
+
kind: typeof TraceKinds.Attack;
|
|
34
|
+
tick: number;
|
|
35
|
+
attackerId: number;
|
|
36
|
+
targetId: number;
|
|
37
|
+
weaponId?: string;
|
|
38
|
+
region: BodyRegion | BodySegmentId;
|
|
39
|
+
energy_J: number;
|
|
40
|
+
blocked: boolean;
|
|
41
|
+
parried: boolean;
|
|
42
|
+
shieldBlocked: boolean;
|
|
43
|
+
armoured: boolean;
|
|
44
|
+
hitQuality: Q;
|
|
45
|
+
} | {
|
|
46
|
+
kind: typeof TraceKinds.AttackAttempt;
|
|
47
|
+
tick: number;
|
|
48
|
+
attackerId: number;
|
|
49
|
+
targetId: number;
|
|
50
|
+
hit: boolean;
|
|
51
|
+
blocked: boolean;
|
|
52
|
+
parried: boolean;
|
|
53
|
+
hitQuality: number;
|
|
54
|
+
area: HitArea;
|
|
55
|
+
} | {
|
|
56
|
+
kind: typeof TraceKinds.KO;
|
|
57
|
+
tick: number;
|
|
58
|
+
entityId: number;
|
|
59
|
+
} | {
|
|
60
|
+
kind: typeof TraceKinds.Death;
|
|
61
|
+
tick: number;
|
|
62
|
+
entityId: number;
|
|
63
|
+
} | {
|
|
64
|
+
kind: typeof TraceKinds.Grapple;
|
|
65
|
+
tick: number;
|
|
66
|
+
attackerId: number;
|
|
67
|
+
targetId: number;
|
|
68
|
+
phase: "start" | "tick" | "break";
|
|
69
|
+
strengthQ?: number;
|
|
70
|
+
} | {
|
|
71
|
+
kind: typeof TraceKinds.WeaponBind;
|
|
72
|
+
tick: number;
|
|
73
|
+
attackerId: number;
|
|
74
|
+
targetId: number;
|
|
75
|
+
durationTicks: number;
|
|
76
|
+
} | {
|
|
77
|
+
kind: typeof TraceKinds.WeaponBindBreak;
|
|
78
|
+
tick: number;
|
|
79
|
+
entityId: number;
|
|
80
|
+
partnerId: number;
|
|
81
|
+
reason: "timeout" | "forced";
|
|
82
|
+
} | {
|
|
83
|
+
kind: typeof TraceKinds.ProjectileHit;
|
|
84
|
+
tick: number;
|
|
85
|
+
shooterId: number;
|
|
86
|
+
targetId: number;
|
|
87
|
+
weaponId?: string;
|
|
88
|
+
hit: boolean;
|
|
89
|
+
region?: BodyRegion | BodySegmentId;
|
|
90
|
+
distance_m: I32;
|
|
91
|
+
energyAtImpact_J: number;
|
|
92
|
+
suppressed: boolean;
|
|
93
|
+
} | {
|
|
94
|
+
kind: typeof TraceKinds.MoraleRoute;
|
|
95
|
+
tick: number;
|
|
96
|
+
entityId: number;
|
|
97
|
+
fearQ: Q;
|
|
98
|
+
} | {
|
|
99
|
+
kind: typeof TraceKinds.MoraleRally;
|
|
100
|
+
tick: number;
|
|
101
|
+
entityId: number;
|
|
102
|
+
fearQ: Q;
|
|
103
|
+
} | {
|
|
104
|
+
kind: typeof TraceKinds.Fracture;
|
|
105
|
+
tick: number;
|
|
106
|
+
entityId: number;
|
|
107
|
+
region: BodyRegion | BodySegmentId;
|
|
108
|
+
} | {
|
|
109
|
+
kind: typeof TraceKinds.TreatmentApplied;
|
|
110
|
+
tick: number;
|
|
111
|
+
treaterId: number;
|
|
112
|
+
targetId: number;
|
|
113
|
+
action: MedicalAction;
|
|
114
|
+
regionId?: string;
|
|
115
|
+
} | {
|
|
116
|
+
kind: typeof TraceKinds.BlastHit;
|
|
117
|
+
tick: number;
|
|
118
|
+
entityId: number;
|
|
119
|
+
blastEnergy_J: number;
|
|
120
|
+
fragHits: number;
|
|
121
|
+
} | {
|
|
122
|
+
kind: typeof TraceKinds.CapabilityActivated;
|
|
123
|
+
tick: number;
|
|
124
|
+
entityId: number;
|
|
125
|
+
sourceId: string;
|
|
126
|
+
effectId: string;
|
|
127
|
+
} | {
|
|
128
|
+
kind: typeof TraceKinds.CapabilitySuppressed;
|
|
129
|
+
tick: number;
|
|
130
|
+
entityId: number;
|
|
131
|
+
sourceId: string;
|
|
132
|
+
effectId: string;
|
|
133
|
+
} | {
|
|
134
|
+
kind: typeof TraceKinds.CastInterrupted;
|
|
135
|
+
tick: number;
|
|
136
|
+
entityId: number;
|
|
137
|
+
};
|
|
138
|
+
export interface TraceSink {
|
|
139
|
+
onEvent(ev: TraceEvent): void;
|
|
140
|
+
}
|
|
141
|
+
export declare const nullTrace: TraceSink;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const nullTrace = { onEvent() { } };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
export type RealismLevel = "arcade" | "tactical" | "sim";
|
|
3
|
+
export interface SimulationTuning {
|
|
4
|
+
realism: RealismLevel;
|
|
5
|
+
armDisableThreshold: Q;
|
|
6
|
+
legDisableThreshold: Q;
|
|
7
|
+
standFailThreshold: Q;
|
|
8
|
+
unconsciousThreshold: Q;
|
|
9
|
+
stumbleBaseChance: Q;
|
|
10
|
+
fallBaseChance: Q;
|
|
11
|
+
dropWeaponsOnUnconscious: boolean;
|
|
12
|
+
standUpBaseTicks: number;
|
|
13
|
+
standUpMaxExtraTicks: number;
|
|
14
|
+
unconsciousBaseTicks: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const TUNING: Record<RealismLevel, SimulationTuning>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { q } from "../units.js";
|
|
2
|
+
export const TUNING = {
|
|
3
|
+
arcade: {
|
|
4
|
+
realism: "arcade",
|
|
5
|
+
armDisableThreshold: q(0.98),
|
|
6
|
+
legDisableThreshold: q(0.98),
|
|
7
|
+
standFailThreshold: q(0.05),
|
|
8
|
+
unconsciousThreshold: q(0.03),
|
|
9
|
+
stumbleBaseChance: q(0.0),
|
|
10
|
+
fallBaseChance: q(0.0),
|
|
11
|
+
dropWeaponsOnUnconscious: false,
|
|
12
|
+
standUpBaseTicks: 0,
|
|
13
|
+
standUpMaxExtraTicks: 0,
|
|
14
|
+
unconsciousBaseTicks: 5,
|
|
15
|
+
},
|
|
16
|
+
tactical: {
|
|
17
|
+
realism: "tactical",
|
|
18
|
+
armDisableThreshold: q(0.85),
|
|
19
|
+
legDisableThreshold: q(0.90),
|
|
20
|
+
standFailThreshold: q(0.20),
|
|
21
|
+
unconsciousThreshold: q(0.10),
|
|
22
|
+
stumbleBaseChance: q(0.002),
|
|
23
|
+
fallBaseChance: q(0.001),
|
|
24
|
+
dropWeaponsOnUnconscious: false,
|
|
25
|
+
standUpBaseTicks: 15,
|
|
26
|
+
standUpMaxExtraTicks: 45,
|
|
27
|
+
unconsciousBaseTicks: 30,
|
|
28
|
+
},
|
|
29
|
+
sim: {
|
|
30
|
+
realism: "sim",
|
|
31
|
+
armDisableThreshold: q(0.75),
|
|
32
|
+
legDisableThreshold: q(0.80),
|
|
33
|
+
standFailThreshold: q(0.30),
|
|
34
|
+
unconsciousThreshold: q(0.15),
|
|
35
|
+
stumbleBaseChance: q(0.010),
|
|
36
|
+
fallBaseChance: q(0.005),
|
|
37
|
+
dropWeaponsOnUnconscious: true,
|
|
38
|
+
standUpBaseTicks: 25,
|
|
39
|
+
standUpMaxExtraTicks: 80,
|
|
40
|
+
unconsciousBaseTicks: 60,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type I32 } from "../units.js";
|
|
2
|
+
import type { Q } from "../units.js";
|
|
3
|
+
export interface Vec3 {
|
|
4
|
+
x: I32;
|
|
5
|
+
y: I32;
|
|
6
|
+
z: I32;
|
|
7
|
+
}
|
|
8
|
+
export declare const v3: (x?: I32, y?: I32, z?: I32) => Vec3;
|
|
9
|
+
export declare const vAdd: (a: Vec3, b: Vec3) => Vec3;
|
|
10
|
+
export declare const vSub: (a: Vec3, b: Vec3) => Vec3;
|
|
11
|
+
export declare const vScaleQ: (a: Vec3, q: Q) => Vec3;
|
|
12
|
+
export declare function normaliseDirCheapQ(d: Vec3): Vec3;
|
|
13
|
+
export declare const integratePos: (pos: Vec3, vel_mps: Vec3, dt_s: I32) => Vec3;
|
|
14
|
+
export declare function dotDirQ(a: Vec3, b: Vec3): Q;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SCALE } from "../units.js";
|
|
2
|
+
import { mulDiv } from "../units.js";
|
|
3
|
+
export const v3 = (x = 0, y = 0, z = 0) => ({ x, y, z });
|
|
4
|
+
export const vAdd = (a, b) => ({ x: a.x + b.x, y: a.y + b.y, z: a.z + b.z });
|
|
5
|
+
export const vSub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z });
|
|
6
|
+
export const vScaleQ = (a, q) => ({
|
|
7
|
+
x: Math.trunc((a.x * q) / SCALE.Q),
|
|
8
|
+
y: Math.trunc((a.y * q) / SCALE.Q),
|
|
9
|
+
z: Math.trunc((a.z * q) / SCALE.Q),
|
|
10
|
+
});
|
|
11
|
+
// Normalise direction into "Q-space": components scaled so max(|x|,|y|,|z|) becomes SCALE.Q.
|
|
12
|
+
// Deterministic, no sqrt. Good enough for movement intents and facing.
|
|
13
|
+
export function normaliseDirCheapQ(d) {
|
|
14
|
+
const ax = Math.abs(d.x), ay = Math.abs(d.y), az = Math.abs(d.z);
|
|
15
|
+
const m = Math.max(1, ax, ay, az);
|
|
16
|
+
return {
|
|
17
|
+
x: Math.trunc((d.x * SCALE.Q) / m),
|
|
18
|
+
y: Math.trunc((d.y * SCALE.Q) / m),
|
|
19
|
+
z: Math.trunc((d.z * SCALE.Q) / m),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// velocity is fixed m/s (SCALE.mps), dt is fixed seconds (SCALE.s)
|
|
23
|
+
export const integratePos = (pos, vel_mps, dt_s) => ({
|
|
24
|
+
x: pos.x + Math.trunc((vel_mps.x * dt_s) / SCALE.s),
|
|
25
|
+
y: pos.y + Math.trunc((vel_mps.y * dt_s) / SCALE.s),
|
|
26
|
+
z: pos.z + Math.trunc((vel_mps.z * dt_s) / SCALE.s),
|
|
27
|
+
});
|
|
28
|
+
export function dotDirQ(a, b) {
|
|
29
|
+
const d = mulDiv(a.x, b.x, SCALE.Q) + mulDiv(a.y, b.y, SCALE.Q) + mulDiv(a.z, b.z, SCALE.Q);
|
|
30
|
+
return Math.max(-SCALE.Q, Math.min(SCALE.Q, d));
|
|
31
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2C: Weapon Dynamics Expansion
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for:
|
|
5
|
+
* - Reach dominance: short weapon penalised vs longer weapon
|
|
6
|
+
* - Two-handed leverage bonus: N·m advantage on attack energy
|
|
7
|
+
* - Miss recovery: extra cooldown ticks from weapon angular momentum
|
|
8
|
+
* - Weapon bind: opposing weapons locked on parry; requires contest to clear
|
|
9
|
+
*/
|
|
10
|
+
import { type Q, type I32 } from "../units.js";
|
|
11
|
+
import type { Weapon } from "../equipment.js";
|
|
12
|
+
/**
|
|
13
|
+
* Attack skill multiplier when attacker has shorter reach than target.
|
|
14
|
+
*
|
|
15
|
+
* The penalty ramps linearly with the reach deficit fraction:
|
|
16
|
+
* penalty_frac = (targetReach - attackerReach) / targetReach × 0.40
|
|
17
|
+
* multiplier = clamp(1.0 - penalty_frac, 0.60, 1.0)
|
|
18
|
+
*
|
|
19
|
+
* Examples (at 20 Hz):
|
|
20
|
+
* knife (0.20 m) vs sword (0.80 m) → deficit 75 % → penalty 30 % → ×0.70
|
|
21
|
+
* club (0.70 m) vs club (0.70 m) → no penalty → ×1.00
|
|
22
|
+
* sword (0.80 m) vs knife (0.20 m) → no penalty (attacker longer) → ×1.00
|
|
23
|
+
*/
|
|
24
|
+
export declare function reachDomPenaltyQ(attackerReach_m: I32, targetReach_m: I32): Q;
|
|
25
|
+
/**
|
|
26
|
+
* Attack energy multiplier for two-handed weapons.
|
|
27
|
+
* Two-handed grip extends effective moment arm → more torque → higher energy transfer.
|
|
28
|
+
* Calibrated to the 1.10× parry leverage bonus: attack gets 1.12× (slightly higher, as
|
|
29
|
+
* a powered swing benefits more than a static parry).
|
|
30
|
+
*
|
|
31
|
+
* The bonus only applies when both arms are functional and no off-hand item is present.
|
|
32
|
+
*
|
|
33
|
+
* @param leftArmDisabled from FunctionalState.leftArmDisabled
|
|
34
|
+
* @param rightArmDisabled from FunctionalState.rightArmDisabled
|
|
35
|
+
* @param hasOffHand true if entity carries a shield or second weapon
|
|
36
|
+
*/
|
|
37
|
+
export declare function twoHandedAttackBonusQ(wpn: Weapon, leftArmDisabled: boolean, rightArmDisabled: boolean, hasOffHand: boolean): Q;
|
|
38
|
+
/**
|
|
39
|
+
* Extra attack cooldown ticks after a missed strike.
|
|
40
|
+
* Derived from angular momentum proxy: mass × reach.
|
|
41
|
+
*
|
|
42
|
+
* Formula: floor(mass_kg_real × reach_m_real × 2)
|
|
43
|
+
* where *_real means the value in SI units (not fixed-point).
|
|
44
|
+
*
|
|
45
|
+
* Calibration (at 20 Hz = 50 ms per tick):
|
|
46
|
+
* knife (0.30 kg, 0.20 m) → 0 ticks (0 ms extra)
|
|
47
|
+
* club (1.20 kg, 0.70 m) → 1 tick (50 ms extra)
|
|
48
|
+
* sword (1.20 kg, 0.80 m) → 1 tick (50 ms extra)
|
|
49
|
+
* halberd(2.00 kg, 1.50 m) → 6 ticks (300 ms extra)
|
|
50
|
+
*/
|
|
51
|
+
export declare function missRecoveryTicks(wpn: Weapon): number;
|
|
52
|
+
/**
|
|
53
|
+
* Probability of weapon bind occurring on a successful parry.
|
|
54
|
+
*
|
|
55
|
+
* Longer moment arms on both weapons → higher contact surface → more likely to lock.
|
|
56
|
+
*
|
|
57
|
+
* Formula:
|
|
58
|
+
* aFrac = clamp(aArm / ref, 0.1, 2.0)
|
|
59
|
+
* dFrac = clamp(dArm / ref, 0.1, 2.0)
|
|
60
|
+
* chance = clamp(arithmetic_mean(aFrac, dFrac) × 0.25, 0, 0.45)
|
|
61
|
+
*
|
|
62
|
+
* Reference arm = 0.60 m (≈ arming sword).
|
|
63
|
+
*
|
|
64
|
+
* Examples:
|
|
65
|
+
* two knives (arm 0.18 m each): chance ≈ 7.5 %
|
|
66
|
+
* two swords (arm 0.45 m each): chance ≈ 18.8 %
|
|
67
|
+
* two polearms (arm 1.50 m each, capped at 2.0): chance = 45 %
|
|
68
|
+
*/
|
|
69
|
+
export declare function bindChanceQ(attackerWpn: Weapon, defenderWpn: Weapon): Q;
|
|
70
|
+
/**
|
|
71
|
+
* Bind duration in ticks (at 20 Hz).
|
|
72
|
+
* Heavier weapons take longer to disengage.
|
|
73
|
+
*
|
|
74
|
+
* Formula: clamp(2 + floor(avgMass_kg_real), 2, 8)
|
|
75
|
+
*
|
|
76
|
+
* Examples:
|
|
77
|
+
* two knives (0.3 kg each): 2 ticks (100 ms)
|
|
78
|
+
* two clubs (1.2 kg each): 3 ticks (150 ms)
|
|
79
|
+
* heavy axes (3.0 kg each): 5 ticks (250 ms)
|
|
80
|
+
*/
|
|
81
|
+
export declare function bindDurationTicks(attackerWpn: Weapon, defenderWpn: Weapon): number;
|
|
82
|
+
/**
|
|
83
|
+
* Win probability Q for the entity trying to actively break a weapon bind.
|
|
84
|
+
*
|
|
85
|
+
* Contest: torque = peakForce_N × momentArm_m (both fixed-point, units cancel in ratio).
|
|
86
|
+
* P(break) = torque_breaker / (torque_breaker + torque_holder)
|
|
87
|
+
*
|
|
88
|
+
* Uses BigInt to avoid overflow (force ~184000 × arm ~5500 ≈ 1 billion).
|
|
89
|
+
*
|
|
90
|
+
* Returns Q ∈ [q(0.05), q(0.95)].
|
|
91
|
+
*
|
|
92
|
+
* Examples (both average humans with same weapon):
|
|
93
|
+
* Equal: P = 0.50
|
|
94
|
+
* Stronger breaker (2× force): P ≈ 0.67
|
|
95
|
+
* Much longer lever (2× arm): P ≈ 0.67
|
|
96
|
+
*
|
|
97
|
+
* @param breakerForce_N attacker's peakForce_N (fixed-point)
|
|
98
|
+
* @param holderForce_N partner's peakForce_N (fixed-point)
|
|
99
|
+
* @param breakerArm_m attacker's weapon momentArm_m (fixed-point)
|
|
100
|
+
* @param holderArm_m partner's weapon momentArm_m (fixed-point)
|
|
101
|
+
*/
|
|
102
|
+
export declare function breakBindContestQ(breakerForce_N: number, holderForce_N: number, breakerArm_m: number, holderArm_m: number): Q;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2C: Weapon Dynamics Expansion
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for:
|
|
5
|
+
* - Reach dominance: short weapon penalised vs longer weapon
|
|
6
|
+
* - Two-handed leverage bonus: N·m advantage on attack energy
|
|
7
|
+
* - Miss recovery: extra cooldown ticks from weapon angular momentum
|
|
8
|
+
* - Weapon bind: opposing weapons locked on parry; requires contest to clear
|
|
9
|
+
*/
|
|
10
|
+
import { SCALE, q, clampQ, qMul, mulDiv } from "../units.js";
|
|
11
|
+
// ---- Reach dominance ----
|
|
12
|
+
/**
|
|
13
|
+
* Attack skill multiplier when attacker has shorter reach than target.
|
|
14
|
+
*
|
|
15
|
+
* The penalty ramps linearly with the reach deficit fraction:
|
|
16
|
+
* penalty_frac = (targetReach - attackerReach) / targetReach × 0.40
|
|
17
|
+
* multiplier = clamp(1.0 - penalty_frac, 0.60, 1.0)
|
|
18
|
+
*
|
|
19
|
+
* Examples (at 20 Hz):
|
|
20
|
+
* knife (0.20 m) vs sword (0.80 m) → deficit 75 % → penalty 30 % → ×0.70
|
|
21
|
+
* club (0.70 m) vs club (0.70 m) → no penalty → ×1.00
|
|
22
|
+
* sword (0.80 m) vs knife (0.20 m) → no penalty (attacker longer) → ×1.00
|
|
23
|
+
*/
|
|
24
|
+
export function reachDomPenaltyQ(attackerReach_m, targetReach_m) {
|
|
25
|
+
if (attackerReach_m >= targetReach_m)
|
|
26
|
+
return q(1.0);
|
|
27
|
+
const deficit = targetReach_m - attackerReach_m;
|
|
28
|
+
const deficitFrac = clampQ(mulDiv(deficit, SCALE.Q, Math.max(1, targetReach_m)), q(0), q(1.0));
|
|
29
|
+
return clampQ((q(1.0) - qMul(deficitFrac, q(0.40))), q(0.60), q(1.0));
|
|
30
|
+
}
|
|
31
|
+
// ---- Two-handed leverage bonus ----
|
|
32
|
+
/**
|
|
33
|
+
* Attack energy multiplier for two-handed weapons.
|
|
34
|
+
* Two-handed grip extends effective moment arm → more torque → higher energy transfer.
|
|
35
|
+
* Calibrated to the 1.10× parry leverage bonus: attack gets 1.12× (slightly higher, as
|
|
36
|
+
* a powered swing benefits more than a static parry).
|
|
37
|
+
*
|
|
38
|
+
* The bonus only applies when both arms are functional and no off-hand item is present.
|
|
39
|
+
*
|
|
40
|
+
* @param leftArmDisabled from FunctionalState.leftArmDisabled
|
|
41
|
+
* @param rightArmDisabled from FunctionalState.rightArmDisabled
|
|
42
|
+
* @param hasOffHand true if entity carries a shield or second weapon
|
|
43
|
+
*/
|
|
44
|
+
export function twoHandedAttackBonusQ(wpn, leftArmDisabled, rightArmDisabled, hasOffHand) {
|
|
45
|
+
if ((wpn.handedness ?? "oneHand") !== "twoHand")
|
|
46
|
+
return q(1.0);
|
|
47
|
+
if (leftArmDisabled || rightArmDisabled || hasOffHand)
|
|
48
|
+
return q(1.0);
|
|
49
|
+
return q(1.12);
|
|
50
|
+
}
|
|
51
|
+
// ---- Miss recovery ----
|
|
52
|
+
/**
|
|
53
|
+
* Extra attack cooldown ticks after a missed strike.
|
|
54
|
+
* Derived from angular momentum proxy: mass × reach.
|
|
55
|
+
*
|
|
56
|
+
* Formula: floor(mass_kg_real × reach_m_real × 2)
|
|
57
|
+
* where *_real means the value in SI units (not fixed-point).
|
|
58
|
+
*
|
|
59
|
+
* Calibration (at 20 Hz = 50 ms per tick):
|
|
60
|
+
* knife (0.30 kg, 0.20 m) → 0 ticks (0 ms extra)
|
|
61
|
+
* club (1.20 kg, 0.70 m) → 1 tick (50 ms extra)
|
|
62
|
+
* sword (1.20 kg, 0.80 m) → 1 tick (50 ms extra)
|
|
63
|
+
* halberd(2.00 kg, 1.50 m) → 6 ticks (300 ms extra)
|
|
64
|
+
*/
|
|
65
|
+
export function missRecoveryTicks(wpn) {
|
|
66
|
+
const reach_m = wpn.reach_m ?? Math.trunc(0.7 * SCALE.m);
|
|
67
|
+
const num = BigInt(wpn.mass_kg) * BigInt(reach_m) * 2n;
|
|
68
|
+
const den = BigInt(SCALE.kg) * BigInt(SCALE.m);
|
|
69
|
+
return Math.max(0, Number(num / den));
|
|
70
|
+
}
|
|
71
|
+
// ---- Weapon bind ----
|
|
72
|
+
/**
|
|
73
|
+
* Probability of weapon bind occurring on a successful parry.
|
|
74
|
+
*
|
|
75
|
+
* Longer moment arms on both weapons → higher contact surface → more likely to lock.
|
|
76
|
+
*
|
|
77
|
+
* Formula:
|
|
78
|
+
* aFrac = clamp(aArm / ref, 0.1, 2.0)
|
|
79
|
+
* dFrac = clamp(dArm / ref, 0.1, 2.0)
|
|
80
|
+
* chance = clamp(arithmetic_mean(aFrac, dFrac) × 0.25, 0, 0.45)
|
|
81
|
+
*
|
|
82
|
+
* Reference arm = 0.60 m (≈ arming sword).
|
|
83
|
+
*
|
|
84
|
+
* Examples:
|
|
85
|
+
* two knives (arm 0.18 m each): chance ≈ 7.5 %
|
|
86
|
+
* two swords (arm 0.45 m each): chance ≈ 18.8 %
|
|
87
|
+
* two polearms (arm 1.50 m each, capped at 2.0): chance = 45 %
|
|
88
|
+
*/
|
|
89
|
+
export function bindChanceQ(attackerWpn, defenderWpn) {
|
|
90
|
+
const refArm = Math.trunc(0.6 * SCALE.m);
|
|
91
|
+
const aArm = attackerWpn.momentArm_m ?? Math.trunc(0.55 * SCALE.m);
|
|
92
|
+
const dArm = defenderWpn.momentArm_m ?? Math.trunc(0.55 * SCALE.m);
|
|
93
|
+
const aFrac = clampQ(mulDiv(aArm, SCALE.Q, refArm), q(0.1), q(2.0));
|
|
94
|
+
const dFrac = clampQ(mulDiv(dArm, SCALE.Q, refArm), q(0.1), q(2.0));
|
|
95
|
+
const mean = ((aFrac + dFrac) >>> 1);
|
|
96
|
+
return clampQ(qMul(mean, q(0.25)), q(0.0), q(0.45));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Bind duration in ticks (at 20 Hz).
|
|
100
|
+
* Heavier weapons take longer to disengage.
|
|
101
|
+
*
|
|
102
|
+
* Formula: clamp(2 + floor(avgMass_kg_real), 2, 8)
|
|
103
|
+
*
|
|
104
|
+
* Examples:
|
|
105
|
+
* two knives (0.3 kg each): 2 ticks (100 ms)
|
|
106
|
+
* two clubs (1.2 kg each): 3 ticks (150 ms)
|
|
107
|
+
* heavy axes (3.0 kg each): 5 ticks (250 ms)
|
|
108
|
+
*/
|
|
109
|
+
export function bindDurationTicks(attackerWpn, defenderWpn) {
|
|
110
|
+
const avgMass = (attackerWpn.mass_kg + defenderWpn.mass_kg) >>> 1;
|
|
111
|
+
const massReal = avgMass / SCALE.kg;
|
|
112
|
+
return Math.max(2, Math.min(8, Math.trunc(2 + massReal)));
|
|
113
|
+
}
|
|
114
|
+
// ---- Bind breaking contest ----
|
|
115
|
+
/**
|
|
116
|
+
* Win probability Q for the entity trying to actively break a weapon bind.
|
|
117
|
+
*
|
|
118
|
+
* Contest: torque = peakForce_N × momentArm_m (both fixed-point, units cancel in ratio).
|
|
119
|
+
* P(break) = torque_breaker / (torque_breaker + torque_holder)
|
|
120
|
+
*
|
|
121
|
+
* Uses BigInt to avoid overflow (force ~184000 × arm ~5500 ≈ 1 billion).
|
|
122
|
+
*
|
|
123
|
+
* Returns Q ∈ [q(0.05), q(0.95)].
|
|
124
|
+
*
|
|
125
|
+
* Examples (both average humans with same weapon):
|
|
126
|
+
* Equal: P = 0.50
|
|
127
|
+
* Stronger breaker (2× force): P ≈ 0.67
|
|
128
|
+
* Much longer lever (2× arm): P ≈ 0.67
|
|
129
|
+
*
|
|
130
|
+
* @param breakerForce_N attacker's peakForce_N (fixed-point)
|
|
131
|
+
* @param holderForce_N partner's peakForce_N (fixed-point)
|
|
132
|
+
* @param breakerArm_m attacker's weapon momentArm_m (fixed-point)
|
|
133
|
+
* @param holderArm_m partner's weapon momentArm_m (fixed-point)
|
|
134
|
+
*/
|
|
135
|
+
export function breakBindContestQ(breakerForce_N, holderForce_N, breakerArm_m, holderArm_m) {
|
|
136
|
+
const tBreaker = BigInt(breakerForce_N) * BigInt(breakerArm_m);
|
|
137
|
+
const tHolder = BigInt(holderForce_N) * BigInt(holderArm_m);
|
|
138
|
+
const total = tBreaker + tHolder;
|
|
139
|
+
if (total === 0n)
|
|
140
|
+
return q(0.5);
|
|
141
|
+
return clampQ(Number(tBreaker * BigInt(SCALE.Q) / total), q(0.05), q(0.95));
|
|
142
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
/** Precipitation category. */
|
|
3
|
+
export type PrecipitationType = "none" | "rain" | "heavy_rain" | "snow" | "blizzard" | "hail";
|
|
4
|
+
/**
|
|
5
|
+
* 2D wind vector.
|
|
6
|
+
* `dx_m` and `dy_m` form a unit vector in SCALE.m space: |(dx_m, dy_m)| = SCALE.m.
|
|
7
|
+
* `speed_mps` is the wind speed in SCALE.mps units.
|
|
8
|
+
*/
|
|
9
|
+
export interface WindField {
|
|
10
|
+
/** X component of wind direction (SCALE.m = 1000; unit vector). */
|
|
11
|
+
dx_m: number;
|
|
12
|
+
/** Y component of wind direction (SCALE.m = 1000; unit vector). */
|
|
13
|
+
dy_m: number;
|
|
14
|
+
/** Wind speed [SCALE.mps]. 1000 = 10 m/s. */
|
|
15
|
+
speed_mps: number;
|
|
16
|
+
}
|
|
17
|
+
/** Weather conditions passed into a simulation tick via KernelContext.weather. */
|
|
18
|
+
export interface WeatherState {
|
|
19
|
+
wind?: WindField;
|
|
20
|
+
precipitation?: PrecipitationType;
|
|
21
|
+
/**
|
|
22
|
+
* Fog density (Q 0..SCALE.Q).
|
|
23
|
+
* q(0) = clear; q(1.0) = pea-soup fog → vision capped to q(0.10) of normal.
|
|
24
|
+
*/
|
|
25
|
+
fogDensity_Q?: Q;
|
|
26
|
+
}
|
|
27
|
+
/** Derived modifiers ready for the kernel to apply each tick. */
|
|
28
|
+
export interface WeatherModifiers {
|
|
29
|
+
/**
|
|
30
|
+
* Multiply KernelContext.tractionCoeff.
|
|
31
|
+
* q(1.0) = unchanged; q(0.70) = 30% slipperier surface.
|
|
32
|
+
*/
|
|
33
|
+
tractionMul_Q: Q;
|
|
34
|
+
/**
|
|
35
|
+
* Multiply SensoryEnvironment.lightMul (fog + precipitation combined).
|
|
36
|
+
* q(1.0) = clear; q(0.30) = dense blizzard / fog.
|
|
37
|
+
*/
|
|
38
|
+
lightMul_Q: Q;
|
|
39
|
+
/**
|
|
40
|
+
* Multiply SensoryEnvironment.smokeMul (precipitation visual masking only).
|
|
41
|
+
* Heavy rain / blizzard partially obscures vision like smoke.
|
|
42
|
+
*/
|
|
43
|
+
precipVisionMul_Q: Q;
|
|
44
|
+
/**
|
|
45
|
+
* Additive offset to KernelContext.thermalAmbient_Q (Phase-29 Q encoding).
|
|
46
|
+
* Negative = cooling effect. 1 °C ≈ WEATHER_Q_PER_DEG_C Q units.
|
|
47
|
+
*/
|
|
48
|
+
thermalOffset_Q: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Approximate Q units per degree Celsius in the Phase-29 thermal encoding.
|
|
52
|
+
* (SCALE.Q / 54) where 54 = 64°C − 10°C temperature range).
|
|
53
|
+
*/
|
|
54
|
+
export declare const WEATHER_Q_PER_DEG_C = 185;
|
|
55
|
+
/**
|
|
56
|
+
* Compute all derived modifiers for a WeatherState.
|
|
57
|
+
* Pure function — no side effects.
|
|
58
|
+
*/
|
|
59
|
+
export declare function deriveWeatherModifiers(weather: WeatherState): WeatherModifiers;
|
|
60
|
+
/**
|
|
61
|
+
* Additional aim grouping radius from wind drift [SCALE.m].
|
|
62
|
+
*
|
|
63
|
+
* Physics: drift = v_wind_perp × (range / v_proj)
|
|
64
|
+
*
|
|
65
|
+
* The perpendicular wind component is the 2D cross product of shot direction
|
|
66
|
+
* and wind direction. Algebraic simplification yields:
|
|
67
|
+
*
|
|
68
|
+
* drift_scaled_m = wind.speed_mps × |shot × wind| / (SCALE.m × v_proj_mps)
|
|
69
|
+
*
|
|
70
|
+
* where |shot × wind| is the cross-product magnitude in SCALE.m² units with
|
|
71
|
+
* shot direction of magnitude `dist_m` and wind direction of magnitude SCALE.m.
|
|
72
|
+
* The `dist_m` factors cancel, leaving a formula independent of scale choice.
|
|
73
|
+
*
|
|
74
|
+
* @param wind Wind field (direction + speed).
|
|
75
|
+
* @param shotDx_m Shot direction x [SCALE.m] (raw, not normalised).
|
|
76
|
+
* @param shotDy_m Shot direction y [SCALE.m] (raw, not normalised).
|
|
77
|
+
* @param dist_m Range to target [SCALE.m].
|
|
78
|
+
* @param v_proj_mps Projectile speed [SCALE.mps].
|
|
79
|
+
* @returns Additional grouping radius [SCALE.m] (≥ 0).
|
|
80
|
+
*/
|
|
81
|
+
export declare function computeWindAimError(wind: WindField, shotDx_m: number, shotDy_m: number, dist_m: number, v_proj_mps: number): number;
|
|
82
|
+
/**
|
|
83
|
+
* Adjust cone range for wind effects (breath weapons, gas clouds, flamethrowers).
|
|
84
|
+
*
|
|
85
|
+
* - Headwind (wind opposes cone direction) reduces range up to −20%.
|
|
86
|
+
* - Tailwind extends range up to +10%.
|
|
87
|
+
* - Crosswind (perpendicular) has no effect on range.
|
|
88
|
+
*
|
|
89
|
+
* @param wind Wind field.
|
|
90
|
+
* @param coneDx_m Cone facing direction x (SCALE.m unit vector).
|
|
91
|
+
* @param coneDy_m Cone facing direction y (SCALE.m unit vector).
|
|
92
|
+
* @param range_m Base cone range [SCALE.m].
|
|
93
|
+
* @returns Adjusted range [SCALE.m] (≥ 0).
|
|
94
|
+
*/
|
|
95
|
+
export declare function adjustConeRange(wind: WindField, coneDx_m: number, coneDy_m: number, range_m: number): number;
|