gamemindpilot 3.3.0 → 3.5.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/README.md +12 -11
- package/dist/commands/assets.d.ts +4 -0
- package/dist/commands/assets.js +89 -0
- package/dist/commands/assets.js.map +1 -1
- package/dist/commands/init.js +5 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/utility.d.ts +2 -0
- package/dist/commands/utility.js +43 -4
- package/dist/commands/utility.js.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/audio.d.ts +4 -0
- package/dist/utils/audio.js +55 -0
- package/dist/utils/audio.js.map +1 -0
- package/dist/utils/blender.d.ts +4 -0
- package/dist/utils/blender.js +60 -0
- package/dist/utils/blender.js.map +1 -0
- package/dist/utils/bridge.d.ts +4 -0
- package/dist/utils/bridge.js +55 -0
- package/dist/utils/bridge.js.map +1 -0
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/economy.d.ts +3 -0
- package/dist/utils/economy.js +37 -0
- package/dist/utils/economy.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/assets.ts +96 -0
- package/src/commands/init.ts +7 -0
- package/src/commands/utility.ts +45 -4
- package/src/index.ts +31 -1
- package/src/utils/audio.ts +54 -0
- package/src/utils/blender.ts +55 -0
- package/src/utils/bridge.ts +52 -0
- package/src/utils/config.ts +1 -0
- package/src/utils/economy.ts +36 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.blenderManager = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
13
|
+
exports.blenderManager = {
|
|
14
|
+
detect: async () => {
|
|
15
|
+
const commonPaths = [
|
|
16
|
+
'blender', // If in PATH
|
|
17
|
+
'C:\\Program Files\\Blender Foundation\\Blender\\blender.exe',
|
|
18
|
+
'C:\\Program Files\\Blender Foundation\\Blender 3.6\\blender.exe',
|
|
19
|
+
'C:\\Program Files\\Blender Foundation\\Blender 4.0\\blender.exe',
|
|
20
|
+
'/Applications/Blender.app/Contents/MacOS/Blender',
|
|
21
|
+
'/usr/bin/blender'
|
|
22
|
+
];
|
|
23
|
+
for (const p of commonPaths) {
|
|
24
|
+
try {
|
|
25
|
+
await execAsync(`${p.includes(' ') ? `"${p}"` : p} --version`);
|
|
26
|
+
return p;
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
},
|
|
34
|
+
render3D: async (pythonScript, outputFile) => {
|
|
35
|
+
const blenderPath = await exports.blenderManager.detect();
|
|
36
|
+
if (!blenderPath) {
|
|
37
|
+
logger_1.logger.warn('Blender not detected on this system.');
|
|
38
|
+
logger_1.logger.info('💡 TIP: For 3D Asset Forge, install Blender: https://www.blender.org/download/');
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const scriptPath = path_1.default.join(process.cwd(), '.gmpilot', 'temp_blender_script.py');
|
|
42
|
+
fs_1.default.writeFileSync(scriptPath, pythonScript);
|
|
43
|
+
try {
|
|
44
|
+
logger_1.logger.info('🚀 Launching Headless Blender Forge...');
|
|
45
|
+
const command = `"${blenderPath}" --background --python "${scriptPath}"`;
|
|
46
|
+
await execAsync(command);
|
|
47
|
+
logger_1.logger.success(`3D Asset generated successfully: ${outputFile}`);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
logger_1.logger.error(`Blender Forge Error: ${err.message}`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
if (fs_1.default.existsSync(scriptPath))
|
|
56
|
+
fs_1.default.unlinkSync(scriptPath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=blender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blender.js","sourceRoot":"","sources":["../../src/utils/blender.ts"],"names":[],"mappings":";;;;;;AAAA,iDAAqC;AACrC,+BAAiC;AACjC,4CAAoB;AACpB,gDAAwB;AACxB,qCAAkC;AAElC,MAAM,SAAS,GAAG,IAAA,gBAAS,EAAC,oBAAI,CAAC,CAAC;AAErB,QAAA,cAAc,GAAG;IAC5B,MAAM,EAAE,KAAK,IAA4B,EAAE;QACzC,MAAM,WAAW,GAAG;YAClB,SAAS,EAAE,aAAa;YACxB,6DAA6D;YAC7D,iEAAiE;YACjE,iEAAiE;YACjE,kDAAkD;YAClD,kBAAkB;SACnB,CAAC;QAEF,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;gBAC/D,OAAO,CAAC,CAAC;YACX,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,SAAS;YACX,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,EAAE,KAAK,EAAE,YAAoB,EAAE,UAAkB,EAAoB,EAAE;QAC7E,MAAM,WAAW,GAAG,MAAM,sBAAc,CAAC,MAAM,EAAE,CAAC;QAClD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,eAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACpD,eAAM,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;YAC9F,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,wBAAwB,CAAC,CAAC;QAClF,YAAE,CAAC,aAAa,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAE3C,IAAI,CAAC;YACH,eAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;YACtD,MAAM,OAAO,GAAG,IAAI,WAAW,4BAA4B,UAAU,GAAG,CAAC;YACzE,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;YACzB,eAAM,CAAC,OAAO,CAAC,oCAAoC,UAAU,EAAE,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,eAAM,CAAC,KAAK,CAAC,wBAAwB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACpD,OAAO,KAAK,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,IAAI,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC;gBAAE,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.bridgeManager = void 0;
|
|
7
|
+
const http_1 = __importDefault(require("http"));
|
|
8
|
+
const logger_1 = require("./logger");
|
|
9
|
+
const project_1 = require("./project");
|
|
10
|
+
exports.bridgeManager = {
|
|
11
|
+
start: (port = 4242) => {
|
|
12
|
+
logger_1.logger.info(`🌐 Starting Engine Bridge on port ${port}...`);
|
|
13
|
+
const server = http_1.default.createServer((req, res) => {
|
|
14
|
+
// Set CORS for engine editors
|
|
15
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
16
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
17
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
18
|
+
if (req.method === 'OPTIONS') {
|
|
19
|
+
res.writeHead(200);
|
|
20
|
+
res.end();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (req.url === '/sync' && req.method === 'GET') {
|
|
24
|
+
const status = {
|
|
25
|
+
projectName: project_1.projectManager.getSummary(),
|
|
26
|
+
timestamp: Date.now(),
|
|
27
|
+
connected: true,
|
|
28
|
+
status: 'online'
|
|
29
|
+
};
|
|
30
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
31
|
+
res.end(JSON.stringify(status));
|
|
32
|
+
}
|
|
33
|
+
else if (req.url === '/push' && req.method === 'POST') {
|
|
34
|
+
let body = '';
|
|
35
|
+
req.on('data', chunk => { body += chunk; });
|
|
36
|
+
req.on('end', () => {
|
|
37
|
+
logger_1.logger.info(`📥 Engine Sync Received: ${body.substring(0, 50)}...`);
|
|
38
|
+
project_1.projectManager.addEntry('Engine Live Sync', body);
|
|
39
|
+
res.writeHead(200);
|
|
40
|
+
res.end('OK');
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
res.writeHead(404);
|
|
45
|
+
res.end();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
server.listen(port, () => {
|
|
49
|
+
logger_1.logger.success(`🚀 Engine Bridge live at http://localhost:${port}`);
|
|
50
|
+
logger_1.logger.info('💡 Connect your Unity/Godot plugin to this endpoint for live sync.');
|
|
51
|
+
});
|
|
52
|
+
return server;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/utils/bridge.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AACxB,qCAAkC;AAClC,uCAA2C;AAE9B,QAAA,aAAa,GAAG;IAC3B,KAAK,EAAE,CAAC,OAAe,IAAI,EAAE,EAAE;QAC7B,eAAM,CAAC,IAAI,CAAC,qCAAqC,IAAI,KAAK,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,cAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,8BAA8B;YAC9B,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;YAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,oBAAoB,CAAC,CAAC;YACpE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;YAE9D,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,IAAI,GAAG,CAAC,GAAG,KAAK,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAChD,MAAM,MAAM,GAAG;oBACb,WAAW,EAAE,wBAAc,CAAC,UAAU,EAAE;oBACxC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;oBACrB,SAAS,EAAE,IAAI;oBACf,MAAM,EAAE,QAAQ;iBACjB,CAAC;gBACF,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;YAClC,CAAC;iBAAM,IAAI,GAAG,CAAC,GAAG,KAAK,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACxD,IAAI,IAAI,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,eAAM,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;oBACpE,wBAAc,CAAC,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;oBAClD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACnB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACvB,eAAM,CAAC,OAAO,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC;YACpE,eAAM,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
|
package/dist/utils/config.d.ts
CHANGED
package/dist/utils/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACvD,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACvD,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAe5C,QAAA,aAAa,GAAG;IAC3B,IAAI,EAAE,GAAG,EAAE;QACT,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,YAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,YAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,GAAG,EAAE,GAAW,EAAE;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,GAAG,EAAE,CAAC,SAA0B,EAAE,EAAE;QAClC,MAAM,OAAO,GAAG,qBAAa,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,SAAS,EAAE,CAAC;QAC7C,YAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,EAAE,GAAG,EAAE;QACV,YAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.economyManager = void 0;
|
|
7
|
+
const ai_service_1 = require("./ai-service");
|
|
8
|
+
const project_1 = require("./project");
|
|
9
|
+
const logger_1 = require("./logger");
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
exports.economyManager = {
|
|
13
|
+
analyze: async (narrativeContext) => {
|
|
14
|
+
logger_1.logger.info('🧠 AI Economists at work... Analyzing game loops...');
|
|
15
|
+
const summary = project_1.projectManager.getSummary();
|
|
16
|
+
const prompt = `As a Senior Game Economy Designer, analyze this game project's summary and specific context:
|
|
17
|
+
|
|
18
|
+
Project Summary: ${summary}
|
|
19
|
+
Current Focus: ${narrativeContext}
|
|
20
|
+
|
|
21
|
+
Task:
|
|
22
|
+
1. Identify 3 critical "Monetization Nodes" (where players feel value).
|
|
23
|
+
2. Suggest an IAP (In-App Purchase) strategy that respects player agency.
|
|
24
|
+
3. Analyze potential "Hyper-Casual" vs "Hardcore" monetization balance.
|
|
25
|
+
4. Propose a "Season Pass" or "Battle Pass" tier structure if applicable.
|
|
26
|
+
|
|
27
|
+
Format the response as a professional Technical Report.`;
|
|
28
|
+
const report = await ai_service_1.AIService.chat(prompt);
|
|
29
|
+
if (!fs_1.default.existsSync('.gmpilot/analysis')) {
|
|
30
|
+
fs_1.default.mkdirSync('.gmpilot/analysis', { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
const filename = path_1.default.join('.gmpilot/analysis', `economy_report_${Date.now()}.md`);
|
|
33
|
+
fs_1.default.writeFileSync(filename, report);
|
|
34
|
+
return report;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=economy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"economy.js","sourceRoot":"","sources":["../../src/utils/economy.ts"],"names":[],"mappings":";;;;;;AAAA,6CAAyC;AACzC,uCAA2C;AAC3C,qCAAkC;AAClC,4CAAoB;AACpB,gDAAwB;AAEX,QAAA,cAAc,GAAG;IAC5B,OAAO,EAAE,KAAK,EAAE,gBAAwB,EAAmB,EAAE;QAC3D,eAAM,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QAEnE,MAAM,OAAO,GAAG,wBAAc,CAAC,UAAU,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG;;uBAEI,OAAO;qBACT,gBAAgB;;;;;;;;4DAQuB,CAAC;QAEzD,MAAM,MAAM,GAAG,MAAM,sBAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE5C,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACxC,YAAE,CAAC,SAAS,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,kBAAkB,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnF,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEnC,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
|
package/package.json
CHANGED
package/src/commands/assets.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { logger } from '../utils/logger';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { projectManager } from '../utils/project';
|
|
7
|
+
import { blenderManager } from '../utils/blender';
|
|
8
|
+
import { audioManager } from '../utils/audio';
|
|
7
9
|
|
|
8
10
|
export const assetCommands = {
|
|
9
11
|
script: async (engine: string = 'unity') => {
|
|
@@ -172,4 +174,98 @@ export const assetCommands = {
|
|
|
172
174
|
logger.error(err.message);
|
|
173
175
|
}
|
|
174
176
|
},
|
|
177
|
+
|
|
178
|
+
forge3d: async (prompt: string) => {
|
|
179
|
+
const spinner = ora(`Forging 3D Asset: ${prompt}...`).start();
|
|
180
|
+
try {
|
|
181
|
+
const aiPrompt = `As a Senior Blender Technical Artist, write a Blender Python (bpy) script to create a 3D model: "${prompt}".
|
|
182
|
+
Requirements:
|
|
183
|
+
1. Create the mesh procedurally.
|
|
184
|
+
2. Export the result as '.glb' to a file named 'output.glb' in the current directory.
|
|
185
|
+
3. ONLY return the raw Python code. No explanations, no markdown code blocks.`;
|
|
186
|
+
|
|
187
|
+
const pythonScript = await AIService.chat(aiPrompt);
|
|
188
|
+
spinner.text = 'AI script received. Launching Blender Processor...';
|
|
189
|
+
|
|
190
|
+
if (!fs.existsSync('.gmpilot/assets')) {
|
|
191
|
+
fs.mkdirSync('.gmpilot/assets', { recursive: true });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const timestamp = Date.now();
|
|
195
|
+
const outputFile = path.join(process.cwd(), '.gmpilot', 'assets', `forge_${timestamp}.glb`);
|
|
196
|
+
|
|
197
|
+
// We need to modify the script slightly to ensure it exports to the correct absolute path
|
|
198
|
+
const modifiedScript = pythonScript + `\nimport bpy\nimport os\nbpy.ops.export_scene.gltf(filepath=r"${outputFile}")`;
|
|
199
|
+
|
|
200
|
+
const success = await blenderManager.render3D(modifiedScript, outputFile);
|
|
201
|
+
spinner.stop();
|
|
202
|
+
|
|
203
|
+
if (success) {
|
|
204
|
+
logger.success(`3D Forge Complete: ${outputFile}`);
|
|
205
|
+
projectManager.addEntry('3D Forge', `Generated 3D Asset for: ${prompt}. Saved to: ${outputFile}`);
|
|
206
|
+
} else {
|
|
207
|
+
logger.error('3D Forge failed. Ensure Blender is installed and accessible.');
|
|
208
|
+
}
|
|
209
|
+
} catch (err: any) {
|
|
210
|
+
spinner.stop();
|
|
211
|
+
logger.error(`Forge Error: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
character: async (prompt: string) => {
|
|
216
|
+
const spinner = ora(`Designing Character Sheet: ${prompt}...`).start();
|
|
217
|
+
try {
|
|
218
|
+
const response = await AIService.chat(`Generate a professional 2D Character Concept Sheet description for: "${prompt}".
|
|
219
|
+
Include:
|
|
220
|
+
1. Front, Side, and Back view descriptions.
|
|
221
|
+
2. Color Palette (HEX).
|
|
222
|
+
3. Key Equipment & Accessories.
|
|
223
|
+
4. Unique visual motifs.`);
|
|
224
|
+
|
|
225
|
+
spinner.stop();
|
|
226
|
+
logger.bold('\n--- 🎨 Character Concept Sheet ---');
|
|
227
|
+
console.log(response);
|
|
228
|
+
|
|
229
|
+
if (!fs.existsSync('.gmpilot/assets')) {
|
|
230
|
+
fs.mkdirSync('.gmpilot/assets', { recursive: true });
|
|
231
|
+
}
|
|
232
|
+
const filename = `.gmpilot/assets/char_${prompt.replace(/\s+/g, '_')}_${Date.now()}.md`;
|
|
233
|
+
fs.writeFileSync(filename, response);
|
|
234
|
+
logger.success(`Character sheet saved to: ${filename}`);
|
|
235
|
+
projectManager.addEntry('Character Design', response);
|
|
236
|
+
} catch (err: any) {
|
|
237
|
+
spinner.stop();
|
|
238
|
+
logger.error(err.message);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
voice: async (text: string) => {
|
|
243
|
+
const spinner = ora('Forging AI Voice...').start();
|
|
244
|
+
try {
|
|
245
|
+
const filename = await audioManager.generateVoice(text);
|
|
246
|
+
spinner.stop();
|
|
247
|
+
if (filename) {
|
|
248
|
+
logger.success(`Voice-over generated: ${filename}`);
|
|
249
|
+
projectManager.addEntry('Voice Gen', `Text: ${text}. Saved to: ${filename}`);
|
|
250
|
+
}
|
|
251
|
+
} catch (err: any) {
|
|
252
|
+
spinner.stop();
|
|
253
|
+
logger.error(err.message);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
music: async (prompt: string) => {
|
|
258
|
+
const spinner = ora('Architecting Soundscape...').start();
|
|
259
|
+
try {
|
|
260
|
+
const filename = await audioManager.generateSound(prompt);
|
|
261
|
+
spinner.stop();
|
|
262
|
+
if (filename) {
|
|
263
|
+
logger.success(`Soundscape prompt/metadata archived: ${filename}`);
|
|
264
|
+
projectManager.addEntry('Sound Gen', `Prompt: ${prompt}. Saved to: ${filename}`);
|
|
265
|
+
}
|
|
266
|
+
} catch (err: any) {
|
|
267
|
+
spinner.stop();
|
|
268
|
+
logger.error(err.message);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
175
271
|
};
|
package/src/commands/init.ts
CHANGED
|
@@ -2,6 +2,7 @@ import inquirer from 'inquirer';
|
|
|
2
2
|
import { logger } from '../utils/logger';
|
|
3
3
|
import { configManager } from '../utils/config';
|
|
4
4
|
import { projectManager } from '../utils/project';
|
|
5
|
+
import { blenderManager } from '../utils/blender';
|
|
5
6
|
|
|
6
7
|
export const initCommand = async () => {
|
|
7
8
|
logger.bold('--- Initializing GameMindPilot Project ---');
|
|
@@ -27,5 +28,11 @@ export const initCommand = async () => {
|
|
|
27
28
|
projectManager.init(answers.projectName, answers.author);
|
|
28
29
|
|
|
29
30
|
logger.success(`Project "${answers.projectName}" initialized for ${answers.author}!`);
|
|
31
|
+
|
|
32
|
+
const blenderPath = await blenderManager.detect();
|
|
33
|
+
if (!blenderPath) {
|
|
34
|
+
logger.info('💡 TIP: For 3D Asset Forge capabilities, we recommend installing Blender (https://www.blender.org/)');
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
logger.info('Run "gmpilot --help" to see available features.');
|
|
31
38
|
};
|
package/src/commands/utility.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { assetCommands } from './assets';
|
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { projectManager } from '../utils/project';
|
|
14
|
+
import { economyManager } from '../utils/economy';
|
|
15
|
+
import { bridgeManager } from '../utils/bridge';
|
|
14
16
|
|
|
15
17
|
export const utilityCommands = {
|
|
16
18
|
update: async () => {
|
|
@@ -241,9 +243,8 @@ Run \`gmpilot --help\` for a full list of commands.
|
|
|
241
243
|
},
|
|
242
244
|
|
|
243
245
|
liveSync: async () => {
|
|
244
|
-
logger.
|
|
245
|
-
|
|
246
|
-
logger.warn('Bridge active. CLI commands will now reflect in the engine editor.');
|
|
246
|
+
logger.bold('\n--- 🚀 Launching Engine Live Bridge ---');
|
|
247
|
+
bridgeManager.start();
|
|
247
248
|
},
|
|
248
249
|
|
|
249
250
|
runTestBots: async (count: number = 10) => {
|
|
@@ -882,5 +883,45 @@ Run \`gmpilot --help\` for a full list of commands.
|
|
|
882
883
|
logger.info('Testing hyper-inflation... Simulating rare drop exploits...');
|
|
883
884
|
logger.warn('[Alert]: Economy broke after 1000 simulated days. Adjust drop rates in Monetization-Sim.');
|
|
884
885
|
logger.success('Eco-chaos report generated.');
|
|
885
|
-
}
|
|
886
|
+
},
|
|
887
|
+
|
|
888
|
+
economy: async (prompt: string) => {
|
|
889
|
+
const spinner = ora('Analyzing Game Economy & Monetization...').start();
|
|
890
|
+
try {
|
|
891
|
+
const report = await economyManager.analyze(prompt);
|
|
892
|
+
spinner.stop();
|
|
893
|
+
logger.bold('\n--- 💎 Monetization Strategy Report ---');
|
|
894
|
+
console.log(report);
|
|
895
|
+
logger.success(`Report saved to .gmpilot/analysis/`);
|
|
896
|
+
} catch (err: any) {
|
|
897
|
+
spinner.stop();
|
|
898
|
+
logger.error(err.message);
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
|
|
902
|
+
netCode: async (prompt: string) => {
|
|
903
|
+
const spinner = ora('Generating Multiplayer Net-Code...').start();
|
|
904
|
+
try {
|
|
905
|
+
const response = await AIService.chat(`As a Senior Network Engineer, generate a multiplayer synchronization script for: "${prompt}".
|
|
906
|
+
Requirements:
|
|
907
|
+
1. Use a modern networking library (e.g., Unity Netcode for GameObjects, Photon, or Mirror).
|
|
908
|
+
2. Implement State Synchronization and RPCs.
|
|
909
|
+
3. Include comments explaining client-side prediction and server reconciliation.`);
|
|
910
|
+
|
|
911
|
+
spinner.stop();
|
|
912
|
+
logger.bold('\n--- 🌐 Multiplayer Net-Code ---');
|
|
913
|
+
console.log(response);
|
|
914
|
+
|
|
915
|
+
const netDir = path.join('.gmpilot', 'assets', 'netcode');
|
|
916
|
+
if (!fs.existsSync(netDir)) fs.mkdirSync(netDir, { recursive: true });
|
|
917
|
+
|
|
918
|
+
const filename = path.join(netDir, `netcode_${Date.now()}.cs`);
|
|
919
|
+
fs.writeFileSync(filename, response);
|
|
920
|
+
logger.success(`Net-code saved to: ${filename}`);
|
|
921
|
+
projectManager.addEntry('Net-Code Gen', response);
|
|
922
|
+
} catch (err: any) {
|
|
923
|
+
spinner.stop();
|
|
924
|
+
logger.error(err.message);
|
|
925
|
+
}
|
|
926
|
+
},
|
|
886
927
|
};
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ const program = new Command();
|
|
|
12
12
|
program
|
|
13
13
|
.name('gmpilot')
|
|
14
14
|
.description('GameMindPilot CLI - Your AI Game Development Assistant')
|
|
15
|
-
.version('3.
|
|
15
|
+
.version('3.5.0');
|
|
16
16
|
|
|
17
17
|
import { loginCommand, logoutCommand } from './commands/login';
|
|
18
18
|
import { chatCommand } from './commands/chat';
|
|
@@ -138,6 +138,26 @@ assets
|
|
|
138
138
|
.description('Generate a complete asset suite (Mastery Level)')
|
|
139
139
|
.action(assetCommands.all);
|
|
140
140
|
|
|
141
|
+
assets
|
|
142
|
+
.command('forge3d <prompt>')
|
|
143
|
+
.description('Autonomous 3D Asset Forge (Requires Blender)')
|
|
144
|
+
.action((prompt) => assetCommands.forge3d(prompt));
|
|
145
|
+
|
|
146
|
+
assets
|
|
147
|
+
.command('character <prompt>')
|
|
148
|
+
.description('Generate a detailed 2D Character Concept Sheet')
|
|
149
|
+
.action((prompt) => assetCommands.character(prompt));
|
|
150
|
+
|
|
151
|
+
assets
|
|
152
|
+
.command('voice <text>')
|
|
153
|
+
.description('Generate AI Voice-over (Requires ElevenLabs Key)')
|
|
154
|
+
.action((text) => assetCommands.voice(text));
|
|
155
|
+
|
|
156
|
+
assets
|
|
157
|
+
.command('music <prompt>')
|
|
158
|
+
.description('Generate AI Soundscape metadata/prompts')
|
|
159
|
+
.action((prompt) => assetCommands.music(prompt));
|
|
160
|
+
|
|
141
161
|
program
|
|
142
162
|
.command('script')
|
|
143
163
|
.description('Generate code for Unity, Unreal, Godot')
|
|
@@ -583,6 +603,16 @@ program
|
|
|
583
603
|
.description('Extreme stress-test for virtual game economies')
|
|
584
604
|
.action(utilityCommands.ecoChaos);
|
|
585
605
|
|
|
606
|
+
program
|
|
607
|
+
.command('economy <prompt>')
|
|
608
|
+
.description('AI Monetization Strategist: Generate an economic report')
|
|
609
|
+
.action((prompt) => utilityCommands.economy(prompt));
|
|
610
|
+
|
|
611
|
+
program
|
|
612
|
+
.command('net-code <prompt>')
|
|
613
|
+
.description('AI Multiplayer Architect: Generate networking logic')
|
|
614
|
+
.action((prompt) => utilityCommands.netCode(prompt));
|
|
615
|
+
|
|
586
616
|
// Default action: Dashboard
|
|
587
617
|
if (!process.argv.slice(2).length || (process.argv[2] && !program.commands.map(c => c.name()).includes(process.argv[2]) && !process.argv[2].startsWith('-'))) {
|
|
588
618
|
utilityCommands.interactiveDashboard();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { logger } from './logger';
|
|
5
|
+
import { configManager } from './config';
|
|
6
|
+
|
|
7
|
+
export const audioManager = {
|
|
8
|
+
generateVoice: async (text: string, voiceId: string = 'adam'): Promise<string | null> => {
|
|
9
|
+
logger.info(`🎙️ Generating AI Voice for: "${text.substring(0, 30)}..."`);
|
|
10
|
+
|
|
11
|
+
// Check for API Key (ElevenLabs placeholder)
|
|
12
|
+
const apiKey = configManager.get().elevenLabsKey;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
logger.warn('ElevenLabs API Key missing.');
|
|
15
|
+
logger.info('💡 TIP: Set your key using "gmpilot login" or integrate it in config.');
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const response = await axios({
|
|
21
|
+
method: 'POST',
|
|
22
|
+
url: `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`,
|
|
23
|
+
data: { text, model_id: 'eleven_monolingual_v1' },
|
|
24
|
+
headers: { 'xi-api-key': apiKey, 'Content-Type': 'application/json' },
|
|
25
|
+
responseType: 'arraybuffer'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const audioDir = path.join(process.cwd(), '.gmpilot', 'assets', 'audio');
|
|
29
|
+
if (!fs.existsSync(audioDir)) fs.mkdirSync(audioDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
const filename = path.join(audioDir, `voice_${Date.now()}.mp3`);
|
|
32
|
+
fs.writeFileSync(filename, Buffer.from(response.data as any));
|
|
33
|
+
return filename;
|
|
34
|
+
} catch (err: any) {
|
|
35
|
+
logger.error(`Voice Generation Error: ${err.message}`);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
generateSound: async (prompt: string): Promise<string | null> => {
|
|
41
|
+
logger.info(`🎵 Generating Soundscape for: "${prompt}"`);
|
|
42
|
+
// NOTE: Sound generation usually requires a diff API like Stable Audio or Suno.
|
|
43
|
+
// For now, we generate the Technical Metadata/Prompt for external tools.
|
|
44
|
+
|
|
45
|
+
const audioDir = path.join(process.cwd(), '.gmpilot', 'assets', 'audio');
|
|
46
|
+
if (!fs.existsSync(audioDir)) fs.mkdirSync(audioDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
const filename = path.join(audioDir, `sound_prompt_${Date.now()}.md`);
|
|
49
|
+
const content = `# Audio Generation Prompt\n**Subject**: ${prompt}\n\n**AI Prompt**: "High-quality, immersive sound effect for ${prompt}, professional game audio, 44.1kHz, clear spatiality."`;
|
|
50
|
+
|
|
51
|
+
fs.writeFileSync(filename, content);
|
|
52
|
+
return filename;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { logger } from './logger';
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
export const blenderManager = {
|
|
10
|
+
detect: async (): Promise<string | null> => {
|
|
11
|
+
const commonPaths = [
|
|
12
|
+
'blender', // If in PATH
|
|
13
|
+
'C:\\Program Files\\Blender Foundation\\Blender\\blender.exe',
|
|
14
|
+
'C:\\Program Files\\Blender Foundation\\Blender 3.6\\blender.exe',
|
|
15
|
+
'C:\\Program Files\\Blender Foundation\\Blender 4.0\\blender.exe',
|
|
16
|
+
'/Applications/Blender.app/Contents/MacOS/Blender',
|
|
17
|
+
'/usr/bin/blender'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
for (const p of commonPaths) {
|
|
21
|
+
try {
|
|
22
|
+
await execAsync(`${p.includes(' ') ? `"${p}"` : p} --version`);
|
|
23
|
+
return p;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
render3D: async (pythonScript: string, outputFile: string): Promise<boolean> => {
|
|
32
|
+
const blenderPath = await blenderManager.detect();
|
|
33
|
+
if (!blenderPath) {
|
|
34
|
+
logger.warn('Blender not detected on this system.');
|
|
35
|
+
logger.info('💡 TIP: For 3D Asset Forge, install Blender: https://www.blender.org/download/');
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const scriptPath = path.join(process.cwd(), '.gmpilot', 'temp_blender_script.py');
|
|
40
|
+
fs.writeFileSync(scriptPath, pythonScript);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
logger.info('🚀 Launching Headless Blender Forge...');
|
|
44
|
+
const command = `"${blenderPath}" --background --python "${scriptPath}"`;
|
|
45
|
+
await execAsync(command);
|
|
46
|
+
logger.success(`3D Asset generated successfully: ${outputFile}`);
|
|
47
|
+
return true;
|
|
48
|
+
} catch (err: any) {
|
|
49
|
+
logger.error(`Blender Forge Error: ${err.message}`);
|
|
50
|
+
return false;
|
|
51
|
+
} finally {
|
|
52
|
+
if (fs.existsSync(scriptPath)) fs.unlinkSync(scriptPath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import { logger } from './logger';
|
|
3
|
+
import { projectManager } from './project';
|
|
4
|
+
|
|
5
|
+
export const bridgeManager = {
|
|
6
|
+
start: (port: number = 4242) => {
|
|
7
|
+
logger.info(`🌐 Starting Engine Bridge on port ${port}...`);
|
|
8
|
+
|
|
9
|
+
const server = http.createServer((req, res) => {
|
|
10
|
+
// Set CORS for engine editors
|
|
11
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
12
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
13
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
14
|
+
|
|
15
|
+
if (req.method === 'OPTIONS') {
|
|
16
|
+
res.writeHead(200);
|
|
17
|
+
res.end();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (req.url === '/sync' && req.method === 'GET') {
|
|
22
|
+
const status = {
|
|
23
|
+
projectName: projectManager.getSummary(),
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
connected: true,
|
|
26
|
+
status: 'online'
|
|
27
|
+
};
|
|
28
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
29
|
+
res.end(JSON.stringify(status));
|
|
30
|
+
} else if (req.url === '/push' && req.method === 'POST') {
|
|
31
|
+
let body = '';
|
|
32
|
+
req.on('data', chunk => { body += chunk; });
|
|
33
|
+
req.on('end', () => {
|
|
34
|
+
logger.info(`📥 Engine Sync Received: ${body.substring(0, 50)}...`);
|
|
35
|
+
projectManager.addEntry('Engine Live Sync', body);
|
|
36
|
+
res.writeHead(200);
|
|
37
|
+
res.end('OK');
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
res.writeHead(404);
|
|
41
|
+
res.end();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
server.listen(port, () => {
|
|
46
|
+
logger.success(`🚀 Engine Bridge live at http://localhost:${port}`);
|
|
47
|
+
logger.info('💡 Connect your Unity/Godot plugin to this endpoint for live sync.');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return server;
|
|
51
|
+
}
|
|
52
|
+
};
|
package/src/utils/config.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AIService } from './ai-service';
|
|
2
|
+
import { projectManager } from './project';
|
|
3
|
+
import { logger } from './logger';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
export const economyManager = {
|
|
8
|
+
analyze: async (narrativeContext: string): Promise<string> => {
|
|
9
|
+
logger.info('🧠 AI Economists at work... Analyzing game loops...');
|
|
10
|
+
|
|
11
|
+
const summary = projectManager.getSummary();
|
|
12
|
+
const prompt = `As a Senior Game Economy Designer, analyze this game project's summary and specific context:
|
|
13
|
+
|
|
14
|
+
Project Summary: ${summary}
|
|
15
|
+
Current Focus: ${narrativeContext}
|
|
16
|
+
|
|
17
|
+
Task:
|
|
18
|
+
1. Identify 3 critical "Monetization Nodes" (where players feel value).
|
|
19
|
+
2. Suggest an IAP (In-App Purchase) strategy that respects player agency.
|
|
20
|
+
3. Analyze potential "Hyper-Casual" vs "Hardcore" monetization balance.
|
|
21
|
+
4. Propose a "Season Pass" or "Battle Pass" tier structure if applicable.
|
|
22
|
+
|
|
23
|
+
Format the response as a professional Technical Report.`;
|
|
24
|
+
|
|
25
|
+
const report = await AIService.chat(prompt);
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync('.gmpilot/analysis')) {
|
|
28
|
+
fs.mkdirSync('.gmpilot/analysis', { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const filename = path.join('.gmpilot/analysis', `economy_report_${Date.now()}.md`);
|
|
32
|
+
fs.writeFileSync(filename, report);
|
|
33
|
+
|
|
34
|
+
return report;
|
|
35
|
+
}
|
|
36
|
+
};
|