@adatechnology/game-characters 0.1.6 → 0.2.1
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/README.md +28 -0
- package/dist/index.d.ts +100 -1
- package/dist/index.global.js +507 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +507 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -17,3 +17,31 @@ person.lastHit // { points, killed, headshot, through, exitEnergy }
|
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Plain script: `dist/index.global.js` exposes the `GameCharacters` global.
|
|
20
|
+
|
|
21
|
+
## Pose clips
|
|
22
|
+
|
|
23
|
+
Keyframed poses over the joint names of the rich body (`lean`, `twist`, `side`, `drop`, `headX/Y/Z`, `armR/L`, `elbR/L`, `armRz/Lz`, `legL/R`, `kneeL/R`). `samplePoseClip(clip, t)` is
|
|
24
|
+
the Bestiário's `sample` (find the segment, clamp, smoothstep, `lerpPose`); `clipEventsBetween(clip, t0, t1)` gives the events crossed in `(t0, t1]` (`impact` for "the blow lands
|
|
25
|
+
now", `phase` with a locale key). `channels` says what a clip drives: `full`, `upper` (the legs keep walking) or `lower`. `POSE_CLIPS` has the two clips of the "grab and bite"
|
|
26
|
+
scene (`grabbed_bitten_human`, `grab_bite_zombie`, with `x` and `run` as extra channels), texts in `POSE_CLIP_LOCALES` (`pt-BR`, `en`). A game adds or replaces clips with
|
|
27
|
+
`createPoseClipRegistry(POSE_CLIPS).register(clip)`.
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { POSE_CLIPS, createPoseClipRegistry, samplePoseClip } from '@adatechnology/game-characters'
|
|
31
|
+
|
|
32
|
+
const clips = createPoseClipRegistry(POSE_CLIPS)
|
|
33
|
+
samplePoseClip(clips.get('grabbed_bitten_human')!, 2.5) // { lean, headY, armL, ... }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Clip format (JSON).** A team can add clips, attacks included, as data and register them; the core has no clip of its own beyond the two Bestiário ones.
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"id": "my_clip", "duration": 1.2, "loop": false, "channels": "upper",
|
|
41
|
+
"keys": [{ "t": 0, "pose": { "armR": -0.1 } }, { "t": 0.4, "pose": { "armR": -1.6, "lean": 0.3 } }, { "t": 1.2, "pose": { "armR": -0.1 } }],
|
|
42
|
+
"events": [{ "t": 0.4, "type": "impact", "data": { "kind": "blow" } }]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Event types: `impact` is the moment the blow, bite or hit lands (the game applies the damage when it fires); `phase` carries `data.label`, a locale key. Any other type is the game's.
|
|
47
|
+
`clipEventsBetween` returns an event once, for the interval it falls in `(t0, t1]`.
|
package/dist/index.d.ts
CHANGED
|
@@ -139,6 +139,8 @@ type BlastRules = {
|
|
|
139
139
|
readonly headDestroyPoints: number;
|
|
140
140
|
/** A blast this strong on a segment destroys it when it reaches 0. */
|
|
141
141
|
readonly bigPoints: number;
|
|
142
|
+
/** Most points a segment takes in the injury zone of a blast with a `lethalRadius` (between the lethal radius and `radius`); default 15, which leaves a person just past the lethal radius badly hurt and one at the middle of the zone with about a third of the vitality gone. */
|
|
143
|
+
readonly injuryPoints?: number;
|
|
142
144
|
};
|
|
143
145
|
type DeathRule = 'vitality-or-head' | 'head-only';
|
|
144
146
|
type SegmentRuleId = 'kit' | 'arsenal' | 'elenco';
|
|
@@ -1102,4 +1104,101 @@ type ProtectParams = {
|
|
|
1102
1104
|
/** A stopped projectile does the layer's `blunt` share; one that goes through, `pass` (default `blunt`). Non-projectiles ignore ballistic layers. */
|
|
1103
1105
|
declare const protect: ({ layers, projectile, model }: ProtectParams) => ProtectionOutcome;
|
|
1104
1106
|
|
|
1105
|
-
|
|
1107
|
+
/** One key of a clip: the time in seconds and the joint targets by name (the names of the rich body's `setPose`; a clip may add channels of its own, such as `x` or `run`). */
|
|
1108
|
+
type PoseKey = {
|
|
1109
|
+
t: number;
|
|
1110
|
+
pose: Record<string, number>;
|
|
1111
|
+
};
|
|
1112
|
+
/** What a clip says happens at a time: `impact` ("the blow lands now"), `phase` (a label for the screen), or any type a game defines. */
|
|
1113
|
+
type PoseClipEvent = {
|
|
1114
|
+
t: number;
|
|
1115
|
+
type: string;
|
|
1116
|
+
data?: Record<string, unknown>;
|
|
1117
|
+
};
|
|
1118
|
+
/** Which joints a clip drives: `upper` leaves the legs to the gait, `lower` leaves the trunk, head and arms, `full` sets everything. */
|
|
1119
|
+
type PoseClipChannels = 'full' | 'upper' | 'lower';
|
|
1120
|
+
type PoseClip = {
|
|
1121
|
+
id: string;
|
|
1122
|
+
duration: number;
|
|
1123
|
+
loop?: boolean;
|
|
1124
|
+
keys: PoseKey[];
|
|
1125
|
+
events?: PoseClipEvent[];
|
|
1126
|
+
channels?: PoseClipChannels;
|
|
1127
|
+
/** Where the clip was ported from (file and variable), for the diff against the team page. */
|
|
1128
|
+
source?: Record<string, unknown>;
|
|
1129
|
+
};
|
|
1130
|
+
/**
|
|
1131
|
+
* The Bestiário's `sample(K, t)`: finds the segment of two keys, clamps the fraction to [0, 1], eases it with smoothstep `k·k·(3 − 2k)` and
|
|
1132
|
+
* blends the two poses by joint name (`lerpPose`). Before the first key it holds the first segment's start, after the last it holds the end.
|
|
1133
|
+
*/
|
|
1134
|
+
declare const samplePoseClip: (clip: Pick<PoseClip, "keys">, time: number) => Record<string, number>;
|
|
1135
|
+
/** The events a clip fires while the clock goes from `from` (excluded) to `to` (included), in time order. */
|
|
1136
|
+
declare const clipEventsBetween: (clip: Pick<PoseClip, "events">, from: number, to: number) => PoseClipEvent[];
|
|
1137
|
+
/** The names of the joints a channel set drives, from the names `PoseTargets` of the rich animator knows. */
|
|
1138
|
+
declare const POSE_CHANNEL_JOINTS: Readonly<Record<PoseClipChannels, readonly string[] | undefined>>;
|
|
1139
|
+
/** Keeps from a sampled pose only the joints of the channel set (`full` keeps everything, including extra channels such as `x`). */
|
|
1140
|
+
declare const filterPoseChannels: (pose: Record<string, number>, channels?: PoseClipChannels) => Record<string, number>;
|
|
1141
|
+
/** The clip as seen in a mirror: left and right swap and the sideways channels change sign; the times, the events and the duration stay, so the impact falls at the same second. */
|
|
1142
|
+
declare const mirrorPoseClip: (clip: PoseClip, id: string) => PoseClip;
|
|
1143
|
+
|
|
1144
|
+
type PoseClipRegistry = {
|
|
1145
|
+
/** Adds a clip, or replaces the one with the same id. Returns the registry. */
|
|
1146
|
+
register(clip: PoseClip): PoseClipRegistry;
|
|
1147
|
+
get(id: string): PoseClip | undefined;
|
|
1148
|
+
list(): PoseClip[];
|
|
1149
|
+
remove(id: string): boolean;
|
|
1150
|
+
};
|
|
1151
|
+
/** A registry of clips by id: games add or replace clips without touching the core. */
|
|
1152
|
+
declare const createPoseClipRegistry: (initial?: readonly PoseClip[]) => PoseClipRegistry;
|
|
1153
|
+
/** The clips ported from the Bestiário "grab and bite" scene (human and zombie). Phase labels are locale keys, see `POSE_CLIP_LOCALES`. */
|
|
1154
|
+
declare const POSE_CLIPS: readonly PoseClip[];
|
|
1155
|
+
/** Texts of the phase labels by locale (`pt-BR` is the team's text). */
|
|
1156
|
+
declare const POSE_CLIP_LOCALES: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
1157
|
+
|
|
1158
|
+
type Triple = [number, number, number];
|
|
1159
|
+
/**
|
|
1160
|
+
* A hand or a foot in the camera's space (x to the right, y up, the view looks down -z), in metres and radians. `p` is the middle of the palm (the ankle for a foot) and `r` the Euler angles
|
|
1161
|
+
* (order XYZ) of its frame: x from the wrist to the knuckles (from the heel to the toes), y the back of the hand (the top of the foot), z the rest of the right-handed frame; a left hand is the
|
|
1162
|
+
* mirror image of a right one, so the same numbers describe the same grip.
|
|
1163
|
+
*/
|
|
1164
|
+
type FirstPersonPoseKey = {
|
|
1165
|
+
p: Triple;
|
|
1166
|
+
r: Triple;
|
|
1167
|
+
/** How closed the fingers are, 0 open to 1 closed; left out, the fingers keep what the stance has. */
|
|
1168
|
+
grip?: number;
|
|
1169
|
+
/** How much of the clip replaces what the hand is doing otherwise (holding a weapon, the stance), 0 none to 1 all. Default 1. */
|
|
1170
|
+
w?: number;
|
|
1171
|
+
};
|
|
1172
|
+
/** One key: at `t` seconds the right hand (`R`), the left hand (`L`, or `'mirror'` for the mirror image of the right) and the right foot (`F`) are where the entries say. A side without an entry here is not driven by this key. */
|
|
1173
|
+
type FirstPersonKey = {
|
|
1174
|
+
t: number;
|
|
1175
|
+
R?: FirstPersonPoseKey;
|
|
1176
|
+
L?: FirstPersonPoseKey | 'mirror';
|
|
1177
|
+
F?: FirstPersonPoseKey;
|
|
1178
|
+
};
|
|
1179
|
+
/** A first-person animation of the hands and the foot: the counterpart of a third-person `PoseClip`, with the same events. */
|
|
1180
|
+
type FirstPersonClip = {
|
|
1181
|
+
id: string;
|
|
1182
|
+
duration: number;
|
|
1183
|
+
keys: FirstPersonKey[];
|
|
1184
|
+
events?: PoseClipEvent[];
|
|
1185
|
+
/** Where the clip was ported from, or who authored it. */
|
|
1186
|
+
source?: Record<string, unknown>;
|
|
1187
|
+
};
|
|
1188
|
+
/** What a channel does at a time. `mirror` says the numbers are the right hand's and the left hand is their mirror image. */
|
|
1189
|
+
type FirstPersonSampleChannel = {
|
|
1190
|
+
p: Triple;
|
|
1191
|
+
r: Triple;
|
|
1192
|
+
grip: number | undefined;
|
|
1193
|
+
w: number;
|
|
1194
|
+
mirror: boolean;
|
|
1195
|
+
};
|
|
1196
|
+
type FirstPersonSample = {
|
|
1197
|
+
R?: FirstPersonSampleChannel;
|
|
1198
|
+
L?: FirstPersonSampleChannel;
|
|
1199
|
+
F?: FirstPersonSampleChannel;
|
|
1200
|
+
};
|
|
1201
|
+
/** Where each driven channel is at `time`: between two keys of a channel the numbers are blended with the smoothstep of `samplePoseClip`; before its first key and after its last it holds them. */
|
|
1202
|
+
declare const sampleFirstPersonClip: (clip: Pick<FirstPersonClip, "keys">, time: number) => FirstPersonSample;
|
|
1203
|
+
|
|
1204
|
+
export { ARSENAL_RULES, type AgeGroup, type AnimateProfileParams, palette as AppearancePalette, type AppearanceParams, type AppearanceTraits, BLOOD_LITERS_PER_DROP, BLOOD_SEED_OFFSET, BODY_MATERIAL, BODY_REGIONS, type BallisticClass, type BlastRules, type Bleeder, type BodyHit, type BodyMultipliers, type BodySpec, type BodyVariance, type BuildClass, CASCADE, CRAWL_TILT_RATE, type CaliberEntry, type CascadeJoint, type Character, type CharacterKit, type CharacterPack, type CharacterPreset, type CharacterSpec, type CharactersOptions, type ConditionOptions, type ConditionPhase, type ConditionSample, type ConditionTimeline, type CorpseEntry, type CorpseLedger, type CorpseOptions, type CrawlPose, type CreateRigParams, DAMAGE_PRESETS, DEAD_TILT_RATE, DEFAULT_CORPSES, DEFAULT_DAMAGE_RULES, type DamageCharacter, type DamageOptions, type DamageRules, type DeadPose, type DeathRule, type Detail, type DrawnAppearance, ELENCO_RULES, type FirstPersonClip, type FirstPersonKey, type FirstPersonPoseKey, type FirstPersonSample, type FirstPersonSampleChannel, GAME_BONES, GUIDE, type GaitProfile, type GaitSample, type HairStyle, type HitReport, type HumanArm, type HumanPose, type HumanPoseParams, INSTANCE_POSES, type InstancePose, JOINT_IDS, JOINT_PARENTS, type JointAngles, type JointId, LAYERED_PROTECTION_CAP, LIMB_REGIONS, LIMP, type LegAngles, type LimbRegion, type LineHit, type LineResult, type LineShot, type LineTarget, MOVES, MOVE_BLEND_RATE, type MoveBlend, type MoveId, type OutfitLook, PENETRATION_MODELS, PERSON_PRESET, PHYSICAL_PENETRATION, POSE_CHANNEL_JOINTS, POSE_CLIPS, POSE_CLIP_LOCALES, PROTECTION_PRESETS, type PenetrateLineParams, type PenetrationModel, type PoseClip, type PoseClipChannels, type PoseClipEvent, type PoseClipRegistry, type PoseKey, type ProjectileInfo, type ProtectionLayer, type ProtectionOutcome, REGION_HIT_POINTS, REGION_SEGMENTS, type ResolvedCharacterSpec, type RestPose, type Rig, SEGMENT_IDS, SEGMENT_NUMBERS, SEGMENT_TREE, type SegmentId, type SegmentNode, type SegmentNumbers, type SegmentRuleId, type SegmentSide, type SegmentState, type SegmentStatus, type SeverityRules, type Stride, type StrideParams, TEAM_GAME, TEAM_GAME_CALIBERS, TEAM_GAME_CALIBER_KEYS, TEAM_GAME_PENETRATION, TORSO_RESISTANCE, type VarianceScale, animateProfile, applyPatches, applyPreset, bleedRateAt, blendMoves, clipEventsBetween, crawlPose, createBloodStream, createBodySpec, createCharacters, createConditionTimeline, createCorpseLedger, createPoseClipRegistry, createRig, createRng, cutOf, deadPose, decodePoseHeight, describeBody, drawAppearance, drawBodyVariance, emptySample, encodePoseHeight, energyKept, filterPoseChannels, humanPose, isBodyRegion, isSegmentId, layeredProtection, layersOn, lerpPose, mergeById, mirrorPoseClip, moveBlendOf, penetrateLine, penetrationForce, pointsFromEnergy, projectileOf, protect, removeIds, resolveDamageRules, resolvePoints, sampleFirstPersonClip, samplePoseClip, segmentNode, segmentResistance, segmentsBelow, spreadPoints, stepCascade, strideFor, teamGameCost, teamGameFalloff, variantCountFor, variantDecay, variantSeed, walkCadence, walkGait };
|