Skip to content

Instantly share code, notes, and snippets.

@insideone
Created May 10, 2017 18:29
Show Gist options
  • Save insideone/4fac15190e07f04513e45c96447d4be4 to your computer and use it in GitHub Desktop.
Save insideone/4fac15190e07f04513e45c96447d4be4 to your computer and use it in GitHub Desktop.
Darkest Dungeon: получение данных предметов из Wiki и запись их в google-docs таблицу
'use strict';
Object.ksort = function(oldObject) {
var newObject = {};
Object.keys(oldObject).sort().forEach(function(key) {
newObject[key] = oldObject[key];
});
return newObject;
}
if ( typeof Object.values == 'undefined' ) {
Object.values = function(oldObject) {
var objectValues = [];
Object.keys(oldObject).forEach(function(key) {
objectValues.push(oldObject[key]);
});
return objectValues;
}
}
function alert(text)
{
var ui = SpreadsheetApp.getUi();
return ui.alert("Alert", text, ui.ButtonSet.OK);
}
function importWiki()
{
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName("Trinkets");
var data = fetchTrinketsData();
var propsListKeys = Object.keys(data.propsList);
var values = [],
notes = [];
// header
values.push([" Property →\n↓ Trinket"].concat(propsListKeys));
notes.push([""].concat(Object.values(data.propsList)));
// trinket lines
data.trinkets.forEach(function(trinket, trinketNum) {
var line = [
(trinket.className == 'All' ? '' : '['+trinket.className.substr(0, 2)+'] ') +
(trinket.isBacker ? '[!] ' : '') +
trinket.name
];
Object.keys(data.propsList).forEach(function(propName) {
line.push(propName in trinket.props ? trinket.props[propName] : "");
});
values.push(line);
});
// cleanup
sheet.getDataRange().setValue("").setNote("");
// write
sheet
.getRange(1, 1, values.length, values[0].length)
.setValues(values);
sheet
.getRange(1, 1, 1, values[0].length)
.setNotes(notes);
}
function fetchTrinketsData() {
var wrapper = ['<table class="wikitable', '</table>'];
var parseList = {
"http://darkestdungeon.gamepedia.com/Trinkets": {
// 0-based
NAME: 1,
CLASS_NAME: 3,
PROPS: 5
},
"http://darkestdungeon.gamepedia.com/Backer_trinkets": {
NAME: 1,
PROPS: 2,
CLASS_NAME: -1
}
};
var propsAlias = {
"virtue chance": "VC",
"death blow resist": "DBR",
"stress healed": "SH",
"dmg when starving": "SDMG",
"heals received": "HR",
"move resist": "MR",
"bleed resist": "BDR",
"blight resist": "BTR",
"healing skills": "HS",
"trap disarm": "TD",
"max hp": "HP",
"disease resist": "DSSR",
"chance party surprised": "CPS",
"chance monsters surprised": "CMS",
"scouting chance": "SC",
"stun resist": "SR",
"ranged skills": "RS",
"food consumed": "FC",
"resolve xp": "XP",
"debuff resist": "DBDR",
"bleed skill chance": "BDSC",
"blight skill chance": "BTSC",
"debuff skill chance": "DSC",
"move skill chance": "MSC",
"stun skill chance": "SSC",
"transformation stress": "TS"
};
var propsMods = {
"at Death\'s Door": "@D",
"while Camping": "@C",
"(Melee|Ranged) Skills": "!$1",
"(on|after) First Round": ":$1F",
"if in position (\\d)": ".$1",
"if (HP|Torch) (below|above) (\\d{2})%?": "?$1$2$3",
"vs (Eldritch|Beast|Unholy|Marked|Human|not size 1)": "+$1",
"to Party from skill Transform": "&T",
"from skill Revelation": "&R"
};
var propRegex = new RegExp('(\\+|\\-)\\s?([\\d\\.]+)%?\\s+(('+Object.keys(propsAlias).join(')|(')+')|(\\S+))', 'i');
// Список тринкетов
var trinkets = [];
// И возможных свойств
var propsList = {};
// Спарсим со спец. страниц
for(var url in parseList) {
if ( ! parseList.hasOwnProperty(url) ) {
continue;
}
var isBacker = url.indexOf('Backer') != -1;
var COLMAP = parseList[url];
var html = UrlFetchApp
.fetch(url)
.getContentText()
html = html
.substr(html.indexOf(wrapper[0]));
html = html.substr(0, html.indexOf(wrapper[1]) + wrapper[1].length);
var html = XmlService.parse(html).getRootElement();
html.getChildren('tr').forEach(function(line) {
var tds = line.getChildren('td');
if ( ! tds.length ) {
return;
}
var trinket = {
name: '',
className: 'All',
isBacker: isBacker,
props: {}
};
tds.forEach(function(col, colNum) {
var val = '';
switch(colNum) {
case COLMAP.NAME:
trinket.name = col.getText().trim();
break;
case COLMAP.PROPS:
col.getChildren('ul')[0].getChildren('li').forEach(function(prop) {
var propText = prop.getText().trim()
propRawText = propText;
var m = propText.match(propRegex);
if ( m === null ) {
alert("Wrong trinket property: `"+propText+'`; trinket: `'+trinket.name+'`');
return;
}
var propValue = (m[1] == '+' ? 1 : -1) * parseFloat(m[2]);
var propName = m[3].toLowerCase();
if ( propName in propsAlias ) {
propName = propsAlias[propName];
} else {
propName = propName.toUpperCase();
}
propText = propText.replace(m[0], '').trim();
if ( propText ) {
Object.keys(propsMods).forEach(function(modRegex) {
var m = propText.match(new RegExp(modRegex, 'i'));
if ( ! m ) {
return;
}
var postfix = propsMods[modRegex];
for(var n in m) {
if ( n == 0 ) { continue; }
var mVal = m[n];
if ( mVal == 'below' ) {
mVal = '<';
} else if ( mVal == 'above' ) {
mVal = '>';
} else if ( mVal.length > 2 ) {
mVal = mVal.substr(0, 1);
}
postfix = postfix.replace('$'+n, mVal);
}
if ( postfix.indexOf('$') != -1 ) {
alert("Postfix error: "+postfix);
return;
}
propName += postfix;
propText = propText.replace(m[0], '').trim();
});
}
if ( propText ) {
alert("Unknown mods: `"+propText+'`; trinket: `'+trinket.name+'`');
}
propsList[propName] = propRawText;
if ( propName in trinket.props ) {
trinket.props[propName] += propValue;
} else {
trinket.props[propName] = propValue;
}
});
break;
case COLMAP.CLASS_NAME:
trinket.className = col.getText().trim();
break;
}
});
trinkets.push(trinket);
});
}
return {trinkets: trinkets, propsList: Object.ksort(propsList)};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment