rpg-event-generator 1.1.4 → 1.2.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 +105 -1
- package/dist/index.js +610 -105
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,13 @@ console.log(event.choices); // Array of meaningful choices
|
|
|
40
40
|
- **Game Integration**: Proper save/load state management for real games
|
|
41
41
|
- **Comprehensive Testing**: 45+ automated tests ensuring reliability
|
|
42
42
|
|
|
43
|
+
### Enhanced Features (v1.2.0)
|
|
44
|
+
|
|
45
|
+
- **🌍 Multi-Language Support**: Generate events in different languages with cultural adaptation
|
|
46
|
+
- **🔗 Event Dependencies**: Complex prerequisite systems for event triggering
|
|
47
|
+
- **🌤️ Environmental Modifiers**: Weather, season, and location-based event modifications
|
|
48
|
+
- **👥 NPC Relationships**: Dynamic character relationship networks and social consequences
|
|
49
|
+
|
|
43
50
|
## 📖 Usage Guide
|
|
44
51
|
|
|
45
52
|
### Basic Event Generation
|
|
@@ -195,6 +202,96 @@ const currentTime = generator.getCurrentTime();
|
|
|
195
202
|
console.log(`Day ${currentTime.day}, Season: ${currentTime.season}`);
|
|
196
203
|
```
|
|
197
204
|
|
|
205
|
+
### Multi-Language Support 🌍
|
|
206
|
+
|
|
207
|
+
Generate events in different languages with cultural adaptation:
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
const generator = new RPGEventGenerator({
|
|
211
|
+
language: 'es', // Spanish events
|
|
212
|
+
theme: 'fantasy'
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Load additional language packs
|
|
216
|
+
generator.loadLanguagePack('fr', frenchTranslations);
|
|
217
|
+
generator.setLanguage('fr');
|
|
218
|
+
|
|
219
|
+
// Events now generate in French
|
|
220
|
+
const event = generator.generateEvent();
|
|
221
|
+
console.log(event.title); // "Embuscade de Brigands"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Environmental Modifiers 🌤️
|
|
225
|
+
|
|
226
|
+
Weather, season, and location affect event generation:
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
const generator = new RPGEventGenerator({ enableModifiers: true });
|
|
230
|
+
|
|
231
|
+
// Set environmental context
|
|
232
|
+
generator.setEnvironmentalContext({
|
|
233
|
+
weather: 'storm',
|
|
234
|
+
season: 'winter',
|
|
235
|
+
timeOfDay: 'night'
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Events adapt to harsh winter storm conditions
|
|
239
|
+
const event = generator.generateEnhancedEvent({
|
|
240
|
+
environment: { weather: 'storm', season: 'winter' }
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Event Dependencies 🔗
|
|
245
|
+
|
|
246
|
+
Complex prerequisite systems control when events can occur:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// Register event dependencies
|
|
250
|
+
generator.registerEventDependency('ROYAL_WEDDING', {
|
|
251
|
+
type: 'event_completed',
|
|
252
|
+
eventId: 'COURT_INTRODUCTION'
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
generator.registerEventDependency('ADVANCED_QUEST', {
|
|
256
|
+
operator: 'AND',
|
|
257
|
+
conditions: [
|
|
258
|
+
{ type: 'stat_requirement', stat: 'level', min: 5 },
|
|
259
|
+
{ type: 'event_completed', eventId: 'BASIC_QUEST' }
|
|
260
|
+
]
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Only generate events whose dependencies are met
|
|
264
|
+
const gameState = { completedEvents: new Set(['COURT_INTRODUCTION']) };
|
|
265
|
+
const event = generator.generateEnhancedEvent({ gameState });
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### NPC Relationship Networks 👥
|
|
269
|
+
|
|
270
|
+
Dynamic character relationships that evolve based on player actions:
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
const generator = new RPGEventGenerator({ enableRelationships: true });
|
|
274
|
+
|
|
275
|
+
// Add NPCs to the relationship network
|
|
276
|
+
generator.addNPC({
|
|
277
|
+
id: 'king_arthur',
|
|
278
|
+
name: 'King Arthur',
|
|
279
|
+
type: 'noble'
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Update relationships based on player actions
|
|
283
|
+
generator.applyRelationshipRule('king_arthur', 'player', 'save_life');
|
|
284
|
+
// Relationship with king improves significantly
|
|
285
|
+
|
|
286
|
+
generator.updateRelationship('merchant_john', 'player', -10, 'stole goods');
|
|
287
|
+
// Relationship with merchant deteriorates
|
|
288
|
+
|
|
289
|
+
// Generate events that consider relationship context
|
|
290
|
+
const event = generator.generateEnhancedEvent({
|
|
291
|
+
player: { id: 'player' }
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
198
295
|
### Modular Event System
|
|
199
296
|
|
|
200
297
|
```javascript
|
|
@@ -298,10 +395,17 @@ console.log(`Difficulty: ${epicEvent.difficulty}`); // "legendary"
|
|
|
298
395
|
|
|
299
396
|
```javascript
|
|
300
397
|
const generator = new RPGEventGenerator({
|
|
398
|
+
// Core options
|
|
301
399
|
stateSize: 2, // Markov chain state size (default: 2)
|
|
302
400
|
trainingData: [...], // Custom training data array
|
|
303
401
|
theme: 'fantasy', // 'fantasy', 'sci-fi', 'historical'
|
|
304
|
-
culture: 'norse'
|
|
402
|
+
culture: 'norse', // Cultural variant within theme
|
|
403
|
+
|
|
404
|
+
// Enhanced features (all enabled by default)
|
|
405
|
+
enableDependencies: true, // Complex event prerequisites
|
|
406
|
+
enableModifiers: true, // Weather/season/location modifiers
|
|
407
|
+
enableRelationships: true, // NPC relationship networks
|
|
408
|
+
language: 'en' // Default language for events
|
|
305
409
|
});
|
|
306
410
|
```
|
|
307
411
|
|
package/dist/index.js
CHANGED
|
@@ -133,6 +133,11 @@ class RPGEventGenerator {
|
|
|
133
133
|
this.customTemplates = new Set();
|
|
134
134
|
this.customChains = new Set();
|
|
135
135
|
this.customTrainingData = {};
|
|
136
|
+
this.enableDependencies = options.enableDependencies !== false;
|
|
137
|
+
this.enableModifiers = options.enableModifiers !== false;
|
|
138
|
+
this.enableRelationships = options.enableRelationships !== false;
|
|
139
|
+
this.language = options.language || 'en';
|
|
140
|
+
this.initializeEnhancedFeatures(options);
|
|
136
141
|
this.initializeThemes();
|
|
137
142
|
this.activeChains = new Map();
|
|
138
143
|
this.chainDefinitions = this.initializeChainDefinitions();
|
|
@@ -158,8 +163,6 @@ class RPGEventGenerator {
|
|
|
158
163
|
penaltyMultiplier: 1.6
|
|
159
164
|
}
|
|
160
165
|
};
|
|
161
|
-
|
|
162
|
-
// Initialize time system
|
|
163
166
|
this.timeSystem = {
|
|
164
167
|
currentDay: 1,
|
|
165
168
|
currentSeason: 'spring',
|
|
@@ -554,7 +557,6 @@ class RPGEventGenerator {
|
|
|
554
557
|
consequence: 'prudent'
|
|
555
558
|
}]
|
|
556
559
|
},
|
|
557
|
-
// Event Chain Templates
|
|
558
560
|
BANDIT_AMBUSH: {
|
|
559
561
|
title: 'Bandit Ambush',
|
|
560
562
|
narrative: 'A small group of bandits springs an ambush on your caravan, demanding tribute or blood.',
|
|
@@ -710,7 +712,6 @@ class RPGEventGenerator {
|
|
|
710
712
|
consequence: 'cursed'
|
|
711
713
|
}]
|
|
712
714
|
},
|
|
713
|
-
// Seasonal Event Templates
|
|
714
715
|
BLOOMING_ROMANCE: {
|
|
715
716
|
title: 'Spring Blossoms',
|
|
716
717
|
narrative: 'The spring air carries the sweet scent of blooming flowers, and romance fills the atmosphere.',
|
|
@@ -841,7 +842,6 @@ class RPGEventGenerator {
|
|
|
841
842
|
consequence: 'philosophical'
|
|
842
843
|
}]
|
|
843
844
|
},
|
|
844
|
-
// Time-based Event Templates
|
|
845
845
|
RUMORS_OF_DISSENT: {
|
|
846
846
|
title: 'Whispers of Unrest',
|
|
847
847
|
narrative: 'Rumors of dissatisfaction with the current regime begin circulating through the streets.',
|
|
@@ -963,76 +963,31 @@ class RPGEventGenerator {
|
|
|
963
963
|
* Initialize thematic training data sets
|
|
964
964
|
* @private
|
|
965
965
|
*/
|
|
966
|
+
/**
|
|
967
|
+
* Initialize enhanced features based on options
|
|
968
|
+
* @private
|
|
969
|
+
* @param {Object} options - Constructor options
|
|
970
|
+
*/
|
|
971
|
+
initializeEnhancedFeatures(options) {
|
|
972
|
+
this.locales = new Map();
|
|
973
|
+
this.dependencies = new Map();
|
|
974
|
+
this.modifiers = new Map();
|
|
975
|
+
this.relationships = new Map();
|
|
976
|
+
this.activeModifiers = new Set();
|
|
977
|
+
this.loadDefaultLocale();
|
|
978
|
+
if (this.enableModifiers) {
|
|
979
|
+
this.initializeBuiltInModifiers();
|
|
980
|
+
}
|
|
981
|
+
if (this.enableRelationships) {
|
|
982
|
+
this.initializeRelationshipRules();
|
|
983
|
+
}
|
|
984
|
+
}
|
|
966
985
|
initializeThemes() {
|
|
967
986
|
this.themes = {
|
|
968
|
-
fantasy: [
|
|
969
|
-
|
|
970
|
-
'The royal court
|
|
971
|
-
// Criminal Underworld
|
|
972
|
-
'The thieves\' guild has put out a contract that bears your name', 'Shadowy figures lurk in alleyways, watching your every move', 'The black market thrives under the cover of night, offering forbidden luxuries', 'A notorious crime lord has taken an interest in your activities', 'Corrupt guards demand tribute while turning a blind eye to greater crimes',
|
|
973
|
-
// Supernatural & Mysterious
|
|
974
|
-
'Strange runes appear on your bedroom wall, glowing with ethereal light', 'An ancient prophecy speaks of a hero who matches your description exactly', 'Ghostly apparitions warn of impending doom in fevered dreams', 'A witch in the woods offers you power beyond mortal comprehension', 'Cursed artifacts surface in the market, promising great power at terrible cost',
|
|
975
|
-
// Personal & Dramatic
|
|
976
|
-
'Your past sins come back to haunt you in the most unexpected ways', 'A long-lost relative appears with a tale that shakes your world', 'Your reputation draws admirers and enemies in equal measure', 'A betrayal cuts deeper than any blade, leaving scars on your soul', 'Love and ambition war within your heart as opportunities arise',
|
|
977
|
-
// Adventure & Exploration
|
|
978
|
-
'Ancient ruins whisper secrets to those brave enough to listen', 'A dragon\'s hoard lies hidden, protected by trials and tribulations', 'Bandits rule the roads, but their leader seems oddly familiar', 'A legendary artifact calls to you from across distant lands', 'The wilderness holds both peril and promise for the bold adventurer',
|
|
979
|
-
// Social & Relationship
|
|
980
|
-
'Romantic entanglements complicate your carefully laid plans', 'Old friends become new enemies as loyalties are tested', 'Family secrets emerge that threaten to destroy everything you hold dear', 'Mentors offer wisdom that comes with strings attached', 'Rivals emerge from unexpected places, challenging your hard-won position',
|
|
981
|
-
// Economic & Mercantile
|
|
982
|
-
'The market crashes send shockwaves through the merchant class', 'A get-rich-quick scheme promises fortunes but demands your soul', 'Trade wars erupt between rival merchant houses', 'Investment opportunities arise that could make or break your fortune', 'Black market deals offer power but carry the weight of damnation',
|
|
983
|
-
// Military & Combat
|
|
984
|
-
'War drums beat as kingdoms prepare for inevitable conflict', 'Desertion offers freedom but brands you a coward forever', 'A duel of honor is proposed, with your reputation on the line', 'Mercenary companies seek captains brave enough to lead them', 'Battle scars tell stories of glory and horror in equal measure',
|
|
985
|
-
// Magical & Mystical
|
|
986
|
-
'The veil between worlds thins, allowing magic to seep into reality', 'Curses and blessings intertwine in ways you never expected', 'Ancient bloodlines awaken powers dormant for generations', 'Rituals performed in secret grant power at terrible personal cost', 'The stars themselves seem to conspire in your favor or against you',
|
|
987
|
-
// Seasonal & Natural
|
|
988
|
-
'Winter\'s cruel bite forces desperate measures from the populace', 'Spring\'s renewal brings both hope and dangerous new beginnings', 'Summer tournaments test the mettle of warriors and nobles alike', 'Autumn harvests reveal secrets long buried in the fields', 'The changing seasons mirror the turmoil in your own life',
|
|
989
|
-
// Dramatic Twists
|
|
990
|
-
'What seems like a blessing reveals itself as a curse in disguise', 'Enemies become allies, and allies become your greatest threat', 'The path of righteousness leads to ruin, while villainy brings reward', 'Fate itself seems to take notice of your actions, for good or ill', 'The world bends around you as your choices reshape reality itself'],
|
|
991
|
-
'sci-fi': [
|
|
992
|
-
// Corporate Intrigue
|
|
993
|
-
'Megacorporations wage shadow wars through proxy conflicts and data manipulation', 'Neural implants malfunction, flooding your mind with corporate secrets', 'A black-market AI offers forbidden knowledge at the cost of your sanity', 'Corporate espionage turns deadly when rival agents converge on your location', 'Whistleblower data reveals systemic corruption in the highest levels of power',
|
|
994
|
-
// Cyberpunk Underworld
|
|
995
|
-
'Hackers breach your personal network, exposing vulnerabilities you never knew existed', 'Street gangs control the undercity with drone swarms and illegal augmentations', 'The dark web pulses with illegal tech trades and forbidden experiments', 'Corporate bounty hunters track you through the neon-lit sprawl', 'Data runners risk everything for the ultimate information score',
|
|
996
|
-
// Space & Exploration
|
|
997
|
-
'Alien artifacts whisper secrets from beyond the galactic rim', 'Space pirates demand tribute or face the void of decompression', 'Ancient derelict ships hold treasures and horrors from forgotten eras', 'Wormhole anomalies bend reality, creating unpredictable temporal events', 'Colony worlds rebel against Earth\'s iron-fisted governance',
|
|
998
|
-
// AI & Technology
|
|
999
|
-
'Sentient AIs manipulate events from within the global network', 'Nanobot swarms escape containment, evolving beyond their programming', 'Virtual reality simulations bleed into the real world disastrously', 'Genetic engineering experiments create monstrous hybrid beings', 'Quantum computers predict the future, but at what cost to free will?',
|
|
1000
|
-
// Dystopian Society
|
|
1001
|
-
'Social credit systems reward compliance and punish deviation', 'Surveillance drones watch every move, every transaction, every thought', 'Rebellions simmer beneath the surface of totalitarian control', 'Resource wars rage over the last viable mining asteroids', 'Climate engineering projects backfire spectacularly',
|
|
1002
|
-
// Personal & Cybernetic
|
|
1003
|
-
'Implant malfunctions cause vivid hallucinations and system crashes', 'Identity theft becomes literal as someone steals your digital consciousness', 'Memory wipes erase crucial knowledge, leaving you vulnerable', 'Cybernetic enhancements demand maintenance that costs more than you can afford', 'Neural links create unintended telepathic connections with strangers',
|
|
1004
|
-
// Economic & Corporate
|
|
1005
|
-
'Cryptocurrency markets crash, wiping out fortunes in digital dust', 'Hostile takeovers turn friendly acquisitions into bloody boardroom coups', 'Trade wars erupt between orbital habitats and planetary governments', 'Investment opportunities in black-market tech promise riches or ruin', 'Corporate espionage turns personal when family members are leveraged',
|
|
1006
|
-
// Military & Combat
|
|
1007
|
-
'Drone wars rage in the skies above abandoned cities', 'Cyber warfare disables entire infrastructure networks', 'Private military companies offer their services to the highest bidder', 'Nuclear deterrence fails when EMP weapons render missiles useless', 'Bioterrorism releases engineered plagues upon unsuspecting populations',
|
|
1008
|
-
// Mystery & Conspiracy
|
|
1009
|
-
'Government cover-ups hide the truth about alien visitations', 'Time travel experiments create paradoxical timeline fractures', 'Parallel dimensions bleed through, creating impossible phenomena', 'Ancient alien technology reactivates with catastrophic consequences', 'Conspiracy theories prove true, but the reality is far worse',
|
|
1010
|
-
// Environmental & Planetary
|
|
1011
|
-
'Terraforming projects unleash ancient microbes from the soil', 'Climate control systems fail, creating unpredictable weather patterns', 'Asteroid mining operations awaken dormant extraterrestrial threats', 'Ocean acidification drives aquatic mutations to the surface', 'Atmospheric processors malfunction, creating toxic breathing conditions'],
|
|
1012
|
-
historical: [
|
|
1013
|
-
// Medieval Court Intrigue
|
|
1014
|
-
'The king\'s advisors plot in shadowed corners of the great hall', 'Noble houses form secret alliances against the crown', 'Whispers of heresy spread through the royal court like wildfire', 'A foreign diplomat offers treacherous propositions under diplomatic immunity', 'Court jesters hide dangerous truths behind masks of folly',
|
|
1015
|
-
// Warfare & Conquest
|
|
1016
|
-
'Armies mass at the borders, their banners fluttering in the wind of war', 'Siege engines pound ancient walls as defenders hold the line', 'Cavalry charges break enemy formations in clouds of dust and blood', 'Naval blockades starve cities into submission or desperate rebellion', 'Mercenary companies switch sides based on the highest bidder',
|
|
1017
|
-
// Exploration & Discovery
|
|
1018
|
-
'New World expeditions return with gold and strange diseases', 'Ancient artifacts surface from archaeological digs, rewriting history', 'Trade caravans brave bandits and harsh terrains for exotic goods', 'Mapping expeditions chart unknown territories and hostile natives', 'Scientific discoveries challenge religious doctrines and established truths',
|
|
1019
|
-
// Social & Class Conflict
|
|
1020
|
-
'Peasant revolts spread like contagion through the countryside', 'Guilds clash over trade monopolies and apprenticeship rights', 'Religious schisms divide communities along ideological lines', 'Women maneuver within restrictive social structures for power and influence', 'Immigrants bring new customs that clash with traditional ways',
|
|
1021
|
-
// Economic & Mercantile
|
|
1022
|
-
'Market monopolies drive independent merchants to desperation', 'Banking houses manipulate currency values for political gain', 'Trade embargoes create black markets and smuggling operations', 'Agricultural failures lead to famines and social unrest', 'Colonial exploitation enriches the crown while impoverishing subjects',
|
|
1023
|
-
// Political & Diplomatic
|
|
1024
|
-
'Treaty negotiations mask underlying territorial ambitions', 'Spy networks weave webs of deception across national borders', 'Succession crises threaten to plunge nations into civil war', 'Diplomatic incidents escalate into full-scale military conflicts', 'Royal marriages forge alliances that crumble under stress',
|
|
1025
|
-
// Religious & Ideological
|
|
1026
|
-
'Inquisitions root out heresy with fire and torture', 'Crusades call warriors to distant lands for faith and glory', 'Reformation movements challenge the authority of established churches', 'Cult leaders promise salvation through devotion and sacrifice', 'Religious relics inspire pilgrimages and bloody conflicts',
|
|
1027
|
-
// Personal & Dramatic
|
|
1028
|
-
'Family feuds span generations, poisoning bloodlines with vengeance', 'Scandals rock aristocratic families, threatening social standing', 'Personal ambitions clash with familial duties and expectations', 'Love affairs defy social conventions and familial arrangements', 'Betrayals cut deeper than any blade, leaving lasting scars',
|
|
1029
|
-
// Natural & Environmental
|
|
1030
|
-
'Plagues sweep through densely packed cities, claiming thousands', 'Famines force desperate migrations and social breakdowns', 'Natural disasters reveal underlying social and political tensions', 'Harsh winters test the limits of human endurance and charity', 'Droughts drive conflicts over dwindling water resources',
|
|
1031
|
-
// Innovation & Progress
|
|
1032
|
-
'Scientific discoveries revolutionize warfare and exploration', 'Technological innovations disrupt traditional crafts and livelihoods', 'Medical breakthroughs save lives but challenge religious views', 'Educational reforms open knowledge to new social classes', 'Engineering marvels push the boundaries of human capability']
|
|
987
|
+
fantasy: ['The royal court is abuzz with whispers of scandal and betrayal', 'A noble lord approaches you with a proposition that could change your destiny', 'Court politics have reached a fever pitch as alliances shift like desert sands', 'The king\'s advisors plot in shadowed corners while the court dances obliviously', 'A mysterious letter arrives sealed with wax from a noble house you don\'t recognize', 'The thieves\' guild has put out a contract that bears your name', 'Shadowy figures lurk in alleyways, watching your every move', 'The black market thrives under the cover of night, offering forbidden luxuries', 'A notorious crime lord has taken an interest in your activities', 'Corrupt guards demand tribute while turning a blind eye to greater crimes', 'Strange runes appear on your bedroom wall, glowing with ethereal light', 'An ancient prophecy speaks of a hero who matches your description exactly', 'Ghostly apparitions warn of impending doom in fevered dreams', 'A witch in the woods offers you power beyond mortal comprehension', 'Cursed artifacts surface in the market, promising great power at terrible cost', 'Your past sins come back to haunt you in the most unexpected ways', 'A long-lost relative appears with a tale that shakes your world', 'Your reputation draws admirers and enemies in equal measure', 'A betrayal cuts deeper than any blade, leaving scars on your soul', 'Love and ambition war within your heart as opportunities arise', 'Ancient ruins whisper secrets to those brave enough to listen', 'A dragon\'s hoard lies hidden, protected by trials and tribulations', 'Bandits rule the roads, but their leader seems oddly familiar', 'A legendary artifact calls to you from across distant lands', 'The wilderness holds both peril and promise for the bold adventurer', 'Romantic entanglements complicate your carefully laid plans', 'Old friends become new enemies as loyalties are tested', 'Family secrets emerge that threaten to destroy everything you hold dear', 'Mentors offer wisdom that comes with strings attached', 'Rivals emerge from unexpected places, challenging your hard-won position', 'The market crashes send shockwaves through the merchant class', 'A get-rich-quick scheme promises fortunes but demands your soul', 'Trade wars erupt between rival merchant houses', 'Investment opportunities arise that could make or break your fortune', 'Black market deals offer power but carry the weight of damnation', 'War drums beat as kingdoms prepare for inevitable conflict', 'Desertion offers freedom but brands you a coward forever', 'A duel of honor is proposed, with your reputation on the line', 'Mercenary companies seek captains brave enough to lead them', 'Battle scars tell stories of glory and horror in equal measure', 'The veil between worlds thins, allowing magic to seep into reality', 'Curses and blessings intertwine in ways you never expected', 'Ancient bloodlines awaken powers dormant for generations', 'Rituals performed in secret grant power at terrible personal cost', 'The stars themselves seem to conspire in your favor or against you', 'Winter\'s cruel bite forces desperate measures from the populace', 'Spring\'s renewal brings both hope and dangerous new beginnings', 'Summer tournaments test the mettle of warriors and nobles alike', 'Autumn harvests reveal secrets long buried in the fields', 'The changing seasons mirror the turmoil in your own life', 'What seems like a blessing reveals itself as a curse in disguise', 'Enemies become allies, and allies become your greatest threat', 'The path of righteousness leads to ruin, while villainy brings reward', 'Fate itself seems to take notice of your actions, for good or ill', 'The world bends around you as your choices reshape reality itself'],
|
|
988
|
+
'sci-fi': ['Megacorporations wage shadow wars through proxy conflicts and data manipulation', 'Neural implants malfunction, flooding your mind with corporate secrets', 'A black-market AI offers forbidden knowledge at the cost of your sanity', 'Corporate espionage turns deadly when rival agents converge on your location', 'Whistleblower data reveals systemic corruption in the highest levels of power', 'Hackers breach your personal network, exposing vulnerabilities you never knew existed', 'Street gangs control the undercity with drone swarms and illegal augmentations', 'The dark web pulses with illegal tech trades and forbidden experiments', 'Corporate bounty hunters track you through the neon-lit sprawl', 'Data runners risk everything for the ultimate information score', 'Alien artifacts whisper secrets from beyond the galactic rim', 'Space pirates demand tribute or face the void of decompression', 'Ancient derelict ships hold treasures and horrors from forgotten eras', 'Wormhole anomalies bend reality, creating unpredictable temporal events', 'Colony worlds rebel against Earth\'s iron-fisted governance', 'Sentient AIs manipulate events from within the global network', 'Nanobot swarms escape containment, evolving beyond their programming', 'Virtual reality simulations bleed into the real world disastrously', 'Genetic engineering experiments create monstrous hybrid beings', 'Quantum computers predict the future, but at what cost to free will?', 'Social credit systems reward compliance and punish deviation', 'Surveillance drones watch every move, every transaction, every thought', 'Rebellions simmer beneath the surface of totalitarian control', 'Resource wars rage over the last viable mining asteroids', 'Climate engineering projects backfire spectacularly', 'Implant malfunctions cause vivid hallucinations and system crashes', 'Identity theft becomes literal as someone steals your digital consciousness', 'Memory wipes erase crucial knowledge, leaving you vulnerable', 'Cybernetic enhancements demand maintenance that costs more than you can afford', 'Neural links create unintended telepathic connections with strangers', 'Cryptocurrency markets crash, wiping out fortunes in digital dust', 'Hostile takeovers turn friendly acquisitions into bloody boardroom coups', 'Trade wars erupt between orbital habitats and planetary governments', 'Investment opportunities in black-market tech promise riches or ruin', 'Corporate espionage turns personal when family members are leveraged', 'Drone wars rage in the skies above abandoned cities', 'Cyber warfare disables entire infrastructure networks', 'Private military companies offer their services to the highest bidder', 'Nuclear deterrence fails when EMP weapons render missiles useless', 'Bioterrorism releases engineered plagues upon unsuspecting populations', 'Government cover-ups hide the truth about alien visitations', 'Time travel experiments create paradoxical timeline fractures', 'Parallel dimensions bleed through, creating impossible phenomena', 'Ancient alien technology reactivates with catastrophic consequences', 'Conspiracy theories prove true, but the reality is far worse', 'Terraforming projects unleash ancient microbes from the soil', 'Climate control systems fail, creating unpredictable weather patterns', 'Asteroid mining operations awaken dormant extraterrestrial threats', 'Ocean acidification drives aquatic mutations to the surface', 'Atmospheric processors malfunction, creating toxic breathing conditions'],
|
|
989
|
+
historical: ['The king\'s advisors plot in shadowed corners of the great hall', 'Noble houses form secret alliances against the crown', 'Whispers of heresy spread through the royal court like wildfire', 'A foreign diplomat offers treacherous propositions under diplomatic immunity', 'Court jesters hide dangerous truths behind masks of folly', 'Armies mass at the borders, their banners fluttering in the wind of war', 'Siege engines pound ancient walls as defenders hold the line', 'Cavalry charges break enemy formations in clouds of dust and blood', 'Naval blockades starve cities into submission or desperate rebellion', 'Mercenary companies switch sides based on the highest bidder', 'New World expeditions return with gold and strange diseases', 'Ancient artifacts surface from archaeological digs, rewriting history', 'Trade caravans brave bandits and harsh terrains for exotic goods', 'Mapping expeditions chart unknown territories and hostile natives', 'Scientific discoveries challenge religious doctrines and established truths', 'Peasant revolts spread like contagion through the countryside', 'Guilds clash over trade monopolies and apprenticeship rights', 'Religious schisms divide communities along ideological lines', 'Women maneuver within restrictive social structures for power and influence', 'Immigrants bring new customs that clash with traditional ways', 'Market monopolies drive independent merchants to desperation', 'Banking houses manipulate currency values for political gain', 'Trade embargoes create black markets and smuggling operations', 'Agricultural failures lead to famines and social unrest', 'Colonial exploitation enriches the crown while impoverishing subjects', 'Treaty negotiations mask underlying territorial ambitions', 'Spy networks weave webs of deception across national borders', 'Succession crises threaten to plunge nations into civil war', 'Diplomatic incidents escalate into full-scale military conflicts', 'Royal marriages forge alliances that crumble under stress', 'Inquisitions root out heresy with fire and torture', 'Crusades call warriors to distant lands for faith and glory', 'Reformation movements challenge the authority of established churches', 'Cult leaders promise salvation through devotion and sacrifice', 'Religious relics inspire pilgrimages and bloody conflicts', 'Family feuds span generations, poisoning bloodlines with vengeance', 'Scandals rock aristocratic families, threatening social standing', 'Personal ambitions clash with familial duties and expectations', 'Love affairs defy social conventions and familial arrangements', 'Betrayals cut deeper than any blade, leaving lasting scars', 'Plagues sweep through densely packed cities, claiming thousands', 'Famines force desperate migrations and social breakdowns', 'Natural disasters reveal underlying social and political tensions', 'Harsh winters test the limits of human endurance and charity', 'Droughts drive conflicts over dwindling water resources', 'Scientific discoveries revolutionize warfare and exploration', 'Technological innovations disrupt traditional crafts and livelihoods', 'Medical breakthroughs save lives but challenge religious views', 'Educational reforms open knowledge to new social classes', 'Engineering marvels push the boundaries of human capability']
|
|
1033
990
|
};
|
|
1034
|
-
|
|
1035
|
-
// Cultural variants within themes
|
|
1036
991
|
this.cultures = {
|
|
1037
992
|
fantasy: {
|
|
1038
993
|
norse: ['The longships sail into the fjord under the northern lights', 'Runes carved in ancient stone whisper forgotten secrets', 'The mead hall echoes with tales of heroic deeds and tragic fates', 'Berserkers rage through the village leaving destruction in their wake', 'The gods themselves seem to take notice of mortal affairs', 'Viking raiders demand tribute or face the wrath of the axe', 'Shamanic visions reveal the threads of fate woven by the Norns', 'The sacred grove hides a portal to the realm of the Aesir', 'Blood oaths bind warriors to quests of honor and vengeance', 'Thunder rolls as Thor\'s hammer strikes down the unworthy'],
|
|
@@ -1051,8 +1006,6 @@ class RPGEventGenerator {
|
|
|
1051
1006
|
ancient_roman: ['Gladiators fight for glory in the Colosseum\'s sandy arena', 'The Roman Senate debates matters of republic and empire', 'Barbarian hordes threaten the borders of civilized lands', 'The aqueducts carry water across vast engineering marvels', 'Political assassinations reshape the corridors of power', 'Slave revolts challenge the foundations of imperial society', 'The gods demand tribute through elaborate public ceremonies', 'Military legions march under eagles forged in sacred fires', 'Patrician families feud over matters of honor and inheritance', 'The imperial palace echoes with plots and betrayals']
|
|
1052
1007
|
}
|
|
1053
1008
|
};
|
|
1054
|
-
|
|
1055
|
-
// Seasonal and time-based event templates
|
|
1056
1009
|
this.seasonalEvents = {
|
|
1057
1010
|
spring: {
|
|
1058
1011
|
templates: ['BLOOMING_ROMANCE', 'SPRING_FESTIVAL', 'NEW_BEGINNINGS'],
|
|
@@ -1088,8 +1041,6 @@ class RPGEventGenerator {
|
|
|
1088
1041
|
}
|
|
1089
1042
|
}
|
|
1090
1043
|
};
|
|
1091
|
-
|
|
1092
|
-
// Time-based event chains (events that evolve over time)
|
|
1093
1044
|
this.timeBasedEventChains = {
|
|
1094
1045
|
POLITICAL_UPRISING: {
|
|
1095
1046
|
name: 'Political Uprising',
|
|
@@ -1281,8 +1232,6 @@ class RPGEventGenerator {
|
|
|
1281
1232
|
}
|
|
1282
1233
|
};
|
|
1283
1234
|
this.activeChains.set(chain.id, chain);
|
|
1284
|
-
|
|
1285
|
-
// Generate first event in chain
|
|
1286
1235
|
return this.generateChainEvent(chain);
|
|
1287
1236
|
}
|
|
1288
1237
|
|
|
@@ -1326,20 +1275,15 @@ class RPGEventGenerator {
|
|
|
1326
1275
|
if (!chain) return null;
|
|
1327
1276
|
const chainDef = this.chainDefinitions[chain.definition];
|
|
1328
1277
|
const currentStage = chainDef.stages[chain.stage];
|
|
1329
|
-
|
|
1330
|
-
// Check if current stage triggers next based on choice
|
|
1331
1278
|
if (currentStage.triggerNext && currentStage.triggerNext.choice === choice) {
|
|
1332
1279
|
chain.stage++;
|
|
1333
1280
|
chain.playerContext = {
|
|
1334
1281
|
...chain.playerContext,
|
|
1335
1282
|
lastChoice: choice
|
|
1336
1283
|
};
|
|
1337
|
-
|
|
1338
|
-
// Schedule next event for game integration
|
|
1339
1284
|
if (chain.stage < chainDef.stages.length) {
|
|
1340
1285
|
chain.nextEventTime = this.timeSystem.currentDay + currentStage.triggerNext.delay;
|
|
1341
1286
|
chain.nextEventTemplate = chainDef.stages[chain.stage].template;
|
|
1342
|
-
// In a real game, save this chain state and check for due events on each game day
|
|
1343
1287
|
}
|
|
1344
1288
|
return this.generateChainEvent(chain);
|
|
1345
1289
|
}
|
|
@@ -1383,8 +1327,6 @@ class RPGEventGenerator {
|
|
|
1383
1327
|
day: this.timeSystem.currentDay
|
|
1384
1328
|
});
|
|
1385
1329
|
}
|
|
1386
|
-
|
|
1387
|
-
// Check for time-based event triggers
|
|
1388
1330
|
const dayEvents = this.checkTimeBasedEventTriggers();
|
|
1389
1331
|
triggeredEvents.push(...dayEvents);
|
|
1390
1332
|
}
|
|
@@ -2160,8 +2102,6 @@ class RPGEventGenerator {
|
|
|
2160
2102
|
weights.DESERTION_TEMPTATION *= 1.5;
|
|
2161
2103
|
}
|
|
2162
2104
|
}
|
|
2163
|
-
|
|
2164
|
-
// Age-based modifiers
|
|
2165
2105
|
if (context.age < 25) {
|
|
2166
2106
|
weights.FORBIDDEN_LOVE *= 1.6;
|
|
2167
2107
|
weights.FAMILY_SECRET *= 1.4;
|
|
@@ -2169,21 +2109,15 @@ class RPGEventGenerator {
|
|
|
2169
2109
|
weights.GHOSTLY_VISITATION *= 1.8;
|
|
2170
2110
|
weights.FAMILY_SECRET *= 1.6;
|
|
2171
2111
|
}
|
|
2172
|
-
|
|
2173
|
-
// Wealth-based modifiers
|
|
2174
2112
|
if (context.wealth > 1000) {
|
|
2175
2113
|
weights.MARKET_CRASH *= 1.8;
|
|
2176
2114
|
weights.TRADE_WAR *= 1.6;
|
|
2177
2115
|
weights.BLACKMAIL_OPPORTUNITY *= 1.4;
|
|
2178
2116
|
}
|
|
2179
|
-
|
|
2180
|
-
// Influence-based modifiers
|
|
2181
2117
|
if (context.influence > 50) {
|
|
2182
2118
|
weights.COURT_SCANDAL *= 2.0;
|
|
2183
2119
|
weights.NOBLE_DUEL *= 1.8;
|
|
2184
2120
|
}
|
|
2185
|
-
|
|
2186
|
-
// Skill-based modifiers
|
|
2187
2121
|
if (context.skills.combat > 60) {
|
|
2188
2122
|
weights.NOBLE_DUEL *= 1.6;
|
|
2189
2123
|
weights.MERCENARY_CONTRACT *= 1.4;
|
|
@@ -2196,21 +2130,15 @@ class RPGEventGenerator {
|
|
|
2196
2130
|
weights.THIEVES_GUILD *= 1.7;
|
|
2197
2131
|
weights.BLACKMAIL_OPPORTUNITY *= 1.5;
|
|
2198
2132
|
}
|
|
2199
|
-
|
|
2200
|
-
// Reputation-based modifiers
|
|
2201
2133
|
if (context.reputation < -20) {
|
|
2202
2134
|
weights.THIEVES_GUILD *= 1.8;
|
|
2203
2135
|
weights.BLACKMAIL_OPPORTUNITY *= 2.0;
|
|
2204
2136
|
}
|
|
2205
|
-
|
|
2206
|
-
// Relationship-based modifiers
|
|
2207
2137
|
if (context.relationships && context.relationships.length > 3) {
|
|
2208
2138
|
weights.FORBIDDEN_LOVE *= 1.4;
|
|
2209
2139
|
weights.FAMILY_SECRET *= 1.6;
|
|
2210
2140
|
weights.COURT_SCANDAL *= 1.3;
|
|
2211
2141
|
}
|
|
2212
|
-
|
|
2213
|
-
// Seasonal modifiers
|
|
2214
2142
|
if (context.season === 'winter') {
|
|
2215
2143
|
weights.GHOSTLY_VISITATION *= 1.5;
|
|
2216
2144
|
weights.ANCIENT_CURSE *= 1.3;
|
|
@@ -2300,21 +2228,15 @@ class RPGEventGenerator {
|
|
|
2300
2228
|
additions.push('The battlefield calls, and honor demands a response.');
|
|
2301
2229
|
}
|
|
2302
2230
|
}
|
|
2303
|
-
|
|
2304
|
-
// Age-based additions
|
|
2305
2231
|
if (context.age < 25) {
|
|
2306
2232
|
additions.push('At your young age, this experience could shape your entire future.');
|
|
2307
2233
|
} else if (context.age > 60) {
|
|
2308
2234
|
additions.push('With age comes wisdom, but also the weight of past decisions.');
|
|
2309
2235
|
}
|
|
2310
|
-
|
|
2311
|
-
// Relationship-based additions
|
|
2312
2236
|
if (context.relationships && context.relationships.length > 0) {
|
|
2313
2237
|
additions.push('Your personal connections may influence how this unfolds.');
|
|
2314
2238
|
additions.push('Someone you know is intimately involved in this matter.');
|
|
2315
2239
|
}
|
|
2316
|
-
|
|
2317
|
-
// Seasonal additions
|
|
2318
2240
|
if (context.season) {
|
|
2319
2241
|
const seasonAdditions = {
|
|
2320
2242
|
winter: 'The harsh winter weather makes resolution all the more urgent.',
|
|
@@ -2591,6 +2513,589 @@ class RPGEventGenerator {
|
|
|
2591
2513
|
});
|
|
2592
2514
|
this.addTrainingData(data);
|
|
2593
2515
|
}
|
|
2516
|
+
|
|
2517
|
+
/**
|
|
2518
|
+
* Load default English locale
|
|
2519
|
+
* @private
|
|
2520
|
+
*/
|
|
2521
|
+
loadDefaultLocale() {
|
|
2522
|
+
this.locales.set('en', {
|
|
2523
|
+
templates: {},
|
|
2524
|
+
trainingData: [],
|
|
2525
|
+
ui: {
|
|
2526
|
+
'event.title.default': 'Unexpected Event',
|
|
2527
|
+
'event.description.default': 'Something unexpected occurs...',
|
|
2528
|
+
'choice.accept': 'Accept',
|
|
2529
|
+
'choice.decline': 'Decline',
|
|
2530
|
+
'choice.fight': 'Fight',
|
|
2531
|
+
'choice.flee': 'Flee',
|
|
2532
|
+
'choice.negotiate': 'Negotiate'
|
|
2533
|
+
},
|
|
2534
|
+
culture: {
|
|
2535
|
+
nameFormats: ['western'],
|
|
2536
|
+
dateFormats: ['MM/DD/YYYY'],
|
|
2537
|
+
currencySymbols: ['$'],
|
|
2538
|
+
honorifics: ['Sir', 'Lady', 'Lord']
|
|
2539
|
+
}
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
/**
|
|
2544
|
+
* Load a language pack
|
|
2545
|
+
* @param {string} language - Language code
|
|
2546
|
+
* @param {Object} languagePack - Language pack data
|
|
2547
|
+
*/
|
|
2548
|
+
loadLanguagePack(language, languagePack) {
|
|
2549
|
+
this.locales.set(language, languagePack);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
/**
|
|
2553
|
+
* Set the current language
|
|
2554
|
+
* @param {string} language - Language code
|
|
2555
|
+
*/
|
|
2556
|
+
setLanguage(language) {
|
|
2557
|
+
if (this.locales.has(language)) {
|
|
2558
|
+
this.language = language;
|
|
2559
|
+
} else {
|
|
2560
|
+
console.warn(`Language '${language}' not loaded, staying with '${this.language}'`);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
/**
|
|
2565
|
+
* Get translated text
|
|
2566
|
+
* @param {string} key - Translation key
|
|
2567
|
+
* @param {Object} variables - Substitution variables
|
|
2568
|
+
* @returns {string} Translated text
|
|
2569
|
+
*/
|
|
2570
|
+
translate(key, variables = {}) {
|
|
2571
|
+
const locale = this.locales.get(this.language) || this.locales.get('en');
|
|
2572
|
+
if (!locale || !locale.ui[key]) {
|
|
2573
|
+
return key;
|
|
2574
|
+
}
|
|
2575
|
+
let text = locale.ui[key];
|
|
2576
|
+
Object.entries(variables).forEach(([varKey, value]) => {
|
|
2577
|
+
text = text.replace(new RegExp(`{{${varKey}}}`, 'g'), value);
|
|
2578
|
+
});
|
|
2579
|
+
return text;
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
/**
|
|
2583
|
+
* Initialize built-in modifiers
|
|
2584
|
+
* @private
|
|
2585
|
+
*/
|
|
2586
|
+
initializeBuiltInModifiers() {
|
|
2587
|
+
this.modifiers.set('rain', {
|
|
2588
|
+
type: 'weather',
|
|
2589
|
+
effects: {
|
|
2590
|
+
visibility: 0.7,
|
|
2591
|
+
movement_penalty: 0.2,
|
|
2592
|
+
encounter_rate: 0.8,
|
|
2593
|
+
health_drain: 2
|
|
2594
|
+
},
|
|
2595
|
+
text_modifiers: {
|
|
2596
|
+
atmosphere: 'dismal',
|
|
2597
|
+
add_descriptors: ['wet', 'rainy']
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2600
|
+
this.modifiers.set('storm', {
|
|
2601
|
+
type: 'weather',
|
|
2602
|
+
effects: {
|
|
2603
|
+
visibility: 0.4,
|
|
2604
|
+
movement_penalty: 0.5,
|
|
2605
|
+
encounter_rate: 1.3,
|
|
2606
|
+
health_drain: 5
|
|
2607
|
+
},
|
|
2608
|
+
text_modifiers: {
|
|
2609
|
+
atmosphere: 'chaotic',
|
|
2610
|
+
add_descriptors: ['thunderous', 'tempestuous']
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
this.modifiers.set('winter', {
|
|
2614
|
+
type: 'season',
|
|
2615
|
+
effects: {
|
|
2616
|
+
cold_modifier: 1.5,
|
|
2617
|
+
movement_penalty: 0.2
|
|
2618
|
+
},
|
|
2619
|
+
text_modifiers: {
|
|
2620
|
+
add_descriptors: ['frozen', 'harsh'],
|
|
2621
|
+
atmosphere: 'bleak'
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
this.modifiers.set('summer', {
|
|
2625
|
+
type: 'season',
|
|
2626
|
+
effects: {
|
|
2627
|
+
heat_modifier: 1.3,
|
|
2628
|
+
energy_bonus: 10
|
|
2629
|
+
},
|
|
2630
|
+
text_modifiers: {
|
|
2631
|
+
add_descriptors: ['sunny', 'warm'],
|
|
2632
|
+
atmosphere: 'energetic'
|
|
2633
|
+
}
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
/**
|
|
2638
|
+
* Activate environmental modifiers
|
|
2639
|
+
* @param {Object} context - Environmental context
|
|
2640
|
+
*/
|
|
2641
|
+
setEnvironmentalContext(context) {
|
|
2642
|
+
if (!this.enableModifiers) return;
|
|
2643
|
+
this.activeModifiers.clear();
|
|
2644
|
+
if (context.weather && this.modifiers.has(context.weather)) {
|
|
2645
|
+
this.activeModifiers.add(context.weather);
|
|
2646
|
+
}
|
|
2647
|
+
if (context.season && this.modifiers.has(context.season)) {
|
|
2648
|
+
this.activeModifiers.add(context.season);
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
/**
|
|
2653
|
+
* Apply active modifiers to an event
|
|
2654
|
+
* @param {Object} event - Event to modify
|
|
2655
|
+
* @returns {Object} Modified event
|
|
2656
|
+
*/
|
|
2657
|
+
applyModifiers(event) {
|
|
2658
|
+
if (!this.enableModifiers || this.activeModifiers.size === 0) {
|
|
2659
|
+
return event;
|
|
2660
|
+
}
|
|
2661
|
+
let modifiedEvent = {
|
|
2662
|
+
...event
|
|
2663
|
+
};
|
|
2664
|
+
const modifierOrder = ['storm', 'rain', 'winter', 'summer', 'spring', 'autumn'];
|
|
2665
|
+
const orderedModifiers = Array.from(this.activeModifiers).sort((a, b) => {
|
|
2666
|
+
const aIndex = modifierOrder.indexOf(a);
|
|
2667
|
+
const bIndex = modifierOrder.indexOf(b);
|
|
2668
|
+
return (aIndex === -1 ? 999 : aIndex) - (bIndex === -1 ? 999 : bIndex);
|
|
2669
|
+
});
|
|
2670
|
+
for (const modifierId of orderedModifiers) {
|
|
2671
|
+
const modifier = this.modifiers.get(modifierId);
|
|
2672
|
+
if (modifier) {
|
|
2673
|
+
modifiedEvent = this.applySingleModifier(modifiedEvent, modifier);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
return modifiedEvent;
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
/**
|
|
2680
|
+
* Apply a single modifier to an event
|
|
2681
|
+
* @private
|
|
2682
|
+
* @param {Object} event - Event to modify
|
|
2683
|
+
* @param {Object} modifier - Modifier to apply
|
|
2684
|
+
* @returns {Object} Modified event
|
|
2685
|
+
*/
|
|
2686
|
+
applySingleModifier(event, modifier) {
|
|
2687
|
+
const modifiedEvent = {
|
|
2688
|
+
...event
|
|
2689
|
+
};
|
|
2690
|
+
if (modifier.effects && modifiedEvent.choices) {
|
|
2691
|
+
modifiedEvent.choices = modifiedEvent.choices.map(choice => {
|
|
2692
|
+
const modifiedChoice = {
|
|
2693
|
+
...choice,
|
|
2694
|
+
effect: {
|
|
2695
|
+
...choice.effect
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
if (modifier.effects.movement_penalty && modifiedChoice.effect.movement !== undefined) {
|
|
2699
|
+
modifiedChoice.effect.movement *= 1 - modifier.effects.movement_penalty;
|
|
2700
|
+
}
|
|
2701
|
+
if (modifier.effects.health_drain) {
|
|
2702
|
+
modifiedChoice.effect.health = (modifiedChoice.effect.health || 0) - modifier.effects.health_drain;
|
|
2703
|
+
}
|
|
2704
|
+
if (modifier.effects.cold_modifier && modifiedChoice.effect.health !== undefined) {
|
|
2705
|
+
modifiedChoice.effect.health = Math.floor(modifiedChoice.effect.health * modifier.effects.cold_modifier);
|
|
2706
|
+
}
|
|
2707
|
+
return modifiedChoice;
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
if (modifier.text_modifiers) {
|
|
2711
|
+
if (modifier.text_modifiers.atmosphere) {
|
|
2712
|
+
const atmospheres = {
|
|
2713
|
+
'dismal': ' under gloomy skies',
|
|
2714
|
+
'chaotic': ' amidst the chaos',
|
|
2715
|
+
'bleak': ' in the bleak cold',
|
|
2716
|
+
'energetic': ' with vibrant energy'
|
|
2717
|
+
};
|
|
2718
|
+
const atmosphereText = atmospheres[modifier.text_modifiers.atmosphere];
|
|
2719
|
+
if (atmosphereText && !modifiedEvent.description.includes(atmosphereText)) {
|
|
2720
|
+
modifiedEvent.description += atmosphereText;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
return modifiedEvent;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
/**
|
|
2728
|
+
* Register an event dependency
|
|
2729
|
+
* @param {string} eventId - Event identifier
|
|
2730
|
+
* @param {Object} dependencyConfig - Dependency configuration
|
|
2731
|
+
*/
|
|
2732
|
+
registerEventDependency(eventId, dependencyConfig) {
|
|
2733
|
+
if (!this.enableDependencies) {
|
|
2734
|
+
console.warn('Dependencies feature is disabled');
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
this.dependencies.set(eventId, dependencyConfig);
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
/**
|
|
2741
|
+
* Check if event dependencies are met
|
|
2742
|
+
* @param {string} eventId - Event identifier
|
|
2743
|
+
* @param {Object} gameState - Current game state
|
|
2744
|
+
* @returns {boolean} True if dependencies are met
|
|
2745
|
+
*/
|
|
2746
|
+
checkEventDependencies(eventId, gameState) {
|
|
2747
|
+
if (!this.enableDependencies) return true;
|
|
2748
|
+
const dependency = this.dependencies.get(eventId);
|
|
2749
|
+
if (!dependency) return true;
|
|
2750
|
+
return this.evaluateDependencyCondition(dependency, gameState);
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
/**
|
|
2754
|
+
* Evaluate a dependency condition
|
|
2755
|
+
* @private
|
|
2756
|
+
* @param {Object} condition - Condition to evaluate
|
|
2757
|
+
* @param {Object} gameState - Current game state
|
|
2758
|
+
* @returns {boolean} Evaluation result
|
|
2759
|
+
*/
|
|
2760
|
+
evaluateDependencyCondition(condition, gameState) {
|
|
2761
|
+
if (condition.operator) {
|
|
2762
|
+
return this.evaluateDependencyOperator(condition, gameState);
|
|
2763
|
+
}
|
|
2764
|
+
return this.evaluateSingleDependency(condition, gameState);
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
/**
|
|
2768
|
+
* Evaluate operator-based dependency conditions
|
|
2769
|
+
* @private
|
|
2770
|
+
* @param {Object} condition - Operator condition
|
|
2771
|
+
* @param {Object} gameState - Current game state
|
|
2772
|
+
* @returns {boolean} Evaluation result
|
|
2773
|
+
*/
|
|
2774
|
+
evaluateDependencyOperator(condition, gameState) {
|
|
2775
|
+
const {
|
|
2776
|
+
operator,
|
|
2777
|
+
conditions
|
|
2778
|
+
} = condition;
|
|
2779
|
+
switch (operator.toUpperCase()) {
|
|
2780
|
+
case 'AND':
|
|
2781
|
+
return conditions.every(cond => this.evaluateDependencyCondition(cond, gameState));
|
|
2782
|
+
case 'OR':
|
|
2783
|
+
return conditions.some(cond => this.evaluateDependencyCondition(cond, gameState));
|
|
2784
|
+
default:
|
|
2785
|
+
return false;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
/**
|
|
2790
|
+
* Evaluate a single dependency condition
|
|
2791
|
+
* @private
|
|
2792
|
+
* @param {Object} condition - Single condition
|
|
2793
|
+
* @param {Object} gameState - Current game state
|
|
2794
|
+
* @returns {boolean} Evaluation result
|
|
2795
|
+
*/
|
|
2796
|
+
evaluateSingleDependency(condition, gameState) {
|
|
2797
|
+
const {
|
|
2798
|
+
type
|
|
2799
|
+
} = condition;
|
|
2800
|
+
switch (type) {
|
|
2801
|
+
case 'event_completed':
|
|
2802
|
+
const completedEvents = gameState.completedEvents || new Set();
|
|
2803
|
+
return condition.eventIds ? condition.eventIds.every(id => completedEvents.has(id)) : completedEvents.has(condition.eventId);
|
|
2804
|
+
case 'stat_requirement':
|
|
2805
|
+
const playerStats = gameState.player || {};
|
|
2806
|
+
const statValue = playerStats[condition.stat] || 0;
|
|
2807
|
+
const minReq = condition.min || 0;
|
|
2808
|
+
const maxReq = condition.max;
|
|
2809
|
+
return statValue >= minReq && (maxReq === undefined || statValue <= maxReq);
|
|
2810
|
+
default:
|
|
2811
|
+
return false;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
/**
|
|
2816
|
+
* Initialize relationship rules
|
|
2817
|
+
* @private
|
|
2818
|
+
*/
|
|
2819
|
+
initializeRelationshipRules() {
|
|
2820
|
+
this.relationshipRules = new Map();
|
|
2821
|
+
this.relationshipRules.set('help_combat', {
|
|
2822
|
+
change: 15,
|
|
2823
|
+
reason: 'aided in battle'
|
|
2824
|
+
});
|
|
2825
|
+
this.relationshipRules.set('save_life', {
|
|
2826
|
+
change: 25,
|
|
2827
|
+
reason: 'life saved'
|
|
2828
|
+
});
|
|
2829
|
+
this.relationshipRules.set('betray_trust', {
|
|
2830
|
+
change: -30,
|
|
2831
|
+
reason: 'betrayed trust'
|
|
2832
|
+
});
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
/**
|
|
2836
|
+
* Add an NPC to the relationship system
|
|
2837
|
+
* @param {Object} npcConfig - NPC configuration
|
|
2838
|
+
*/
|
|
2839
|
+
addNPC(npcConfig) {
|
|
2840
|
+
if (!this.enableRelationships) {
|
|
2841
|
+
console.warn('Relationships feature is disabled');
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
this.relationships.set(npcConfig.id, {
|
|
2845
|
+
id: npcConfig.id,
|
|
2846
|
+
name: npcConfig.name,
|
|
2847
|
+
type: npcConfig.type,
|
|
2848
|
+
relationships: new Map(),
|
|
2849
|
+
history: [],
|
|
2850
|
+
createdAt: Date.now()
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
/**
|
|
2855
|
+
* Update NPC relationship
|
|
2856
|
+
* @param {string} npcId - NPC identifier
|
|
2857
|
+
* @param {string} targetId - Target NPC (usually player)
|
|
2858
|
+
* @param {number} change - Relationship change
|
|
2859
|
+
* @param {string} reason - Reason for change
|
|
2860
|
+
*/
|
|
2861
|
+
updateRelationship(npcId, targetId, change, reason) {
|
|
2862
|
+
if (!this.enableRelationships) return;
|
|
2863
|
+
const npc = this.relationships.get(npcId);
|
|
2864
|
+
if (!npc) return;
|
|
2865
|
+
if (!npc.relationships.has(targetId)) {
|
|
2866
|
+
npc.relationships.set(targetId, {
|
|
2867
|
+
strength: 0,
|
|
2868
|
+
type: 'acquaintance'
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
const relationship = npc.relationships.get(targetId);
|
|
2872
|
+
relationship.strength = Math.max(-100, Math.min(100, relationship.strength + change));
|
|
2873
|
+
npc.history.push({
|
|
2874
|
+
timestamp: Date.now(),
|
|
2875
|
+
targetId,
|
|
2876
|
+
change,
|
|
2877
|
+
newStrength: relationship.strength,
|
|
2878
|
+
reason
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
/**
|
|
2883
|
+
* Apply relationship evolution rule
|
|
2884
|
+
* @param {string} npcId - NPC affected
|
|
2885
|
+
* @param {string} targetId - Target NPC
|
|
2886
|
+
* @param {string} ruleId - Evolution rule
|
|
2887
|
+
*/
|
|
2888
|
+
applyRelationshipRule(npcId, targetId, ruleId) {
|
|
2889
|
+
if (!this.enableRelationships) return;
|
|
2890
|
+
const rule = this.relationshipRules.get(ruleId);
|
|
2891
|
+
if (rule) {
|
|
2892
|
+
this.updateRelationship(npcId, targetId, rule.change, rule.reason);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
/**
|
|
2897
|
+
* Get relationship data between two NPCs
|
|
2898
|
+
* @param {string} npcId - NPC identifier
|
|
2899
|
+
* @param {string} targetId - Target NPC
|
|
2900
|
+
* @returns {Object} Relationship data or null
|
|
2901
|
+
*/
|
|
2902
|
+
getRelationship(npcId, targetId) {
|
|
2903
|
+
if (!this.enableRelationships) return null;
|
|
2904
|
+
const npc = this.relationships.get(npcId);
|
|
2905
|
+
if (!npc) return null;
|
|
2906
|
+
return npc.relationships.get(targetId) || null;
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
/**
|
|
2910
|
+
* Enhanced event generation with all features
|
|
2911
|
+
* @param {Object} context - Generation context
|
|
2912
|
+
* @returns {Object} Generated event
|
|
2913
|
+
*/
|
|
2914
|
+
generateEnhancedEvent(context = {}) {
|
|
2915
|
+
const event = this.generateEvent(context.player || context);
|
|
2916
|
+
let enhancedEvent = event;
|
|
2917
|
+
if (this.enableModifiers) {
|
|
2918
|
+
this.setEnvironmentalContext(context.environment || {});
|
|
2919
|
+
enhancedEvent = this.applyModifiers(event);
|
|
2920
|
+
}
|
|
2921
|
+
if (this.enableDependencies && context.gameState) {}
|
|
2922
|
+
if (this.enableRelationships && context.player) {
|
|
2923
|
+
enhancedEvent = this.addRelationshipContext(enhancedEvent, context.player);
|
|
2924
|
+
}
|
|
2925
|
+
return enhancedEvent;
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
/**
|
|
2929
|
+
* Get relationship data between two NPCs
|
|
2930
|
+
* @param {string} npcId - NPC identifier
|
|
2931
|
+
* @param {string} targetId - Target NPC
|
|
2932
|
+
* @returns {Object} Relationship data or null
|
|
2933
|
+
*/
|
|
2934
|
+
getRelationship(npcId, targetId) {
|
|
2935
|
+
if (!this.enableRelationships) return null;
|
|
2936
|
+
const npc = this.relationships.get(npcId);
|
|
2937
|
+
if (!npc) return null;
|
|
2938
|
+
return npc.relationships.get(targetId) || null;
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
/**
|
|
2942
|
+
* Get relationship summary for an NPC
|
|
2943
|
+
* @param {string} npcId - NPC identifier
|
|
2944
|
+
* @returns {Object} Relationship summary
|
|
2945
|
+
*/
|
|
2946
|
+
getRelationshipSummary(npcId) {
|
|
2947
|
+
if (!this.enableRelationships) return {
|
|
2948
|
+
totalRelationships: 0,
|
|
2949
|
+
averageStrength: 0,
|
|
2950
|
+
allyCount: 0,
|
|
2951
|
+
enemyCount: 0,
|
|
2952
|
+
neutralCount: 0
|
|
2953
|
+
};
|
|
2954
|
+
const npc = this.relationships.get(npcId);
|
|
2955
|
+
if (!npc) return {
|
|
2956
|
+
totalRelationships: 0,
|
|
2957
|
+
averageStrength: 0,
|
|
2958
|
+
allyCount: 0,
|
|
2959
|
+
enemyCount: 0,
|
|
2960
|
+
neutralCount: 0
|
|
2961
|
+
};
|
|
2962
|
+
const relationships = Array.from(npc.relationships.values());
|
|
2963
|
+
const summary = {
|
|
2964
|
+
totalRelationships: relationships.length,
|
|
2965
|
+
averageStrength: 0,
|
|
2966
|
+
allyCount: 0,
|
|
2967
|
+
enemyCount: 0,
|
|
2968
|
+
neutralCount: 0
|
|
2969
|
+
};
|
|
2970
|
+
if (relationships.length > 0) {
|
|
2971
|
+
const totalStrength = relationships.reduce((sum, rel) => sum + rel.strength, 0);
|
|
2972
|
+
summary.averageStrength = totalStrength / relationships.length;
|
|
2973
|
+
relationships.forEach(rel => {
|
|
2974
|
+
if (rel.strength > 50) summary.allyCount++;else if (rel.strength < -30) summary.enemyCount++;else summary.neutralCount++;
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
return summary;
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
/**
|
|
2981
|
+
* Get relationship network around an NPC
|
|
2982
|
+
* @param {string} npcId - Central NPC
|
|
2983
|
+
* @param {number} depth - Network depth (default: 1)
|
|
2984
|
+
* @returns {Object} Relationship network
|
|
2985
|
+
*/
|
|
2986
|
+
getRelationshipNetwork(npcId, depth = 1) {
|
|
2987
|
+
if (!this.enableRelationships) return {
|
|
2988
|
+
nodes: new Map(),
|
|
2989
|
+
edges: []
|
|
2990
|
+
};
|
|
2991
|
+
const network = {
|
|
2992
|
+
center: npcId,
|
|
2993
|
+
nodes: new Map(),
|
|
2994
|
+
edges: []
|
|
2995
|
+
};
|
|
2996
|
+
const visited = new Set();
|
|
2997
|
+
const queue = [{
|
|
2998
|
+
id: npcId,
|
|
2999
|
+
currentDepth: 0
|
|
3000
|
+
}];
|
|
3001
|
+
while (queue.length > 0) {
|
|
3002
|
+
const {
|
|
3003
|
+
id,
|
|
3004
|
+
currentDepth
|
|
3005
|
+
} = queue.shift();
|
|
3006
|
+
if (visited.has(id) || currentDepth > depth) continue;
|
|
3007
|
+
visited.add(id);
|
|
3008
|
+
const npc = this.relationships.get(id);
|
|
3009
|
+
if (!npc) continue;
|
|
3010
|
+
network.nodes.set(id, {
|
|
3011
|
+
id,
|
|
3012
|
+
name: npc.name,
|
|
3013
|
+
type: npc.type,
|
|
3014
|
+
relationshipCount: npc.relationships.size
|
|
3015
|
+
});
|
|
3016
|
+
for (const [targetId, relationship] of npc.relationships) {
|
|
3017
|
+
network.edges.push({
|
|
3018
|
+
source: id,
|
|
3019
|
+
target: targetId,
|
|
3020
|
+
type: relationship.type,
|
|
3021
|
+
strength: relationship.strength,
|
|
3022
|
+
trust: relationship.trust || 50,
|
|
3023
|
+
respect: relationship.respect || 50
|
|
3024
|
+
});
|
|
3025
|
+
if (!visited.has(targetId) && currentDepth < depth) {
|
|
3026
|
+
queue.push({
|
|
3027
|
+
id: targetId,
|
|
3028
|
+
currentDepth: currentDepth + 1
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
return network;
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
/**
|
|
3037
|
+
* Register a custom modifier
|
|
3038
|
+
* @param {string} modifierId - Modifier identifier
|
|
3039
|
+
* @param {Object} modifierConfig - Modifier configuration
|
|
3040
|
+
*/
|
|
3041
|
+
registerModifier(modifierId, modifierConfig) {
|
|
3042
|
+
if (!this.enableModifiers) {
|
|
3043
|
+
console.warn('Modifiers feature is disabled');
|
|
3044
|
+
return;
|
|
3045
|
+
}
|
|
3046
|
+
this.modifiers.set(modifierId, {
|
|
3047
|
+
id: modifierId,
|
|
3048
|
+
...modifierConfig,
|
|
3049
|
+
registeredAt: Date.now()
|
|
3050
|
+
});
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
/**
|
|
3054
|
+
* Get system status information
|
|
3055
|
+
* @returns {Object} System status
|
|
3056
|
+
*/
|
|
3057
|
+
getSystemStatus() {
|
|
3058
|
+
return {
|
|
3059
|
+
version: '1.2.0',
|
|
3060
|
+
language: this.language,
|
|
3061
|
+
availableLanguages: Array.from(this.locales.keys()),
|
|
3062
|
+
modifiersEnabled: this.enableModifiers,
|
|
3063
|
+
relationshipsEnabled: this.enableRelationships,
|
|
3064
|
+
dependenciesEnabled: this.enableDependencies,
|
|
3065
|
+
totalNPCs: this.enableRelationships ? this.relationships.size : 0,
|
|
3066
|
+
activeModifiers: this.enableModifiers ? Array.from(this.activeModifiers) : [],
|
|
3067
|
+
totalLocales: this.locales.size,
|
|
3068
|
+
timeSystem: {
|
|
3069
|
+
currentDay: this.timeSystem.currentDay,
|
|
3070
|
+
currentSeason: this.timeSystem.currentSeason
|
|
3071
|
+
}
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
/**
|
|
3076
|
+
* Add relationship context to event
|
|
3077
|
+
* @private
|
|
3078
|
+
* @param {Object} event - Event to enhance
|
|
3079
|
+
* @param {Object} player - Player data
|
|
3080
|
+
* @returns {Object} Enhanced event
|
|
3081
|
+
*/
|
|
3082
|
+
addRelationshipContext(event, player) {
|
|
3083
|
+
const enhancedEvent = {
|
|
3084
|
+
...event
|
|
3085
|
+
};
|
|
3086
|
+
if (event.npcs && event.npcs.length > 0) {
|
|
3087
|
+
event.npcs.forEach(npcId => {
|
|
3088
|
+
const npc = this.relationships.get(npcId);
|
|
3089
|
+
if (npc) {
|
|
3090
|
+
const playerRel = npc.relationships.get('player');
|
|
3091
|
+
if (playerRel && playerRel.strength > 50) {
|
|
3092
|
+
enhancedEvent.description = enhancedEvent.description.replace(new RegExp(npcId, 'gi'), `your ally ${npcId}`);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
return enhancedEvent;
|
|
3098
|
+
}
|
|
2594
3099
|
}
|
|
2595
3100
|
exports.RPGEventGenerator = RPGEventGenerator;
|
|
2596
3101
|
function generateRPGEvent(playerContext = {}) {
|