/
index.js
180 lines (176 loc) · 11.3 KB
/
index.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
const fs = require("fs");
const path = require("path");
const https = require("https");
const querystring = require('querystring');
const godalertExportEnabled = false;
const godalertExportURL = process.env.UPLOAD_URL;
var godalertExport = {};
const pathInDungeon = ['dungeondb2_ru','dungeondb2_en'];
const pathInSail = ['seadb2_ru','seadb2_en'];
const pathOut = 'build';
const dungeonTestPhrases = {
ru: {
bonusGodpower: 'Помолившись своим богам, они шагают по невидимому мосту над пропастью.',
bonusHealth: 'Смонтированный в нише фонтан живой еды позволил приключенцам слегка поправить здоровье.',
bossHint: 'Какой-то текст. Шестое чувство героев дает тревожный звоночек.',
boss: 'Появившийся в комнате Random Boss Name лёгким движением руки изящно превращает визит вежливости в кровавую драку.',
custom: 'За потерю героями здоровья, золота и трофеев администрация подземелья — Random God Name — ответственности не несёт.',
deadEnd: 'Какой-то текст. В этом тупике приключенцы с удивлением читают на стене надпись: «Верной дорогой идёте, товарищи!»',
directionless: 'Приключенцы следуют за большим зелёным камнеедом, прогрызающим себе путь сквозь толщу камня. Далее другой текст.',
discard: 'Какой-то текст. Другие направления здесь ничем не хуже, но дорога на восток выстлана скатертью.',
longJump: 'Яркая вспышка заставляет приключенцев прикрыть глаза, а открывают они их совершенно в другом месте. Далее другой текст.',
jumpingDungeon: 'Не рассчитав усилий, герои со всей дури прыгают на направление. Далее другой текст.',
pointerMarker: 'В этой комнате приключенцы по очереди встают на весы, но стрелка каждый раз показывает на направление.',
sideMarker: 'Вырезанный в полу символ намекает героям, что сокровище в какой-то половине подземелья.',
staircaseHint: 'Здесь можно спуститься вниз, если команду на спуск подаст бог с персональным боссом.',
staircase: 'По гласу хозяина +Random Boss Name+ открывает огромный люк, и герои осторожно спускаются на второй этаж подземелья.',
trapGold: 'Плывущий в воздухе мираж таверны заставил героев привычно потерять часть наличности.',
trapLowDamage: 'На стенах развешаны клетки, но канарейки в них лежат кверху лапками.',
trapModerateDamage: 'Обитающий в этой комнате элементаль льда очень прохладно принимает гостей.',
trapMoveLoss: 'Здесь ничего не понимающие приключенцы оказываются заперты вместе с мабританскими зоологами на заседании гаражного кооператива.',
trapTrophy: 'При виде вращающегося чёрно-белого барабана герои непроизвольно выкладывают из сумок по трофею.',
trapUnknown: 'Жуткая ловушка здесь пугает команду, но Random Hero что-то шепчет питомцу, и послушный Random Pet на несколько минут нейтрализует опасность.',
treasureChest: 'Сокровищница! Измождённый старец чахнет над грудами злата и не способен оказать сопротивление молодым лоботрясам.',
vault: 'Да это же кладовая! Здесь всё как в сокровищнице, только лучше.'
},
en: {
bonusGodpower: 'Footsteps resonate here. The adventurers take advantage of the acoustics and form an impromptu a cappella group.',
bonusHealth: 'The group finds a stone basin filled with condensed living water in the center of the room. Each member gets a sip.',
bossHint: 'Some other text. Suddenly the entire team gets a case of the jitters.',
boss: 'Random Hero polishes a monocle, peers into the darkness, and asks, “Doctor Random Boss Name, I presume?”',
custom: 'A peculiar fact: this dungeon has a little divine touch.',
deadEnd: null,
directionless: 'The heroes closed their eyes and went somewhere. Some other text.',
discard: 'Some other text. All directions are available, but the door leading south has been brightly painted for better visibility.',
longJump: 'The party wakes up in another part of the dungeon, questioning if everything before now had just been a dream.',
jumpingDungeon: 'A springboard trap in the floor catapults the party at some direction. Some other text.',
pointerMarker: 'Random Hero passes a perception check and notices an arrow pointing at direction.',
sideMarker: 'A gleaming glyph unequivocally suggests that the treasure is in some half of the dungeon.',
staircaseHint: 'The adventurers can go down from here, but only if a boss-owning deity commands to do so.',
staircase: 'Flexing its muscles, +Random Boss Name+ opens up a passage leading the team into the basement of this dungeon.',
trapGold: 'A tricky device on the ceiling pulled some gold coins straight from the pockets of the careless adventurers.',
trapLowDamage: 'The floor is covered with molten lava, except for the parts of it that are not.',
trapModerateDamage: 'Random Hero pulled a rope hanging from the ceiling with all his might and got a slab with a concrete plate on his head.',
trapMoveLoss: 'The adventurers need an additional turn to get through the quicksand in this room.',
trapTrophy: 'With great difficulty Random Hero escaped a cobweb full of giant flies, leaving behind the Random Item.',
trapUnknown: 'The heroes were about to step into a trap when Random Pet somehow blocked it and saved the day. Random Hero is proud of his pet.',
treasureChest: 'The treasure! Diving into piles of gemstones, each adventurer swears to use this wealth to make a fresh start, get sober, and finally write that novel.',
vault: 'This looks like a secret treasury! It\'s just like a regular treasury, but better.'
}
};
(async () => {
try {
await fs.promises.mkdir(pathOut,{recursive: true});
await Promise.all(pathInDungeon.map(async (dir) => {
const stats = await fs.promises.stat(dir).catch(e => null);
if (!stats || !stats.isDirectory()) {
console.warn(dir + ' is not a directory or does not exist, skipping');
return;
}
const content = {};
const files = await fs.promises.readdir(dir);
const lang = dir.substr(-2);
await Promise.all(files.map(async (file) => {
const contents = await fs.promises.readFile(path.join(dir, file), 'utf8');
content[file] = contents.replace(/\r\n/g,'\n').split('\n').filter(a => !!a.trim() && !/^\s*#/.test(a)).join('|');
godalertExport[lang + ' ' + file] = contents;
try {
if (!content[file]) throw 'expression must not be empty';
new RegExp(content[file]);
} catch(e) {
throw 'regex is invalid in ' + dir + '/' + file + ':\n' + e;
}
}));
if (!Object.keys(content).length) {
console.warn('no content in ' + dir + ', skipping');
return;
}
if (content['bossFinish']) {
content['boss'] += '|' + content['bossFinish'];
delete content['bossFinish'];
}
const regExps = {};
Object.keys(content).forEach(cat => regExps[cat] = new RegExp(content[cat]));
await Promise.all(Object.keys(dungeonTestPhrases[lang]).map(async (cat) => {
if (dungeonTestPhrases[lang][cat] === null) {
if (regExps[cat] && !regExps[cat].test('random text that should not match')) {
return;
}
throw 'regex test failed ' + dir + '/' + cat + ': this is yet unused category that should not match an arbitrary text';
}
let matched = false;
for (const rcat in regExps) {
if (regExps[rcat].test(dungeonTestPhrases[lang][cat])) {
if (rcat !== cat) {
throw 'regex test failed in ' + dir + '/' + rcat + ': matched phrase from ' + cat;
}
matched = true;
}
}
if (!matched) {
throw 'regex test failed in ' + dir + '/' + cat + ': does not match with test phrase';
}
}));
content.status = "success";
await fs.promises.writeFile(path.join(pathOut, dir + '.json'), JSON.stringify(Object.keys(content).sort().reduce((obj, key) => { obj[key] = content[key]; return obj; }, {}), 'utf8'));
}));
await Promise.all(pathInSail.map(async (dir) => {
const stats = await fs.promises.stat(dir).catch(e => null);
if (!stats || !stats.isDirectory()) {
console.warn(dir + ' is not a directory or does not exist, skipping');
return;
}
const content = {status: "success", "beasties": []};
const files = await fs.promises.readdir(dir);
await Promise.all(files.map(async (file) => {
var item, found;
const contents = await fs.promises.readFile(path.join(dir, file), 'utf8');
contents.replace(/\r\n/g,'\n').split('\n').filter(a => !!a.trim() && !/^\s*#/.test(a)).forEach(beastie => {
item = {name: beastie, hp: file};
if (+item.hp.split('-').shift() >= 50) {
item.tre = 1;
}
if (found = content.beasties.find(element => element.name === beastie)) {
throw 'duplicated beastie "' + beastie + '" in ' + file + ' (' + found.hp + ')';
}
content.beasties.push(item);
});
}));
if (!content.beasties.length) {
console.warn('no content in ' + dir + ', skipping');
return;
}
content.beasties.sort((a,b) => {
return -a.hp.localeCompare(b.hp, undefined, {numeric: true}) || a.name.localeCompare(b.name);
});
content.beasties.forEach(a => {
a.hp = a.hp.replace('-','–');
})
await fs.promises.writeFile(path.join(pathOut, dir + '.json'), JSON.stringify(content), 'utf8');
}));
if (godalertExportEnabled && godalertExportURL) {
godalertExport = querystring.stringify(godalertExport);
const req = https.request(godalertExportURL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': godalertExport.length
}
}, (res) => {
console.log('godalertExport: statusCode:', res.statusCode);
console.log('godalertExport: headers:', res.headers);
res.on('data', (d) => {
console.log('godalertExport: response:', d.toString('utf-8'));
});
});
req.on('error', (e) => {
console.error('godalertExport:', e);
});
req.write(godalertExport);
req.end();
}
} catch (e) {
console.error("exception:", e);
process.exit(1);
}
})();