Skip to content

Instantly share code, notes, and snippets.

@e-ll-c
Created August 22, 2017 06:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save e-ll-c/b6ea472ac34b01db403bb154eee67765 to your computer and use it in GitHub Desktop.
Save e-ll-c/b6ea472ac34b01db403bb154eee67765 to your computer and use it in GitHub Desktop.
const fs = require('fs');
const path = require('path');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const dataDir = path.join('ktst');
const ws = fs.createWriteStream('ktst-passive.tsv', 'utf-8');
let pk = 0;
ws.on('error', function (err) {
console.error(err);
process.exit(1);
});
ws.write([
'pk',
'logID',
'turn',
'action',
'parent',
'target',
'effect',
'type',
'direction',
'cycle',
'actual',
'cycle * actual',
'passive'].join('\t') + '\n');
const list = fs.readdirSync(dataDir);
console.log(list.length + ' files');
for (let i = 0; i < list.length; i++) {
readLog(path.join(dataDir, list[i]));
}
function readLog(fullpath) {
fs.stat(fullpath, (err, stats) => {
if (err) {
return;
}
let m = fullpath.match(/([0-9]+)\.html$/);
if (!stats.isFile() || !m) {
return;
}
if (m[1] != 12648) {
//return;
}
_readLog(fullpath, m[1])
.then(result => console.log(result.fullpath));
});
}
function _readLog(fullpath, battleId) {
return new Promise((resolve, reject) => {
fs.readFile(fullpath, 'utf8', function(err, data) {
if (err) {
reject(err);
return;
}
const result = parse(data, battleId);
result.fullpath = fullpath;
resolve(result);
});
});
}
function parseFormation(mae, naka, usiro) {
const line = [mae, naka, usiro];
return {
line: line,
zenTarget: line.reduce((pre, cur) => pre + cur),
retuTarget: line.sort((pre, cur) => cur - pre)[0],
kanTarget: line.filter(cur => cur).length,
}
}
function parse(data, battleId) {
const result = {};
const formation = {};
const document = new JSDOM(data).window.document;
const contents = document.querySelector('body > .AL > div[style]');
if (!contents) {
return result;
}
let cursor = contents.firstElementChild;
let m;
const state = {
battleId: battleId,
turn: 0,
formation: {
}
};
while (cursor) {
if (cursor.tagName == 'TABLE' && cursor.className == 'FRB1') {
const title = cursor.querySelector('.B7i');
if (title && (m = title.textContent.match(/(\d+)/))) {
state.turn = ~~ m[1];
}
const left = cursor.querySelector('td[width = "490"] > .SE > table > tbody');
const right = cursor.querySelector('td[width = "490"] > .SE2 > table > tbody');
if (left && right) {
state.formation.left = parseFormation(
left.querySelectorAll('td[width = "100"][align = "right"]').length,
left.querySelectorAll('td[width = "100"][align = "center"]').length,
left.querySelectorAll('td[width = "100"]:not([align])').length
);
state.formation.right = parseFormation(
right.querySelectorAll('td[width = "100"]:not([align])').length,
right.querySelectorAll('td[width = "100"][align = "center"]').length,
right.querySelectorAll('td[width = "100"][align = "right"]').length
);
}
cursor
.querySelectorAll('.FRB2 > div[align = LEFT]')
.forEach(e => parseBA2(state, e, 'root', '', ''));
}
else if (cursor.tagName == 'DIV') {
cursor
.querySelectorAll('.FRS2 > .BA2')
.forEach(e => parseBA2(state, e, 'root', '', ''));
}
cursor = cursor.nextElementSibling;
}
return result;
}
function parseBA2(state, cursor, parent, currentTarget, currentEffect) {
let m;
const regPassiveName = /の([^!]+)/,
regActiveName = /^([^!]+)/,
regSkillEffect = /^(.+?):([^!]+?)!$/;
if (!cursor) {
return;
}
cursor = cursor.firstElementChild;
while (cursor) {
if (cursor.nodeType == 1) {
/*
() は走査のルート要素
行動前
(.FRS2 > b.BA2) > b.BA2
アクティブ
(.FRS2 > b.BA2) > dl. > dl.BA2
パッシブ
> dl.BA2
通常攻撃
(.FRS2 > b.BA2) > dl. > dl.BA2
パッシブ
通常攻撃の兄弟要素 dl.BA2
ターン開始
(.FRB2 > div[align = LEFT]) > b.BA2
*/
if (cursor.textContent == '通常攻撃!' && cursor.className == 'HK2') {
parent = '通常攻撃';
currentTarget = '敵';
currentEffect = '攻撃';
}
else if (cursor.className == 'F7') {
m = cursor.textContent.match(regActiveName);
parent = m[1];
}
else if (cursor.tagName == 'B' && cursor.className == 'HK2') {
m = cursor.textContent.match(regPassiveName);
if (m[1] == "' Θ '") {
logger(state, 'action', parent, currentTarget, currentEffect, m[1]);
}
parent = m[1];
}
else if (cursor.className == 'P2') {
m = cursor.textContent.match(regSkillEffect);
currentTarget = m[1];
currentEffect = m[2];
}
else if (cursor.className == 'BA2') {
parseBA2(state, cursor, parent, currentTarget, currentEffect);
}
else if (cursor.tagName == 'DL') {
parseBA2(state, cursor, parent, currentTarget, currentEffect);
}
}
else if (cursor.nodeType == 3) {
if (cursor.textContent.match(/のダメージ!$/)) {
logger(state, 'hit', parent, currentTarget, currentEffect, '');
}
}
cursor = cursor.nextSibling;
}
}
function logger(state, action, parent, target, effect, skill) {
if (!state.formation) {
return;
}
pk++;
const foe = state.formation.left;
const ally = state.formation.right;
let actual = 1;
let type = '単';
let cycle = 1;
let direction = '';
let m;
if (m = target.match(/(\d+)/)) {
cycle = ~~ m[1];
}
if (m = target.match(/敵/)) {
direction = '敵';
}
else if (m = target.match(/味/)) {
direction = '味';
}
else if (m = target.match(/他/)) {
direction = '他';
}
if (target.match(/貫/)) {
actual = (direction == '敵') ? foe.kanTarget : ally.kanTarget;
type = '貫';
}
if (target.match(/列/)) {
actual = (direction == '敵') ? foe.retuTarget : ally.retuTarget;
type = '列';
}
else if (m = effect.match(/(\d+)連鎖/)) {
actual = ~~ m[1];
type = '連鎖';
}
else if (m = effect.match(/(\d+)連撃/)) {
actual = ~~ m[1];
type = '連撃';
}
else if (m = target.match(/全/)) {
type = '全';
if (direction == '敵') {
actual = foe.zenTarget;
}
else if (direction == '味') {
actual = ally.zenTarget;
}
else if (direction == '他') {
actual = -1 + foe.zenTarget + ally.zenTarget;
}
}
ws.write([
pk,
state.battleId,
state.turn,
action,
"'" + parent,
target,
effect,
type,
direction,
cycle,
actual,
cycle * actual,
"'" + skill].join('\t') + '\n');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment