@sharpee/ext-basic-combat 0.9.86

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.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Basic Combat Interceptor
3
+ *
4
+ * Wraps CombatService as an ActionInterceptor for PC→NPC attacks.
5
+ * Registered on CombatantTrait for if.action.attacking.
6
+ */
7
+ import { ActionInterceptor } from "./src/index";
8
+ /**
9
+ * ActionInterceptor that uses CombatService for PC→NPC combat resolution.
10
+ *
11
+ * postExecute populates sharedData with:
12
+ * - attackResult: AttackResult-shaped object
13
+ * - combatResult: CombatResult from CombatService
14
+ * - usedCombatService: true
15
+ */
16
+ export declare const BasicCombatInterceptor: ActionInterceptor;
17
+ //# sourceMappingURL=basic-combat-interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basic-combat-interceptor.d.ts","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/basic-combat-interceptor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,iBAAiB,EAMlB,MAAM,sBAAsB,CAAC;AAS9B;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBA4CpC,CAAC"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Basic Combat Interceptor
4
+ *
5
+ * Wraps CombatService as an ActionInterceptor for PC→NPC attacks.
6
+ * Registered on CombatantTrait for if.action.attacking.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.BasicCombatInterceptor = void 0;
10
+ const core_1 = require("./src/index.js");
11
+ const stdlib_1 = require("./src/index.js");
12
+ const combat_service_js_1 = require("./combat-service.js");
13
+ /**
14
+ * Module-level random instance for consistent combat rolls.
15
+ */
16
+ const combatRandom = (0, core_1.createSeededRandom)();
17
+ /**
18
+ * ActionInterceptor that uses CombatService for PC→NPC combat resolution.
19
+ *
20
+ * postExecute populates sharedData with:
21
+ * - attackResult: AttackResult-shaped object
22
+ * - combatResult: CombatResult from CombatService
23
+ * - usedCombatService: true
24
+ */
25
+ exports.BasicCombatInterceptor = {
26
+ postExecute(target, world, actorId, sharedData) {
27
+ const attacker = world.getEntity(actorId);
28
+ if (!attacker)
29
+ return;
30
+ // Use weapon from sharedData (attacking.ts passes it) or find one
31
+ const weaponId = sharedData.weaponId;
32
+ const weapon = weaponId
33
+ ? world.getEntity(weaponId)
34
+ : (0, stdlib_1.findWieldedWeapon)(attacker, world);
35
+ const combatService = new combat_service_js_1.CombatService();
36
+ const combatResult = combatService.resolveAttack({
37
+ attacker,
38
+ target,
39
+ weapon: weapon || undefined,
40
+ world,
41
+ random: combatRandom,
42
+ });
43
+ // Apply combat result to target (handles health, death, inventory dropping)
44
+ const combatApplyResult = (0, combat_service_js_1.applyCombatResult)(target, combatResult, world);
45
+ // Populate sharedData for the report phase
46
+ sharedData.attackResult = {
47
+ success: true,
48
+ type: combatResult.targetKilled ? 'killed' :
49
+ combatResult.targetKnockedOut ? 'knocked_out' :
50
+ combatResult.hit ? 'hit' : 'missed',
51
+ damage: combatResult.damage,
52
+ remainingHitPoints: combatResult.targetNewHealth,
53
+ targetDestroyed: false,
54
+ targetKilled: combatResult.targetKilled,
55
+ targetKnockedOut: combatResult.targetKnockedOut,
56
+ itemsDropped: combatApplyResult.droppedItems,
57
+ };
58
+ sharedData.combatResult = combatResult;
59
+ sharedData.usedCombatService = true;
60
+ },
61
+ };
62
+ //# sourceMappingURL=basic-combat-interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basic-combat-interceptor.js","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/basic-combat-interceptor.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,wCAAiE;AASjE,4CAAoD;AACpD,2DAAqF;AAErF;;GAEG;AACH,MAAM,YAAY,GAAiB,IAAA,yBAAkB,GAAE,CAAC;AAExD;;;;;;;GAOG;AACU,QAAA,sBAAsB,GAAsB;IACvD,WAAW,CACT,MAAgB,EAChB,KAAiB,EACjB,OAAe,EACf,UAAiC;QAEjC,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,kEAAkE;QAClE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAA8B,CAAC;QAC3D,MAAM,MAAM,GAAG,QAAQ;YACrB,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC3B,CAAC,CAAC,IAAA,0BAAiB,EAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAEvC,MAAM,aAAa,GAAG,IAAI,iCAAa,EAAE,CAAC;QAC1C,MAAM,YAAY,GAAG,aAAa,CAAC,aAAa,CAAC;YAC/C,QAAQ;YACR,MAAM;YACN,MAAM,EAAE,MAAM,IAAI,SAAS;YAC3B,KAAK;YACL,MAAM,EAAE,YAAY;SACrB,CAAC,CAAC;QAEH,4EAA4E;QAC5E,MAAM,iBAAiB,GAAG,IAAA,qCAAiB,EAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAEzE,2CAA2C;QAC3C,UAAU,CAAC,YAAY,GAAG;YACxB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBACtC,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;oBAC/C,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;YACzC,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,kBAAkB,EAAE,YAAY,CAAC,eAAe;YAChD,eAAe,EAAE,KAAK;YACtB,YAAY,EAAE,YAAY,CAAC,YAAY;YACvC,gBAAgB,EAAE,YAAY,CAAC,gBAAgB;YAC/C,YAAY,EAAE,iBAAiB,CAAC,YAAY;SAC7C,CAAC;QACF,UAAU,CAAC,YAAY,GAAG,YAAY,CAAC;QACvC,UAAU,CAAC,iBAAiB,GAAG,IAAI,CAAC;IACtC,CAAC;CACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Basic NPC Combat Resolver
3
+ *
4
+ * Wraps CombatService as an NpcCombatResolver for NPC→PC (and NPC→NPC) attacks.
5
+ * Used by npc-service.ts executeAttack() when registered.
6
+ */
7
+ import type { NpcCombatResolver } from "./src/index";
8
+ /**
9
+ * Basic NPC combat resolver using CombatService.
10
+ *
11
+ * Resolves NPC attacks using the skill-based probability system.
12
+ * Returns semantic events for the attack result and optional death.
13
+ */
14
+ export declare const basicNpcResolver: NpcCombatResolver;
15
+ //# sourceMappingURL=basic-npc-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basic-npc-resolver.d.ts","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/basic-npc-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AA8BzD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,iBAuE9B,CAAC"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * Basic NPC Combat Resolver
4
+ *
5
+ * Wraps CombatService as an NpcCombatResolver for NPC→PC (and NPC→NPC) attacks.
6
+ * Used by npc-service.ts executeAttack() when registered.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.basicNpcResolver = void 0;
10
+ const core_1 = require("./src/index.js");
11
+ const world_model_1 = require("./src/index.js");
12
+ const stdlib_1 = require("./src/index.js");
13
+ const combat_service_js_1 = require("./combat-service.js");
14
+ /**
15
+ * Module-level random instance for NPC combat rolls.
16
+ */
17
+ const npcCombatRandom = (0, core_1.createSeededRandom)();
18
+ let eventCounter = 0;
19
+ /**
20
+ * Create a semantic event (mirrors npc-service's createEvent pattern).
21
+ */
22
+ function createEvent(type, data, sourceId) {
23
+ return {
24
+ id: `npc-combat-${Date.now()}-${++eventCounter}`,
25
+ type,
26
+ data,
27
+ entities: {
28
+ actor: sourceId,
29
+ },
30
+ timestamp: Date.now(),
31
+ };
32
+ }
33
+ /**
34
+ * Basic NPC combat resolver using CombatService.
35
+ *
36
+ * Resolves NPC attacks using the skill-based probability system.
37
+ * Returns semantic events for the attack result and optional death.
38
+ */
39
+ const basicNpcResolver = (npc, target, world, random) => {
40
+ const events = [];
41
+ // Only resolve for combatant targets
42
+ if (!target.has(world_model_1.TraitType.COMBATANT)) {
43
+ events.push(createEvent('npc.attacked', {
44
+ npc: npc.id,
45
+ target: target.id,
46
+ }, npc.id));
47
+ return events;
48
+ }
49
+ const combatService = new combat_service_js_1.CombatService();
50
+ // Find NPC's weapon
51
+ const npcInventory = world.getContents(npc.id);
52
+ const weapon = (0, stdlib_1.findWieldedWeapon)(npc, world) ||
53
+ npcInventory.find(item => item.isWeapon);
54
+ const combatResult = combatService.resolveAttack({
55
+ attacker: npc,
56
+ target,
57
+ weapon: weapon || undefined,
58
+ world,
59
+ random,
60
+ });
61
+ // Apply combat result to target
62
+ (0, combat_service_js_1.applyCombatResult)(target, combatResult, world);
63
+ // Emit attack event with combat result
64
+ const npcMessageId = combatResult.messageId.replace('combat.attack.', 'npc.combat.attack.');
65
+ events.push(createEvent('npc.attacked', {
66
+ npc: npc.id,
67
+ npcName: npc.name,
68
+ target: target.id,
69
+ targetName: target.name,
70
+ hit: combatResult.hit,
71
+ damage: combatResult.damage,
72
+ messageId: npcMessageId,
73
+ targetKilled: combatResult.targetKilled,
74
+ targetKnockedOut: combatResult.targetKnockedOut,
75
+ }, npc.id));
76
+ // If target was killed, emit death event
77
+ if (combatResult.targetKilled) {
78
+ events.push(createEvent('if.event.death', {
79
+ target: target.id,
80
+ targetName: target.name,
81
+ killedBy: npc.id,
82
+ }, npc.id));
83
+ }
84
+ return events;
85
+ };
86
+ exports.basicNpcResolver = basicNpcResolver;
87
+ //# sourceMappingURL=basic-npc-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basic-npc-resolver.js","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/basic-npc-resolver.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,wCAAiF;AACjF,sDAI8B;AAC9B,4CAAoD;AAEpD,2DAAuE;AAGvE;;GAEG;AACH,MAAM,eAAe,GAAiB,IAAA,yBAAkB,GAAE,CAAC;AAE3D,IAAI,YAAY,GAAG,CAAC,CAAC;AAErB;;GAEG;AACH,SAAS,WAAW,CAClB,IAAY,EACZ,IAA6B,EAC7B,QAAiB;IAEjB,OAAO;QACL,EAAE,EAAE,cAAc,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE;QAChD,IAAI;QACJ,IAAI;QACJ,QAAQ,EAAE;YACR,KAAK,EAAE,QAAQ;SAChB;QACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACI,MAAM,gBAAgB,GAAsB,CACjD,GAAa,EACb,MAAgB,EAChB,KAAiB,EACjB,MAAoB,EACF,EAAE;IACpB,MAAM,MAAM,GAAqB,EAAE,CAAC;IAEpC,qCAAqC;IACrC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,WAAW,CACrB,cAAc,EACd;YACE,GAAG,EAAE,GAAG,CAAC,EAAE;YACX,MAAM,EAAE,MAAM,CAAC,EAAE;SAClB,EACD,GAAG,CAAC,EAAE,CACP,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,iCAAa,EAAE,CAAC;IAE1C,oBAAoB;IACpB,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAA,0BAAiB,EAAC,GAAG,EAAE,KAAK,CAAC;QAC1C,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAE,IAAY,CAAC,QAAQ,CAAC,CAAC;IAEpD,MAAM,YAAY,GAAG,aAAa,CAAC,aAAa,CAAC;QAC/C,QAAQ,EAAE,GAAG;QACb,MAAM;QACN,MAAM,EAAE,MAAM,IAAI,SAAS;QAC3B,KAAK;QACL,MAAM;KACP,CAAC,CAAC;IAEH,gCAAgC;IAChC,IAAA,qCAAiB,EAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;IAE/C,uCAAuC;IACvC,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;IAC5F,MAAM,CAAC,IAAI,CAAC,WAAW,CACrB,cAAc,EACd;QACE,GAAG,EAAE,GAAG,CAAC,EAAE;QACX,OAAO,EAAE,GAAG,CAAC,IAAI;QACjB,MAAM,EAAE,MAAM,CAAC,EAAE;QACjB,UAAU,EAAE,MAAM,CAAC,IAAI;QACvB,GAAG,EAAE,YAAY,CAAC,GAAG;QACrB,MAAM,EAAE,YAAY,CAAC,MAAM;QAC3B,SAAS,EAAE,YAAY;QACvB,YAAY,EAAE,YAAY,CAAC,YAAY;QACvC,gBAAgB,EAAE,YAAY,CAAC,gBAAgB;KAChD,EACD,GAAG,CAAC,EAAE,CACP,CAAC,CAAC;IAEH,yCAAyC;IACzC,IAAI,YAAY,CAAC,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,WAAW,CACrB,gBAAgB,EAChB;YACE,MAAM,EAAE,MAAM,CAAC,EAAE;YACjB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,QAAQ,EAAE,GAAG,CAAC,EAAE;SACjB,EACD,GAAG,CAAC,EAAE,CACP,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAvEW,QAAA,gBAAgB,oBAuE3B"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Combat Message IDs (ADR-072)
3
+ *
4
+ * Semantic message IDs for combat-related events.
5
+ * Actual text is provided by the language layer.
6
+ */
7
+ /**
8
+ * Message IDs for combat events
9
+ */
10
+ export declare const CombatMessages: {
11
+ readonly ATTACK_MISSED: "combat.attack.missed";
12
+ readonly ATTACK_HIT: "combat.attack.hit";
13
+ readonly ATTACK_HIT_LIGHT: "combat.attack.hit_light";
14
+ readonly ATTACK_HIT_HEAVY: "combat.attack.hit_heavy";
15
+ readonly ATTACK_KNOCKED_OUT: "combat.attack.knocked_out";
16
+ readonly ATTACK_KILLED: "combat.attack.killed";
17
+ readonly DEFEND_BLOCKED: "combat.defend.blocked";
18
+ readonly DEFEND_PARRIED: "combat.defend.parried";
19
+ readonly DEFEND_DODGED: "combat.defend.dodged";
20
+ readonly HEALTH_HEALTHY: "combat.health.healthy";
21
+ readonly HEALTH_WOUNDED: "combat.health.wounded";
22
+ readonly HEALTH_BADLY_WOUNDED: "combat.health.badly_wounded";
23
+ readonly HEALTH_NEAR_DEATH: "combat.health.near_death";
24
+ readonly HEALTH_UNCONSCIOUS: "combat.health.unconscious";
25
+ readonly HEALTH_DEAD: "combat.health.dead";
26
+ readonly SWORD_GLOWS: "combat.special.sword_glows";
27
+ readonly SWORD_STOPS_GLOWING: "combat.special.sword_stops_glowing";
28
+ readonly BLESSED_WEAPON_EFFECT: "combat.special.blessed_weapon";
29
+ readonly CANNOT_ATTACK: "combat.cannot_attack";
30
+ readonly ALREADY_DEAD: "combat.already_dead";
31
+ readonly NOT_HOSTILE: "combat.not_hostile";
32
+ readonly NO_TARGET: "combat.no_target";
33
+ readonly TARGET_UNCONSCIOUS: "combat.target_unconscious";
34
+ readonly NEED_WEAPON: "combat.need_weapon";
35
+ readonly COMBAT_STARTED: "combat.started";
36
+ readonly COMBAT_ENDED: "combat.ended";
37
+ readonly PLAYER_DIED: "combat.player_died";
38
+ readonly PLAYER_RESURRECTED: "combat.player_resurrected";
39
+ };
40
+ /**
41
+ * Type for combat message IDs
42
+ */
43
+ export type CombatMessageId = (typeof CombatMessages)[keyof typeof CombatMessages];
44
+ /**
45
+ * Health status levels
46
+ */
47
+ export type HealthStatus = 'healthy' | 'wounded' | 'badly_wounded' | 'near_death' | 'unconscious' | 'dead';
48
+ /**
49
+ * Get message ID for a health status
50
+ */
51
+ export declare function getHealthStatusMessageId(status: HealthStatus): CombatMessageId;
52
+ //# sourceMappingURL=combat-messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combat-messages.d.ts","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/combat-messages.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCjB,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAEnF;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,SAAS,GACT,eAAe,GACf,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,CAe9E"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ /**
3
+ * Combat Message IDs (ADR-072)
4
+ *
5
+ * Semantic message IDs for combat-related events.
6
+ * Actual text is provided by the language layer.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CombatMessages = void 0;
10
+ exports.getHealthStatusMessageId = getHealthStatusMessageId;
11
+ /**
12
+ * Message IDs for combat events
13
+ */
14
+ exports.CombatMessages = {
15
+ // Attack outcomes
16
+ ATTACK_MISSED: 'combat.attack.missed',
17
+ ATTACK_HIT: 'combat.attack.hit',
18
+ ATTACK_HIT_LIGHT: 'combat.attack.hit_light',
19
+ ATTACK_HIT_HEAVY: 'combat.attack.hit_heavy',
20
+ ATTACK_KNOCKED_OUT: 'combat.attack.knocked_out',
21
+ ATTACK_KILLED: 'combat.attack.killed',
22
+ // Defense outcomes
23
+ DEFEND_BLOCKED: 'combat.defend.blocked',
24
+ DEFEND_PARRIED: 'combat.defend.parried',
25
+ DEFEND_DODGED: 'combat.defend.dodged',
26
+ // Health status descriptions
27
+ HEALTH_HEALTHY: 'combat.health.healthy',
28
+ HEALTH_WOUNDED: 'combat.health.wounded',
29
+ HEALTH_BADLY_WOUNDED: 'combat.health.badly_wounded',
30
+ HEALTH_NEAR_DEATH: 'combat.health.near_death',
31
+ HEALTH_UNCONSCIOUS: 'combat.health.unconscious',
32
+ HEALTH_DEAD: 'combat.health.dead',
33
+ // Special weapon messages
34
+ SWORD_GLOWS: 'combat.special.sword_glows',
35
+ SWORD_STOPS_GLOWING: 'combat.special.sword_stops_glowing',
36
+ BLESSED_WEAPON_EFFECT: 'combat.special.blessed_weapon',
37
+ // Error/validation messages
38
+ CANNOT_ATTACK: 'combat.cannot_attack',
39
+ ALREADY_DEAD: 'combat.already_dead',
40
+ NOT_HOSTILE: 'combat.not_hostile',
41
+ NO_TARGET: 'combat.no_target',
42
+ TARGET_UNCONSCIOUS: 'combat.target_unconscious',
43
+ NEED_WEAPON: 'combat.need_weapon',
44
+ // Combat state messages
45
+ COMBAT_STARTED: 'combat.started',
46
+ COMBAT_ENDED: 'combat.ended',
47
+ PLAYER_DIED: 'combat.player_died',
48
+ PLAYER_RESURRECTED: 'combat.player_resurrected',
49
+ };
50
+ /**
51
+ * Get message ID for a health status
52
+ */
53
+ function getHealthStatusMessageId(status) {
54
+ switch (status) {
55
+ case 'healthy':
56
+ return exports.CombatMessages.HEALTH_HEALTHY;
57
+ case 'wounded':
58
+ return exports.CombatMessages.HEALTH_WOUNDED;
59
+ case 'badly_wounded':
60
+ return exports.CombatMessages.HEALTH_BADLY_WOUNDED;
61
+ case 'near_death':
62
+ return exports.CombatMessages.HEALTH_NEAR_DEATH;
63
+ case 'unconscious':
64
+ return exports.CombatMessages.HEALTH_UNCONSCIOUS;
65
+ case 'dead':
66
+ return exports.CombatMessages.HEALTH_DEAD;
67
+ }
68
+ }
69
+ //# sourceMappingURL=combat-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combat-messages.js","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/combat-messages.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAkEH,4DAeC;AA/ED;;GAEG;AACU,QAAA,cAAc,GAAG;IAC5B,kBAAkB;IAClB,aAAa,EAAE,sBAAsB;IACrC,UAAU,EAAE,mBAAmB;IAC/B,gBAAgB,EAAE,yBAAyB;IAC3C,gBAAgB,EAAE,yBAAyB;IAC3C,kBAAkB,EAAE,2BAA2B;IAC/C,aAAa,EAAE,sBAAsB;IAErC,mBAAmB;IACnB,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,uBAAuB;IACvC,aAAa,EAAE,sBAAsB;IAErC,6BAA6B;IAC7B,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,uBAAuB;IACvC,oBAAoB,EAAE,6BAA6B;IACnD,iBAAiB,EAAE,0BAA0B;IAC7C,kBAAkB,EAAE,2BAA2B;IAC/C,WAAW,EAAE,oBAAoB;IAEjC,0BAA0B;IAC1B,WAAW,EAAE,4BAA4B;IACzC,mBAAmB,EAAE,oCAAoC;IACzD,qBAAqB,EAAE,+BAA+B;IAEtD,4BAA4B;IAC5B,aAAa,EAAE,sBAAsB;IACrC,YAAY,EAAE,qBAAqB;IACnC,WAAW,EAAE,oBAAoB;IACjC,SAAS,EAAE,kBAAkB;IAC7B,kBAAkB,EAAE,2BAA2B;IAC/C,WAAW,EAAE,oBAAoB;IAEjC,wBAAwB;IACxB,cAAc,EAAE,gBAAgB;IAChC,YAAY,EAAE,cAAc;IAC5B,WAAW,EAAE,oBAAoB;IACjC,kBAAkB,EAAE,2BAA2B;CACvC,CAAC;AAkBX;;GAEG;AACH,SAAgB,wBAAwB,CAAC,MAAoB;IAC3D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,sBAAc,CAAC,cAAc,CAAC;QACvC,KAAK,SAAS;YACZ,OAAO,sBAAc,CAAC,cAAc,CAAC;QACvC,KAAK,eAAe;YAClB,OAAO,sBAAc,CAAC,oBAAoB,CAAC;QAC7C,KAAK,YAAY;YACf,OAAO,sBAAc,CAAC,iBAAiB,CAAC;QAC1C,KAAK,aAAa;YAChB,OAAO,sBAAc,CAAC,kBAAkB,CAAC;QAC3C,KAAK,MAAM;YACT,OAAO,sBAAc,CAAC,WAAW,CAAC;IACtC,CAAC;AACH,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Combat Service (ADR-072)
3
+ *
4
+ * Handles combat resolution using a skill-based probability system.
5
+ * Moved from stdlib to basic-combat extension — stories opt in by
6
+ * calling registerBasicCombat().
7
+ */
8
+ import { EntityId, SeededRandom } from "./src/index";
9
+ import { IFEntity, WorldModel } from "./src/index";
10
+ import { HealthStatus } from './combat-messages.js';
11
+ /**
12
+ * Context for combat resolution
13
+ */
14
+ export interface CombatContext {
15
+ /** The attacker entity */
16
+ attacker: IFEntity;
17
+ /** The target entity */
18
+ target: IFEntity;
19
+ /** Weapon being used (if any) */
20
+ weapon?: IFEntity;
21
+ /** The world model */
22
+ world: WorldModel;
23
+ /** Seeded random number generator */
24
+ random: SeededRandom;
25
+ }
26
+ /**
27
+ * Result of combat resolution
28
+ */
29
+ export interface CombatResult {
30
+ /** Whether the attack hit */
31
+ hit: boolean;
32
+ /** Damage dealt (0 if missed) */
33
+ damage: number;
34
+ /** Target's new health after damage */
35
+ targetNewHealth: number;
36
+ /** Whether target was knocked out (unconscious) */
37
+ targetKnockedOut: boolean;
38
+ /** Whether target was killed */
39
+ targetKilled: boolean;
40
+ /** Message ID for the result */
41
+ messageId: string;
42
+ /** Data for the message */
43
+ messageData: Record<string, unknown>;
44
+ }
45
+ /**
46
+ * Validation result for combat
47
+ */
48
+ export interface CombatValidation {
49
+ valid: boolean;
50
+ messageId?: string;
51
+ messageData?: Record<string, unknown>;
52
+ }
53
+ /**
54
+ * Combat Service interface
55
+ */
56
+ export interface ICombatService {
57
+ /** Resolve a single attack */
58
+ resolveAttack(context: CombatContext): CombatResult;
59
+ /** Check if attacker can attack target */
60
+ canAttack(attacker: IFEntity, target: IFEntity): CombatValidation;
61
+ /** Get descriptive health status */
62
+ getHealthStatus(entity: IFEntity): HealthStatus;
63
+ /** Calculate hit chance */
64
+ calculateHitChance(attackerSkill: number, defenderSkill: number, weaponBonus: number): number;
65
+ }
66
+ /**
67
+ * Combat Service implementation
68
+ */
69
+ export declare class CombatService implements ICombatService {
70
+ /**
71
+ * Calculate hit chance based on skills
72
+ *
73
+ * Base chance starts at 50%.
74
+ * Each point of skill advantage = +1%.
75
+ * Clamped to 10%-95% (always some chance either way).
76
+ */
77
+ calculateHitChance(attackerSkill: number, defenderSkill: number, weaponBonus: number): number;
78
+ /**
79
+ * Resolve an attack
80
+ */
81
+ resolveAttack(context: CombatContext): CombatResult;
82
+ /**
83
+ * Check if an entity can attack another
84
+ */
85
+ canAttack(attacker: IFEntity, target: IFEntity): CombatValidation;
86
+ /**
87
+ * Get health status for an entity
88
+ */
89
+ getHealthStatus(entity: IFEntity): HealthStatus;
90
+ }
91
+ /**
92
+ * Create a new Combat Service instance
93
+ */
94
+ export declare function createCombatService(): ICombatService;
95
+ /**
96
+ * Result of applying combat damage
97
+ */
98
+ export interface ApplyCombatResultInfo {
99
+ /** IDs of items dropped from target's inventory */
100
+ droppedItems: EntityId[];
101
+ }
102
+ /**
103
+ * Apply combat result to the target entity
104
+ * Handles health updates, consciousness changes, death, and inventory dropping
105
+ */
106
+ export declare function applyCombatResult(target: IFEntity, result: CombatResult, world: WorldModel): ApplyCombatResultInfo;
107
+ //# sourceMappingURL=combat-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combat-service.d.ts","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/combat-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,QAAQ,EACR,UAAU,EAIX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAkB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IAEnB,wBAAwB;IACxB,MAAM,EAAE,QAAQ,CAAC;IAEjB,iCAAiC;IACjC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAElB,sBAAsB;IACtB,KAAK,EAAE,UAAU,CAAC;IAElB,qCAAqC;IACrC,MAAM,EAAE,YAAY,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6BAA6B;IAC7B,GAAG,EAAE,OAAO,CAAC;IAEb,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IAEf,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAC;IAExB,mDAAmD;IACnD,gBAAgB,EAAE,OAAO,CAAC;IAE1B,gCAAgC;IAChC,YAAY,EAAE,OAAO,CAAC;IAEtB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAElB,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAAC;IAEpD,0CAA0C;IAC1C,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,gBAAgB,CAAC;IAElE,oCAAoC;IACpC,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAC;IAEhD,2BAA2B;IAC3B,kBAAkB,CAChB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC;CACX;AAED;;GAEG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD;;;;;;OAMG;IACH,kBAAkB,CAChB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,MAAM;IAMT;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY;IA8FnD;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,gBAAgB;IAuBjE;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,YAAY;CA0BhD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAEpD;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,YAAY,EAAE,QAAQ,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,GAChB,qBAAqB,CAkCvB"}
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * Combat Service (ADR-072)
4
+ *
5
+ * Handles combat resolution using a skill-based probability system.
6
+ * Moved from stdlib to basic-combat extension — stories opt in by
7
+ * calling registerBasicCombat().
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CombatService = void 0;
11
+ exports.createCombatService = createCombatService;
12
+ exports.applyCombatResult = applyCombatResult;
13
+ const world_model_1 = require("./src/index.js");
14
+ const combat_messages_js_1 = require("./combat-messages.js");
15
+ /**
16
+ * Combat Service implementation
17
+ */
18
+ class CombatService {
19
+ /**
20
+ * Calculate hit chance based on skills
21
+ *
22
+ * Base chance starts at 50%.
23
+ * Each point of skill advantage = +1%.
24
+ * Clamped to 10%-95% (always some chance either way).
25
+ */
26
+ calculateHitChance(attackerSkill, defenderSkill, weaponBonus) {
27
+ const baseChance = 50;
28
+ const skillDiff = attackerSkill + weaponBonus - defenderSkill;
29
+ return Math.max(10, Math.min(95, baseChance + skillDiff));
30
+ }
31
+ /**
32
+ * Resolve an attack
33
+ */
34
+ resolveAttack(context) {
35
+ const { attacker, target, weapon, random } = context;
36
+ // Get combatant traits
37
+ const attackerCombat = attacker.get(world_model_1.TraitType.COMBATANT);
38
+ const targetCombat = target.get(world_model_1.TraitType.COMBATANT);
39
+ const weaponTrait = weapon?.get(world_model_1.TraitType.WEAPON);
40
+ // Get skill values
41
+ const attackerSkill = attackerCombat?.skill ?? 30;
42
+ const defenderSkill = targetCombat?.skill ?? 30;
43
+ const weaponBonus = weaponTrait?.skillBonus ?? 0;
44
+ // Calculate hit chance
45
+ const hitChance = this.calculateHitChance(attackerSkill, defenderSkill, weaponBonus);
46
+ // Roll to hit
47
+ const roll = random.int(1, 100);
48
+ const hit = roll <= hitChance;
49
+ // Base message data
50
+ const messageData = {
51
+ attackerName: attacker.name,
52
+ targetName: target.name,
53
+ weaponName: weapon?.name,
54
+ };
55
+ if (!hit) {
56
+ return {
57
+ hit: false,
58
+ damage: 0,
59
+ targetNewHealth: targetCombat?.health ?? 0,
60
+ targetKnockedOut: false,
61
+ targetKilled: false,
62
+ messageId: combat_messages_js_1.CombatMessages.ATTACK_MISSED,
63
+ messageData,
64
+ };
65
+ }
66
+ // Calculate damage
67
+ const baseDamage = attackerCombat?.baseDamage ?? 1;
68
+ const weaponDamage = weaponTrait?.damage ?? 0;
69
+ let totalDamage = baseDamage + weaponDamage;
70
+ // Blessed weapon bonus against undead (if applicable)
71
+ if (weaponTrait?.isBlessed) {
72
+ // Check if target is undead (story would define this trait)
73
+ const targetIdentity = target.get(world_model_1.TraitType.IDENTITY);
74
+ if (targetIdentity?.isUndead) {
75
+ totalDamage = Math.floor(totalDamage * 1.5);
76
+ }
77
+ }
78
+ // Apply armor reduction
79
+ const armor = targetCombat?.armor ?? 0;
80
+ const effectiveDamage = Math.max(1, totalDamage - armor); // Always do at least 1 damage
81
+ // Calculate new health
82
+ const currentHealth = targetCombat?.health ?? 10;
83
+ const newHealth = Math.max(0, currentHealth - effectiveDamage);
84
+ // Determine outcome
85
+ const killed = newHealth <= 0;
86
+ const maxHealth = targetCombat?.maxHealth ?? 10;
87
+ const knockedOut = !killed && newHealth <= maxHealth * 0.2;
88
+ // Add damage to message data
89
+ messageData.damage = effectiveDamage;
90
+ // Determine message based on outcome
91
+ let messageId;
92
+ if (killed) {
93
+ messageId = combat_messages_js_1.CombatMessages.ATTACK_KILLED;
94
+ }
95
+ else if (knockedOut) {
96
+ messageId = combat_messages_js_1.CombatMessages.ATTACK_KNOCKED_OUT;
97
+ }
98
+ else if (effectiveDamage >= maxHealth * 0.3) {
99
+ messageId = combat_messages_js_1.CombatMessages.ATTACK_HIT_HEAVY;
100
+ }
101
+ else if (effectiveDamage <= maxHealth * 0.1) {
102
+ messageId = combat_messages_js_1.CombatMessages.ATTACK_HIT_LIGHT;
103
+ }
104
+ else {
105
+ messageId = combat_messages_js_1.CombatMessages.ATTACK_HIT;
106
+ }
107
+ return {
108
+ hit: true,
109
+ damage: effectiveDamage,
110
+ targetNewHealth: newHealth,
111
+ targetKnockedOut: knockedOut,
112
+ targetKilled: killed,
113
+ messageId,
114
+ messageData,
115
+ };
116
+ }
117
+ /**
118
+ * Check if an entity can attack another
119
+ */
120
+ canAttack(attacker, target) {
121
+ // Check if target has combatant trait
122
+ const targetCombat = target.get(world_model_1.TraitType.COMBATANT);
123
+ if (!targetCombat) {
124
+ return {
125
+ valid: false,
126
+ messageId: combat_messages_js_1.CombatMessages.CANNOT_ATTACK,
127
+ messageData: { targetName: target.name },
128
+ };
129
+ }
130
+ // Check if target is alive
131
+ if (!targetCombat.isAlive) {
132
+ return {
133
+ valid: false,
134
+ messageId: combat_messages_js_1.CombatMessages.ALREADY_DEAD,
135
+ messageData: { targetName: target.name },
136
+ };
137
+ }
138
+ return { valid: true };
139
+ }
140
+ /**
141
+ * Get health status for an entity
142
+ */
143
+ getHealthStatus(entity) {
144
+ const combatant = entity.get(world_model_1.TraitType.COMBATANT);
145
+ if (!combatant) {
146
+ return 'healthy'; // Non-combatants are "healthy"
147
+ }
148
+ if (!combatant.isAlive) {
149
+ return 'dead';
150
+ }
151
+ if (!combatant.isConscious) {
152
+ return 'unconscious';
153
+ }
154
+ const healthPercent = combatant.health / combatant.maxHealth;
155
+ if (healthPercent <= 0.1) {
156
+ return 'near_death';
157
+ }
158
+ else if (healthPercent <= 0.3) {
159
+ return 'badly_wounded';
160
+ }
161
+ else if (healthPercent <= 0.7) {
162
+ return 'wounded';
163
+ }
164
+ else {
165
+ return 'healthy';
166
+ }
167
+ }
168
+ }
169
+ exports.CombatService = CombatService;
170
+ /**
171
+ * Create a new Combat Service instance
172
+ */
173
+ function createCombatService() {
174
+ return new CombatService();
175
+ }
176
+ /**
177
+ * Apply combat result to the target entity
178
+ * Handles health updates, consciousness changes, death, and inventory dropping
179
+ */
180
+ function applyCombatResult(target, result, world) {
181
+ const info = { droppedItems: [] };
182
+ if (!result.hit)
183
+ return info;
184
+ const combatant = target.get(world_model_1.TraitType.COMBATANT);
185
+ if (!combatant)
186
+ return info;
187
+ // Update health
188
+ combatant.health = result.targetNewHealth;
189
+ // Update consciousness
190
+ if (result.targetKnockedOut) {
191
+ combatant.knockOut();
192
+ }
193
+ // Update alive status and handle death
194
+ if (result.targetKilled) {
195
+ combatant.kill();
196
+ // Drop inventory if configured
197
+ if (combatant.dropsInventory) {
198
+ const location = world.getLocation(target.id);
199
+ if (location) {
200
+ const inventory = world.getContents(target.id);
201
+ for (const item of inventory) {
202
+ world.moveEntity(item.id, location);
203
+ info.droppedItems.push(item.id);
204
+ }
205
+ }
206
+ }
207
+ }
208
+ return info;
209
+ }
210
+ //# sourceMappingURL=combat-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combat-service.js","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/combat-service.ts"],"names":[],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @sharpee/ext-basic-combat
3
+ *
4
+ * Generic skill-based combat extension for Sharpee IF engine.
5
+ *
6
+ * Provides opt-in combat resolution for both attack directions:
7
+ * - PC→NPC: BasicCombatInterceptor (registered on CombatantTrait + if.action.attacking)
8
+ * - NPC→PC: basicNpcResolver (registered as NpcCombatResolver)
9
+ *
10
+ * Stories with custom combat (e.g., Dungeo's melee system) register their
11
+ * own interceptor and resolver instead of calling registerBasicCombat().
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { registerBasicCombat } from "./src/index";
16
+ *
17
+ * // In story's initializeWorld():
18
+ * registerBasicCombat();
19
+ * ```
20
+ */
21
+ /**
22
+ * Register the basic combat system for both attack directions.
23
+ *
24
+ * Call this in your story's initializeWorld() to enable generic
25
+ * skill-based combat. Do NOT call this if your story registers
26
+ * its own combat interceptor/resolver.
27
+ *
28
+ * Registers:
29
+ * 1. BasicCombatInterceptor on CombatantTrait + if.action.attacking (PC→NPC)
30
+ * 2. basicNpcResolver as the NPC combat resolver (NPC→PC)
31
+ */
32
+ export declare function registerBasicCombat(): void;
33
+ export { CombatService, createCombatService, applyCombatResult, type ICombatService, type CombatContext, type CombatResult, type CombatValidation, type ApplyCombatResultInfo, } from './combat-service.js';
34
+ export { CombatMessages, getHealthStatusMessageId, type CombatMessageId, type HealthStatus, } from './combat-messages.js';
35
+ export { BasicCombatInterceptor } from './basic-combat-interceptor.js';
36
+ export { basicNpcResolver } from './basic-npc-resolver.js';
37
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAGD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,KAAK,eAAe,EACpB,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
package/index.js ADDED
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * @sharpee/ext-basic-combat
4
+ *
5
+ * Generic skill-based combat extension for Sharpee IF engine.
6
+ *
7
+ * Provides opt-in combat resolution for both attack directions:
8
+ * - PC→NPC: BasicCombatInterceptor (registered on CombatantTrait + if.action.attacking)
9
+ * - NPC→PC: basicNpcResolver (registered as NpcCombatResolver)
10
+ *
11
+ * Stories with custom combat (e.g., Dungeo's melee system) register their
12
+ * own interceptor and resolver instead of calling registerBasicCombat().
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { registerBasicCombat } from "./src/index.js";
17
+ *
18
+ * // In story's initializeWorld():
19
+ * registerBasicCombat();
20
+ * ```
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.basicNpcResolver = exports.BasicCombatInterceptor = exports.getHealthStatusMessageId = exports.CombatMessages = exports.applyCombatResult = exports.createCombatService = exports.CombatService = void 0;
24
+ exports.registerBasicCombat = registerBasicCombat;
25
+ const world_model_1 = require("./src/index.js");
26
+ const stdlib_1 = require("./src/index.js");
27
+ const basic_combat_interceptor_js_1 = require("./basic-combat-interceptor.js");
28
+ const basic_npc_resolver_js_1 = require("./basic-npc-resolver.js");
29
+ /**
30
+ * Register the basic combat system for both attack directions.
31
+ *
32
+ * Call this in your story's initializeWorld() to enable generic
33
+ * skill-based combat. Do NOT call this if your story registers
34
+ * its own combat interceptor/resolver.
35
+ *
36
+ * Registers:
37
+ * 1. BasicCombatInterceptor on CombatantTrait + if.action.attacking (PC→NPC)
38
+ * 2. basicNpcResolver as the NPC combat resolver (NPC→PC)
39
+ */
40
+ function registerBasicCombat() {
41
+ (0, world_model_1.registerActionInterceptor)(world_model_1.TraitType.COMBATANT, 'if.action.attacking', basic_combat_interceptor_js_1.BasicCombatInterceptor);
42
+ (0, stdlib_1.registerNpcCombatResolver)(basic_npc_resolver_js_1.basicNpcResolver);
43
+ }
44
+ // Combat service and types
45
+ var combat_service_js_1 = require("./combat-service.js");
46
+ Object.defineProperty(exports, "CombatService", { enumerable: true, get: function () { return combat_service_js_1.CombatService; } });
47
+ Object.defineProperty(exports, "createCombatService", { enumerable: true, get: function () { return combat_service_js_1.createCombatService; } });
48
+ Object.defineProperty(exports, "applyCombatResult", { enumerable: true, get: function () { return combat_service_js_1.applyCombatResult; } });
49
+ // Combat messages
50
+ var combat_messages_js_1 = require("./combat-messages.js");
51
+ Object.defineProperty(exports, "CombatMessages", { enumerable: true, get: function () { return combat_messages_js_1.CombatMessages; } });
52
+ Object.defineProperty(exports, "getHealthStatusMessageId", { enumerable: true, get: function () { return combat_messages_js_1.getHealthStatusMessageId; } });
53
+ // Individual components (for stories that want to register only one direction)
54
+ var basic_combat_interceptor_js_2 = require("./basic-combat-interceptor.js");
55
+ Object.defineProperty(exports, "BasicCombatInterceptor", { enumerable: true, get: function () { return basic_combat_interceptor_js_2.BasicCombatInterceptor; } });
56
+ var basic_npc_resolver_js_2 = require("./basic-npc-resolver.js");
57
+ Object.defineProperty(exports, "basicNpcResolver", { enumerable: true, get: function () { return basic_npc_resolver_js_2.basicNpcResolver; } });
58
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../mnt/c/repotemp/sharpee/packages/extensions/basic-combat/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAqBH,kDAOC;AA1BD,sDAG8B;AAC9B,4CAA4D;AAC5D,+EAAuE;AACvE,mEAA2D;AAE3D;;;;;;;;;;GAUG;AACH,SAAgB,mBAAmB;IACjC,IAAA,uCAAyB,EACvB,uBAAS,CAAC,SAAS,EACnB,qBAAqB,EACrB,oDAAsB,CACvB,CAAC;IACF,IAAA,kCAAyB,EAAC,wCAAgB,CAAC,CAAC;AAC9C,CAAC;AAED,2BAA2B;AAC3B,yDAS6B;AAR3B,kHAAA,aAAa,OAAA;AACb,wHAAA,mBAAmB,OAAA;AACnB,sHAAA,iBAAiB,OAAA;AAQnB,kBAAkB;AAClB,2DAK8B;AAJ5B,oHAAA,cAAc,OAAA;AACd,8HAAA,wBAAwB,OAAA;AAK1B,+EAA+E;AAC/E,6EAAuE;AAA9D,qIAAA,sBAAsB,OAAA;AAC/B,iEAA2D;AAAlD,yHAAA,gBAAgB,OAAA"}
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@sharpee/ext-basic-combat",
3
+ "version": "0.9.86",
4
+ "description": "Generic skill-based combat extension for Sharpee IF engine",
5
+ "main": "./index.js",
6
+ "types": "./index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./index.d.ts",
10
+ "require": "./index.js",
11
+ "default": "./index.js"
12
+ }
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "keywords": [
18
+ "sharpee",
19
+ "interactive-fiction",
20
+ "combat"
21
+ ],
22
+ "license": "MIT"
23
+ }