@its-not-rocket-science/ananke 0.1.47 → 0.1.48
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 +14 -0
- package/dist/src/crafting/index.d.ts +1 -0
- package/dist/src/crafting/index.js +11 -8
- package/dist/src/crafting/manufacturing.d.ts +4 -2
- package/dist/src/crafting/manufacturing.js +26 -13
- package/dist/src/crafting/materials.js +2 -2
- package/dist/src/crafting/workshops.js +8 -6
- package/dist/src/inventory.js +1 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.1.48] — 2026-03-28
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Crafting subsystem — TODO/placeholder items resolved:**
|
|
14
|
+
- `src/crafting/materials.ts` — `createMaterialItem`: corrected `mass_kg` (was double-scaled by `SCALE.kg`; now `quantity_kg * SCALE.kg / SCALE.Q`); `bulk` now computed proportionally from quantity instead of a fixed `q(1.0)` placeholder.
|
|
15
|
+
- `src/inventory.ts` — `findMaterialsByType`: replaced loose `templateId.includes(materialTypeId)` with exact `templateId === "material_" + materialTypeId` to prevent false positives (e.g. "iron" matching "iron_ore").
|
|
16
|
+
- `src/crafting/manufacturing.ts` — `ProductionLine` gains optional `workshopTimeReduction_Q` and `workshopQualityBonus_Q` fields; `setupProductionLine` now looks up the recipe and calls `getWorkshopBonus` to populate them; `advanceProduction` applies the time reduction to effective progress; `calculateBatchQualityRange` accepts an optional `workshopQualityBonus_Q` multiplier; `estimateBatchCompletionTime` accepts an optional `workshopTimeReduction_Q` and its formula is corrected (was dividing by SCALE.Q twice, producing near-zero results).
|
|
17
|
+
- `src/crafting/workshops.ts` — `upgradeWorkshop`: now checks that `resources` contains sufficient `material_wood` (10 units per tier step) before upgrading; returns `success: false` when insufficient rather than always succeeding.
|
|
18
|
+
- `src/crafting/index.ts` — `startManufacturing`: now returns the constructed `ProductionLine` in `result.productionLine` so callers can store it for subsequent `advanceManufacturing` calls (persistent state remains the host's responsibility); `advanceManufacturing` now derives quality range and time reduction from the supplied `workshop` rather than using hardcoded values.
|
|
19
|
+
- Build: clean. Tests: 5,261 passing. Coverage: statements 97.1 %, branches 87.83 %, functions 95.65 %, lines 97.1 %.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
9
23
|
## [0.1.47] — 2026-03-27
|
|
10
24
|
|
|
11
25
|
### Changed
|
|
@@ -8,7 +8,7 @@ import { consumeItemsByTemplateId, addItemToInventory } from "../inventory.js";
|
|
|
8
8
|
import { validateRecipeFeasibility, resolveRecipe, getRecipeById, getAllRecipes, } from "./recipes.js";
|
|
9
9
|
import { getMaterialTypeById, calculateMaterialEffect, createMaterialItem, } from "./materials.js";
|
|
10
10
|
import { getWorkshopBonus, validateWorkshopRequirements, createWorkshop, upgradeWorkshop, } from "./workshops.js";
|
|
11
|
-
import { setupProductionLine, advanceProduction, estimateBatchCompletionTime, isProductionLineComplete, } from "./manufacturing.js";
|
|
11
|
+
import { setupProductionLine, advanceProduction, calculateBatchQualityRange, estimateBatchCompletionTime, isProductionLineComplete, } from "./manufacturing.js";
|
|
12
12
|
// ── Main Crafting API ─────────────────────────────────────────────────────────
|
|
13
13
|
/**
|
|
14
14
|
* Craft a single item using a recipe, entity, inventory, and workshop.
|
|
@@ -117,10 +117,9 @@ export function startManufacturing(recipeId, quantity, workshop, workers, worldS
|
|
|
117
117
|
quantity,
|
|
118
118
|
workshop,
|
|
119
119
|
};
|
|
120
|
-
// Setup production line
|
|
120
|
+
// Setup production line (persistent storage is the host's responsibility)
|
|
121
121
|
const line = setupProductionLine(order, workers);
|
|
122
|
-
|
|
123
|
-
return { success: true, lineId: line.lineId };
|
|
122
|
+
return { success: true, lineId: line.lineId, productionLine: line };
|
|
124
123
|
}
|
|
125
124
|
/**
|
|
126
125
|
* Advance manufacturing for a production line.
|
|
@@ -132,19 +131,23 @@ export function advanceManufacturing(lineId, deltaTime_s, workers, workshop, wor
|
|
|
132
131
|
for (let i = 0; i < lineId.length; i++)
|
|
133
132
|
lineIdHash += lineId.charCodeAt(i);
|
|
134
133
|
const _seed = eventSeed(worldSeed, tick, lineIdHash, 0, salt);
|
|
135
|
-
//
|
|
134
|
+
// Retrieve production line from persistent state (host responsibility).
|
|
135
|
+
// Without a stored line, compute a fresh one using the available workshop and workers.
|
|
136
|
+
const workshopBonus = getWorkshopBonus(workshop, { toolRequirements: [] });
|
|
137
|
+
const qualityRange = calculateBatchQualityRange(workers, workshopBonus.qualityBonus_Q);
|
|
136
138
|
const line = {
|
|
137
139
|
lineId,
|
|
138
|
-
recipeId: "
|
|
140
|
+
recipeId: "unknown",
|
|
139
141
|
batchSize: 10,
|
|
140
142
|
itemsProduced: 0,
|
|
141
143
|
progress_Q: q(0),
|
|
142
144
|
assignedWorkers: workers.map(w => w.id),
|
|
143
145
|
priority: 1,
|
|
144
|
-
qualityRange
|
|
146
|
+
qualityRange,
|
|
147
|
+
workshopTimeReduction_Q: workshopBonus.timeReduction_Q,
|
|
148
|
+
workshopQualityBonus_Q: workshopBonus.qualityBonus_Q,
|
|
145
149
|
};
|
|
146
150
|
const result = advanceProduction(line, deltaTime_s, workers);
|
|
147
|
-
// TODO: update stored line
|
|
148
151
|
return {
|
|
149
152
|
itemsCompleted: result.itemsCompleted,
|
|
150
153
|
totalProduced: result.totalItemsProduced,
|
|
@@ -17,6 +17,8 @@ export interface ProductionLine {
|
|
|
17
17
|
assignedWorkers: number[];
|
|
18
18
|
priority: number;
|
|
19
19
|
qualityRange: ProductQualityRange;
|
|
20
|
+
workshopTimeReduction_Q?: Q;
|
|
21
|
+
workshopQualityBonus_Q?: Q;
|
|
20
22
|
}
|
|
21
23
|
/** Manufacturing order for starting batch production. */
|
|
22
24
|
export interface ManufacturingOrder {
|
|
@@ -55,7 +57,7 @@ export declare function advanceProduction(productionLine: ProductionLine, deltaT
|
|
|
55
57
|
* Calculate predicted quality range for a batch based on workers, materials, workshop.
|
|
56
58
|
* Returns min, max, and average expected quality.
|
|
57
59
|
*/
|
|
58
|
-
export declare function calculateBatchQualityRange(workers: Entity[]): {
|
|
60
|
+
export declare function calculateBatchQualityRange(workers: Entity[], workshopQualityBonus_Q?: Q): {
|
|
59
61
|
min_Q: Q;
|
|
60
62
|
max_Q: Q;
|
|
61
63
|
avg_Q: Q;
|
|
@@ -72,7 +74,7 @@ export declare function advanceAssemblyStep(step: AssemblyStep, worker: Entity,
|
|
|
72
74
|
completed: boolean;
|
|
73
75
|
};
|
|
74
76
|
/** Estimate time to complete a batch given workers and workshop. */
|
|
75
|
-
export declare function estimateBatchCompletionTime(batchSize: number, workers: Entity[]): number;
|
|
77
|
+
export declare function estimateBatchCompletionTime(batchSize: number, workers: Entity[], workshopTimeReduction_Q?: Q): number;
|
|
76
78
|
/** Check if production line is complete. */
|
|
77
79
|
export declare function isProductionLineComplete(line: ProductionLine): boolean;
|
|
78
80
|
/** Get progress percentage (0–1). */
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// Batch production lines, progress accumulation, quality variance.
|
|
4
4
|
// Deterministic batch quality range based on workers, materials, workshop.
|
|
5
5
|
import { SCALE, q, clampQ, qMul, mulDiv } from "../units.js";
|
|
6
|
+
import { getWorkshopBonus } from "./workshops.js";
|
|
7
|
+
import { getRecipeById } from "./recipes.js";
|
|
6
8
|
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
7
9
|
/** Quality variance factor based on number of workers (more workers → less variance). */
|
|
8
10
|
const WORKER_VARIANCE_REDUCTION = q(0.10);
|
|
@@ -13,8 +15,12 @@ const MIN_QUALITY_VARIANCE = q(0.05);
|
|
|
13
15
|
* Initialize a production line for batch manufacturing.
|
|
14
16
|
*/
|
|
15
17
|
export function setupProductionLine(order, workers) {
|
|
18
|
+
const recipe = getRecipeById(order.recipeId);
|
|
19
|
+
const workshopBonus = recipe
|
|
20
|
+
? getWorkshopBonus(order.workshop, recipe)
|
|
21
|
+
: { toolBonus_Q: q(0), timeReduction_Q: q(1.0), qualityBonus_Q: q(1.0) };
|
|
16
22
|
const workerIds = workers.map(w => w.id);
|
|
17
|
-
const qualityRange = calculateBatchQualityRange(workers);
|
|
23
|
+
const qualityRange = calculateBatchQualityRange(workers, workshopBonus.qualityBonus_Q);
|
|
18
24
|
return {
|
|
19
25
|
lineId: `line_${order.orderId}`,
|
|
20
26
|
recipeId: order.recipeId,
|
|
@@ -24,6 +30,8 @@ export function setupProductionLine(order, workers) {
|
|
|
24
30
|
assignedWorkers: workerIds,
|
|
25
31
|
priority: 1,
|
|
26
32
|
qualityRange,
|
|
33
|
+
workshopTimeReduction_Q: workshopBonus.timeReduction_Q,
|
|
34
|
+
workshopQualityBonus_Q: workshopBonus.qualityBonus_Q,
|
|
27
35
|
};
|
|
28
36
|
}
|
|
29
37
|
/**
|
|
@@ -48,8 +56,11 @@ export function advanceProduction(productionLine, deltaTime_s, workers) {
|
|
|
48
56
|
const workerProgress = mulDiv(skill, deltaTime_s * SCALE.Q, 3600);
|
|
49
57
|
totalProgress += workerProgress;
|
|
50
58
|
}
|
|
51
|
-
// Apply workshop time reduction
|
|
52
|
-
const
|
|
59
|
+
// Apply workshop time reduction: timeReduction_Q < SCALE.Q means faster production
|
|
60
|
+
const timeReduction = productionLine.workshopTimeReduction_Q ?? SCALE.Q;
|
|
61
|
+
const effectiveProgress = timeReduction > 0
|
|
62
|
+
? Math.round(totalProgress * SCALE.Q / timeReduction)
|
|
63
|
+
: totalProgress;
|
|
53
64
|
// Advance progress
|
|
54
65
|
let newProgress = productionLine.progress_Q + effectiveProgress;
|
|
55
66
|
let itemsCompleted = 0;
|
|
@@ -73,7 +84,7 @@ export function advanceProduction(productionLine, deltaTime_s, workers) {
|
|
|
73
84
|
* Calculate predicted quality range for a batch based on workers, materials, workshop.
|
|
74
85
|
* Returns min, max, and average expected quality.
|
|
75
86
|
*/
|
|
76
|
-
export function calculateBatchQualityRange(workers) {
|
|
87
|
+
export function calculateBatchQualityRange(workers, workshopQualityBonus_Q = q(1.0)) {
|
|
77
88
|
if (workers.length === 0) {
|
|
78
89
|
return { min_Q: q(0), max_Q: q(0), avg_Q: q(0) };
|
|
79
90
|
}
|
|
@@ -83,10 +94,8 @@ export function calculateBatchQualityRange(workers) {
|
|
|
83
94
|
totalSkill += worker.attributes.cognition?.bodilyKinesthetic ?? q(0.50);
|
|
84
95
|
}
|
|
85
96
|
const avgSkill = totalSkill / workers.length;
|
|
86
|
-
//
|
|
87
|
-
const
|
|
88
|
-
// Base average quality = avgSkill × workshopBonus
|
|
89
|
-
const avg_Q = clampQ(qMul(avgSkill, workshopBonus), q(0), SCALE.Q);
|
|
97
|
+
// Base average quality = avgSkill × workshopQualityBonus
|
|
98
|
+
const avg_Q = clampQ(qMul(avgSkill, workshopQualityBonus_Q), q(0), SCALE.Q);
|
|
90
99
|
// Variance decreases with more workers
|
|
91
100
|
const variance = Math.max(MIN_QUALITY_VARIANCE, q(0.20) - mulDiv(WORKER_VARIANCE_REDUCTION, workers.length, 1));
|
|
92
101
|
const min_Q = clampQ((avg_Q - variance), q(0), SCALE.Q);
|
|
@@ -135,19 +144,23 @@ export function advanceAssemblyStep(step, worker, deltaTime_s, availableTools) {
|
|
|
135
144
|
}
|
|
136
145
|
// ── Utility Functions ────────────────────────────────────────────────────────
|
|
137
146
|
/** Estimate time to complete a batch given workers and workshop. */
|
|
138
|
-
export function estimateBatchCompletionTime(batchSize, workers) {
|
|
147
|
+
export function estimateBatchCompletionTime(batchSize, workers, workshopTimeReduction_Q = q(1.0)) {
|
|
139
148
|
if (workers.length === 0)
|
|
140
149
|
return Infinity;
|
|
150
|
+
if (batchSize === 0)
|
|
151
|
+
return 0;
|
|
141
152
|
let totalSkill = 0;
|
|
142
153
|
for (const worker of workers) {
|
|
143
154
|
totalSkill += worker.attributes.cognition?.bodilyKinesthetic ?? q(0.50);
|
|
144
155
|
}
|
|
145
156
|
const avgSkill = totalSkill / workers.length;
|
|
146
|
-
|
|
157
|
+
if (avgSkill <= 0)
|
|
158
|
+
return Infinity;
|
|
159
|
+
// At skill q(1.0), one item takes baseTimePerItem_s seconds.
|
|
160
|
+
// With time reduction q(0.90), items take 90% as long (10% faster).
|
|
147
161
|
const baseTimePerItem_s = 3600;
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
return effectiveTimePerItem * batchSize / SCALE.Q;
|
|
162
|
+
const timePerItem_s = Math.round(baseTimePerItem_s * workshopTimeReduction_Q / avgSkill);
|
|
163
|
+
return timePerItem_s * batchSize;
|
|
151
164
|
}
|
|
152
165
|
/** Check if production line is complete. */
|
|
153
166
|
export function isProductionLineComplete(line) {
|
|
@@ -119,8 +119,8 @@ export function createMaterialItem(materialTypeId, quality_Q, quantity_kg, itemI
|
|
|
119
119
|
id: itemId,
|
|
120
120
|
kind: "material",
|
|
121
121
|
name: displayName,
|
|
122
|
-
mass_kg: Math.round(quantity_kg * SCALE.kg
|
|
123
|
-
bulk: q(
|
|
122
|
+
mass_kg: Math.round(quantity_kg * SCALE.kg / SCALE.Q),
|
|
123
|
+
bulk: clampQ(Math.round(quantity_kg / 10), q(0.05), 5 * SCALE.Q),
|
|
124
124
|
materialTypeId,
|
|
125
125
|
quality_Q,
|
|
126
126
|
quantity_kg,
|
|
@@ -125,17 +125,19 @@ targetLevel) {
|
|
|
125
125
|
if (targetTier <= currentTier) {
|
|
126
126
|
return { success: false, upgradedWorkshop: workshop, consumedResources: new Map() };
|
|
127
127
|
}
|
|
128
|
-
//
|
|
129
|
-
|
|
128
|
+
// Check resource requirements: 10 units of "material_wood" per tier step
|
|
129
|
+
const tierSteps = targetTier - currentTier;
|
|
130
|
+
const woodRequired = 10 * tierSteps;
|
|
131
|
+
const woodAvailable = resources.get("material_wood") ?? 0;
|
|
132
|
+
if (woodAvailable < woodRequired) {
|
|
133
|
+
return { success: false, upgradedWorkshop: workshop, consumedResources: new Map() };
|
|
134
|
+
}
|
|
130
135
|
const upgradedWorkshop = {
|
|
131
136
|
...workshop,
|
|
132
137
|
facilityLevel: targetLevel,
|
|
133
138
|
};
|
|
134
|
-
// Consume resources (placeholder)
|
|
135
139
|
const consumedResources = new Map();
|
|
136
|
-
|
|
137
|
-
const tierSteps = targetTier - currentTier;
|
|
138
|
-
consumedResources.set("material_wood", 10 * tierSteps);
|
|
140
|
+
consumedResources.set("material_wood", woodRequired);
|
|
139
141
|
return { success: true, upgradedWorkshop, consumedResources };
|
|
140
142
|
}
|
|
141
143
|
// ── Workshop Creation ─────────────────────────────────────────────────────────
|
package/dist/src/inventory.js
CHANGED
|
@@ -381,12 +381,7 @@ export function findMaterialsByType(inventory, materialTypeId) {
|
|
|
381
381
|
const results = [];
|
|
382
382
|
// Helper to check and add
|
|
383
383
|
const checkItem = (item) => {
|
|
384
|
-
|
|
385
|
-
// Since ItemInstance doesn't have materialTypeId, we need to rely on the templateId mapping
|
|
386
|
-
// or assume that the item is a Material (which extends ItemBase).
|
|
387
|
-
// For now, we'll assume that templateId indicates material type (e.g., "material_iron").
|
|
388
|
-
// This is a placeholder; we need to integrate with crafting material system.
|
|
389
|
-
if (item.templateId.startsWith("material_") && item.templateId.includes(materialTypeId)) {
|
|
384
|
+
if (item.templateId === `material_${materialTypeId}`) {
|
|
390
385
|
results.push(item);
|
|
391
386
|
}
|
|
392
387
|
};
|