您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Generate additional statistical data in the dungeon and duel report pages
//----------------------------------------------------------------------------- // [WoD] Extra Statistics // Version 1.20, 2010-04-15 // Copyright (c) Fenghou, Tomy // This script can generate additional statistical data in the dungeon and duel report pages. // When you entered the details or statistics page of reports, a new button will appear beside // the details button. At the details page, the new button is "Extra Stat", which will show // the statistics of the current level when you click it. At the statistics page, the new // button is "Entire Extra Stat", which will show the statistics of entire dungeon. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // If you want to add a new Stat table, you should create a new sub class of CInfoList, // and use CStat::RegInfoList() to register your new info list. // A detailed example is CILItemDamage. //----------------------------------------------------------------------------- // ==UserScript== // @name Extra Statistics // @namespace fenghou // @version 1.20 // @description Generate additional statistical data in the dungeon and duel report pages // @include http*://*.world-of-dungeons.*/wod/spiel/*dungeon/report.php* // @include http*://*.world-of-dungeons.*/wod/spiel/tournament/*duell.php* // @include file:///*f1.htm // ==/UserScript== // COMMON FUNCTIONS /////////////////////////////////////////////////////////// function $(id) {return document.getElementById(id);} // Choose contents of the corresponding language // Contents: {Name1 : [lang1, lang2, ...], Name2 : [lang1, lang2, ...], ...} // return: Local contents, or null // It will edit the input contents directly, so the returned object is not necessary function GetLocalContents(Contents) { function GetLanguageId() { var langText = null; var allMetas = document.getElementsByTagName("meta"); for (var i = 0; i < allMetas.length; ++i) { if (allMetas[i].httpEquiv == "Content-Language") { langText = allMetas[i].content; break; } } if (langText == null) return false; switch (langText) { case "en": return 0; case "cn": return 1; default: return null; } } var nLangId = GetLanguageId(); if (nLangId == null) return null; if (Contents instanceof Object) { for (var name in Contents) Contents[name] = Contents[name][nLangId]; return Contents; } else return null; } function CompareString(a, b) { a = a || ""; b = b || ""; return a.toLowerCase().localeCompare( b.toLowerCase() ); } function CreateElementHTML(Name, Content /* , [AttrName1, AttrValue1], [AttrName2, AttrValue2], ... */) { var HTML = '<' + Name; for (var i = 2; i < arguments.length; ++i) HTML += ' ' + arguments[i][0] + '="' + arguments[i][1] + '"'; HTML += (Content != null && Content != "") ? ('>' + Content + '</' + Name + '>') : (' />'); return HTML; } function DbgMsg(Text) {if (DEBUG) alert(Text);} // EXTERN FUNCTIONS /////////////////////////////////////////////////////////// /** * A utility function for defining JavaScript classes. * * This function expects a single object as its only argument. It defines * a new JavaScript class based on the data in that object and returns the * constructor function of the new class. This function handles the repetitive * tasks of defining classes: setting up the prototype object for correct * inheritance, copying methods from other types, and so on. * * The object passed as an argument should have some or all of the * following properties: * * name: The name of the class being defined. * If specified, this value will be stored in the classname * property of the prototype object. * * extend: The constructor of the class to be extended. If omitted, * the Object( ) constructor will be used. This value will * be stored in the superclass property of the prototype object. * * construct: The constructor function for the class. If omitted, a new * empty function will be used. This value becomes the return * value of the function, and is also stored in the constructor * property of the prototype object. * * methods: An object that specifies the instance methods (and other shared * properties) for the class. The properties of this object are * copied into the prototype object of the class. If omitted, * an empty object is used instead. Properties named * "classname", "superclass", and "constructor" are reserved * and should not be used in this object. * * statics: An object that specifies the static methods (and other static * properties) for the class. The properties of this object become * properties of the constructor function. If omitted, an empty * object is used instead. * * borrows: A constructor function or array of constructor functions. * The instance methods of each of the specified classes are copied * into the prototype object of this new class so that the * new class borrows the methods of each specified class. * Constructors are processed in the order they are specified, * so the methods of a class listed at the end of the array may * overwrite the methods of those specified earlier. Note that * borrowed methods are stored in the prototype object before * the properties of the methods object above. Therefore, * methods specified in the methods object can overwrite borrowed * methods. If this property is not specified, no methods are * borrowed. * * provides: A constructor function or array of constructor functions. * After the prototype object is fully initialized, this function * verifies that the prototype includes methods whose names and * number of arguments match the instance methods defined by each * of these classes. No methods are copied; this is simply an * assertion that this class "provides" the functionality of the * specified classes. If the assertion fails, this method will * throw an exception. If no exception is thrown, any * instance of the new class can also be considered (using "duck * typing") to be an instance of these other types. If this * property is not specified, no such verification is performed. **/ function DefineClass(data) { // Extract the fields we'll use from the argument object. // Set up default values. var classname = data.name; var superclass = data.extend || Object; var constructor = data.construct || function(){}; var methods = data.methods || {}; var statics = data.statics || {}; var borrows; var provides; // Borrows may be a single constructor or an array of them. if (!data.borrows) borrows = []; else if (data.borrows instanceof Array) borrows = data.borrows; else borrows = [ data.borrows ]; // Ditto for the provides property. if (!data.provides) provides = []; else if (data.provides instanceof Array) provides = data.provides; else provides = [ data.provides ]; // Create the object that will become the prototype for our class. var proto = new superclass( ); // Delete any noninherited properties of this new prototype object. for(var p in proto) if (proto.hasOwnProperty(p)) delete proto[p]; // Borrow methods from "mixin" classes by copying to our prototype. for(var i = 0; i < borrows.length; i++) { var c = data.borrows[i]; borrows[i] = c; // Copy method properties from prototype of c to our prototype for(var p in c.prototype) { if (typeof c.prototype[p] != "function") continue; proto[p] = c.prototype[p]; } } // Copy instance methods to the prototype object // This may overwrite methods of the mixin classes for(var p in methods) proto[p] = methods[p]; // Set up the reserved "constructor", "superclass", and "classname" // properties of the prototype. proto.constructor = constructor; proto.superclass = superclass; // classname is set only if a name was actually specified. if (classname) proto.classname = classname; // Verify that our prototype provides all of the methods it is supposed to. for(var i = 0; i < provides.length; i++) { // for each class var c = provides[i]; for(var p in c.prototype) { // for each property if (typeof c.prototype[p] != "function") continue; // methods only if (p == "constructor" || p == "superclass") continue; // Check that we have a method with the same name and that // it has the same number of declared arguments. If so, move on if (p in proto && typeof proto[p] == "function" && proto[p].length == c.prototype[p].length) continue; // Otherwise, throw an exception throw new Error("Class " + classname + " does not provide method "+ c.classname + "." + p); } } // Associate the prototype object with the constructor function constructor.prototype = proto; // Copy static properties to the constructor for(var p in statics) constructor[p] = statics[p]; // Finally, return the constructor function return constructor; } /** * Throughout, whitespace is defined as one of the characters * "\t" TAB \u0009 * "\n" LF \u000A * "\r" CR \u000D * " " SPC \u0020 * * This does not use Javascript's "\s" because that includes non-breaking * spaces (and also some other characters). */ /** * Determine whether a node's text content is entirely whitespace. * * @param nod A node implementing the |CharacterData| interface (i.e., * a |Text|, |Comment|, or |CDATASection| node * @return True if all of the text content of |nod| is whitespace, * otherwise false. */ function is_all_ws( nod ) { // Use ECMA-262 Edition 3 String and RegExp features return !(/[^\t\n\r ]/.test(nod.data)); } /** * Determine if a node should be ignored by the iterator functions. * * @param nod An object implementing the DOM1 |Node| interface. * @return true if the node is: * 1) A |Text| node that is all whitespace * 2) A |Comment| node * and otherwise false. */ function is_ignorable( nod ) { return ( nod.nodeType == 8) || // A comment node ( (nod.nodeType == 3) && is_all_ws(nod) ); // a text node, all ws } /** * Version of |previousSibling| that skips nodes that are entirely * whitespace or comments. (Normally |previousSibling| is a property * of all DOM nodes that gives the sibling node, the node that is * a child of the same parent, that occurs immediately before the * reference node.) * * @param sib The reference node. * @return Either: * 1) The closest previous sibling to |sib| that is not * ignorable according to |is_ignorable|, or * 2) null if no such node exists. */ function node_before( sib ) { while ((sib = sib.previousSibling)) { if (!is_ignorable(sib)) return sib; } return null; } /** * Version of |nextSibling| that skips nodes that are entirely * whitespace or comments. * * @param sib The reference node. * @return Either: * 1) The closest next sibling to |sib| that is not * ignorable according to |is_ignorable|, or * 2) null if no such node exists. */ function node_after( sib ) { while ((sib = sib.nextSibling)) { if (!is_ignorable(sib)) return sib; } return null; } /** * Version of |lastChild| that skips nodes that are entirely * whitespace or comments. (Normally |lastChild| is a property * of all DOM nodes that gives the last of the nodes contained * directly in the reference node.) * * @param par The reference node. * @return Either: * 1) The last child of |sib| that is not * ignorable according to |is_ignorable|, or * 2) null if no such node exists. */ function last_child( par ) { var res=par.lastChild; while (res) { if (!is_ignorable(res)) return res; res = res.previousSibling; } return null; } /** * Version of |firstChild| that skips nodes that are entirely * whitespace and comments. * * @param par The reference node. * @return Either: * 1) The first child of |sib| that is not * ignorable according to |is_ignorable|, or * 2) null if no such node exists. */ function first_child( par ) { var res=par.firstChild; while (res) { if (!is_ignorable(res)) return res; res = res.nextSibling; } return null; } /** * Version of |data| that doesn't include whitespace at the beginning * and end and normalizes all whitespace to a single space. (Normally * |data| is a property of text nodes that gives the text of the node.) * * @param txt The text node whose data should be returned * @return A string giving the contents of the text node with * whitespace collapsed. */ function data_of( txt ) { var data = txt.data; // Use ECMA-262 Edition 3 String and RegExp features data = data.replace(/[\t\n\r ]+/g, " "); if (data.charAt(0) == " ") data = data.substring(1, data.length); if (data.charAt(data.length - 1) == " ") data = data.substring(0, data.length - 1); return data; } // CLASSES //////////////////////////////////////////////////////////////////// // NextNode: the node next to the statistics node when it is created function CStat(NextNode) { var NewSection = document.createElement("div"); NewSection.id = "stat_all"; this._Node = NextNode.parentNode.insertBefore(NewSection, NextNode); this._HTML = ''; this._gInfoList = []; this.nTotalPages = 0; this.nReadPages = 0; } CStat.prototype._Write = function(Text) {this._HTML += Text;}; CStat.prototype._Flush = function() {this._Node.innerHTML = this._HTML;}; CStat.prototype.RegInfoList = function(InfoList) { if (InfoList instanceof CInfoList) { this._gInfoList.push(InfoList); return true; } return false; }; CStat.prototype.SaveInfo = function(Info) { for (var i = 0; i < this._gInfoList.length; ++i) this._gInfoList[i].SaveInfo(Info); }; CStat.prototype.Show = function() { this._Write("<hr />"); for (var i = 0; i < this._gInfoList.length; ++i) this._Write( this._gInfoList[i].Show() ); this._Write(this._OptionsHTML()); this._Write("<hr />"); this._Flush(); for (var i = 0; i < this._gInfoList.length; ++i) this._gInfoList[i].AddEvents(); this._AddEvents(); }; CStat.prototype.ShowProgress = function() { this._Node.innerHTML = '<hr /><h1>' + Local.Text_Loading + ' (' + this.nReadPages + '/' + this.nTotalPages + ') ...</h1><hr />'; }; CStat.prototype._OptionsHTML = function() { var Str = '<div id="stat_options">' + '<div class="stat_header"><span class="stat_title">' + Local.Text_Options + '</span>'; Str += CreateElementHTML("input", null, ["type", "button"], ["class", "button"], ["id", "stat_options_default"], ["value", Local.Text_Button_Default]); Str += '</div></div>'; return Str; }; CStat.prototype._AddEvents = function() { function OnDelGMValues() { try { var ValueList = GM_listValues(); for (var name in ValueList) {GM_deleteValue(ValueList[name]);} alert(Local.Text_DefaultMsg); } catch (e) {alert("OnDelGMValues(): " + e);} } $("stat_options_default").addEventListener("click", OnDelGMValues, false); }; /////////////////////////////////////////////////////////////////////////////// function CTable(Title, Id, nColumns) { this._Title = Title; this._Id = Id; this._nColumns = nColumns; this._HeadCellContents = new Array(nColumns); this._BodyCellContentTypes = new Array(nColumns); this._BodyCellContents = []; this._HTML = ''; this._bShow = GM_getValue(Id, true); } CTable._ContentAttrs = { string : '', number : 'align="right"', button : 'align="center"'}; CTable.prototype.SetHeadCellContents = function(/* Content1, Content2, ... */) { for (var i = 0; i < this._nColumns; ++i) this._HeadCellContents[i] = arguments[i] != null ? arguments[i] : ""; }; // Type: a string that is the property name of CTable::ContentAttrs CTable.prototype.SetBodyCellContentTypes = function(/* Type1, Type2, ... */) { for (var i = 0; i < this._nColumns; ++i) this._BodyCellContentTypes[i] = arguments[i] != null ? CTable._ContentAttrs[arguments[i]] : ""; }; CTable.prototype.SetBodyCellContents = function(/* Content1, Content2, ... */) { var Contents = new Array(this._nColumns); for (var i = 0; i < this._nColumns; ++i) Contents[i] = arguments[i] != null ? arguments[i] : ""; this._BodyCellContents.push(Contents); }; CTable.prototype.CreateHTML = function() { this._HTML = '<div id="' + this._Id + '">' + '<div class="stat_header"><span class="stat_title clickable">' + this._Title + '</span></div>' + '<table class="content_table" ' + (this._bShow ? '' : 'hide="hide"') + '>' + '<tr class="content_table_header">'; for (var i = 0; i < this._nColumns; ++i) this._HTML += '<th class="content_table">' + this._HeadCellContents[i] + '</th>'; this._HTML += '</tr>'; for (var i = 0; i < this._BodyCellContents.length; ++i) { this._HTML += '<tr class="content_table_row_' + i % 2 + '">'; for (var j = 0; j < this._nColumns; ++j) { this._HTML += '<td class="content_table" ' + this._BodyCellContentTypes[j] + '>' + this._BodyCellContents[i][j] + '</td>'; } this._HTML += '</tr>'; } this._HTML += '</table></div>'; return this._HTML; }; CTable.prototype.GetHTML = function() {return this._HTML;}; CTable.prototype.AddEvents = function() { var Title = $(this._Id).getElementsByTagName("span")[0]; function Factory(Id) {return function(){CTable.OnClickTitle(Id);};} Title.addEventListener("click", Factory(this._Id), false); }; CTable.OnClickTitle = function(Id) { try { var Table = $(Id).getElementsByTagName("table")[0]; if (Table.hasAttribute("hide")) { Table.removeAttribute("hide"); GM_setValue(Id, true); } else { Table.setAttribute("hide", "hide"); GM_setValue(Id, false); } } catch (e) {alert("CTable.OnClickTitle(): " + e);} }; /////////////////////////////////////////////////////////////////////////////// function CActiveInfo() { this.nIniRoll; this.nCurrAction; this.nTotalActions; this.Char = new CChar(); this.nCharId; this.ActionType = new CActionType(); this.Skill = new CSkill(); this.nAttackRoll; this.nSkillMP; this.gItem = new CKeyList(); } function CPassiveInfo() { this.Char = new CChar(); this.nCharId; this.Skill = new CSkill(); this.nDefenceRoll; this.nSkillMP; this.gItem = new CKeyList(); this.HitType = new CHitType(); this.bStruckDown; this.gDamage = []; this.DamagedItem = new CItem(); this.nItemDamage; this.nHealedHP; this.nHealedMP; } function CNavi(nLevel, nRoom, nRound, nRow) { this.nLevel = nLevel; this.nRoom = nRoom; this.nRound = nRound; this.nRow = nRow; } function CActionInfo(Navi) { this.Navi = Navi; this.Active = new CActiveInfo(); this.gPassive = []; } /////////////////////////////////////////////////////////////////////////////// // Class: Key // Every key should have two function properties: compareTo() and toString(), // and can work without initialization parameters var CKey = DefineClass({ methods: { compareTo: function(that) {return this - that;}, toString: function() {return "";} } }); var CKeyList = DefineClass({ extend: CKey, construct: function() {this._gKey = [];}, methods: { push: function(Key) {return this._gKey.push(Key);}, compareTo: function(that) { var result = this._gKey.length - that._gKey.length; if (result !== 0) return result; var i = 0; while (i < this._gKey.length && this._gKey[i].compareTo(that._gKey[i]) === 0) ++i; if (i === this._gKey.length) return 0; else return this._gKey[i].compareTo(that._gKey[i]); }, toString: function() {return this._gKey.join(", ");} } }); var CChar = DefineClass({ extend: CKey, construct: function(HTMLElement) { this._Name; this._Href; this._OnClick; this._Class; this._nType; if (HTMLElement != null) { this._Name = HTMLElement.firstChild.data; this._Href = HTMLElement.getAttribute("href"); this._OnClick = HTMLElement.getAttribute("onclick"); this._Class = HTMLElement.className; this._nType = CChar._GetCharType(this._Class); if (this._nType === null) DbgMsg("CChar(): Unknown type: " + this._Class); } }, methods: { GetType: function() {return this._nType;}, compareTo: function(that) { var result = this._nType - that._nType; if (result !== 0) return result; return CompareString(this._Name, that._Name); }, toString: function() { if (this._Name != null) return CreateElementHTML("a", this._Name, ["href", this._Href], ["onclick", this._OnClick], ["class", this._Class]); else return ""; } }, statics: { _GetCharType: function(Class) { switch (Class) { case "rep_hero": case "rep_myhero": return 0; case "rep_monster": case "rep_myhero_defender": return 1; default: return null; } } } }); // In-round actions var CActionType = DefineClass({ extend: CKey, construct: function(ActionText) { this._nType; this._nKind; if (ActionText != null) { var ret = CActionType._GetActionTypeAndKind(ActionText); this._nType = ret[0]; this._nKind = ret[1]; } }, methods: { GetType: function() {return this._nType;}, GetKind: function() {return this._nKind;}, compareTo: function(that) {return this._nType - that._nType;}, toString: function() { switch (this._nKind) { case 0: return Local.TextList_AttackType[this._nType]; case 1: return "heal"; case 2: return "buff"; case 3: return "wait"; default: return "unknown"; } } }, statics: { // return: an array, [0]: _nType, [1]: _nKind _GetActionTypeAndKind: function(ActionText) { switch (ActionText) { case Local.OrigTextList_ActionType[0]: // melee return [0, 0]; case Local.OrigTextList_ActionType[1]: // ranged return [1, 0]; case Local.OrigTextList_ActionType[2]: // magic return [2, 0]; case Local.OrigTextList_ActionType[3]: // social return [3, 0]; case Local.OrigTextList_ActionType[4]: // ambush return [4, 0]; case Local.OrigTextList_ActionType[5]: // trap return [5, 0]; case Local.OrigTextList_ActionType[6]: // nature return [6, 0]; case Local.OrigTextList_ActionType[7]: // disease return [7, 0]; case Local.OrigTextList_ActionType[8]: // detonate return [8, 0]; case Local.OrigTextList_ActionType[9]: // disarm trap return [9, 0]; case Local.OrigTextList_ActionType[10]: // magic projectile return [10, 0]; case Local.OrigTextList_ActionType[11]: // curse return [11, 0]; case Local.OrigTextList_ActionType[12]: // scare return [12, 0]; case Local.OrigTextList_ActionType[13]: // heal return [13, 1]; case Local.OrigTextList_ActionType[14]: // buff return [14, 2]; case Local.OrigTextList_ActionType[15]: // summon return [15, 2]; case Local.OrigTextList_ActionType[16]: // do nothing return [16, 3]; case Local.OrigTextList_ActionType[17]: // wait return [17, 3]; default: return [null, null]; } } } }); var CSkill = DefineClass({ extend: CKey, construct: function(HTMLElement) { this._Name; this._Href; this._OnClick; if (HTMLElement != null) { this._Name = HTMLElement.firstChild.data; this._Href = HTMLElement.getAttribute("href"); this._OnClick = HTMLElement.getAttribute("onclick"); } }, methods: { compareTo: function(that) {return CompareString(this._Name, that._Name);}, toString: function() { if (this._Name != null) return CreateElementHTML("a", this._Name, ["href", this._Href], ["onclick", this._OnClick]); else return ""; } } }); var CItem = DefineClass({ extend: CKey, construct: function(HTMLElement) { this._Name; this._Href; this._OnClick; this._Class; if (HTMLElement != null) { this._Name = HTMLElement.firstChild.data; this._Href = HTMLElement.getAttribute("href"); this._OnClick = HTMLElement.getAttribute("onclick"); this._Class = HTMLElement.className; } }, methods: { compareTo: function(that) {return CompareString(this._Name, that._Name);}, toString: function() { if (this._Name != null) return CreateElementHTML("a", this._Name, ["href", this._Href], ["onclick", this._OnClick], ["class", this._Class]); else return ""; } } }); var CHitType = DefineClass({ extend: CKey, construct: function(HitClassText) { this._nType; if (HitClassText != null) { this._nType = CHitType._GetHitType(HitClassText); if (this._nType === null) DbgMsg("CHitType(): Unknown type: " + HitClassText); } }, methods: { GetType: function() {return this._nType;}, compareTo: function(that) {return this._nType - that._nType;}, toString: function() { if (this._nType != null) return Local.TextList_HitType[this._nType]; else return ""; } }, statics: { _GetHitType: function(Class) { switch (Class) { case "rep_miss": return 0; case "rep_hit": return 1; case "rep_hit_good": return 2; case "rep_hit_crit": return 3; default: return null; } } } }); var CDamage = DefineClass({ extend: CKey, construct: function(HTMLElement) { this._nBasicDmg; this._nActualDmg; this._nArmor; this._nType; if (HTMLElement != null) { var Str; if (HTMLElement.nodeType != 3) { Str = HTMLElement.getAttribute("onmouseover"); // \1 basic damage var Patt_BasicDamage = Local.Pattern_BasicDamage; var result = Patt_BasicDamage.exec(Str); if (result == null) throw "CDamage() :" + Str; this._nBasicDmg = Number(result[1]); Str = HTMLElement.firstChild.data; } else Str = HTMLElement.data; // \1 actual damage // \2 armor // \3 damage type var Patt_Damage = Local.Pattern_Damage; var result = Patt_Damage.exec(Str); if (result == null) throw "CDamage() :" + Str; this._nActualDmg = Number(result[1]); this._nArmor = result[2] != null ? Number(result[2]) : 0; this._nType = CDamage._GetDamageType(result[3]); if (this._nType === null) DbgMsg("CDamage(): Unknown type: " + result[3]); if (this._nBasicDmg == null) this._nBasicDmg = this._nActualDmg + this._nArmor; } }, methods: { GetType: function() {return this._nType;}, GetBasicDmg: function() {return this._nBasicDmg;}, GetArmor: function() {return this._nArmor;}, GetActualDmg: function() {return this._nActualDmg;}, IsHPDamage: function() {return this._nType !== 11;}, compareTo: function(that) {return this._nBasicDmg - that._nBasicDmg;}, toString: function() { if (this._nType != null) { var Str = String(this._nBasicDmg); if (this._nArmor > 0) Str += " - " + this._nArmor + " -> " + this._nActualDmg; else if (this._nBasicDmg !== this._nActualDmg) Str += " -> " + this._nActualDmg; Str += " " + Local.OrigTextList_DamageType[this._nType] + " damage"; return Str; } else return ""; } }, statics: { _GetDamageType: function(Text) { switch (Text) { case Local.OrigTextList_DamageType[0]: // crushing return 0; case Local.OrigTextList_DamageType[1]: // cutting return 1; case Local.OrigTextList_DamageType[2]: // piercing return 2; case Local.OrigTextList_DamageType[3]: // fire return 3; case Local.OrigTextList_DamageType[4]: // ice return 4; case Local.OrigTextList_DamageType[5]: // lightning return 5; case Local.OrigTextList_DamageType[6]: // poison return 6; case Local.OrigTextList_DamageType[7]: // acid return 7; case Local.OrigTextList_DamageType[8]: // psychological return 8; case Local.OrigTextList_DamageType[9]: // holy return 9; case Local.OrigTextList_DamageType[10]: // disarm trap return 10; case Local.OrigTextList_DamageType[11]: // mana return 11; default: return null; } } } }); /////////////////////////////////////////////////////////////////////////////// // Class: Value list // Value list is a special key, it can contains any type of values, including keys var CValueList = DefineClass({ extend: CKey, construct: function() { this._gValue = []; this._nAvgValue; // unsure type }, methods: { GetLength: function() {return this._gValue.length;}, Calculate: function() {}, push: function(Value) {return this._gValue.push(Value);}, compareTo: function(that) {return this._nAvgValue - that._nAvgValue;}, AvgValueStr: function() {return String(this._nAvgValue);}, toString: function() {return this._gValue.join(", ");} } }); var CVLNumber = DefineClass({ extend: CValueList, construct: function() {this.superclass();}, methods: { Calculate: function() { var nTotalValue = 0; for (var i = 0; i < this._gValue.length; ++i) nTotalValue += this._gValue[i]; this._nAvgValue = Number((nTotalValue / this._gValue.length).toFixed(2)); } } }); // value: [Number1, Number2] var CVLPairNumber = DefineClass({ extend: CValueList, construct: function() {this.superclass();}, methods: { Calculate: function() { var nTotalValue = [0, 0]; for (var i = 0; i < this._gValue.length; ++i) { nTotalValue[0] += this._gValue[i][0]; nTotalValue[1] += this._gValue[i][1]; } this._nAvgValue = new Array(2); this._nAvgValue[0] = Number((nTotalValue[0] / this._gValue.length).toFixed(2)); this._nAvgValue[1] = Number((nTotalValue[1] / this._gValue.length).toFixed(2)); }, compareTo: function(that) { if (this._nAvgValue[0] !== 0 || that._nAvgValue[0] !== 0) return this._nAvgValue[0] - that._nAvgValue[0]; else return this._nAvgValue[1] - that._nAvgValue[1]; }, AvgValueStr: function() { return '<table class="pair_value"><tr><td>' + ((this._nAvgValue[0] !== 0) ? String(this._nAvgValue[0]) : '') + '</td><td>' + ((this._nAvgValue[1] !== 0) ? String(this._nAvgValue[1]) : '') + '</td></tr></table>'; }, toString: function() { var Str = ""; for (var i = 0; i < this._gValue.length; ++i) { Str += (this._gValue[i][0] != null) ? this._gValue[i][0] : 0; Str += "/"; Str += (this._gValue[i][1] != null) ? this._gValue[i][1] : 0; if (i < this._gValue.length -1) Str += ", "; } return Str; } } }); // value: An Array of CDamage var CVLDamage = DefineClass({ extend: CValueList, construct: function() {this.superclass();}, methods: { Calculate: function() { var nTotalValue = [0, 0]; for (var i = 0; i < this._gValue.length; ++i) { for (var j = 0; j < this._gValue[i].length; ++j) { if (this._gValue[i][j].IsHPDamage()) { nTotalValue[0] += this._gValue[i][j].GetBasicDmg(); nTotalValue[1] += this._gValue[i][j].GetActualDmg(); } } } this._nAvgValue = new Array(2); this._nAvgValue[0] = Number((nTotalValue[0] / this._gValue.length).toFixed(2)); this._nAvgValue[1] = Number((nTotalValue[1] / this._gValue.length).toFixed(2)); }, compareTo: function(that) {return this._nAvgValue[1] - that._nAvgValue[1];}, AvgValueStr: function() { return '<table class="pair_value"><tr><td>' + this._nAvgValue[1] + '</td><td>' + this._nAvgValue[0] + '</td></tr></table>'; }, toString: function() { var Str = ""; for (var i = 0; i < this._gValue.length; ++i) { var nTotalValue = [0, 0]; for (var j = 0; j < this._gValue[i].length; ++j) { if (this._gValue[i][j].IsHPDamage()) { nTotalValue[0] += this._gValue[i][j].GetBasicDmg(); nTotalValue[1] += this._gValue[i][j].GetActualDmg(); } } Str += nTotalValue[1] + "/" + nTotalValue[0]; if (i < this._gValue.length -1) Str += ", "; } return Str; } } }); /////////////////////////////////////////////////////////////////////////////// // Class: Info list var CInfoList = DefineClass({ construct: function(nKeys, CValueList) { this._gInfo = []; this._nKeys = nKeys; this._CValueList = CValueList; this._Table = null; }, methods: { _CompareKeys: function(gKeyA, gKeyB) { for (var i = 0; i < this._nKeys; ++i) { var result = gKeyA[i].compareTo( gKeyB[i] ); if (result !== 0) return result; } return 0; }, _SetTableBodyCellContents: function() { for (var i = 0; i < this._gInfo.length; ++i) { var gBodyCellContent = []; for (var j = 0; j < this._gInfo[i].gKey.length; ++j) gBodyCellContent.push( this._gInfo[i].gKey[j] ); gBodyCellContent.push( this._gInfo[i].ValueList.AvgValueStr(), String(this._gInfo[i].ValueList.GetLength()), CreateElementHTML("input", null, ["type", "button"], ["class", "button"], ["value", Local.Text_Button_Show], ["onclick", 'alert("' + this._gInfo[i].ValueList.toString() + '");']) ); this._Table.SetBodyCellContents.apply(this._Table, gBodyCellContent); } }, SaveInfo: function(Info) {}, Show: function() {}, // Call this function when read all data, and before sort and export data CalculateValue: function() { for (var i = 0; i < this._gInfo.length; ++i) this._gInfo[i].ValueList.Calculate(); }, CreateTable: function(Title, Id, gKeyName) { // Key1, Key2, ..., AverageValue, Times, ValueList this._Table = new CTable(Title, Id, this._nKeys + 3); var gHeadCellContent = new Array(this._nKeys + 3); for (var i = 0; i < this._nKeys; ++i) gHeadCellContent[i] = gKeyName[i]; gHeadCellContent[this._nKeys] = Local.Text_Table_AvgRoll; gHeadCellContent[this._nKeys + 1] = Local.Text_Table_Times; gHeadCellContent[this._nKeys + 2] = Local.Text_Table_RollList; this._Table.SetHeadCellContents.apply(this._Table, gHeadCellContent); var gBodyCellContentType = new Array(this._nKeys + 3); for (var i = 0; i < this._nKeys; ++i) gBodyCellContentType[i] = "string"; gBodyCellContentType[this._nKeys] = "number"; gBodyCellContentType[this._nKeys + 1] = "number"; gBodyCellContentType[this._nKeys + 2] = "button"; this._Table.SetBodyCellContentTypes.apply(this._Table, gBodyCellContentType); this._SetTableBodyCellContents(); return this._Table.CreateHTML(); }, // Call this function when edited the info list (for example, re-sorted it) ReCreateTableHTML: function() { this._SetTableBodyCellContents(); return this._Table.CreateHTML(); }, GetTableHTML: function() {return this._Table.GetHTML();}, AddEvents: function() {if (this._Table != null) this._Table.AddEvents();}, push: function(gKey, Value) { for (var i = 0; i < this._gInfo.length; ++i) { if (this._CompareKeys(this._gInfo[i].gKey, gKey) === 0) { this._gInfo[i].ValueList.push(Value); return this._gInfo.length; } } var ValueList = new this._CValueList(); ValueList.push(Value); return this._gInfo.push(new CInfoList._CInfo(gKey, ValueList)); }, sort: function(gSortKeyId) { function Factory(gId) {return function(A, B){return CInfoList._CompareInfo(A, B, gId);};} return this._gInfo.sort(Factory(gSortKeyId)); } }, statics: { _CInfo: function(gKey, ValueList) { this.gKey = gKey; this.ValueList = ValueList; }, // SortKeyId: Id of keys, or null // The list will be sorted in this way: sort them by the first key, if there are // elements are still equal, then sort them by the second key, and so on. // If SortKeyId is null, then sort the list by value // If gSortKeyId is null, then sort the list by default order of keys _CompareInfo: function(InfoA, InfoB, gSortKeyId) { if (gSortKeyId == null) { for (var i = 0; i < InfoA.gKey.length; ++i) { var result = InfoA.gKey[i].compareTo(InfoB.gKey[i]); if (result !== 0) return result; } return 0; } else { for (var i = 0; i < gSortKeyId.length; ++i) { var KeyId = gSortKeyId[i]; var result = (KeyId != null) ? InfoA.gKey[KeyId].compareTo(InfoB.gKey[KeyId]) : InfoA.ValueList.compareTo(InfoB.ValueList); if (result !== 0) return result; } return 0; } } } }); /////////////////////////////////////////////////////////////////////////////// // Sub classes of CInfoList // // var CIL_ = DefineClass({ // extend: CInfoList, // construct: function(_nKeys, CValueList) {this.superclass(_nKeys, CValueList);}, // methods: // { // _SetTableBodyCellContents: function() {}, // SaveInfo: function(Info) {}, // Show: function() {}, // CreateTable: function(Title, Id, gKeyName) {} // } // }); var CILIni = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { SaveInfo: function(Info) { if (Info.Active.nCurrAction === 1) this.push([Info.Active.Char], Info.Active.nIniRoll); }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable(Local.Text_Table_Ini, "stat_ini", [Local.Text_Table_Char]); } return ""; } } }); var CILAttackRoll = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { SaveInfo: function(Info) { if (Info.Active.ActionType.GetKind() === 0 && Info.Active.nAttackRoll != null) this.push([Info.Active.Char, Info.Active.ActionType, Info.Active.Skill, Info.Active.gItem], Info.Active.nAttackRoll); }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable( Local.Text_Table_Attack, "stat_attack", [Local.Text_Table_Char, Local.Text_Table_AttackType, Local.Text_Table_Skill, Local.Text_Table_Item]); } return ""; } } }); var CILDefenceRoll = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { SaveInfo: function(Info) { if (Info.Active.ActionType.GetKind() === 0) { for (var i = 0; i < Info.gPassive.length; ++i) { if (Info.gPassive[i].nDefenceRoll != null) this.push([Info.gPassive[i].Char, Info.Active.ActionType, Info.gPassive[i].Skill, Info.gPassive[i].gItem], Info.gPassive[i].nDefenceRoll); } } }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable(Local.Text_Table_Defence, "stat_defence", [Local.Text_Table_Char, Local.Text_Table_DefenceType, Local.Text_Table_Skill, Local.Text_Table_Item]); } return ""; } } }); var CILDamage = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { SaveInfo: function(Info) { if (Info.Active.ActionType.GetKind() === 0) { for (var i = 0; i < Info.gPassive.length; ++i) { if (Info.gPassive[i].gDamage.length > 0) this.push([Info.Active.Char, Info.Active.ActionType, Info.Active.Skill, Info.Active.gItem], Info.gPassive[i].gDamage); } } }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable(Local.Text_Table_Damage, "stat_damage", [Local.Text_Table_Char, Local.Text_Table_AttackType, Local.Text_Table_Skill, Local.Text_Table_Item]); } return ""; } } }); var CILHeal = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { SaveInfo: function(Info) { if (Info.Active.ActionType.GetKind() === 1) { for (var i = 0; i < Info.gPassive.length; ++i) { if (Info.gPassive[i].nHealedHP != null || Info.gPassive[i].nHealedMP != null) this.push([Info.Active.Char, Info.Active.Skill, Info.Active.gItem], [Info.gPassive[i].nHealedHP, Info.gPassive[i].nHealedMP]); } } }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable(Local.Text_Table_Heal, "stat_heal", [Local.Text_Table_Char, Local.Text_Table_Skill, Local.Text_Table_Item]); } return ""; } } }); var CILHealed = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { SaveInfo: function(Info) { if (Info.Active.ActionType.GetKind() === 1) { for (var i = 0; i < Info.gPassive.length; ++i) { if (Info.gPassive[i].nHealedHP != null || Info.gPassive[i].nHealedMP != null) this.push([Info.gPassive[i].Char], [Info.gPassive[i].nHealedHP, Info.gPassive[i].nHealedMP]); } } }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable(Local.Text_Table_Healed, "stat_healed", [Local.Text_Table_Char]); } return ""; } } }); var CILItemDamage = DefineClass({ extend: CInfoList, construct: function(nKeys, CValueList) {this.superclass(nKeys, CValueList);}, methods: { _SetTableBodyCellContents: function() { for (var i = 0; i < this._gInfo.length; ++i) { var gBodyCellContent = []; for (var j = 0; j < this._gInfo[i].gKey.length; ++j) gBodyCellContent.push( this._gInfo[i].gKey[j] ); gBodyCellContent.push( this._gInfo[i].ValueList.AvgValueStr()); this._Table.SetBodyCellContents.apply(this._Table, gBodyCellContent); } }, SaveInfo: function(Info) { if (Info.Active.ActionType.GetKind() === 0) { for (var i = 0; i < Info.gPassive.length; ++i) { if (Info.gPassive[i].nItemDamage != null) this.push([Info.gPassive[i].Char, Info.gPassive[i].DamagedItem], Info.gPassive[i].nItemDamage); } } }, Show: function() { if (this._gInfo.length > 0) { this.CalculateValue(); this.sort(); return this.CreateTable(Local.Text_Table_DamagedItems, "stat_item_damage", [Local.Text_Table_Char, Local.Text_Table_Item]); } return ""; }, CreateTable: function(Title, Id, gKeyName) { // Key1, Key2, ..., nDamage this._Table = new CTable(Title, Id, this._nKeys + 1); var gHeadCellContent = new Array(this._nKeys + 1); for (var i = 0; i < this._nKeys; ++i) gHeadCellContent[i] = gKeyName[i]; gHeadCellContent[this._nKeys] = Local.Text_Table_ItemDamagePoints; this._Table.SetHeadCellContents.apply(this._Table, gHeadCellContent); var gBodyCellContentType = new Array(this._nKeys + 1); for (var i = 0; i < this._nKeys; ++i) gBodyCellContentType[i] = "string"; gBodyCellContentType[this._nKeys] = "number"; this._Table.SetBodyCellContentTypes.apply(this._Table, gBodyCellContentType); this._SetTableBodyCellContents(); return this._Table.CreateHTML(); }, push: function(gKey, Value) { var ValueList = new this._CValueList(); ValueList.push(Value); return this._gInfo.push(new CInfoList._CInfo(gKey, ValueList)); } } }); // FUNCTIONS ////////////////////////////////////////////////////////////////// function CountStat(Document, bLastSubPage) { // Read the last round only when reading the last sub page if (!bLastSubPage) RemoveLastRound(Document); var Navi = new CNavi(0, 0, 0, 0); var allRows = Document.getElementsByTagName("tr"); for (var i = 0; i < allRows.length; ++i) { var Info = new CActionInfo(Navi); var IniColumn = first_child(allRows[i]); GetIniInfo(IniColumn, Info); if (Info.Active.nIniRoll == null) // not a initiative column continue; ++Info.Navi.nRow; var ActiveColumn = node_after(IniColumn); GetActiveInfo(ActiveColumn, Info); switch (Info.Active.ActionType.GetKind()) { case 0: // Attack { var PassiveColumn = node_after(ActiveColumn); GetAttackedInfo(PassiveColumn, Info); break; } case 1: // Heal case 2: // Buff { var PassiveColumn = node_after(ActiveColumn); GetHealedBuffedInfo(PassiveColumn, Info); break; } case 3: // Wait default: // Unknown ; } Stat.SaveInfo(Info); } } function RemoveLastRound(Document) { var allRows = Document.getElementsByTagName("tr"); for (var i = 0; i < allRows.length; ++i) { if (allRows[i].className != null && allRows[i].className.indexOf("content_table_row_") === 0) { var allH1 = allRows[i].getElementsByTagName("h1"); if (allH1[0] != null && allH1[0].firstChild != null && allH1[0].firstChild.nodeType == 3 && allH1[0].firstChild.data == Local.OrigText_LastRound) { allRows[i].parentNode.removeChild(allRows[i]); break; } } } } // return: true: it's not a initiative column, or it's a initiative column and the format is right // false: it's a initiative column but the format is wrong function GetIniInfo(Node, Info) { if (Node == null || Node.className != "rep_initiative") return true; if (Node.innerHTML == " ") return true; // \1 ini // \2 current action // \3 total actions var Patt_Ini = Local.Pattern_Ini; var result = Patt_Ini.exec(Node.innerHTML); if (result == null) { DbgMsgAction(Info, "IniInfo: " + Node.innerHTML); return false; } Info.Active.nIniRoll = Number(result[1]); Info.Active.nCurrAction = Number(result[2]); Info.Active.nTotalActions = Number(result[3]); return true; } // return: whether the format is right function GetActiveInfo(Node, Info) { if (Node == null) { DbgMsgAction(Info, "ActiveInfo: null"); return false; } var nStartNode = 0; var Str = Node.innerHTML; // \1 span node // \2 npc Id var Patt_Char = Local.Pattern_Active_Char; var result = Patt_Char.exec(Str); if (result == null) { DbgMsgAction(Info, "ActiveInfo (Char): " + Node.innerHTML); return true; } var CharNode = result[1] != null ? Node.childNodes[nStartNode].childNodes[0] : Node.childNodes[nStartNode]; Info.Active.Char = new CChar(CharNode); Info.Active.nCharId = result[2] != null ? Number(result[2]) : null; nStartNode += result[1] != null ? 1 : (result[2] != null ? 2 : 1); Str = Str.substring(result[0].length); // \1 attack // \2 heal or buff // \3 left parenthesis var Patt_Action1 = Local.Pattern_Active_Action1; result = Patt_Action1.exec(Str); if (result == null) { // \1 other action var Patt_Action2 = Local.Pattern_Active_Action2; result = Patt_Action2.exec(Str); if (result == null) { DbgMsgAction(Info, "ActiveInfo (Action2): " + Node.innerHTML); return false; } Info.Active.ActionType = new CActionType(result[1]); return true; } if (result[1] != null) { Info.Active.ActionType = new CActionType(result[1]); if (Info.Active.ActionType.GetKind() !== 0) { DbgMsgAction(Info, "ActiveInfo (Attack Type): " + result[1]); return false; } nStartNode += 1; Str = Str.substring(result[0].length); } else { Info.Active.ActionType = new CActionType(result[2]); if (Info.Active.ActionType.GetKind() !== 1 && Info.Active.ActionType.GetKind() !== 2) { DbgMsgAction(Info, "ActiveInfo (Heal/Buff Type): " + result[2]); return false; } Info.Active.Skill = new CSkill(Node.childNodes[nStartNode + 1]); if (result[3] == null) return true; nStartNode += 3; Str = Str.substring(result[0].length); } switch (Info.Active.ActionType.GetKind()) { case 0: // attack { // \1 single roll // \2 position n // \3 multiple roll n // \4 MP // \5 item list var Patt_ActtackDetails = Local.Pattern_Active_AttackDetails; result = Patt_ActtackDetails.exec(Str); if (result == null) { DbgMsgAction(Info, "ActiveInfo (ActtackDetails): " + Node.innerHTML); return false; } Info.Active.Skill = new CSkill(Node.childNodes[nStartNode]); Info.Active.nAttackRoll = Number(result[1] != null ? result[1] : result[3]); Info.Active.nSkillMP = result[4] != null ? Number(result[4]) : null; if (result[5] != null) { Info.Active.gItem = new CKeyList(); nStartNode += result[4] != null ? 4 : 2; var ItemNode; while ((ItemNode = Node.childNodes[nStartNode]) != null) { Info.Active.gItem.push(new CItem(ItemNode)); nStartNode += 2; } } return true; } case 1: // heal case 2: // buff { // \1 MP // \2 normal item list // \3 magical potion var Patt_HealBuffDetails = Local.Pattern_Active_HealBuffDetails; result = Patt_HealBuffDetails.exec(Str); if (result == null) { DbgMsgAction(Info, "ActiveInfo (HealBuffDetails): " + Node.innerHTML); return false; } Info.Active.nSkillMP = result[1] != null ? Number(result[1]) : null; if (result[2] != null) { Info.Active.gItem = new CKeyList(); nStartNode += result[1] != null ? 2 : 0; var ItemNode; while ((ItemNode = Node.childNodes[nStartNode]) != null) { Info.Active.gItem.push(new CItem(ItemNode)); nStartNode += 2; } } else if (result[3] != null) { Info.Active.gItem = new CKeyList(); nStartNode += result[1] != null ? 2 : 0; Info.Active.gItem.push(new CItem(Node.childNodes[nStartNode])); // nStartNode: determine by the number of reagents } return true; } default:// impossible, the value can only be 0, 1, or 2 return false; } } // return: whether the format is right function GetAttackedInfo(Node, Info) { if (Node == null) { DbgMsgAction(Info, "AttackedInfo: null"); return false; } var nStartNode = 0; var Str = Node.innerHTML; // \1 char span node // \2 char Id // \3 skill // \4 defence roll // \5 MP // \6 item list // \7 hit type // \8 struck down // \9 damage list // \10 item damage // \11 next flag var Patt_Attacked = Local.Pattern_Passive_Attacked; var bEnd = false; while (!bEnd) { var PassiveInfo = new CPassiveInfo(); var result = Patt_Attacked.exec(Str); if (result == null) { DbgMsgAction(Info, "AttackedInfo: " + Node.innerHTML); return true; } var CharNode = result[1] != null ? Node.childNodes[nStartNode].childNodes[0] : Node.childNodes[nStartNode]; PassiveInfo.Char = new CChar(CharNode); PassiveInfo.nCharId = result[2] != null ? Number(result[2]) : null; nStartNode += result[1] != null ? 1 : (result[2] != null ? 2 : 1); if (result[3] != null) { PassiveInfo.Skill = new CSkill(Node.childNodes[nStartNode +1]); nStartNode += 2; } PassiveInfo.nDefenceRoll = Number(result[4]); if (result[5] != null) { PassiveInfo.nSkillMP = Number(result[5]); nStartNode += 2; } if (result[6] != null) { PassiveInfo.gItem = new CKeyList(); nStartNode += 1; var ItemNode = Node.childNodes[nStartNode]; while (ItemNode != null && ItemNode.nodeName == "A") { PassiveInfo.gItem.push(new CItem(ItemNode)); nStartNode += 2; ItemNode = Node.childNodes[nStartNode]; } } else nStartNode += 1; PassiveInfo.HitType = new CHitType(result[7]); PassiveInfo.bStruckDown = (result[8] != null); nStartNode += result[8] != null ? 2 : 1; if (result[9] != null) { PassiveInfo.gDamage = []; nStartNode += 1; var DamageNode = Node.childNodes[nStartNode]; while (DamageNode != null && (DamageNode.nodeType == 3 || (DamageNode.nodeName == "SPAN" && DamageNode.firstChild != null && DamageNode.firstChild.nodeType == 3))) { PassiveInfo.gDamage.push(new CDamage(DamageNode)); nStartNode += 2; DamageNode = Node.childNodes[nStartNode]; } nStartNode -= 1; } if (result[10] != null) { PassiveInfo.DamagedItem = new CItem(Node.childNodes[nStartNode +1]); PassiveInfo.nItemDamage = Number(result[10]); nStartNode += 3; } if (result[11] != null) nStartNode += 1; else bEnd = true; Info.gPassive.push(PassiveInfo); Str = Str.substring(result[0].length); } return true; } // return: whether the format is right function GetHealedBuffedInfo(Node, Info) { if (Node == null) { DbgMsgAction(Info, "HealedBuffedInfo: null"); return false; } var nStartNode = 0; var Str = Node.innerHTML; // \1 span node // \2 char Id // \3 self // \4 HP // \5 MP // \6 next flag var Patt_HealedBuffed = Local.Pattern_Passive_Healed_Buffed; var bEnd = false; while (!bEnd) { var PassiveInfo = new CPassiveInfo(); var result = Patt_HealedBuffed.exec(Str); if (result == null) { DbgMsgAction(Info, "HealedBuffedInfo: " + Node.innerHTML); return true; } if (result[3] != null) { PassiveInfo.Char = Info.Active.Char; PassiveInfo.nCharId = Info.Active.nCharId; } else { var CharNode = result[1] != null ? Node.childNodes[nStartNode].childNodes[0] : Node.childNodes[nStartNode]; PassiveInfo.Char = new CChar(CharNode); PassiveInfo.nCharId = result[2] != null ? Number(result[2]) : null; nStartNode += result[1] != null ? 1 : (result[2] != null ? 2 : 1); } PassiveInfo.nHealedHP = result[4] != null ? Number(result[4]) : null; PassiveInfo.nHealedMP = result[5] != null ? Number(result[5]) : null; nStartNode += 1; if (result[6] != null) nStartNode += 1; else bEnd = true; Info.gPassive.push(PassiveInfo); Str = Str.substring(result[0].length); } return true; } function DbgMsgAction(Info, Text) { if (DEBUG) alert("[" + Info.Navi.nLevel + "." + Info.Navi.nRoom + "." + Info.Navi.nRound + "." + Info.Navi.nRow + "] " + Text); } // GLOBAL VARIABLES /////////////////////////////////////////////////////////// var DEBUG = false; var Contents = { OrigText_Button_DungeonDetails : ["details", "详细资料"], OrigText_Button_DuelDetails : ["Details", "详细"], OrigText_Button_DungeonStat : ["statistics", "统计表"], OrigText_Level : ["Level", "层数"], OrigText_LastRound : ["Last round:", "最后回合:"], OrigTextList_ActionType : [["attacks", "ranged attacks", "attacks with magic", "socially attacks", "cunningly attacks", "activates on", "works as a force of nature upon", "infected", "casts an explosion at", "deactivated", "magic projectile", "curse", "scare", "heals with", "uses", "summons with", "is unable to do anything.", "looks around in boredom and waits."], ["近战攻击", "远程攻击", "魔法攻击", "心理攻击", "偷袭", "触发", "作为自然灾害", "散布", "制造爆炸", "解除", "魔法投射", "诅咒", "恐吓", "治疗", "使用", "召唤", "不能执行任何动作.", "无聊的打量四周,等待着."]], OrigTextList_DamageType : [["crushing damage", "cutting damage", "piercing damage", "fire damage", "ice damage", "lightning damage", "poison damage", "acid damage", "psychological damage", "holy damage", "disarm trap", "mana damage"], ["粉碎伤害", "切割伤害", "穿刺伤害", "火焰伤害", "寒冰伤害", "闪电伤害", "毒素伤害", "酸性伤害", "心灵伤害", "神圣伤害", "陷阱伤害", "法力伤害"]], Pattern_Ini : [/^Initiative ([\d]+)<br><span .*?>Action ([\d]+) of ([\d]+)<\/span>$/, /^先攻([\d]+)<br><span .*?>第([\d]+)步行动 \/ 共([\d]+)步<\/span>$/], Pattern_Active_Char : [/^(<span .*?>)?<a .*?>.*?<\/a>(?:<span .*?>([\d]+)<\/span>)?(?:<img .*?><\/span>)?/, /^(<span .*?>)?<a .*?>.*?<\/a>(?:<span .*?>([\d]+)<\/span>)?(?:<img .*?><\/span>)?/], Pattern_Active_Action1 : [/^\s*(?:([A-Za-z][A-Za-z ]+[A-Za-z]) +\(|([A-Za-z][A-Za-z ]+[A-Za-z]) +<a .*?>.*?<\/a>(?:( \()|$| on $))/, /^\s*(?:([^\u0000-\u007F]+) +\(|([^\u0000-\u007F]+)<a .*?>.*?<\/a>(?:( \()|$|给$))/], Pattern_Active_Action2 : [/^\s*([\S].*[\S])\s*$/, /^\s*([\S].*[\S])\s*$/], Pattern_Active_AttackDetails : [/^<a .*?>.*?<\/a>(?:\/([\d]+)|(?:\/([A-Za-z ]+): ([\d]+))+)(?:\/<span .*?>([\d]+) MP<\/span>)?(\/(?:<a .*?>.*?<\/a>,)*<a .*?>.*?<\/a>)?\)$/, /^<a .*?>.*?<\/a>(?:\/([\d]+)|(?:\/([^\u0000-\u007F]+): ([\d]+))+)(?:\/<span .*?>([\d]+) 法力<\/span>)?(\/(?:<a .*?>.*?<\/a>,)*<a .*?>.*?<\/a>)?\)$/], Pattern_Active_HealBuffDetails : [/^(?:<span .*?>([\d]+) MP<\/span>)?(?:\/)?(?:((<a .*?>.*?<\/a>,)*<a .*?>.*?<\/a>)|(<a .*?>.*?<\/a>\s+(?:<img .*?>)+))?\)(?: on )?$/, /^(?:<span .*?>([\d]+) 法力<\/span>)?(?:\/)?(?:((<a .*?>.*?<\/a>,)*<a .*?>.*?<\/a>)|(<a .*?>.*?<\/a>\s+(?:<img .*?>)+))?\)(?:给)?$/], Pattern_Passive_Attacked : [/^(<span .*?>)?<a .*?>.*?<\/a>(?:<span .*?>([\d]+)<\/span>)?(?:<img .*?><\/span>)?\s*\((<a .*?>.*?<\/a>\/)?([\d]+)(?:\/<span .*?>([\d]+) MP<\/span>)?(\/(?:<a .*?>.*?<\/a>,)*<a .*?>.*?<\/a>)?\): <span class="([A-Za-z_]+)">[A-Za-z ]+<\/span>( - [A-Za-z ]+)?(<br>(?:<span .*?>)?(?:-)?[\d]+ (?:\[(?:\+|-)[\d]+\] )?[A-Za-z ]+(?:<img .*?><\/span>)?)*(?:<br><a .*?>.*?<\/a> -([\d]+) HP)?(?:(<br>)|$)/, /^(<span .*?>)?<a .*?>.*?<\/a>(?:<span .*?>([\d]+)<\/span>)?(?:<img .*?><\/span>)?\s*\((<a .*?>.*?<\/a>\/)?([\d]+)(?:\/<span .*?>([\d]+) 法力<\/span>)?(\/(?:<a .*?>.*?<\/a>,)*<a .*?>.*?<\/a>)?\): <span class="([A-Za-z_]+)">[^\u0000-\u007F]+<\/span>( - [^\u0000-\u007F]+ *)?(<br>(?:<span .*?>)?(?:-)?[\d]+ (?:\[(?:\+|-)[\d]+\] )?[^\u0000-\u007F]+(?:<img .*?><\/span>)?)*(?:<br><a .*?>.*?<\/a> -([\d]+) HP)?(?:(<br>)|$)/], Pattern_BasicDamage : [/causes: <b>([\d]+)<\/b>/, /造成: <b>([\d]+)<\/b>/], Pattern_Damage : [/^((?:-)?[\d]+) (?:\[((?:\+|-)[\d]+)\] )?([A-Za-z][A-Za-z ]+[A-Za-z])$/, /^((?:-)?[\d]+) (?:\[((?:\+|-)[\d]+)\] )?([^\u0000-\u007F]+)$/], Pattern_Passive_Healed_Buffed : [/^(?:(<span .*?>)?<a .*?>.*?<\/a>(?:<span .*?>([\d]+)<\/span>)?(?:<img .*?><\/span>)?\s+|(themselves))(?: \+([\d]+) HP)?(?: \+([\d]+) MP)?(?:(<br>)|$)/, /^(?:(<span .*?>)?<a .*?>.*?<\/a>(?:<span .*?>([\d]+)<\/span>)?(?:<img .*?><\/span>)?\s+|(自己))(?: \+([\d]+) HP)?(?: \+([\d]+) 法力)?(?:(<br>)|$)/], Text_Button_ExtraStat : ["Extra Stat", "额外统计"], Text_Button_EntireStat : ["Entire Extra Stat", "全城额外统计"], Text_Button_Show : ["Show", "显示"], Text_Button_Default : ["Default", "默认"], TextList_AttackType : [["melee", "ranged", "spell", "social", "ambush", "trap", "nature", "disease", "detonate","disarm trap", "magic projectile", "curse", "scare"], ["近战", "远程", "魔法", "心理", "偷袭", "陷阱", "自然", "疾病", "爆破", "解除陷阱", "魔法投射", "诅咒", "恐吓"]], TextList_HitType : [["failed", "success", "good success", "critical success"], ["闪避", "普通", "重击", "致命一击"]], Text_Loading : ["Loading", "载入中"], Text_Options : ["Options:", "选项:"], Text_DefaultMsg : ["All the data this script stored in your machine has been cleared.", "此脚本储存在你的机器上的所有数据已被清除。"], Text_Table_Ini : ["Initiative", "先攻权"], Text_Table_Attack : ["Attack", "攻击骰"], Text_Table_Defence : ["Defence", "防御骰"], Text_Table_Damage : ["Damage", "伤害"], Text_Table_Heal : ["Healing By The Hero", "给予治疗"], Text_Table_Healed : ["Healing On The Hero", "接受治疗"], Text_Table_DamagedItems : ["Damaged Items", "物品损坏"], Text_Table_Char : ["Character", "人物"], Text_Table_AttackType : ["Attack type", "攻击类型"], Text_Table_DefenceType : ["Defence type", "防御类型"], Text_Table_Skill : ["Skill", "技能"], Text_Table_Item : ["Item", "物品"], Text_Table_AvgRoll : ["Average roll", "平均值"], Text_Table_Times : ["Times", "次数"], Text_Table_RollList : ["Roll list", "数值列表"], Text_Table_ItemDamagePoints : ["Damage Points", "损坏点数"] }; var Style = "div.stat_header {margin:1em auto 0.5em auto;} " + "span.stat_title {margin: auto 1em auto 0em; font-size:20px; font-weight:bold; color:#FFF;} span.clickable {cursor:pointer;} " + "table[hide] {display:none;} " + "table.pair_value {width:100%;} table.pair_value td {width:50%; min-width:3em; text-align:right; color:#F8A400;} table.pair_value td + td {color:#00CC00;} "; var Local; var Stat; try {Main();} catch(e) {alert("Main(): " + e);} // FUNCTIONS ////////////////////////////////////////////////////////////////// if( typeof(GM_addStyle)=='undefined' ){function GM_addStyle(styles){ var S = document.createElement('style'); S.type = 'text/css'; var T = ''+styles+''; T = document.createTextNode(T) S.appendChild(T); document.body.appendChild(S); return; }} if (!this.GM_getValue || this.GM_getValue.toString().indexOf("not supported")>-1) { this.GM_getValue=function (key,def) { return localStorage[key] || def; }; this.GM_setValue=function (key,value) { return localStorage[key]=value; }; } function Main() { // Language selection Local = GetLocalContents(Contents); if (Local === null) return; //GM_log(Local); // Add CSS GM_addStyle(Style); // Add buttons var KeyButton = AddButtonBesideDisabledButton( [Local.OrigText_Button_DungeonDetails, Local.Text_Button_ExtraStat, OnCountStat], [Local.OrigText_Button_DungeonStat, Local.Text_Button_EntireStat, OnCountEntireStat], [Local.OrigText_Button_DuelDetails, Local.Text_Button_ExtraStat, OnCountStat]); if (KeyButton === null) return; // Stat initialization Stat = new CStat( node_after(KeyButton.parentNode) ); Stat.RegInfoList(new CILIni( 1, CVLNumber)); Stat.RegInfoList(new CILAttackRoll( 4, CVLNumber)); Stat.RegInfoList(new CILDefenceRoll( 4, CVLNumber)); Stat.RegInfoList(new CILDamage( 4, CVLDamage)); Stat.RegInfoList(new CILHeal( 3, CVLPairNumber)); Stat.RegInfoList(new CILHealed( 1, CVLPairNumber)); Stat.RegInfoList(new CILItemDamage( 2, CVLNumber)); } // It will only add the first eligible button // return: the node of the first eligible disabled button, or null if didn't find anyone function AddButtonBesideDisabledButton(/* [DisabledButtonText, ButtonText, ClickEvent], [...], ... */) { var allInputs = document.getElementsByTagName("input"); for (var i = 0; i < allInputs.length; ++i) { if (allInputs[i].className == "button_disabled") { for (var j = 0; j < arguments.length; ++j) { if (allInputs[i].getAttribute("value") == arguments[j][0]) { AddButton(allInputs[i], arguments[j][1], arguments[j][2]); return allInputs[i]; } } } } return null; } // Add a button to the end of the given node's parent node function AddButton(SiblingNode, Value, OnClick) { var newButton = document.createElement("input"); newButton.setAttribute("type", "button"); newButton.setAttribute("class", "button"); newButton.setAttribute("value", Value); newButton.addEventListener("click", OnClick, false); var newBlank = document.createTextNode(" "); SiblingNode.parentNode.appendChild(newBlank); SiblingNode.parentNode.appendChild(newButton); } function OnCountStat() { try { if (this.className == "button_disabled") return; else this.className = "button_disabled"; Stat.nTotalPages = 1; ReadPage(document, true); } catch (e) {alert("OnCountStat(): " + e);} } function OnCountEntireStat() { try { if (this.className == "button_disabled") return; else this.className = "button_disabled"; CountEntireStat(); } catch (e) {alert("OnCountEntireStat(): " + e);} } function CountEntireStat() { var nCurrRepId = GetHiddenInfo(document, "report_id[0]", ""); var nMaxLevel = Stat.nTotalPages = GetStatPageMaxLevel(document, 1); for (var CurrLevel = 1; CurrLevel <= nMaxLevel; ++CurrLevel) GetPage(nCurrRepId, CurrLevel, 1, true); Stat.ShowProgress(); } function GetPage(nRepId, nLevel, nRepPage, bFirstRead) { var XmlHttp = new XMLHttpRequest(); XmlHttp.onreadystatechange = function () { try { if (XmlHttp.readyState == 4 && XmlHttp.status == 200) { var Page = document.createElement("div"); Page.innerHTML = XmlHttp.responseText; ReadPage(Page, bFirstRead); } } catch (e) {alert("XMLHttpRequest.onreadystatechange(): " + e);} }; var URL = location.protocol + "//" + location.host + "/wod/spiel/dungeon/report.php" + "?cur_rep_id=" + nRepId + "&gruppe_id=¤t_level=" + nLevel + "&REPORT_PAGE=" + nRepPage + "&IS_POPUP=1"; XmlHttp.open("GET", URL, true); XmlHttp.send(null); } function ReadPage(Document, bFirstRead) { var ret = GetRepPageInfo(Document, [1, 1]); var nCurrRepPage = ret[0]; var nMaxRepPage = ret[1]; if (bFirstRead && nMaxRepPage > 1) { var nRepId = GetHiddenInfo(Document, "report_id[0]", ""); var nLevel = GetHiddenInfo(Document, "current_level", 1); Stat.nTotalPages += nMaxRepPage -1; for (var i = 1; i <= nMaxRepPage; ++i) { if (i !== nCurrRepPage) GetPage(nRepId, nLevel, i, false); } } CountStat(Document, (nCurrRepPage === nMaxRepPage)); if (++Stat.nReadPages >= Stat.nTotalPages) Stat.Show(); else Stat.ShowProgress(); } function GetHiddenInfo(Document, InfoName, DefaultValue) { var allInputs = Document.getElementsByTagName("input"); for (var i = 0; i < allInputs.length; ++i) { if (allInputs[i].getAttribute("type") == "hidden" && allInputs[i].name == InfoName) return allInputs[i].value; } return DefaultValue; } function GetStatPageMaxLevel(Document, DefaultValue) { var allTds = Document.getElementsByTagName("td"); for (var i = 0; i < allTds.length; ++i) { if (first_child(allTds[i].parentNode) != allTds[i]) continue; var LevelNode = first_child(allTds[i]); if (LevelNode != null && LevelNode.nodeType == 3 && LevelNode.data == Local.OrigText_Level) { var Patt_Level = /^(?:<span .*?>)?(?:[\d]+)\/([\d]+)(?:<\/span>)?$/; var result = Patt_Level.exec(node_after(allTds[i]).innerHTML); if (result == null) return DefaultValue; return Number(result[1]); } } return DefaultValue; } // return: an array, [0]: nCurrRepPage, [1]: nMaxRepPage function GetRepPageInfo(Document, DefaultValue) { var ret = [DefaultValue[0], DefaultValue[1]]; var SubPageIndexNode; var allInputs = Document.getElementsByTagName("input"); for (var i = 0; i < allInputs.length; ++i) { if (allInputs[i].value.indexOf("=1=") !== -1) { SubPageIndexNode = allInputs[i]; break; } } if (SubPageIndexNode == null) return ret; var bIndexEnd = false; while (!bIndexEnd) { var IndexPatt = /=([\d]+)=/; var target = (SubPageIndexNode.value == null)? SubPageIndexNode.firstChild.textContent:SubPageIndexNode.value; var Result = IndexPatt.exec(target); var nCurrIndex = Number(Result[1]); if (SubPageIndexNode.className == "paginator_selected clickable") ret[0] = nCurrIndex; SubPageIndexNode = node_after(node_after(SubPageIndexNode)); if (SubPageIndexNode == null || SubPageIndexNode.nodeName != "A") { ret[1] = nCurrIndex; bIndexEnd = true; } } return ret; }