-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtemplates.js
2171 lines (1699 loc) · 102 KB
/
templates.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
var nTemplates = function(story, world, storyGen) {
var tersely = true;
var blankLine = '';
story = story || [];
story.title = function(god) {
// TODO: so, the pre-created villain might have nothing to do with the story
// there can be lacks and resolutions that involve no villanous agent
// in these case, a villain-based title (and they have happened) is obscrue, if not abstruse
var subject = god.hero;
var villForm;
if (god.hero.health === world.healthLevel.dead && god.villain.health === world.healthLevel.alive && god.villain.introduced) {
subject = god.villain;
} else if (god.villain.form !== 'human') {
villForm = god.villain.form;
}
var tale = god.select('story', 'tale', 'narrative', 'account', 'fable', 'parable', 'allegory', 'legend', 'romance');
var sn = god.select(subject.name, subject.nickname);
var pron = god.pronoun(subject);
var poss = god.possessive(subject);
var mi = '';
var templates = [
'<%= hero.nickname %>',
'The {{TL}} of {{SN}} and {{POSS}} adventures',
(god.coinflip() ? 'The Adventures of ' : '') + '{{SN}} of <%= hero.home.nation %>',
(god.coinflip() ? 'A certain ' : '') + '{{SN}} and what {{PRON}} did' + (god.coinflip() ? ' in <%= hero.home.nation %>' : '')
];
if (god.villain.introduced) {
templates.push(
(villForm ? 'The {{VF}} ' : '') + '{{VN}} and what happened to <%= pronounobject(villain) %>',
'The {{TL}} of {{HN}} and ' + (villForm ? 'the {{VF}} ' : '') + '{{VN}}',
'{{HN}} and {{VN}}',
'{{VN}} and {{HN}}',
'<%= villain.nickname %>'
);
if (villForm) {
templates.push('{{VN}} the {{VF}}');
}
}
if (god.hero.magicalitemused) {
// there could have been a LOT of them, if BOSSFIGHT
mi = god.hero.possessions[god.hero.possessions.length-1];
var miut = [
(god.coinflip() ? '{{HN}}' : '{{VN}}') + ' and the {{MI}}',
'{{HN}} and {{VN}}' + ' and the {{MI}}',
'{{VN}} and {{HN}}' + ' and the {{MI}}',
'{{HN}}, the {{MI}}, and how {{PRON}} came by it',
'{{HN}} and {{POSS}} {{MI}}',
'The {{MI}} of {{HN}}',
'The {{MI}}'
];
templates = templates.concat(miut);
}
if (god.cache.villains) {
var defeated = [ 'defeat', 'extirpation', 'removal', 'elimination', 'trouncing', 'vanquishing', 'scouring', 'overcoming', 'dismissal', 'scorning', 'rebuttal', 'refusal'];
var vills = '{{HN}} and the {{DFT}} of ' + god.list(god.cache.villains, function() { return (god.coinflip() ? 'name' : 'nickname');});
vills = vills.replace(/{{DFT}}/mg, god.pick(defeated));
// console.log(vills);
templates.push(vills, vills, vills);
}
var t = god.pick(templates);
return t.replace(/{{TL}}/mg, tale).replace(/{{PRON}}/mg, pron).replace(/{{POSS}}/mg, poss)
.replace(/{{SN}}/mg, sn).replace(/{{VF}}/mg, villForm)
.replace(/{{MI}}/mg, mi);
};
story.introduceHero = function(god, terse) {
var near = god.select('in', 'near', 'close to', 'not far from', 'just on the verge of', 'within a days walk of');
var nationType = god.select('country', 'province', 'kingdom', 'nation', 'city-state') ;
var res = '<%= hero.home.residence %>';
var vicin = '<%= hero.home.vicinity %>';
var hn = '<%= coinflip() ? hero.name : hero.nickname %>';
var mw = (god.hero.gender === world.gender.male ? 'man' : 'woman');
// Some men are born to good luck: all they do or try to do comes
// right - all that falls to them is so much gain - all their geese are
// swans - all their cards are trumps - toss them which way you will, they
// will always, like poor puss, alight upon their legs, and only move on
// so much the faster. The world may very likely not always think of them
// as they think of themselves, but what care they for the world? what
// can it know about the matter? One of these lucky beings was neighbour
// Hans.rr
var templates = [
'in a certain {{RES}} lived {{HN}}. ',
'a certain {{MW}} was very <%= pick(hero.description) %>. ' + (god.coinflip() ? '<%= capitalize(possessive(hero)) %> name {{was}} {{HN}}.' : '{{HN}} <%= possessive(hero) %> name {{was}}.'),
'there was once an old {{RES}} that stood in the middle of a deep gloomy {{VCN}}, and in the {{RES}} lived {{HN}}.',
'{{HN}} {{lived}} in a {{RES}} {{NEAR}} {{VCN}} in the {{NT}} <%= hero.home.nation %>. ',
'in the distant {{NT}} of <%= hero.home.nation %>, {{HN}} {{lived}} in a {{RES}} {{NEAR}} {{VCN}}. ',
'{{NEAR}} {{VCN}} in the {{NT}} of <%= hero.home.nation %>, there {{was}} a {{RES}} where {{HN}} {{lived}}.'
];
var t = [];
if (terse) {
t.push(god.pick(templates).replace('{{NT}}', nationType).replace('{{NEAR}}', near));
} else {
t.push((god.coinflip() ? story.intro(god) + ' ' : '' ) + god.pick(templates).replace('{{NT}}', nationType).replace('{{NEAR}}', near));
// TODO: list regular name or nickname at random
t.push(blankLine, '<%= hero.name %> {{lived}} with <%= list(hero.family, "nickname") %>.');
if (god.hero.acquaintances.length > 0) {
if (god.hero.acquaintances.length > 1) {
t.push(blankLine, '<%= list(hero.acquaintances, "nickname") %> {{were}} <%= select("friends of", "known to") %> <%= hero.name %>.');
} else {
t.push(blankLine, '<%= list(hero.acquaintances, "nickname") %> {{was}} <%= select("a friend of", "known to") %> <%= hero.name %>.');
}
}
for (var i = 0; i < god.hero.acquaintances.length; i++) {
god.getCharacter(god.hero.acquaintances).introduced = true;
}
for (i = 0; i < god.hero.family.length; i++) {
god.getCharacter(god.hero.family).introduced = true;
}
}
god.hero.introduced = true;
return t.join('\n').replace(/{{RES}}/gm, res).replace(/{{VCN}}/mg, vicin).replace(/{{HN}}/mg, hn).replace(/{{MW}}/mg, mw);
};
// shouldn't this be back in the god() function?
story.createVictim = function(god) {
var victim = god.getCharacter(god.pick(god.hero.family.concat(god.hero.acquaintances)));
god.cache.victim = victim;
return victim;
};
story.interdiction = function(god) {
var ptype = god.randomProperty(world.interdictionType);
var advisor = god.advisor;
var hero = god.hero;
// hey. shouldn't the advisor be... NOT a property?
var interdiction = {
type: ptype,
location: '',
action: '',
person: '',
advisor: advisor
};
switch (ptype) {
case world.interdictionType.movement:
interdiction.place = god.location();
break;
case world.interdictionType.action:
interdiction.action = 'take the Lord\'s name in vain AGAIN';
break;
case world.interdictionType.speak:
// TODO: action should have a target
// that way, we can "travel" to target....
interdiction.action = 'talk to ' + god.villain.nickname;
break;
}
return interdiction;
};
story.journey = function(god, p1, destination) {
// walk, run, ramble, travel
// time-period
// random amount
// random sights?
// Next day Petrusha set off on his visit to the Devil. He walked and
// walked, for three whole days did he walk, and then he reached a great
// forest, dark and dense - impossible even to see the sky from within it!
// And in that forest there stood a rich palace. Well, he entered the
// palace, and a fair maiden caught sight of him. She had been stolen
// from a certain village by the evil spirit. And when she caught sight
// of him she cried:
// So the girl went away, and walked and walked, till she came
// to the place. There stood a hut, and in it sat weaving the
// Baba Yaga, the Bony-shanks.
// So Vasilissa got ready, put her doll in her pocket, crossed
// herself, and went out into the thick forest.
// As she walks she trembles. Suddenly a horseman gallops by. He is
// white, and he is dressed in white, under him is a white horse, and
// the trappings of the horse are white - and the day begins to break.
// She goes a little further, and a second rider gallops by. He is
// red, dressed in red, and sitting on a red horse - and the sun rises.
// Vasilissa went on walking all night and all next day. It was only
// towards the evening that she reached the clearing on which stood
// the dwelling of the Baba Yaga. The fence around it was made of dead
// men's bones; on the top of the fence were stuck human skulls with
// eyes in them; instead of uprights at the gates [Pg 162] were men's
// legs; instead of bolts were arms; instead of a lock was a mouth
// with sharp teeth.
// Vasilissa was frightened out of her wits, and stood still as if
// rooted to the ground.
// Suddenly there rode past another horseman. He was black, dressed
// all in black, and on a black horse. He galloped up to the Baba
// Yaga's gate and disappeared, just as if he had sunk through the
// ground - and night fell. But the darkness did not last long. The eyes
// of all the skulls on the fence began to shine and the whole
// clearing became as bright as if it had been midday. Vasilissa
// shuddered with fear, but stopped where she was, not knowing which
// way to run.
var text = [];
var prn = god.pronoun(p1);
var poss = god.possessive(p1);
var templates = [
'So {{PRN}} went and had a goodish drink, and then started in search of {{DEST}}.',
'So {{HN}} went on walking all night and all next day. Eventually {{PRN}} reached {{DEST}}.',
'Next day {{HN}} set off on {{POSS}} visit to the {{DEST}}. {{PRN}} walked and '
+ 'walked, for three whole days did {{PRN}} walk, and then {{PRN}} reached {{DEST}}.',
'So {{HN}} went away, and walked and walked, till {{PRN}} came to the place.'
];
text.push(god.pick(templates));
return text.join('\n\n').replace(/{{PRN}}/mg, prn).replace(/{{DEST}}/mg, destination).replace(/{{POSS}}/mg, poss);
};
story.introduceVillain = function(god) {
// A certain woman was very bumptious.
// There once was a rich merchant named Marko - a stingier fellow never lived!
// Koshchei is merely one of the many incarnations of the dark spirit
// which takes so many monstrous shapes in the folk-tales of the class
// with which we are now dealing. Sometimes he is described as altogether
// serpent-like in form; sometimes he seems to be of a mixed nature,
// partly human and partly ophidian, but in some of the stories he is
// apparently framed after the fashion of a man. His name is by some
// mythologists derived from kost, a bone whence comes a verb signifying
// to become ossified, petrified, or frozen; either because he is bony of
// limb, or because he produces an effect akin to freezing or
// petrifaction.
// At midnight the demon-enchantress arrives, dancing and "blowing on a
// flute made of a dead man's bone." Fixing her eyes on one of the
// pilgrims, she mutters a spell, accompanied by a wild dance.
// from springhole.arabian.nights.gen.htm
// "With nothing else to do"
// "One day in <place>"
// "One evening in <place>"
// "One morning in <place>"
// "One night in <place>"
var time = god.select('one morning', 'one evening', 'one night', 'one day', 'in the middle of the night', 'when nobody {{was}} paying attention', 'after breakfast', 'around lunchtime');
var person = god.select('person', 'individual');
if (god.villain.form != 'human') {
person = god.villain.form;
}
person = god.pick(god.villain.description) + ' ' + person;
var A = '{{came}} into the region of <%= hero.home.vicinity %>';
var B = 'a <%= coinflip() ? "very " : "" %>{{PP}} known as {{VN}}';
var hh = '<%= hero.home.vicinity %>';
// the villainy of the person is not..... AWESOME... but it will do for now.
var templates = [
time + ' in {{HH}}, {{B}} {{strides}} in.',
time + ', there {{A}} {{B}}.',
'There {{A}} {{B}}.',
'{{B}} {{A}}.',
'{{B}} {{A}} {{TIME}}.',
'{{VN}}, a {{PP}}, {{paid}} a visit to {{HH}}.',
'{{HH}} {{plays}} host to a {{PP}}, {{VN}}' + (god.coinflip() ? ' ' + time: '') + '.'
];
god.villain.introduced = true;
return god.pick(templates).replace(/{{HH}}/mg, hh).replace(/{{B}}/mg, B).replace(/{{A}}/mg, A).replace(/{{TIME}}/mg, time).replace(/{{PP}}/mg, person);
};
story.deathResponse = function(god, person) {
var name = god.coinflip() ? person.name : person.nickname;
var t = [];
// Remember what his name was.
// Long may he be remembered.
// Not too many took notice, but those few were hard-pressed to save their tears for another day.
var templates = [
'It happens to everyone eventually. It {{happened}} to ' + god.pronounobject(person) + ' sooner.',
'These things happen. ' + (god.coinflip() ? 'Unfortunately.' : 'It is unfortunate.'),
'When G-d calls one home, there is never room for argument.',
'It was done.'
];
t.push(god.pick(templates));
// there was no|little solace to their grief.`
if (god.coinflip()) { t.push('There {{was}} much wailing in ' + god.hero.home.vicinity + '.'); }
return t.join(' ');
};
// embedded stories
// too much is hard-coded here, and should be passed-it, instead.
story.subtale = function(narrator, god) {
var text = [];
var presets = god.coinflip() ? storyGen.presets.shortWaterStory : world.util.randomProperty(storyGen.presets);
var setts = {
herogender: 'male',
villaingender: 'male',
peoplegender: 'male',
functions: storyGen.resetProppFunctions(),
funcs: presets.functions,
bossfight: presets.bossfight,
verbtense: 'present',
narrator: storyGen().deepClone(narrator),
// swap them on some key?
// unswapped, the hero gets killled
// which should shake them up a bit....
// TODO: cache.characters not populated in this situation
// fix inside of init()
// what I don't get is WHY IT WORKED FOR A WHILE
hero: storyGen().deepClone(god.hero),
villain: storyGen().deepClone(god.villain),
characters: storyGen().deepClone(god.cache.characters) // needed for hero and villain
};
setts.hero.introduced = false;
setts.villain.introduced = false;
// TODO: need to re-use, or get access to everything
// UGH UGH UGH
// NO GLOBALS!!!!!
// var theme = {
// bank: defaultbank(), // TROUBLE TROUBLE
// templates: nTemplates
// };
// TODO: pass in the narrator, hero, and villain (and other situations, items, etc)
// like.... a magical item. or a violated interdiction that goes disastrously
// except, interdictions are ALWAYS violated.....
var sg = new storyGen(setts);
var tale = sg.generate(setts, god.theme);
tale.universe = sg.universe;
return tale;
};
story.createLack = function(god) {
// TODO: need to have verbs/object set apart, so we can resolve this....
// TODO: need there to be a lack THING, then there can be additional things to say.
var lacks = ['{{needs}} a bride, a friend, or just somebody to talk to',
'{{needs}} a helper or magical agent',
['{{needs}} a wondrous object or two', 'Possibly three. No more than that. Unless they {{were}} collectible'],
['{{needs}} a egg of death or love', 'Either would do'],
['{{needs}} money or means of existence', 'Times {{were}} tough'],
['{{had}} lacks in other forms', 'Tsk tsk. Those lacks']
];
var l = god.pick(lacks);
var p = god.pick(god.hero.family); // argh, this ain't gonna work...
var t = l;
// if special array, capture first separately
if (typeof l === 'object' && l.length && l.length === 2) {
t = l[0] + '. ' + l[1];
l = l[0];
}
var lack = {
lack: l,
text: t,
person: p
};
god.cache.lack = lack;
return lack;
};
// TODO: the {{VN}} tags, etc.
// are replaced in propp.js
// on the assumption that the punishment is meted out to the VILLAIN
// but the person passed in could be the villain, anti-hero (or conceivably other)
// I suppose the person providing the punishment should be made explicit for niceness.
// there's an interesting need to get the functions together, and them build up the template according to some rules
// for instance, make sure we introduce henchmen earlier in the story if they are going to be used
// or if we don't use magical items, don't introduce them (or vice-versa)
// both of these are vice-versa scenarios, I guess...
story.punish = function(god, person) {
var descr = god.pick(person.description);
var eitherName = function(p) {
if (p.form && p.form != 'human') {
return god.select(p.nickname, p.name, 'the ' + descr + ' ' + p.form);
} else {
return god.coinflip() ? p.nickname : p.name;
}
};
var hn = '<%= coinflip() ? hero.nickname : hero.name %>';
var as = '<%= coinflip() ? "and " : coinflip() ? "so " : "" %>';
var ba = '<%= (coinflip() ? "" : coinflip() ? "But " : "And ")%>';
var poss = god.possessive(person);
var proo = god.pronounobject(person);
var vpro = god.pronoun(person); // v:= villain, but it could be false-hero or somebody punishable.
var hpron = god.pronoun(god.hero);
var hprono = god.pronounobject(god.hero);
// Following the advice of her daughters, three fair maidens whom he
// meets in her palace, Ivan does not attempt to touch the magic sword
// while she sleeps. But he awakes her gently, and offers her two golden
// apples on a silver dish. She lifts her head and opens her mouth,
// whereupon he seizes the sword and cuts her head off.
// TODO: helpers - family, advisor, helper or whatnot
// Listen, O Dog! thou wert a Dog (Sobaka), a clean beast; through
// all Paradise the most holy didst thou roam. Henceforward shalt thou
// be a Hound (Pes, or Pyos), an unclean beast. Into a dwelling it
// shall be a sin to admit thee; into a church if thou dost run, the
// church must be consecrated anew.
// NOTE: also contains deception, and ending-thing
// When they had eaten and drank, and were very merry, the old king said
// he would tell them a tale. So he began, and told all the story of the
// princess, as if it was one that he had once heard; and he asked the
// true waiting-maid what she thought ought to be done to anyone who
// would behave thus. "Nothing better," said this false bride, "than that
// she should be thrown into a cask stuck round with sharp nails, and
// that two white horses should be put to it, and should drag it from
// street to street till she was dead." "Thou art she!" said the old
// king; "and as thou has judged thyself, so shall it be done to thee."
// And the young king was then married to his true wife, and they reigned
// over the kingdom in peace and happiness all their lives; and the good
// fairy came to see them, and restored the faithful Falada to life
// again.
// TODO: some of these ending should not have a dispersal. or not a violent one.
// how to BEST separate....
var end = [
'{{AS}}{{PN}} {{was}} <%= punished() %> by {{HN}}.',
'{{AS}}{{HN}} <%= punished() %> {{PN}}.',
// okay. so there's been no mention of a horse. SO IT GOES.
// ALSO: DEAD
'{{HN}}\'s horse smote {{PN}} full swing with its hoof, and cracked {{POSS}} '
+ 'skull, and {{HN}} made an end of {{PROO}} with a club.',
'Such behavior could not be tolerated: {{HN}} fell upon {{PN}}, bound {{PROO}} with ropes.',
'{{VN}} was struck down by the hand of {{HN}}.',
// TODO: introduces a HOUSE
// but who knows WHERE we are, right now....
// also references a specific body part, which may not exist....
'{{HN}} greeted {{PN}}, and caught hold of {{POSS}} right little finger. '
+ '{{PN}} tried to shake {{HPN}} off, flying first '
+ 'about the house and then out of it, but all in vain. At last {{PN}} '
+ 'after soaring on high, struck the ground, and fell to pieces, becoming '
+ 'a fine yellow sand.',
'{{AS}}{{HN}} cut the feet off from {{PN}} and placed {{PROO}} on a stump by the roadside.'
];
// awkward use of magical item
// TODO: this code is in more than one place....
var mi;
if (god.hero.possessions && god.hero.possessions.length > 0) {
mi = god.hero.possessions[god.hero.possessions.length-1];
var mit = ('Seeing that {{VN}} was perfectly enfeebled, {{HN}} snatched up {{HPRNO}} '
+ '{{MI}}, and with a single blow struck off {{PN}}\'s head. Behind {{HPRNO}} '
+ 'voices began to cry: "Strike again! strike again! or {{VPRO}} will come to life!" '
+ '"No," replied {{HN}}, "a hero\'s hand does not strike twice, but '
+ 'finishes its work with a single blow."').replace(/{{MI}}/mg, mi);
end.push(mit);
}
var endsel = god.pick(end);
if (endsel === mit) { god.hero.magicalitemused = true; }
var dispersal = [
(god.coinflip() ? 'Thanks to {{HN}}, ' : '') + '{{PN}} was completely burnt to cinders.',
'{{AS}}that {{was}} that.',
'Afterwards {{HN}} {{heaped}} up a pile of wood, {{set}} fire to it, {{burnt}} {{PN}} on the pyre, '
+ 'and {{scattered}} {{POSS}} ashes to the wind.',
'The body {{was}} left in the possession of {{HN}}, who {{scraped}} together the pieces and {{burned}} them in the stove.',
'{{AS}}they placed {{PROO}} in a coffin, and carried it to church, whereupon it burst into horrible flames, singeing the hands of those who dared carry it.',
// this doesn't work if already a fine yellow sand.....
'{{AS}}{{HN}} cut {{PROO}} into small pieces, which were buried throughout the woods.',
'{{BA}}{{HN}} said, "Into the bottomless pit with you! Out of sight, accursed one!"'
];
god.villain.health = 'dead';
var t = [];
// t.push(god.capitalize(endsel));
// t.push(god.capitalize(god.pick(dispersal)));
t.push(endsel);
t.push(god.pick(dispersal));
if (god.coinflip()) {
var tmpl2 = [
'God <%= (coinflip() ? "evidently " : "")%>{{did}} it to <%= select("punish", "remonstrate", "educate", "rebuke") %> {{PN}} for {{POSS}}'
+ '<%= (coinflip() ? " great" : "")%> <%= nlp.adjective("' + descr + '").conjugate().noun %>.',
'{{BA}}{{PN}} sits to this day in the pit - in Tartarus.',
'{{BA}}{{PN}} ' + (god.coinflip() ? '<%= select("{{disappear}}", "{{vanish}}") %>, and ' : '' ) +'{{was}} never seen again.'
];
t.push(god.pick(tmpl2));
}
return t.join(' ').replace(/{{PN}}/mg, function() { return eitherName(person); }).replace(/{{AS}}/mg, as)
.replace(/{{BA}}/mg, ba).replace(/{{POSS}}/mg, poss).replace(/{{PROO}}/mg, proo)
.replace(/{{HPN}}/mg, hpron).replace(/{{HPRNO}}/mg, hprono).replace(/{{VPRO}}/mg, vpro);
};
story.intro = function(god) {
// There lived in a certain land an old man of this kind
// can't use a complete sentence. DANG. because: capitalization
// 'This is the way the world begins.',
var intros = [
'Once upon a time,',
'Once there was, and there wasn\'t,',
'I\'ve heard it said that once',
'A long time ago,',
'Some years before you were born,',
'In the time when your parents\' parents were but small babies,',
'Once upon a time,',
(god.coinflip() ? 'We <%= coinflip() ? "like to " : "" %><%= coinflip() ? "say" : "think" %> '
+ 'that we are wise<%= select(" folk", " folks", " people", "") %>, '
+ 'but our <%= select("old", "old people", "elders") %> dispute <%= select("the fact", "this fact", "this") %>, '
+ 'saying: "No, no, we were wiser than you are." But '
+ 'stories tell that ' : '') + 'before our grandfathers had learnt anything, and before their grandfathers were born,'.replace(/fathers/mg, (god.coinflip() ? 'fathers' : 'mothers'))
];
// TODO: if blank, don't output the empty lines before the first paragraph.
var intro = god.pick(intros);
return '{{**}}' + intro;
};
// TODO: VERB TENSE STANDARDIZATION - s/b past in all places. OR .... ???
// TODO: verb tense DOES NOT WORK here
// this is the... what tense? past would work.
// if ALL verb ar infinitive and appear as {{verb}}, then we do a global pull, conjugate, replace prior to template parsing. or after. whatever.
// 0: Initial situation
// TODO: multiple sentences within a template may not be punctuated correctly.
// hrm. maybe they should each appear as a sub-component, so they can be processed externally?
// for example, if sentence begins with <%= list(acquainainces()) %> and the first is 'the Easter Bunny' it will not be auto-capitalised
// since that only works on the first letter of the template-output (erroneously called 'sentence' in the code).
// TODO: what about "lives alone." how would THAT be figured out???
// aaaand: Milan are known to Natalie.
// TODO: why am I using "god" instead of "world" - there's a reason, wasn't there?
story['func0'].exec = function(god, subFunc, terseness) {
// do we need an "introduction" flag?
// if the intro is skipped, how do we get this info across?
// ALT: if we start in-media-res, and come BACK to this, skip the "a long time ago" nonsense.
// or, if we are doing it a SECOND time (re: brothers killed by dragon; new baby born, story starts again)
// In a certain village there lived a husband and wife - lived happily, lovingly, peaceably. All their neighbors envied them; the sight of them gave pleasure to honest folks.
// There once lived an old couple who had one son called Ivashko;[207] no one can tell how fond they were of him!
// TODO: also need to get an approximate "age" for the hero -
// In [country] there is a legend about a [adjective] and [adjective] [gender- man, woman, boy, girl depending]
// TODO: hero could use nickname OR adj + adj and name. Or something.
// age of person plus one adjective. or we add a NEW adjective as PRIMARY DESCRIPTION ???
// Once there was an old man who was such an awful drunkard as passes all description.
// A certain woman was very bumptious.
// Some men are born to good luck: all they do or try to do comes
// right all that falls to them is so much gain all their geese are
// swans all their cards are trumps toss them which way you will, they
// will always, like poor puss, alight upon their legs, and only move on so
// much the faster. The world may very likely not always think of them as
// they think of themselves, but what care they for the world? what can it
// know about the matter?
// One of these lucky beings was neighbour Hans.
// There was once an old castle, that stood in the middle of a deep gloomy wood, and in the castle lived an old fairy.
var t = story.introduceHero(god, terseness);
return '{{**}}' + t;
};
// Proppian-function templates
// Absentation: Someone goes missing
// this could be the hero leaving home
// so we\'d have to have more logic to cover this
// TODO: also need to define the action, so it can be dealt with in Resolution (func20) and elsewhere...
// people have a location; if the location is xs"unknown" we can process this elsewhere...
story['func1'].exec = function(god, subFunc) {
// TODO: some way to track missing, and set this up
// TODO: track the death
// [blah blah] and there was an end of him.
// example without introdcution that takes place PRIOR to hero intro
// A certain priest's daughter went strolling in the forest one day,
// without having obtained leave from her father or her mother - and she
// disappeared utterly. Three years went by. Now in the village in which
// her parents dwelt there lived a bold hunter, who went daily roaming
// through the thick woods with his dog and his gun. One day he was going
// through the forest; all of a sudden his dog began to bark, and the
// hair of its back bristled up.
var t = [];
if (!god.hero.introduced) { t.push(story.introduceHero(god, tersely)); }
if (!god.cache.victim) { story.createVictim(god); }
var templates = [
'{{VICN}} went missing.',
'{{VICN}} unexpectedly {{died}}, leaving <%= hero.name %> devastated.' + (god.coinflip() ? ' ' + story.deathResponse(god, god.cache.victim) : ''),
'Sooner or later, {{VICN}} {{died}}.' + (god.coinflip() ? ' ' + story.deathResponse(god, god.cache.victim) : '')
];
god.cache.victim.health = world.healthLevel.dead;
// there should be concern for the first, grief for the latter two
// and abstention could simple be left for work. Sheesh!
t.push(god.pick(templates));
if (god.hero != god.cache.victim) {
t.push(blankLine, god.converse(god.hero).replace('!', '?'));
}
return t.join('\n');
};
// Interdiction: hero is warned
// story['func2'].templates.push('<%= hero.name %> is warned.');
// TODO: introduction of personage from interdiction
// TODO: rework the d**n interdiction template-function
// this is now just a proof-of-concept of executing larger functions to deal with templates
story['func2'].exec = function(god) {
// They spent some time together, and then the Princess took it into her
// head to go a warring. So she handed over all the housekeeping affairs
// to Prince Ivan, and gave him these instructions:
// "Go about everywhere, keep watch over everything, only do not venture
// to look into that closet there."
// He couldn\'t help doing so. The moment Marya Morevna had gone he rushed
// to the closet, pulled open the door, and looked in -there hung Koshchei
// the Deathless, fettered by twelve chains. Then Koshchei entreated
// Prince Ivan, saying, -
// "Have pity upon me and give me to drink! Ten years long have I been
// here in torment, neither eating or drinking; my throat is utterly
// dried up."
// The Prince gave him a bucketful of water; he drank it up and asked for
// more, saying:
// "A single bucket of water will not quench my thirst; give me more!"
// The Prince gave him a second bucketful. Koshchei drank it up and asked
// for a third, and when he had swallowed the [Pg 100] third bucketful,
// he regained his former strength, gave his chains a shake, and broke
// all twelve at once.
// "Thanks, Prince Ivan!" cried Koshchei the deathless, "now you will
// sooner see your own ears than Marya Morevna!" and out of the window he
// flew in the shape of a terrible whirlwind. And he came up with the
// fair Princess Marya Morevna as she was going her way, laid hold of
// her, and carried her off home with him. But Prince Ivan wept full
// sore, and he arrayed himself and set out a wandering, saying to
// himself: "Whatever happens, I will go and look for Marya Morevna!"
var loc;
var person;
var action;
// here for reference...
// var prohibitType = {
// movement: 'movement',
// action: 'action',
// speak: 'speakwith'
// };
var advisor = god.advisor;
var hero = god.hero;
var text = [];
if (!god.hero.introduced) { text.push(story.introduceHero(god, tersely)); }
text.push('<%= hero.name %> met <%= advisor.nickname %>.');
text.push(blankLine);
text.push(god.converse(advisor, hero), blankLine);
// function 2: an interdiction is addressed to protagonist(s) = interdiction - (gamma)
// 1 - interdiction issued
// 2 - inverted form of interdiction issued as order or suggestion
hero.interdiction = story.interdiction(god);
// action and speak are identical sentences
// movement is _slightly_ different
switch (hero.interdiction.type) {
case world.interdictionType.movement:
// TODO: more better coversation
text.push('<%= advisor.name %> warned <%= hero.name %> to avoid <%= randomProperty(hero.interdiction.place) %>.');
break;
case world.interdictionType.action:
// interdiction.action = 'take the Lord\'s name in vain AGAIN';
text.push('<%= advisor.name %> told <%= hero.name %> to not <%= hero.interdiction.action %>.');
break;
case world.interdictionType.speak:
// TODO: action should have a target
// that way, we can "travel" to target....
text.push('<%= hero.interdiction.advisor.name %> warns <%= hero.name %> to not <%= hero.interdiction.action %>.');
break;
}
var mh = god.magicalhelper.name;
// TODO: make the magicalhelper here. I guess
text.push(world.blankLine, hero.interdiction.advisor.name + ' introduced {{MH}} to ' + hero.name);
text.push(world.blankLine, god.converse(hero, god.magicalhelper));
var tale = story.subtale(god.magicalhelper, god);
var intro = god.pick([
'{{MH}} {{said}} "I will tell you a story":',
'"I have a tale for you," {{said}} {{MH}}, "' + tale.title + ':"'
]);
text.push(world.blankLine, intro, world.blankLine);
text.push(tale.tale);
text.push(world.blankLine, '"And now," {{concluded}} {{MH}}, "my tale is done."');
return text.join('\n').replace(/{{MH}}/mg, mh);
};
// Violation of Interdiction
story['func3'].exec = function(god) {
var text = [];
// TODO: if interdiction is undefined, create it!
// which means it needs to be in a function.....
if (!god.hero.interdiction) {
god.hero.interdiction = story.interdiction(god);
}
var interdiction = god.hero.interdiction;
if (!interdiction) {
// this problem should now be solved
// leaving it here so far since there are no unit-tests yet.
console.log('INTERDICTION IS NOT DEFINED IN FUNC3; was func2 skipped??? YOU NEED TO EXTRACT THAT TO A METHOD');
return ' ';
}
switch (interdiction.type) {
case world.interdictionType.movement:
text.push('Despite the warning, <%= hero.name %> went to <%= randomProperty(hero.interdiction.place) %>.');
text.push(world.blankLine, '<%= villain.name %>, a rather <%= list(villain.description) %> person, appeared.');
break;
case world.interdictionType.action:
text.push('Shockingly, <%= hero.name %> proceeded to <%= hero.interdiction.action %>.');
text.push(world.blankLine,'<%= villain.name %>, a rather <%= list(villain.description) %> person, appeared in front of <%= hero.name %>.');
break;
case world.interdictionType.speak:
var t = 'As soon as <%= hero.interdiction.advisor.name %> {{was}} gone, <%= hero.name %> '
+ 'ran off to find <%= villain.name %> and had an interesting conversation.';
text.push(t);
break;
}
god.villain.introduced = true;
text.push(world.blankLine,god.converse(god.villain, god.hero));
// find a way to integrate this
// story['func3'].templates.push('<%= violation() %> <%= list(villain.family) %> are in league with <%= villain.name %>.');
return text.join('\n');
};
// Reconnaissance: Villain seeks something
story['func4'].exec = function(god) {
// function 4: antagonist(s) makes attempt at reconnaissance = reconnaissance - (epsilon)
// 1 - reconnaissance by antagonist(s) to obtain information about victim(s) / protagonist(s)
// 2 - inverted form of reconnaissance by victim(s) / protagonist(s) to obtain information about antagonist(s)
// 3 - reconnaissance by other person(s)
var t = [];
if (!god.villain.introduced) { t.push(story.introduceVillain(god)); }
// WHOAH!!!! where'd the reonnaissance go ?!?!?!?
// oooooh, that's inside of the 'villain introduction'
// hrm.
return t.join('\n');
};
// Delivery: The villain gains information
story['func5'].exec = function(god) {
var t = [];
if (!god.villain.introduced) { t.push(story.introduceVillain(god)); }
var vn = '<%= select(villain.name, villain.nickname) %>';
var tmpls = [
'{{VN}} {{gained}} information.',
'After a chat with <%= getCharacter(pick(hero.family)).name %>, {{VN}} {{learned}} some interesting news.',
'While skulking about <%= hero.home.vicinity %>, {{VN}} {{overheard}} some gossip about <%= hero.name %>.'
];
t.push(god.pick(tmpls).replace(/{{VN}}/mg, vn));
return t.join('\n\n');
};
// Trickery: Villain attempts to deceive victim.
story['func6'].exec = function(god) {
var t = [];
if (!god.villain.introduced) { t.push(story.introduceVillain(god)); }
t.push('{{VN}} {{attempted}} to deceive victim.');
return t.join('\n\n');
};
// Complicity: Unwitting helping of the enemy
story['func7'].exec = function(god) {
var text = [];
if (!god.villain.introduced) { text.push(story.introduceVillain(god)); }
if (!god.hero.introduced) { text.push(story.introduceHero(god, tersely)); }
text.push('{{HN}} unwittingly {{helped}} {{VN}}.');
return text.join('\n\n');
};
// 2nd Sphere: The Body of the story
// 8A - Villainy: The need is identified (Villainy)
// function 8 (and/or 8a) is always present in tale
// antagonist(s) causes harm or injury to victim(s)/member of protagonist's family = villainy - A
story['func8'].exec = function(god, subFunc, skipIntros) {
// this needs to be picked AHEAD OF TIME
// since some of these require other creations earlier
// like 7b 'bride is forgotten'
// the bride should have been introduced earlier....
// if not picked ahead of time, pick a sub-function at random
subFunc = subFunc || god.randomProperty(storyGen.villainyTypes);
var template = []; // text returned to story
var t = []; // common use in sub-funcs
var skipVillain = false;
if (!god.hero.introduced && !skipIntros) { template.push(story.introduceHero(god, tersely)); }
// for testing
// subFunc = 'causes sudden disappearance';
// subFunc = 'commits murder';
// subFunc = 'casting into body of water';
// subFunc = 'theft of daylight';
// subFunc = 'threat of cannibalism';
// subFunc = 'kidnapping of person';
// subFunc = 'tormenting at night (visitation, vampirism)';
// console.log(subFunc);
switch(subFunc) {
case 'kidnapping of person':
// o, god!
var kp = god.getCharacter(god.pick(god.select(god.hero.family, god.hero.acquaintances)));
var name = (god.coinflip() ? kp.name : kp.nickname);
// 'cept they could also be a relative.
var friendof = (kp.introduced ? '' : ', a friend of {{HN}}');
kp.introduced = true;
template.push('{{VN}} kidnapped {{NAME}}{{FO}}.'.replace(/{{NAME}}/mg, name).replace(/{{FO}}/mg, friendof));
break;
// TODO: name not setup???
case 'seizure of magical agent or helper':
template.push('{{VN}} <%= select("forcibly seized", "kidnapped", "{{makes}} off with") %> <%= magicalhelper.name %>.');
break;
case 'forcible seizure of magical helper':
template.push('{{VN}} <%= select("forcibly seized", "kidnapped", "{{makes}} off with") %> <%= magicalhelper.name %>.');
break;
case 'pillaging or ruining of crops':
template.push('The harvest {{was}} destroyed by {{VN}}. All in <%= hero.home.nation %> {{begins}} to feel the pangs of hunger.');
break;
case 'theft of daylight':
t = [
// TODO: all of these need to be CLEANED UP WITH APPROPRIATE REFERENCES
'Suddenly, it became as night. {{VN}} had stolen the daylight!',
'Suddenly, it became as night. {{VN}} had the sun, as easily as eating a corn-cake.',
'Suddenly the sky was covered by a black cloud; a terrible storm arose.',
'The moon came across the sun, turning the landscape a dark, curdled, black-red.',
// http://hbar.phys.msu.su/gorm/atext/ginzele.htm
// TODO: clean this up a bit, still.....
'There {{is}} a shroud of darkness drawn over the '
+ 'everyone in the land from head to foot, their cheeks '
+ '{{were}} wet with tears; the air {{was}} alive with '
+ 'wailing voices; the walls and roof-beams drip blood; '
+ 'the gate of the cloisters and the court beyond them '
+ 'are full of ghosts trooping down into the night of '
+ 'hell; the sun is blotted out of heaven, and a '
+ 'blighting gloom is over all the land. {{VN}} has made <%= villain.object %>self known.',
'Sudden strange and unaccountable disorders and '
+ 'alterations took place in the air; the face of the sun '
+ 'was darkened, and the day turned into night, and that, '
+ 'too, no quiet, peaceable night, but with terrible '
+ 'thunderings, and boisterous winds from all quarters. {{VN}} has made <%= villain.object %>self known.',
// TODO: victim disappears...
'A violent thunderstorm suddenly arose and enveloped {{VN}} in so dense '
+ 'a cloud that he was quite invisible to the assembly. From that hour '
+ 'Romulus was no longer seen on earth. When the fears of the Roman youth '
+ 'were allayed by the return of bright, calm sunshine after such fearful '
+ 'weather, they saw that the royal seat was vacant.',
'{{VN}} has made night out of noonday, hiding the '