rpg-event-generator 1.1.0 โ 1.1.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 +408 -472
- package/dist/index.js +36 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,32 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
A powerful procedural event generation system for RPG games, capable of creating virtually infinite contextual events based on player state and game world.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- **Infinite Event Generation**: Creates unique, contextual events using custom Markov chains and procedural techniques
|
|
8
|
-
- **Player-Aware**: Events adapt to player stats, career, relationships, life stage, reputation, and social standing
|
|
9
|
-
- **14+ Dramatic Event Types**: From court scandals and noble duels to ancient curses and bandit kings
|
|
10
|
-
- **Immersive Storytelling**: Each event features rich narratives with atmospheric descriptions and meaningful consequences
|
|
11
|
-
- **Dynamic Effects**: Context-aware rewards and consequences that scale with character development and power level
|
|
12
|
-
- **Career Integration**: Events specifically tailored to noble, merchant, warrior, and criminal careers
|
|
13
|
-
- **Relationship Impact**: Events that affect and are affected by your social connections and personal history
|
|
14
|
-
- **Seasonal & Location Themes**: Events adapt to time of year and geographical setting for added immersion
|
|
15
|
-
- **Consequence Tracking**: Events can have long-term effects on character development and reputation
|
|
16
|
-
- **Personality-Driven Choices**: Multiple meaningful choices that define your character's personality and path
|
|
17
|
-
- **Quality Filtering**: Built-in filters ensure only interesting, non-generic events are generated
|
|
18
|
-
- **Modular Design**: Easy to integrate into existing game systems
|
|
19
|
-
- **Highly Configurable**: Customize training data, templates, and generation parameters
|
|
20
|
-
|
|
21
|
-
## ๐ Quick Start
|
|
5
|
+
## ๐ Installation
|
|
22
6
|
|
|
23
7
|
```bash
|
|
24
8
|
npm install rpg-event-generator
|
|
25
9
|
```
|
|
26
10
|
|
|
11
|
+
## โจ Quick Start
|
|
12
|
+
|
|
27
13
|
```javascript
|
|
28
14
|
import { RPGEventGenerator, generateRPGEvent } from 'rpg-event-generator';
|
|
29
15
|
|
|
30
|
-
//
|
|
16
|
+
// Simple event generation
|
|
31
17
|
const event = generateRPGEvent({
|
|
32
18
|
age: 25,
|
|
33
19
|
gold: 500,
|
|
@@ -35,65 +21,44 @@ const event = generateRPGEvent({
|
|
|
35
21
|
career: 'merchant'
|
|
36
22
|
});
|
|
37
23
|
|
|
38
|
-
console.log(event.title);
|
|
39
|
-
console.log(event.description);
|
|
40
|
-
console.log(event.choices);
|
|
24
|
+
console.log(event.title); // "Golden Opportunity"
|
|
25
|
+
console.log(event.description); // Procedurally generated narrative
|
|
26
|
+
console.log(event.choices); // Array of meaningful choices
|
|
27
|
+
```
|
|
41
28
|
|
|
42
|
-
|
|
43
|
-
const generator = new RPGEventGenerator({
|
|
44
|
-
stateSize: 2,
|
|
45
|
-
trainingData: ['Your custom story fragments...']
|
|
46
|
-
});
|
|
29
|
+
## ๐ Features
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
31
|
+
- **๐ฒ Infinite Event Generation**: Custom Markov chains create unique, contextual events
|
|
32
|
+
- **๐ค Player-Aware**: Events adapt to stats, career, relationships, reputation, and social standing
|
|
33
|
+
- **๐ญ 14+ Event Types**: Court scandals, noble duels, ancient curses, bandit kings, and more
|
|
34
|
+
- **๐ Immersive Storytelling**: Rich narratives with atmospheric descriptions and meaningful consequences
|
|
35
|
+
- **โ๏ธ Dynamic Difficulty Scaling**: Events automatically scale based on player power level
|
|
36
|
+
- **๐จ Thematic Training Sets**: Fantasy, sci-fi, and historical themes with cultural variants
|
|
37
|
+
- **โ๏ธ Event Chains**: Multi-part story sequences with escalating consequences
|
|
38
|
+
- **โฐ Time-Based Events**: Seasonal changes and evolving long-term storylines
|
|
39
|
+
- **๐ง Modular Event System**: Create and manage custom templates, training data, and chains
|
|
40
|
+
- **๐พ Game Integration**: Proper save/load state management for real games
|
|
41
|
+
- **๐งช Comprehensive Testing**: 45+ automated tests ensuring reliability
|
|
57
42
|
|
|
58
|
-
## ๐ Usage
|
|
43
|
+
## ๐ Usage Guide
|
|
59
44
|
|
|
60
45
|
### Basic Event Generation
|
|
61
46
|
|
|
62
47
|
```javascript
|
|
63
48
|
const generator = new RPGEventGenerator();
|
|
64
|
-
|
|
65
|
-
// Generate a random event
|
|
66
49
|
const event = generator.generateEvent();
|
|
67
50
|
|
|
68
51
|
console.log(event);
|
|
69
52
|
// {
|
|
70
53
|
// id: "event_1704567890123_xyz789",
|
|
71
54
|
// title: "Perilous Bandit King's Challenge",
|
|
72
|
-
// description: "
|
|
73
|
-
// narrative: "The infamous bandit king blocks your path, offering you a choice: join his band or face certain death.",
|
|
55
|
+
// description: "Generated narrative based on Markov chains...",
|
|
74
56
|
// choices: [
|
|
75
|
-
// {
|
|
76
|
-
//
|
|
77
|
-
// effect: { gold: 375, reputation: -23, combat_skill: 12 },
|
|
78
|
-
// consequence: "bandit"
|
|
79
|
-
// },
|
|
80
|
-
// {
|
|
81
|
-
// text: "Challenge him to single combat",
|
|
82
|
-
// effect: { reputation: 32, health: -18 },
|
|
83
|
-
// requirements: { combat_skill: 60 },
|
|
84
|
-
// consequence: "hero"
|
|
85
|
-
// },
|
|
86
|
-
// {
|
|
87
|
-
// text: "Bribe your way past",
|
|
88
|
-
// effect: { gold: -320, safe_passage: true },
|
|
89
|
-
// consequence: "diplomat"
|
|
90
|
-
// }
|
|
57
|
+
// { text: "Fight back", effect: { health: -20, reputation: 25 } },
|
|
58
|
+
// { text: "Pay tribute", effect: { gold: -200, safe_passage: true } }
|
|
91
59
|
// ],
|
|
92
60
|
// type: "BANDIT_KING",
|
|
93
|
-
//
|
|
94
|
-
// urgency: "normal",
|
|
95
|
-
// theme: "adventure",
|
|
96
|
-
// context: { /* player context used */ }
|
|
61
|
+
// difficulty: "normal"
|
|
97
62
|
// }
|
|
98
63
|
```
|
|
99
64
|
|
|
@@ -106,319 +71,43 @@ const playerContext = {
|
|
|
106
71
|
influence: 40,
|
|
107
72
|
reputation: 25,
|
|
108
73
|
career: 'noble',
|
|
109
|
-
skills: {
|
|
110
|
-
diplomacy: 70,
|
|
111
|
-
combat: 45,
|
|
112
|
-
intrigue: 30
|
|
113
|
-
},
|
|
74
|
+
skills: { diplomacy: 70, combat: 45, intrigue: 30 },
|
|
114
75
|
relationships: [
|
|
115
|
-
{ name: 'Lord Harrington', type: 'ally', relationship: 60 }
|
|
116
|
-
{ name: 'Lady Beaumont', type: 'lover', relationship: 45 }
|
|
76
|
+
{ name: 'Lord Harrington', type: 'ally', relationship: 60 }
|
|
117
77
|
],
|
|
118
78
|
location: 'capital',
|
|
119
79
|
season: 'winter'
|
|
120
80
|
};
|
|
121
81
|
|
|
122
82
|
const event = generator.generateEvent(playerContext);
|
|
123
|
-
// Generates events
|
|
124
|
-
// - Diplomatic missions (high diplomacy skill)
|
|
125
|
-
// - Court intrigue opportunities (high influence)
|
|
126
|
-
// - Social events involving relationships
|
|
127
|
-
// - Winter-themed challenges
|
|
83
|
+
// Generates events appropriate to noble career, high influence, winter season
|
|
128
84
|
```
|
|
129
85
|
|
|
130
86
|
### Custom Training Data
|
|
131
87
|
|
|
88
|
+
Enhance text generation with your own story fragments:
|
|
89
|
+
|
|
132
90
|
```javascript
|
|
133
91
|
const customTrainingData = [
|
|
134
92
|
'In the shadowed alleys of the ancient city',
|
|
135
|
-
'The dragon\'s roar echoes through the mountains',
|
|
136
|
-
'Elven merchants display their enchanted wares',
|
|
137
|
-
'A dwarven smith forges weapons of
|
|
138
|
-
'The tavern
|
|
139
|
-
'Ancient runes glow with
|
|
140
|
-
'The forest whispers secrets to those who listen'
|
|
93
|
+
'The dragon\'s roar echoes through the misty mountains',
|
|
94
|
+
'Elven merchants display their enchanted crystal wares',
|
|
95
|
+
'A dwarven smith forges weapons of legendary power',
|
|
96
|
+
'The tavern overflows with adventurers and mysterious strangers',
|
|
97
|
+
'Ancient runes glow with ethereal magical energy',
|
|
98
|
+
'The enchanted forest whispers secrets to those who listen carefully'
|
|
141
99
|
];
|
|
142
100
|
|
|
143
101
|
const generator = new RPGEventGenerator({
|
|
144
102
|
trainingData: customTrainingData
|
|
145
103
|
});
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## ๐ง API Reference
|
|
149
|
-
|
|
150
|
-
### RPGEventGenerator Class
|
|
151
|
-
|
|
152
|
-
#### Constructor Options
|
|
153
|
-
|
|
154
|
-
```javascript
|
|
155
|
-
const generator = new RPGEventGenerator({
|
|
156
|
-
stateSize: 2, // Markov chain state size (default: 2)
|
|
157
|
-
trainingData: [...] // Custom training data array (optional)
|
|
158
|
-
});
|
|
159
|
-
```
|
|
160
104
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- `generateEvents(playerContext, count)` - Generate multiple events
|
|
165
|
-
- `addTrainingData(data)` - Add more training data
|
|
166
|
-
- `resetTrainingData(data)` - Reset with new training data
|
|
167
|
-
|
|
168
|
-
### Player Context Object
|
|
169
|
-
|
|
170
|
-
```javascript
|
|
171
|
-
{
|
|
172
|
-
age: number, // Player age
|
|
173
|
-
gold: number, // Player wealth
|
|
174
|
-
influence: number, // Political/social influence
|
|
175
|
-
reputation: number, // Social reputation
|
|
176
|
-
career: string, // Player's career/job
|
|
177
|
-
skills: { // Skill levels object
|
|
178
|
-
combat: number,
|
|
179
|
-
diplomacy: number,
|
|
180
|
-
// ... other skills
|
|
181
|
-
},
|
|
182
|
-
relationships: [{ // Array of relationships
|
|
183
|
-
name: string,
|
|
184
|
-
type: string, // 'friend', 'lover', 'ally', etc.
|
|
185
|
-
relationship: number // Relationship strength (0-100)
|
|
186
|
-
}],
|
|
187
|
-
location: string, // Current location
|
|
188
|
-
season: string // Current season
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Event Object Structure
|
|
193
|
-
|
|
194
|
-
```javascript
|
|
195
|
-
{
|
|
196
|
-
id: string, // Unique event identifier
|
|
197
|
-
title: string, // Dynamic, context-aware title
|
|
198
|
-
description: string, // Rich procedural description
|
|
199
|
-
narrative: string, // Core story premise
|
|
200
|
-
choices: [{ // Array of meaningful choices
|
|
201
|
-
text: string, // Choice text with contextual flavor
|
|
202
|
-
effect: { // Effects scaled by context
|
|
203
|
-
gold?: number, // Wealth changes
|
|
204
|
-
influence?: number, // Social/political power
|
|
205
|
-
reputation?: number, // Social standing
|
|
206
|
-
health?: number, // Physical well-being
|
|
207
|
-
stress?: number, // Mental strain
|
|
208
|
-
karma?: number, // Moral standing
|
|
209
|
-
// ... many more effects
|
|
210
|
-
},
|
|
211
|
-
consequence: string // Long-term character effect
|
|
212
|
-
}],
|
|
213
|
-
type: string, // Rich event category (COURT_SCANDAL, etc.)
|
|
214
|
-
consequence: string|null, // Applied consequence after choice
|
|
215
|
-
urgency: string, // 'normal', 'high', 'critical'
|
|
216
|
-
theme: string, // 'political', 'criminal', 'supernatural', etc.
|
|
217
|
-
context: object // Full context used for generation
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## ๐ฎ Integration Guide
|
|
222
|
-
|
|
223
|
-
### React Native Game Integration
|
|
224
|
-
|
|
225
|
-
```javascript
|
|
226
|
-
import React, { useState, useEffect } from 'react';
|
|
227
|
-
import { RPGEventGenerator } from 'rpg-event-generator';
|
|
228
|
-
|
|
229
|
-
function GameEventSystem({ playerStats }) {
|
|
230
|
-
const [currentEvent, setCurrentEvent] = useState(null);
|
|
231
|
-
const [generator] = useState(() => new RPGEventGenerator());
|
|
232
|
-
|
|
233
|
-
useEffect(() => {
|
|
234
|
-
// Generate event when time advances
|
|
235
|
-
const event = generator.generateEvent(playerStats);
|
|
236
|
-
setCurrentEvent(event);
|
|
237
|
-
}, [playerStats, generator]);
|
|
238
|
-
|
|
239
|
-
const handleChoice = (choiceIndex) => {
|
|
240
|
-
const choice = currentEvent.choices[choiceIndex];
|
|
241
|
-
// Apply effects to player stats
|
|
242
|
-
applyEffects(choice.effect);
|
|
243
|
-
setCurrentEvent(null); // Clear event
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
if (!currentEvent) return null;
|
|
247
|
-
|
|
248
|
-
return (
|
|
249
|
-
<EventDialog
|
|
250
|
-
event={currentEvent}
|
|
251
|
-
onChoice={handleChoice}
|
|
252
|
-
/>
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Redux Integration
|
|
258
|
-
|
|
259
|
-
```javascript
|
|
260
|
-
// Action creators
|
|
261
|
-
export const generateEvent = (playerContext) => {
|
|
262
|
-
const generator = new RPGEventGenerator();
|
|
263
|
-
const event = generator.generateEvent(playerContext);
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
type: 'GENERATE_EVENT',
|
|
267
|
-
payload: event
|
|
268
|
-
};
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
export const resolveEvent = (eventId, choiceIndex) => ({
|
|
272
|
-
type: 'RESOLVE_EVENT',
|
|
273
|
-
payload: { eventId, choiceIndex }
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Reducer
|
|
277
|
-
const eventReducer = (state = {}, action) => {
|
|
278
|
-
switch (action.type) {
|
|
279
|
-
case 'GENERATE_EVENT':
|
|
280
|
-
return { ...state, currentEvent: action.payload };
|
|
281
|
-
case 'RESOLVE_EVENT':
|
|
282
|
-
return { ...state, currentEvent: null };
|
|
283
|
-
default:
|
|
284
|
-
return state;
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## ๐งช Testing
|
|
290
|
-
|
|
291
|
-
```bash
|
|
292
|
-
npm test
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
The package includes comprehensive tests covering:
|
|
296
|
-
- Event generation with various contexts
|
|
297
|
-
- Effect resolution
|
|
298
|
-
- Markov chain text generation
|
|
299
|
-
- Template selection algorithms
|
|
300
|
-
|
|
301
|
-
## ๐ฏ Event Types
|
|
302
|
-
|
|
303
|
-
### Court & Political (Variable probability)
|
|
304
|
-
- **Court Scandal**: Royal court intrigue with betrayal and scandal
|
|
305
|
-
- **Noble Duel**: Honor challenges and duels of reputation
|
|
306
|
-
- **Market Crash**: Economic disasters affecting trade and wealth
|
|
307
|
-
- **Trade War**: Merchant rivalries and economic warfare
|
|
308
|
-
|
|
309
|
-
### Criminal Underworld (Variable probability)
|
|
310
|
-
- **Thieves' Guild**: Criminal organization recruitment and underworld dealings
|
|
311
|
-
- **Blackmail Opportunity**: Leverage compromising information for gain
|
|
312
|
-
- **Bandit King Challenge**: Highway robbery and outlaw confrontations
|
|
313
|
-
|
|
314
|
-
### Supernatural & Mysterious (Variable probability)
|
|
315
|
-
- **Ancient Curse**: Cursed artifacts and supernatural afflictions
|
|
316
|
-
- **Ghostly Visitation**: Spirits seeking justice or redemption
|
|
317
|
-
- **Lost Civilization**: Archaeological discoveries and ancient treasures
|
|
318
|
-
|
|
319
|
-
### Personal & Dramatic (Variable probability)
|
|
320
|
-
- **Forbidden Love**: Romance across social boundaries with scandal
|
|
321
|
-
- **Family Secret**: Hidden lineage and ancestral revelations
|
|
322
|
-
- **Desertion Temptation**: Military crises testing loyalty and courage
|
|
323
|
-
- **Mercenary Contract**: Dangerous employment with high rewards
|
|
324
|
-
|
|
325
|
-
### Adventure & Exploration (Variable probability)
|
|
326
|
-
- **Bandit King**: Confrontations with notorious outlaws
|
|
327
|
-
- **Lost Civilization**: Exploration of ancient ruins and artifacts
|
|
328
|
-
|
|
329
|
-
Probabilities dynamically adjust based on player career, skills, reputation, wealth, and current life circumstances.
|
|
330
|
-
|
|
331
|
-
## ๐ How It Works
|
|
332
|
-
|
|
333
|
-
### 1. Context Analysis
|
|
334
|
-
The generator analyzes player stats to understand their current situation and capabilities.
|
|
335
|
-
|
|
336
|
-
### 2. Template Selection
|
|
337
|
-
Based on context, it selects an appropriate event template from 14+ rich narrative types (Court Scandal, Forbidden Love, Ancient Curse, etc.).
|
|
338
|
-
|
|
339
|
-
### 3. Procedural Description
|
|
340
|
-
Uses custom Markov chains trained on immersive RPG-themed text to generate unique, atmospheric descriptions.
|
|
341
|
-
|
|
342
|
-
### 4. Dynamic Choices
|
|
343
|
-
Creates contextually appropriate choices with effects that scale based on player stats, career, and relationships.
|
|
344
|
-
|
|
345
|
-
### 5. Effect Resolution
|
|
346
|
-
Converts effect ranges into specific numbers and applies sophisticated context multipliers based on character background.
|
|
347
|
-
|
|
348
|
-
## ๐จ Customization
|
|
349
|
-
|
|
350
|
-
### Custom Event Templates
|
|
351
|
-
|
|
352
|
-
```javascript
|
|
353
|
-
const customTemplates = {
|
|
354
|
-
QUEST: {
|
|
355
|
-
title: 'Epic Quest',
|
|
356
|
-
choices: [
|
|
357
|
-
{ text: 'Accept the quest', effect: { influence: [20, 40] } },
|
|
358
|
-
{ text: 'Decline politely', effect: {} }
|
|
359
|
-
]
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// Extend the generator
|
|
364
|
-
class CustomRPGEventGenerator extends RPGEventGenerator {
|
|
365
|
-
constructor(options) {
|
|
366
|
-
super(options);
|
|
367
|
-
this.templates = { ...this.templates, ...customTemplates };
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Custom Effect Types
|
|
373
|
-
|
|
374
|
-
```javascript
|
|
375
|
-
// Add custom effects
|
|
376
|
-
const event = generator.generateEvent(playerContext);
|
|
377
|
-
|
|
378
|
-
// Custom effect resolution
|
|
379
|
-
function resolveCustomEffects(choice, playerContext) {
|
|
380
|
-
const effects = resolveEffect(choice.effect, playerContext);
|
|
381
|
-
|
|
382
|
-
// Add custom logic
|
|
383
|
-
if (effects.experience) {
|
|
384
|
-
// Handle experience gain
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (effects.relationshipChange) {
|
|
388
|
-
// Update relationship values
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return effects;
|
|
392
|
-
}
|
|
105
|
+
// Events will now incorporate your custom story elements
|
|
106
|
+
const event = generator.generateEvent();
|
|
107
|
+
// May generate: "In the shadowed alleys of the ancient city, a mysterious stranger approaches..."
|
|
393
108
|
```
|
|
394
109
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
- **Generation Speed**: ~10-50ms per event
|
|
398
|
-
- **Memory Usage**: ~2-5MB for trained Markov generator
|
|
399
|
-
- **Infinite Variety**: Can generate millions of unique events
|
|
400
|
-
- **Scalable**: Works with any number of players/contexts
|
|
401
|
-
|
|
402
|
-
## ๐ค Contributing
|
|
403
|
-
|
|
404
|
-
1. Fork the repository
|
|
405
|
-
2. Create a feature branch
|
|
406
|
-
3. Add tests for new functionality
|
|
407
|
-
4. Ensure all tests pass
|
|
408
|
-
5. Submit a pull request
|
|
409
|
-
|
|
410
|
-
## ๐ License
|
|
411
|
-
|
|
412
|
-
MIT License - see LICENSE file for details.
|
|
413
|
-
|
|
414
|
-
## ๐ Acknowledgments
|
|
415
|
-
|
|
416
|
-
- Uses [Chance.js](https://chancejs.com/) for random number generation
|
|
417
|
-
- Custom Markov chain implementation for procedural text generation
|
|
418
|
-
|
|
419
|
-
## ๐จ Thematic Training Sets
|
|
420
|
-
|
|
421
|
-
Choose from different genres and settings for your RPG events:
|
|
110
|
+
### Thematic Training Sets
|
|
422
111
|
|
|
423
112
|
```javascript
|
|
424
113
|
// Fantasy (default) - knights, magic, dragons
|
|
@@ -429,15 +118,19 @@ const norseGen = new RPGEventGenerator({
|
|
|
429
118
|
theme: 'fantasy',
|
|
430
119
|
culture: 'norse'
|
|
431
120
|
});
|
|
121
|
+
const norseEvent = norseGen.generateEvent();
|
|
122
|
+
// Generates: "The longships sail into the fjord under the northern lights..."
|
|
432
123
|
|
|
433
124
|
// Sci-fi - corporations, AI, space exploration
|
|
434
125
|
const sciFiGen = new RPGEventGenerator({ theme: 'sci-fi' });
|
|
435
126
|
|
|
436
|
-
// Cyberpunk sci-fi - neon
|
|
127
|
+
// Cyberpunk sci-fi - neon cities, megacorps, hackers
|
|
437
128
|
const cyberpunkGen = new RPGEventGenerator({
|
|
438
129
|
theme: 'sci-fi',
|
|
439
130
|
culture: 'cyberpunk'
|
|
440
131
|
});
|
|
132
|
+
const cyberEvent = cyberpunkGen.generateEvent();
|
|
133
|
+
// Generates: "Neon lights reflect off rain-slicked streets in the megacity sprawl..."
|
|
441
134
|
|
|
442
135
|
// Historical - medieval politics, exploration, warfare
|
|
443
136
|
const historicalGen = new RPGEventGenerator({ theme: 'historical' });
|
|
@@ -447,44 +140,82 @@ const victorianGen = new RPGEventGenerator({
|
|
|
447
140
|
theme: 'historical',
|
|
448
141
|
culture: 'victorian'
|
|
449
142
|
});
|
|
143
|
+
const victorianEvent = victorianGen.generateEvent();
|
|
144
|
+
// Generates: "Steam engines power the industrial revolution across soot-stained cities..."
|
|
450
145
|
```
|
|
451
146
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
Create multi-part story sequences where events trigger follow-ups:
|
|
147
|
+
### Event Chains
|
|
455
148
|
|
|
456
149
|
```javascript
|
|
150
|
+
// Start a multi-part storyline
|
|
457
151
|
const generator = new RPGEventGenerator();
|
|
458
|
-
|
|
459
|
-
// Start a bandit rising chain
|
|
460
152
|
const firstEvent = generator.startChain('BANDIT_RISING');
|
|
153
|
+
|
|
461
154
|
console.log(firstEvent.title); // "Treacherous Bandit Ambush"
|
|
155
|
+
console.log(firstEvent.chainId); // "chain_1704567890123_xyz789"
|
|
462
156
|
|
|
463
157
|
// Advance chain based on player choice
|
|
464
|
-
const nextEvent = generator.advanceChain(firstEvent.chainId, '
|
|
158
|
+
const nextEvent = generator.advanceChain(firstEvent.chainId, 'hero');
|
|
465
159
|
console.log(nextEvent.title); // "Perilous Bandit King's Challenge"
|
|
466
160
|
|
|
467
|
-
//
|
|
161
|
+
// Check active chains
|
|
162
|
+
const activeChains = generator.getActiveChains();
|
|
163
|
+
console.log(`Active chains: ${activeChains.length}`);
|
|
164
|
+
|
|
165
|
+
// Available built-in chains:
|
|
166
|
+
// - BANDIT_RISING: Highway robbery escalating to kingdom threat
|
|
167
|
+
// - COURT_SCANDAL_CHAIN: Royal intrigue with multiple betrayals
|
|
168
|
+
// - CURSE_OF_THE_ARTIFACT: Ancient curse with escalating effects
|
|
169
|
+
// - MERCHANT_EMPIRE: Trade empire building with setbacks
|
|
468
170
|
```
|
|
469
171
|
|
|
470
|
-
|
|
172
|
+
### Time-Based Events
|
|
471
173
|
|
|
472
|
-
|
|
174
|
+
```javascript
|
|
175
|
+
// Advance game time and handle due events
|
|
176
|
+
const dueEvents = generator.advanceGameDay();
|
|
177
|
+
|
|
178
|
+
dueEvents.forEach(event => {
|
|
179
|
+
if (event.type === 'time_based_chain') {
|
|
180
|
+
const chainData = generator.timeSystem.timeBasedEvents.get(event.chainId);
|
|
181
|
+
const gameEvent = generator.generateChainEvent(chainData);
|
|
182
|
+
// Add to your game's event system
|
|
183
|
+
}
|
|
184
|
+
});
|
|
473
185
|
|
|
474
|
-
|
|
186
|
+
// Start evolving storylines
|
|
187
|
+
generator.startTimeBasedChain('POLITICAL_UPRISING');
|
|
188
|
+
// - Day 1: Whispers of dissent appear
|
|
189
|
+
// - Day 7: Public protests erupt
|
|
190
|
+
// - Day 14: Open rebellion begins
|
|
191
|
+
// - Day 21: Revolutionary climax
|
|
475
192
|
|
|
476
|
-
|
|
477
|
-
const
|
|
193
|
+
// Check current game time
|
|
194
|
+
const currentTime = generator.getCurrentTime();
|
|
195
|
+
console.log(`Day ${currentTime.day}, Season: ${currentTime.season}`);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Modular Event System
|
|
478
199
|
|
|
200
|
+
```javascript
|
|
479
201
|
// Register custom event templates
|
|
480
202
|
const customTemplate = {
|
|
481
203
|
title: 'Mystic Vision',
|
|
482
204
|
narrative: 'You experience a vivid prophetic dream showing future events.',
|
|
483
205
|
choices: [
|
|
484
|
-
{
|
|
485
|
-
|
|
206
|
+
{
|
|
207
|
+
text: 'Seek out the prophecy',
|
|
208
|
+
effect: { wisdom: 15, risk: 20 },
|
|
209
|
+
consequence: 'visionary'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
text: 'Dismiss it as a dream',
|
|
213
|
+
effect: { stress: -10 },
|
|
214
|
+
consequence: 'skeptical'
|
|
215
|
+
}
|
|
486
216
|
]
|
|
487
217
|
};
|
|
218
|
+
|
|
488
219
|
generator.registerEventTemplate('MYSTIC_VISION', customTemplate);
|
|
489
220
|
|
|
490
221
|
// Add custom training data for better text generation
|
|
@@ -504,181 +235,386 @@ const visionChain = {
|
|
|
504
235
|
{ day: 10, template: 'FINAL_PROPHECY' }
|
|
505
236
|
]
|
|
506
237
|
};
|
|
238
|
+
|
|
507
239
|
generator.registerEventChain('PROPHECY_CHAIN', visionChain);
|
|
508
240
|
|
|
509
241
|
// Export/import custom content for sharing
|
|
510
242
|
const customContent = generator.exportCustomContent();
|
|
511
|
-
|
|
243
|
+
console.log('Exported:', Object.keys(customContent.templates).length, 'templates');
|
|
512
244
|
|
|
513
|
-
const
|
|
514
|
-
|
|
245
|
+
const newGenerator = new RPGEventGenerator();
|
|
246
|
+
const importResult = newGenerator.importCustomContent(customContent);
|
|
247
|
+
console.log('Imported:', importResult.templates.success, 'templates');
|
|
515
248
|
```
|
|
516
249
|
|
|
517
|
-
|
|
518
|
-
- **Templates**: `getCustomTemplates()`, `unregisterEventTemplate()`
|
|
519
|
-
- **Training Data**: Organized by categories for easy management
|
|
520
|
-
- **Event Chains**: `getCustomChains()`, `unregisterEventChain()`
|
|
521
|
-
- **Export/Import**: Full backup and sharing capabilities
|
|
522
|
-
|
|
523
|
-
## โ๏ธ Dynamic Difficulty Scaling
|
|
250
|
+
### Dynamic Difficulty Scaling
|
|
524
251
|
|
|
525
252
|
Events automatically scale based on player power level:
|
|
526
253
|
|
|
527
254
|
```javascript
|
|
528
255
|
// Weak character (easy difficulty)
|
|
529
|
-
const weakling = {
|
|
256
|
+
const weakling = {
|
|
257
|
+
gold: 50,
|
|
258
|
+
influence: 10,
|
|
259
|
+
skills: { combat: 20 }
|
|
260
|
+
};
|
|
530
261
|
const easyEvent = generator.generateEvent(weakling);
|
|
531
|
-
|
|
262
|
+
console.log(`Difficulty: ${easyEvent.difficulty}`); // "easy"
|
|
263
|
+
// Effects scaled for beginners: higher rewards, lower penalties
|
|
532
264
|
|
|
533
265
|
// Powerful character (hard difficulty)
|
|
534
|
-
const hero = {
|
|
266
|
+
const hero = {
|
|
267
|
+
gold: 50000,
|
|
268
|
+
influence: 500,
|
|
269
|
+
skills: { combat: 100, diplomacy: 80 },
|
|
270
|
+
relationships: [{ name: 'King', type: 'ally', relationship: 80 }]
|
|
271
|
+
};
|
|
535
272
|
const hardEvent = generator.generateEvent(hero);
|
|
536
|
-
|
|
273
|
+
console.log(`Difficulty: ${hardEvent.difficulty}`); // "hard"
|
|
274
|
+
// Effects scaled for challenge: lower rewards, higher penalties
|
|
537
275
|
|
|
538
276
|
// Legendary character (legendary difficulty)
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
277
|
+
const godlike = {
|
|
278
|
+
gold: 200000,
|
|
279
|
+
influence: 1000,
|
|
280
|
+
skills: { combat: 200, diplomacy: 150 },
|
|
281
|
+
age: 120,
|
|
282
|
+
reputation: 95
|
|
283
|
+
};
|
|
284
|
+
const epicEvent = generator.generateEvent(godlike);
|
|
285
|
+
console.log(`Difficulty: ${epicEvent.difficulty}`); // "legendary"
|
|
286
|
+
// Effects scaled for epic gameplay: minimal rewards, maximum penalties
|
|
542
287
|
```
|
|
543
288
|
|
|
544
289
|
**Difficulty Tiers:**
|
|
545
|
-
- **Easy** (Power 0-50):
|
|
546
|
-
- **Normal** (Power 25-150):
|
|
547
|
-
- **Hard** (Power 100-300):
|
|
548
|
-
- **Legendary** (Power 250+):
|
|
290
|
+
- **Easy** (Power 0-50): Generous rewards, forgiving penalties
|
|
291
|
+
- **Normal** (Power 25-150): Standard scaling, balanced challenges
|
|
292
|
+
- **Hard** (Power 100-300): Reduced rewards, harsher penalties
|
|
293
|
+
- **Legendary** (Power 250+): Minimal rewards, extreme challenges
|
|
549
294
|
|
|
550
|
-
##
|
|
295
|
+
## ๐ง API Reference
|
|
551
296
|
|
|
552
|
-
|
|
297
|
+
### Constructor Options
|
|
553
298
|
|
|
554
299
|
```javascript
|
|
555
|
-
const generator = new RPGEventGenerator(
|
|
300
|
+
const generator = new RPGEventGenerator({
|
|
301
|
+
stateSize: 2, // Markov chain state size (default: 2)
|
|
302
|
+
trainingData: [...], // Custom training data array
|
|
303
|
+
theme: 'fantasy', // 'fantasy', 'sci-fi', 'historical'
|
|
304
|
+
culture: 'norse' // Cultural variant within theme
|
|
305
|
+
});
|
|
306
|
+
```
|
|
556
307
|
|
|
557
|
-
|
|
558
|
-
const timeEvents = generator.advanceTime(30); // Advance 30 days
|
|
559
|
-
console.log('Season:', generator.getCurrentTime().season); // May change
|
|
308
|
+
### Core Methods
|
|
560
309
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
310
|
+
- `generateEvent(playerContext)` - Generate single event
|
|
311
|
+
- `generateEvents(playerContext, count)` - Generate multiple events
|
|
312
|
+
- `generateTimeAwareEvent(playerContext)` - Generate season-aware event
|
|
313
|
+
|
|
314
|
+
### Event Chain Methods
|
|
315
|
+
|
|
316
|
+
- `startChain(chainId)` - Start choice-based event chain
|
|
317
|
+
- `advanceChain(chainId, choice)` - Advance chain based on choice
|
|
318
|
+
- `getActiveChains()` - Get all active chains
|
|
319
|
+
|
|
320
|
+
### Time-Based Methods
|
|
321
|
+
|
|
322
|
+
- `advanceGameDay()` - Advance time and return due events
|
|
323
|
+
- `startTimeBasedChain(chainId)` - Start time-evolving chain
|
|
324
|
+
- `getCurrentTime()` - Get current game time/season
|
|
325
|
+
- `getActiveTimeChains()` - Get active time-based chains
|
|
326
|
+
|
|
327
|
+
### Game State Methods
|
|
328
|
+
|
|
329
|
+
- `getGameState()` - Export complete game state
|
|
330
|
+
- `loadGameState(state)` - Import saved game state
|
|
567
331
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
332
|
+
### Modular Methods
|
|
333
|
+
|
|
334
|
+
- `registerEventTemplate(id, template)` - Add custom event template
|
|
335
|
+
- `addCustomTrainingData(data, category)` - Add custom training data
|
|
336
|
+
- `registerEventChain(id, chainConfig)` - Add custom event chain
|
|
337
|
+
- `exportCustomContent()` - Export custom templates/chains/data
|
|
338
|
+
- `importCustomContent(content)` - Import custom content
|
|
339
|
+
|
|
340
|
+
### Player Context Object
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
{
|
|
344
|
+
age: number, // Player age
|
|
345
|
+
gold: number, // Player wealth
|
|
346
|
+
influence: number, // Political/social influence
|
|
347
|
+
reputation: number, // Social reputation
|
|
348
|
+
career: string, // Player's career/job ('noble', 'merchant', 'warrior', etc.)
|
|
349
|
+
skills: { // Skill levels object
|
|
350
|
+
combat: number, // 0-100 scale
|
|
351
|
+
diplomacy: number,
|
|
352
|
+
intrigue: number,
|
|
353
|
+
magic: number,
|
|
354
|
+
survival: number
|
|
355
|
+
},
|
|
356
|
+
relationships: [{ // Array of NPC relationships
|
|
357
|
+
name: string, // NPC name
|
|
358
|
+
type: string, // 'friend', 'lover', 'ally', 'enemy', 'rival'
|
|
359
|
+
relationship: number // Relationship strength (0-100)
|
|
360
|
+
}],
|
|
361
|
+
location: string, // Current location ('capital', 'village', 'forest', etc.)
|
|
362
|
+
season: string // Current season ('spring', 'summer', 'autumn', 'winter')
|
|
363
|
+
}
|
|
571
364
|
```
|
|
572
365
|
|
|
573
|
-
|
|
574
|
-
- **`POLITICAL_UPRISING`**: Rebellion that builds over weeks
|
|
575
|
-
- **`ECONOMIC_COLLAPSE`**: Market crisis with escalating consequences
|
|
576
|
-
- **`MYSTICAL_AWAKENING`**: Supernatural events that intensify over time
|
|
366
|
+
### Complete Event Object Structure
|
|
577
367
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
368
|
+
```javascript
|
|
369
|
+
{
|
|
370
|
+
id: "event_1704567890123_xyz789", // Unique event identifier
|
|
371
|
+
title: "Perilous Bandit King's Challenge", // Dynamic, context-aware title
|
|
372
|
+
description: "The infamous bandit king blocks your path, offering you a choice...", // Rich procedural description
|
|
373
|
+
narrative: "The infamous bandit king blocks your path, offering you a choice: join his band or face certain death.",
|
|
374
|
+
choices: [
|
|
375
|
+
{
|
|
376
|
+
text: "Join the bandits",
|
|
377
|
+
effect: { gold: 375, reputation: -23, combat_skill: 12 },
|
|
378
|
+
consequence: "bandit"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
text: "Challenge him to single combat",
|
|
382
|
+
effect: { reputation: 32, health: -18 },
|
|
383
|
+
requirements: { combat_skill: 60 },
|
|
384
|
+
consequence: "hero"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
text: "Bribe your way past",
|
|
388
|
+
effect: { gold: -320, safe_passage: true },
|
|
389
|
+
consequence: "diplomat"
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
type: "BANDIT_KING",
|
|
393
|
+
consequence: null,
|
|
394
|
+
context: { /* Full context used for generation */ },
|
|
395
|
+
urgency: "normal",
|
|
396
|
+
theme: "adventure",
|
|
397
|
+
difficulty: "normal"
|
|
398
|
+
}
|
|
399
|
+
```
|
|
583
400
|
|
|
584
401
|
## ๐ฎ Game Integration
|
|
585
402
|
|
|
586
|
-
|
|
403
|
+
### Real-Time Strategy Integration
|
|
587
404
|
|
|
588
405
|
```javascript
|
|
589
|
-
const generator = new RPGEventGenerator();
|
|
590
|
-
|
|
591
|
-
// Each game day, advance time and check for events
|
|
592
406
|
function onNewGameDay() {
|
|
593
407
|
const dueEvents = generator.advanceGameDay();
|
|
594
408
|
|
|
595
|
-
// Process each due event in your game
|
|
596
409
|
dueEvents.forEach(event => {
|
|
597
410
|
if (event.type === 'time_based_chain') {
|
|
598
|
-
// Generate the actual event for your game
|
|
599
411
|
const chainData = generator.timeSystem.timeBasedEvents.get(event.chainId);
|
|
600
412
|
const gameEvent = generator.generateChainEvent(chainData);
|
|
601
|
-
// Add gameEvent to your game's event system
|
|
602
413
|
triggerInGameEvent(gameEvent);
|
|
603
414
|
} else if (event.type === 'seasonal_random') {
|
|
604
|
-
// Generate a seasonal event
|
|
605
415
|
const seasonalEvent = generator.generateTimeAwareEvent(playerContext);
|
|
606
416
|
triggerInGameEvent(seasonalEvent);
|
|
607
417
|
}
|
|
608
418
|
});
|
|
609
419
|
}
|
|
420
|
+
```
|
|
610
421
|
|
|
611
|
-
|
|
612
|
-
function saveGame() {
|
|
613
|
-
const gameState = generator.getGameState();
|
|
614
|
-
// Save gameState to your game's save file
|
|
615
|
-
}
|
|
422
|
+
### React Game Integration
|
|
616
423
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
424
|
+
```javascript
|
|
425
|
+
import React, { useState, useEffect } from 'react';
|
|
426
|
+
import { RPGEventGenerator } from 'rpg-event-generator';
|
|
427
|
+
|
|
428
|
+
function GameEventSystem({ playerStats }) {
|
|
429
|
+
const [currentEvent, setCurrentEvent] = useState(null);
|
|
430
|
+
const [generator] = useState(() => new RPGEventGenerator());
|
|
431
|
+
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
// Generate event when time advances
|
|
434
|
+
const event = generator.generateEvent(playerStats);
|
|
435
|
+
setCurrentEvent(event);
|
|
436
|
+
}, [playerStats, generator]);
|
|
437
|
+
|
|
438
|
+
const handleChoice = (choiceIndex) => {
|
|
439
|
+
const choice = currentEvent.choices[choiceIndex];
|
|
440
|
+
// Apply effects to player stats
|
|
441
|
+
applyEffects(choice.effect);
|
|
442
|
+
setCurrentEvent(null); // Clear event
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
if (!currentEvent) return <div>No active events</div>;
|
|
446
|
+
|
|
447
|
+
return (
|
|
448
|
+
<div className="event-dialog">
|
|
449
|
+
<h2>{currentEvent.title}</h2>
|
|
450
|
+
<p>{currentEvent.description}</p>
|
|
451
|
+
<div className="choices">
|
|
452
|
+
{currentEvent.choices.map((choice, index) => (
|
|
453
|
+
<button
|
|
454
|
+
key={index}
|
|
455
|
+
onClick={() => handleChoice(index)}
|
|
456
|
+
className="choice-button"
|
|
457
|
+
>
|
|
458
|
+
{choice.text}
|
|
459
|
+
</button>
|
|
460
|
+
))}
|
|
461
|
+
</div>
|
|
462
|
+
</div>
|
|
463
|
+
);
|
|
620
464
|
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Redux Integration
|
|
468
|
+
|
|
469
|
+
```javascript
|
|
470
|
+
// Action Types
|
|
471
|
+
export const GENERATE_EVENT = 'GENERATE_EVENT';
|
|
472
|
+
export const RESOLVE_EVENT = 'RESOLVE_EVENT';
|
|
473
|
+
export const ADVANCE_GAME_DAY = 'ADVANCE_GAME_DAY';
|
|
474
|
+
|
|
475
|
+
// Action Creators
|
|
476
|
+
export const generateEvent = (playerContext) => ({
|
|
477
|
+
type: GENERATE_EVENT,
|
|
478
|
+
payload: { playerContext }
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
export const resolveEvent = (eventId, choiceIndex) => ({
|
|
482
|
+
type: RESOLVE_EVENT,
|
|
483
|
+
payload: { eventId, choiceIndex }
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
export const advanceGameDay = () => ({
|
|
487
|
+
type: ADVANCE_GAME_DAY
|
|
488
|
+
});
|
|
621
489
|
|
|
622
|
-
//
|
|
623
|
-
const
|
|
624
|
-
|
|
490
|
+
// Reducer
|
|
491
|
+
const initialState = {
|
|
492
|
+
generator: new RPGEventGenerator(),
|
|
493
|
+
currentEvent: null,
|
|
494
|
+
gameTime: { day: 1, season: 'spring' }
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const eventReducer = (state = initialState, action) => {
|
|
498
|
+
switch (action.type) {
|
|
499
|
+
case GENERATE_EVENT:
|
|
500
|
+
const event = state.generator.generateEvent(action.payload.playerContext);
|
|
501
|
+
return { ...state, currentEvent: event };
|
|
502
|
+
|
|
503
|
+
case RESOLVE_EVENT:
|
|
504
|
+
// Apply choice effects to player state
|
|
505
|
+
const choice = state.currentEvent.choices[action.payload.choiceIndex];
|
|
506
|
+
const newPlayerState = applyChoiceEffects(choice.effect);
|
|
507
|
+
return {
|
|
508
|
+
...state,
|
|
509
|
+
currentEvent: null,
|
|
510
|
+
playerState: newPlayerState
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
case ADVANCE_GAME_DAY:
|
|
514
|
+
const dueEvents = state.generator.advanceGameDay();
|
|
515
|
+
return {
|
|
516
|
+
...state,
|
|
517
|
+
gameTime: state.generator.getCurrentTime(),
|
|
518
|
+
dueEvents: dueEvents
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
default:
|
|
522
|
+
return state;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
625
525
|
```
|
|
626
526
|
|
|
627
|
-
|
|
628
|
-
- **Persistent State**: Chains survive game saves/loads
|
|
629
|
-
- **Event-Driven**: No timers, events trigger when you advance days
|
|
630
|
-
- **Game-Controlled**: You control when time passes
|
|
631
|
-
- **Multiple Events**: Handle multiple events per day
|
|
527
|
+
### Save/Load System
|
|
632
528
|
|
|
633
|
-
|
|
529
|
+
```javascript
|
|
530
|
+
// Save complete game state
|
|
531
|
+
function saveGame(playerState) {
|
|
532
|
+
const gameState = {
|
|
533
|
+
player: playerState,
|
|
534
|
+
events: generator.getGameState(),
|
|
535
|
+
timestamp: Date.now()
|
|
536
|
+
};
|
|
634
537
|
|
|
635
|
-
|
|
538
|
+
localStorage.setItem('rpgGameSave', JSON.stringify(gameState));
|
|
539
|
+
console.log('Game saved successfully');
|
|
540
|
+
}
|
|
636
541
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
542
|
+
// Load complete game state
|
|
543
|
+
function loadGame() {
|
|
544
|
+
try {
|
|
545
|
+
const savedGame = JSON.parse(localStorage.getItem('rpgGameSave'));
|
|
546
|
+
if (!savedGame) return false;
|
|
642
547
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
- **`space_opera`**: Galactic empires, Jedi knights, hyperspace, alien ambassadors
|
|
646
|
-
- **`post_apocalyptic`**: Wastelands, mutants, vault dwellers, irradiated ruins
|
|
548
|
+
// Restore generator state
|
|
549
|
+
generator.loadGameState(savedGame.events);
|
|
647
550
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
- **`victorian`**: Industrial revolution, social reformers, steam power
|
|
651
|
-
- **`ancient_roman`**: Gladiators, senators, aqueducts, barbarian hordes
|
|
551
|
+
// Restore player state
|
|
552
|
+
return savedGame.player;
|
|
652
553
|
|
|
653
|
-
|
|
554
|
+
} catch (error) {
|
|
555
|
+
console.error('Failed to load game:', error);
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
```
|
|
654
560
|
|
|
655
561
|
## ๐งช Testing
|
|
656
562
|
|
|
657
|
-
This package includes comprehensive test coverage with **45+ automated tests** ensuring reliability and quality:
|
|
658
|
-
|
|
659
563
|
```bash
|
|
660
564
|
npm test
|
|
661
565
|
```
|
|
662
566
|
|
|
663
|
-
**
|
|
664
|
-
- โ
|
|
665
|
-
- โ
|
|
666
|
-
- โ
|
|
667
|
-
- โ
|
|
668
|
-
- โ
|
|
669
|
-
- โ
|
|
670
|
-
- โ
|
|
671
|
-
- โ
|
|
672
|
-
- โ
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
- **
|
|
678
|
-
- **
|
|
679
|
-
- **
|
|
680
|
-
- **
|
|
681
|
-
|
|
567
|
+
**Comprehensive test coverage (45+ tests):**
|
|
568
|
+
- โ
Core event generation and validation
|
|
569
|
+
- โ
Context adaptation and player responsiveness
|
|
570
|
+
- โ
Thematic systems and cultural variants
|
|
571
|
+
- โ
Event chains and multi-stage progression
|
|
572
|
+
- โ
Dynamic difficulty scaling
|
|
573
|
+
- โ
Time systems and seasonal mechanics
|
|
574
|
+
- โ
Modular features and custom content
|
|
575
|
+
- โ
Edge cases and error handling
|
|
576
|
+
- โ
Game state persistence and integration
|
|
577
|
+
|
|
578
|
+
## ๐ฏ Event Types
|
|
579
|
+
|
|
580
|
+
### Court & Political
|
|
581
|
+
- **Court Scandal**: Royal intrigue with betrayal and scandal
|
|
582
|
+
- **Noble Duel**: Honour challenges and duels of reputation
|
|
583
|
+
- **Market Crash**: Economic disasters affecting trade
|
|
584
|
+
- **Trade War**: Merchant rivalries and economic warfare
|
|
585
|
+
|
|
586
|
+
### Criminal Underworld
|
|
587
|
+
- **Thieves' Guild**: Criminal organisation recruitment
|
|
588
|
+
- **Blackmail Opportunity**: Leverage compromising information
|
|
589
|
+
- **Bandit King Challenge**: Highway robbery confrontations
|
|
590
|
+
|
|
591
|
+
### Supernatural & Mysterious
|
|
592
|
+
- **Ancient Curse**: Cursed artefacts and afflictions
|
|
593
|
+
- **Ghostly Visitation**: Spirits seeking justice
|
|
594
|
+
- **Lost Civilisation**: Archaeological discoveries
|
|
595
|
+
|
|
596
|
+
### Personal & Dramatic
|
|
597
|
+
- **Forbidden Love**: Romance across social boundaries
|
|
598
|
+
- **Family Secret**: Hidden lineage revelations
|
|
599
|
+
- **Desertion Temptation**: Military loyalty crises
|
|
600
|
+
- **Mercenary Contract**: Dangerous employment opportunities
|
|
601
|
+
|
|
602
|
+
## ๐ค Contributing
|
|
603
|
+
|
|
604
|
+
1. Fork the repository
|
|
605
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
606
|
+
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
|
607
|
+
4. Push to branch (`git push origin feature/amazing-feature`)
|
|
608
|
+
5. Open a Pull Request
|
|
609
|
+
|
|
610
|
+
## ๐ License
|
|
611
|
+
|
|
612
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
613
|
+
|
|
614
|
+
## ๐ Acknowledgments
|
|
615
|
+
|
|
616
|
+
- [Chance.js](https://chancejs.com/) for random number generation
|
|
617
|
+
- Custom Markov chain implementation for procedural text generation
|
|
682
618
|
|
|
683
619
|
---
|
|
684
620
|
|
package/dist/index.js
CHANGED
|
@@ -2274,7 +2274,8 @@ class RPGEventGenerator {
|
|
|
2274
2274
|
}
|
|
2275
2275
|
return description;
|
|
2276
2276
|
} catch (error) {
|
|
2277
|
-
|
|
2277
|
+
const templateType = this.mapTemplateToType(template.type || Object.keys(this.templates).find(key => this.templates[key] === template));
|
|
2278
|
+
return this.getFallbackDescription(templateType);
|
|
2278
2279
|
}
|
|
2279
2280
|
}
|
|
2280
2281
|
|
|
@@ -2524,16 +2525,46 @@ class RPGEventGenerator {
|
|
|
2524
2525
|
return this.chance.pickone(locations);
|
|
2525
2526
|
}
|
|
2526
2527
|
|
|
2528
|
+
/**
|
|
2529
|
+
* Map template ID to fallback type
|
|
2530
|
+
* @param {string} templateId - Template identifier
|
|
2531
|
+
* @returns {string} Fallback type
|
|
2532
|
+
* @private
|
|
2533
|
+
*/
|
|
2534
|
+
mapTemplateToType(templateId) {
|
|
2535
|
+
if (!templateId) return 'ENCOUNTER';
|
|
2536
|
+
const typeMapping = {
|
|
2537
|
+
'COURT_SCANDAL': 'ENCOUNTER',
|
|
2538
|
+
'NOBLE_DUEL': 'CHALLENGE',
|
|
2539
|
+
'FORBIDDEN_LOVE': 'ENCOUNTER',
|
|
2540
|
+
'FAMILY_SECRET': 'MYSTERY',
|
|
2541
|
+
'THIEVES_GUILD': 'CHALLENGE',
|
|
2542
|
+
'BLACKMAIL_OPPORTUNITY': 'OPPORTUNITY',
|
|
2543
|
+
'ANCIENT_CURSE': 'MYSTERY',
|
|
2544
|
+
'GHOSTLY_VISITATION': 'MYSTERY',
|
|
2545
|
+
'LOST_CIVILIZATION': 'MYSTERY',
|
|
2546
|
+
'BANDIT_KING': 'CHALLENGE',
|
|
2547
|
+
'MARKET_CRASH': 'CHALLENGE',
|
|
2548
|
+
'TRADE_WAR': 'CHALLENGE',
|
|
2549
|
+
'MERCENARY_CONTRACT': 'OPPORTUNITY',
|
|
2550
|
+
'DESERTION_TEMPTATION': 'CHALLENGE'
|
|
2551
|
+
};
|
|
2552
|
+
return typeMapping[templateId] || 'ENCOUNTER';
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2527
2555
|
/**
|
|
2528
2556
|
* Get fallback description if Markov generation fails
|
|
2529
2557
|
* @private
|
|
2530
2558
|
*/
|
|
2531
2559
|
getFallbackDescription(templateType) {
|
|
2560
|
+
const professions = ['merchant', 'warrior', 'mage', 'noble', 'thief', 'priest', 'blacksmith', 'alchemist'];
|
|
2561
|
+
const products = ['sword', 'potion', 'artifact', 'weapon', 'armor', 'tool', 'gem', 'scroll'];
|
|
2562
|
+
const locations = ['village', 'town', 'city', 'castle', 'forest', 'mountain', 'ruins'];
|
|
2532
2563
|
const fallbacks = {
|
|
2533
|
-
ENCOUNTER: `A ${
|
|
2534
|
-
OPPORTUNITY: `A rare ${
|
|
2535
|
-
CHALLENGE: `A ${
|
|
2536
|
-
MYSTERY: `Strange occurrences have been reported near ${
|
|
2564
|
+
ENCOUNTER: `A ${this.chance.pickone(professions)} approaches you with an unusual proposition.`,
|
|
2565
|
+
OPPORTUNITY: `A rare ${this.chance.pickone(products)} has become available at an exceptional price.`,
|
|
2566
|
+
CHALLENGE: `A ${this.chance.pickone(professions)} challenges your position in the community.`,
|
|
2567
|
+
MYSTERY: `Strange occurrences have been reported near ${this.chance.pickone(locations)}.`
|
|
2537
2568
|
};
|
|
2538
2569
|
return fallbacks[templateType] || fallbacks.ENCOUNTER;
|
|
2539
2570
|
}
|