From ecb73af9116a5e8d61f1958b9e020fa5cd73ab57 Mon Sep 17 00:00:00 2001 From: fin444 Date: Sat, 23 Jul 2022 13:18:35 -0400 Subject: [PATCH 01/19] texture atlas for atree connectors --- js/atree.js | 92 +++++++++++++++++------------- media/atree/connect_0011.png | Bin 182 -> 0 bytes media/atree/connect_0011_1.png | Bin 1587 -> 0 bytes media/atree/connect_0101.png | Bin 183 -> 0 bytes media/atree/connect_0101_1.png | Bin 1586 -> 0 bytes media/atree/connect_0110.png | Bin 178 -> 0 bytes media/atree/connect_0110_1.png | Bin 1579 -> 0 bytes media/atree/connect_0111.png | Bin 202 -> 0 bytes media/atree/connect_0111_0011.png | Bin 1609 -> 0 bytes media/atree/connect_0111_0101.png | Bin 1615 -> 0 bytes media/atree/connect_0111_0110.png | Bin 1620 -> 0 bytes media/atree/connect_0111_0111.png | Bin 1604 -> 0 bytes media/atree/connect_1001.png | Bin 184 -> 0 bytes media/atree/connect_1001_1.png | Bin 1581 -> 0 bytes media/atree/connect_1010.png | Bin 177 -> 0 bytes media/atree/connect_1010_1.png | Bin 1578 -> 0 bytes media/atree/connect_1011.png | Bin 202 -> 0 bytes media/atree/connect_1011_0011.png | Bin 1609 -> 0 bytes media/atree/connect_1011_1001.png | Bin 1613 -> 0 bytes media/atree/connect_1011_1010.png | Bin 1616 -> 0 bytes media/atree/connect_1011_1011.png | Bin 1602 -> 0 bytes media/atree/connect_1100.png | Bin 184 -> 0 bytes media/atree/connect_1100_1.png | Bin 1590 -> 0 bytes media/atree/connect_1101.png | Bin 205 -> 0 bytes media/atree/connect_1101_0101.png | Bin 1612 -> 0 bytes media/atree/connect_1101_1001.png | Bin 1616 -> 0 bytes media/atree/connect_1101_1100.png | Bin 1615 -> 0 bytes media/atree/connect_1101_1101.png | Bin 1604 -> 0 bytes media/atree/connect_1110.png | Bin 203 -> 0 bytes media/atree/connect_1110_0110.png | Bin 1614 -> 0 bytes media/atree/connect_1110_1010.png | Bin 1610 -> 0 bytes media/atree/connect_1110_1100.png | Bin 1607 -> 0 bytes media/atree/connect_1110_1110.png | Bin 1603 -> 0 bytes media/atree/connect_1111.png | Bin 221 -> 0 bytes media/atree/connect_1111_0011.png | Bin 1630 -> 0 bytes media/atree/connect_1111_0101.png | Bin 1634 -> 0 bytes media/atree/connect_1111_0110.png | Bin 1633 -> 0 bytes media/atree/connect_1111_0111.png | Bin 1629 -> 0 bytes media/atree/connect_1111_1001.png | Bin 1635 -> 0 bytes media/atree/connect_1111_1010.png | Bin 1633 -> 0 bytes media/atree/connect_1111_1011.png | Bin 1630 -> 0 bytes media/atree/connect_1111_1100.png | Bin 1633 -> 0 bytes media/atree/connect_1111_1101.png | Bin 1627 -> 0 bytes media/atree/connect_1111_1110.png | Bin 1630 -> 0 bytes media/atree/connect_1111_1111.png | Bin 1616 -> 0 bytes media/atree/connectors.png | Bin 0 -> 4186 bytes media/atree/node-blocked.png | Bin 1147 -> 0 bytes media/atree/node-selected.png | Bin 1147 -> 0 bytes media/atree/node.png | Bin 1147 -> 0 bytes 49 files changed, 53 insertions(+), 39 deletions(-) delete mode 100644 media/atree/connect_0011.png delete mode 100644 media/atree/connect_0011_1.png delete mode 100644 media/atree/connect_0101.png delete mode 100644 media/atree/connect_0101_1.png delete mode 100644 media/atree/connect_0110.png delete mode 100644 media/atree/connect_0110_1.png delete mode 100644 media/atree/connect_0111.png delete mode 100644 media/atree/connect_0111_0011.png delete mode 100644 media/atree/connect_0111_0101.png delete mode 100644 media/atree/connect_0111_0110.png delete mode 100644 media/atree/connect_0111_0111.png delete mode 100644 media/atree/connect_1001.png delete mode 100644 media/atree/connect_1001_1.png delete mode 100644 media/atree/connect_1010.png delete mode 100644 media/atree/connect_1010_1.png delete mode 100644 media/atree/connect_1011.png delete mode 100644 media/atree/connect_1011_0011.png delete mode 100644 media/atree/connect_1011_1001.png delete mode 100644 media/atree/connect_1011_1010.png delete mode 100644 media/atree/connect_1011_1011.png delete mode 100644 media/atree/connect_1100.png delete mode 100644 media/atree/connect_1100_1.png delete mode 100644 media/atree/connect_1101.png delete mode 100644 media/atree/connect_1101_0101.png delete mode 100644 media/atree/connect_1101_1001.png delete mode 100644 media/atree/connect_1101_1100.png delete mode 100644 media/atree/connect_1101_1101.png delete mode 100644 media/atree/connect_1110.png delete mode 100644 media/atree/connect_1110_0110.png delete mode 100644 media/atree/connect_1110_1010.png delete mode 100644 media/atree/connect_1110_1100.png delete mode 100644 media/atree/connect_1110_1110.png delete mode 100644 media/atree/connect_1111.png delete mode 100644 media/atree/connect_1111_0011.png delete mode 100644 media/atree/connect_1111_0101.png delete mode 100644 media/atree/connect_1111_0110.png delete mode 100644 media/atree/connect_1111_0111.png delete mode 100644 media/atree/connect_1111_1001.png delete mode 100644 media/atree/connect_1111_1010.png delete mode 100644 media/atree/connect_1111_1011.png delete mode 100644 media/atree/connect_1111_1100.png delete mode 100644 media/atree/connect_1111_1101.png delete mode 100644 media/atree/connect_1111_1110.png delete mode 100644 media/atree/connect_1111_1111.png create mode 100644 media/atree/connectors.png delete mode 100644 media/atree/node-blocked.png delete mode 100644 media/atree/node-selected.png delete mode 100644 media/atree/node.png diff --git a/js/atree.js b/js/atree.js index e2249da..e8dc914 100644 --- a/js/atree.js +++ b/js/atree.js @@ -973,7 +973,8 @@ function render_AT(UI_elem, list_elem, tree) { const parent_id = parent_abil.id; let connect_elem = document.createElement("div"); - connect_elem.style = "background-size: cover; width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated;"; + // connect_elem.style = "background-size: cover; width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated;"; + connect_elem.style = "width: 112.5%; height: 112.5%; position: absolute; top: -5.55%; left: -5.55%; image-rendering: pixelated; background-image: url('../media/atree/connectors.png'); background-size: 1200% 400%;" // connect up for (let i = ability.display.row - 1; i > parent_abil.display.row; i--) { const coord = i + "," + ability.display.col; @@ -1233,26 +1234,6 @@ function set_connector_type(connector_info) { // left right up down } } -// draw the connector onto the screen -function atree_render_connection(atree_connectors_map) { - for (let i of atree_connectors_map.keys()) { - let connector_info = atree_connectors_map.get(i); - let connector_elem = connector_info.connector; - let connector_img = document.createElement('img'); - set_connector_type(connector_info); - connector_img.src = '../media/atree/connect_'+connector_info.type+'.png'; - connector_img.style = "width: 100%; height: 100%;" - connector_elem.replaceChildren(connector_img); - connector_info.highlight = [0, 0, 0, 0]; - let target_elem = document.getElementById("atree-row-" + i.split(",")[0]).children[i.split(",")[1]]; - if (target_elem.children.length != 0) { - // janky special case... - connector_elem.style.display = 'none'; - } - target_elem.appendChild(connector_elem); - }; -}; - // toggle the state of a node. function atree_set_state(node_wrapper, new_state) { let icon = node_wrapper.ability.display.icon; @@ -1280,6 +1261,48 @@ function atree_set_state(node_wrapper, new_state) { } }; +// first key is connector type, second key is highlight, then [x, y] pair of 0-index positions in the tile atlas +const atreeConnectorAtlasPositions = { + "1100": {"0000": [0, 0], "1100": [1, 0]}, + "1010": {"0000": [2, 0], "1010": [3, 0]}, + "0110": {"0000": [4, 0], "0110": [5, 0]}, + "1001": {"0000": [6, 0], "1001": [7, 0]}, + "0101": {"0000": [8, 0], "0101": [9, 0]}, + "0011": {"0000": [10, 0], "0011": [11, 0]}, + "1101": {"0000": [0, 1], "1101": [1, 1], "1100": [2, 1], "1001": [3, 1], "0101": [4, 1]}, + "0111": {"0000": [5, 1], "0111": [6, 1], "0110": [7, 1], "0101": [8, 1], "0011": [9, 1]}, + "1110": {"0000": [0, 2], "1110": [1, 2], "1100": [2, 2], "1010": [3, 2], "0110": [4, 2]}, + "1011": {"0000": [5, 2], "1011": [6, 2], "1010": [7, 2], "1001": [8, 2], "0011": [9, 2]}, + "1111": {"0000": [0, 3], "1111": [1, 3], "1110": [2, 3], "1101": [3, 3], "1100": [4, 3], "1011": [5, 3], "1010": [6, 3], "1001": [7, 3], "0111": [8, 3], "0110": [9, 3], "0101": [10, 3], "0011": [11, 3]} +} +function atlasBGPositionCalc(pos, atlasSize) { + // https://css-tricks.com/focusing-background-image-precise-location-percentages/ + // p = (c + 0.5/z - 0.5) * z/(z - 1) + 0.5 + // z = num tiles in direction + // c = starting pos of tile in percent of total atlas (equal to index/z) + let x = ((pos[0]/atlasSize[0]) + 0.5/atlasSize[0] - 0.5) * atlasSize[0]/(atlasSize[0] - 1) + 0.5 + let y = ((pos[1]/atlasSize[1]) + 0.5/atlasSize[1] - 0.5) * atlasSize[1]/(atlasSize[1] - 1) + 0.5 + return (x * 100) + "% " + (y * 100) + "%" // multiply by 100 to convert decimal to percent +} + +// draw the connector onto the screen +function atree_render_connection(atree_connectors_map) { + for (let i of atree_connectors_map.keys()) { + let connector_info = atree_connectors_map.get(i); + let connector_elem = connector_info.connector; + set_connector_type(connector_info); + connector_info.highlight = [0, 0, 0, 0]; + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[connector_info.type]["0000"], [12, 4]); + let target_elem = document.getElementById("atree-row-" + i.split(",")[0]).children[i.split(",")[1]]; + if (target_elem.children.length != 0) { + // janky special case... + connector_elem.style.display = 'none'; + } + target_elem.appendChild(connector_elem); + }; +}; + +// update the connector (after being drawn the first time by atree_render_connection) function atree_set_edge(atree_connectors_map, parent, child, state) { const connectors = child.connectors.get(parent); const parent_row = parent.ability.display.row; @@ -1294,8 +1317,6 @@ function atree_set_edge(atree_connectors_map, parent, child, state) { let connector_info = atree_connectors_map.get(connector_label); let connector_elem = connector_info.connector; let highlight_state = connector_info.highlight; // left right up down - let connector_img_elem = document.createElement("img"); - connector_img_elem.style = "width: 100%; height: 100%;"; const ctype = connector_info.type; let num_1s = 0; for (let i = 0; i < 4; i++) { @@ -1323,23 +1344,16 @@ function atree_set_edge(atree_connectors_map, parent, child, state) { for (let i = 0; i < 4; i++) { render += highlight_state[i] === 0 ? "0" : "1"; } - if (render == "0000") { - connector_img_elem.src = "../media/atree/connect_" + ctype + ".png"; - } else { - connector_img_elem.src = "../media/atree/connect_" + ctype + "_" + render + ".png"; - } - connector_elem.replaceChildren(connector_img_elem); + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][render], [12, 4]); continue; - } - // lol bad overloading, [0] is just the whole state - highlight_state[0] += state_delta; - if (highlight_state[0] > 0) { - connector_img_elem.src = '../media/atree/connect_' + ctype + '_1.png'; - connector_elem.replaceChildren(connector_img_elem); - } - else { - connector_img_elem.src = '../media/atree/connect_'+ctype+'.png'; - connector_elem.replaceChildren(connector_img_elem); + } else { + // lol bad overloading, [0] is just the whole state + highlight_state[0] += state_delta; + if (highlight_state[0] > 0) { + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][ctype], [12, 4]); + } else { + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype]["0000"], [12, 4]); + } } } } diff --git a/media/atree/connect_0011.png b/media/atree/connect_0011.png deleted file mode 100644 index 24b75d681a5893efdc17e2d0a9a01c4bf6d84069..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*Z=?j1DOs^US?(%x_Snx zs_GLiUK0cgF_i@Q1v4;|O+IS@NwvHyDPeq^;C|(U0bk(}o&}98 Z4AoJ5`^27n5d|8~;OXk;vd$@?2>`P_IiLUl diff --git a/media/atree/connect_0011_1.png b/media/atree/connect_0011_1.png deleted file mode 100644 index d3294b31f4f676ef2fd8b86e2a36923214175201..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1587 zcmah}U1%It6rRKeYf7M2&>}^KDM&@`{Qv9>yKR1Un=EA0HCZ%1nLBsxY=-RoI5Wv^ z0)htA*ayW&gD+MeirAuvPf`@26ns!0M11o_#6J1pgD*aK?(FP#3jqh_=bZV@chC9m zx#yks+Vb4&)3Y4M%{5o*>+t5;ed-Z-zR~I4gV(9{t5CFexHH;V~)? z{0G`Rln?t;q(535!p3|LAxCvGAc*1AW3tQgY1V53Th#Gl5_nGW>8qwqyb49sBr4LW zq9I#1Jw!~^Qi)daDfLG@%_z%pL@46{W-$N~Q&9|qS~k+Hih*=uxu`-c6)6~dvPCu1 zRg6*mwKRa%!@bFT7>@@$u8Upls|M0+#X`Di`>3L*7V>p!Q^VCf*;NGIBhJ-04KZw4 z5aKSClBg^2Oj&24-K+_!D3@lthjSmG(BDDi#r>@GcY=^^>1^)j3L5rir!&M#b>q?Np>ic7Nc|bPks}ru$*$eoh z*BwlTC1iMydP3iD=3&|Br$JlUn&Bmkn zEcWw0PU%7yR0DCK?Fw=DOXYq%H$wKeDH9t60!7dRfoy@7ChDk4?66PPR+UbYVThX; z5(UT53lj(|h#L(xW{n3UjX;1D#$M;U-=Bez`B<{@a>j9Io?!PBcl_aba9(TH7dn@I zBwOd({3l1>cb`1hcAd-yBq=-%1+e=gja;*57stIvOM>oqo`n~k;l?ZvIb{{WAe<5K_t diff --git a/media/atree/connect_0101.png b/media/atree/connect_0101.png deleted file mode 100644 index 46c28e71140dd3f19cb5b850637cac6a976c9410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*Z=?j1DOs^US?(%x_Snx zs_GLiUK0cgF_i@Q1v4;|O+IS@uoGu_=0ijEGO0D8S6jDxYnLeoc`p6 cNc;}QgfqNP>FVdQ&MBb@0HCBkH2?qr diff --git a/media/atree/connect_0101_1.png b/media/atree/connect_0101_1.png deleted file mode 100644 index 774f2e8e29ea0ae916ac03a5bea6115284aa97ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1586 zcmah}O=u)V6z=RMh*`5@3=(7yO>dA0R{u?RPj@r3n@J`=1DQBX28}0EQ(ZmNWcnxF zm6=ISyL(u15%%Ok5j^O@gDmdBlc2C5Uc?1K*_+@&@Fd>%;H#dW%|gJ2{&}yz_tkq} zy?P%u_O=)1U!NBQVWGBDZNgjR_v~eO-fne%g4b-PQm+WY!~0jB;yJKqvgY=>@Zgtg ze}l2u*llfYC%ZT9d|+92@#}Bj|NRdntG_+`;%5$J&AokL?XM@kAk3`?tpj#Y-*rhG z%GitBR33&2pah|GbC_UqM48y8oggYp|9tm@BnDntde^M0^<;x~gPpUK?w{?o$k`Eb zJn80*h56FZ1%{9^EDpn7l)1yQloPw~&E1M5<`8yNmJaF-aU)KtXvl`FqRnB@*Cikk z#Zv0|ZnL^ICIPD~bs0-sMHvhR@}MBcX-Cl<$5B*W(RBm}l$}Nl4^fm|ACZh{sx%{M zkgy<*L{5v_@d+zSkjE&&Fsaw4>`^w>0TfY&I8iiNRl<;Gouf0h*@xh9E*GM+)@eeO zCe7lLl+evSjo9@`xJRb3$w}JFtMG_Ido%=C2CHe)rM7GJ#uUw~?gU|yM}Wdp6bt-k z+B}yJ|C6UbSscQqe9s{#b#fqx;Z|dE!t<$B%MxGI^MY(jlvA{E;zGHi<= z+iz2ZZOf-BH8k6BCOow$V>lv|^8m9P0Ey*bi`djeI@OBEP>VitblX6h-6o!nbxU{5 zN&MSs0Ii36=kwt_9`I1l$5_KAGHuv?17jVv3)n)~vus}_HYpHK5#c7CP%YqPh zsFFk-N#x49l}4>B>9RUD8$HZ?fI@!X)m$^~5#x$-U~4-#VDb`YAEis0QLd+vVbjtKfq+UmS4TzE33;Zw;mO(*NnBrhS5K^wiXYY?09LZ`s<^WKi~TDUT>wbw)ErGH}J;X s(@QViyF2sMrQ3JAS6{ZSJ-KyR_*{J<{^*nMXZeV(ZSGYctRLR_50>}cN&o-= diff --git a/media/atree/connect_0110.png b/media/atree/connect_0110.png deleted file mode 100644 index 56a167826e1ea20e21e75957be4fc3738e92c3a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*Z=?j1DOs^US?(%x_Snx zs_GLiUK0cgF_i@Q1v4;|O+IS@Q%bv)W@po zPP$idHtL8tI2#ccy%awKDy{^@l`sosKtvFnoiH0k#GMQ8t9;Brg9Y`ux6VE9-t+Fe zZ+1J4!s=729LE(}%~}uMJiE_74A0m4gYV#Veo$>!IqtJ}F8qU6z&@Au8k^kh@1MR8 z#uMG`e!UTIU%vjj`N@}tQF`U4dq|Woefrl82F-e%9qzf`e)c$SWz+BPX1ncen?!+# z-N>QhIEVqoapf!H7?T5<@eUpMVMX}kt8WC}cPqlndRuPCTXf%Vo+NbVq|+xS2gGuP zE0+ta<*^M60nIQ!4u)ZBk1Ik>Y{NHmO9G!mvV)4S+wSsPQ9^l5)I=H8$NrHj013~R z6W6nQwW~7{uqwiSmc_OtjYcDJR1~9RASsq*NwO-bDgp#bkHZX)QJ7wwlFVpoG$n~2 zXMPm&j21i5VO9|!k7r}uLhDB~%r4HuU9yOc50hbDg-awlqyfNESWQ_h)o8W53pA^G;0JLY0SYfrneY9l z%>((cKY9A|#UX6L_W*KUCj){Qb}b@@ET2}bBCthmHzK}k=bySv6w6g~R3wBT%`hxf zGA$WVqM3@KI*R7VbDmb1W;i62@c^^v1Bs;?riLwuk}U^mSf{AunnhHURYDYpnwI9w z<6lU8Xgxe!&WG{1z(XMmQ#W0t6Hi8(MLlH6V46i0W8HCFk5GYkiG4Ln0t{Q`2Y5iG zI2;H(Q`W9_TNObSA=%vQ4sqrI6#Cl_-Ds4~{Cz*5I~iuuDTZzux~9RiWSVAihIol4 zDfAX2Q)IEI3OrNZhU7s)%-Rk@^OEeXgyJkp`cX8j2y9uN#Z3RT;c&P(!!?{y;F49< zmQ7nx`-*B8HCr(enDQJPxxRP&f6)9W@o<%=``YwVSp9h6o*j@K`s$Kv<{bKb-s?6d z(-KmAL|tL#B>OlVP#6Z#hf|n;#$M|sgUE`fAho2rNGoC!VWN4cq^mLk7bdDXk53~n z8{vdj2cQ~=18tX!!>`Zwl^PCR#x7A i@mZ%}Jhr#?JI77Fc*OYi$Gcy%+1#pkYPUD{uKx>>Zr_6d diff --git a/media/atree/connect_0111.png b/media/atree/connect_0111.png deleted file mode 100644 index 5ab0abba1f35b7bd2546100df001cc0977c697ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*Z=?j1DOs^US?(%x_Snx zs_GLiUK0cgF_i@Q1v4;|O+IS@x-+ z{Ek1m8p{;Yl(m6NY4fR4mdv_VvWd_<`+Y zCRv4U2oe&AWyK1$2vt=j7Az18B18q`l1y?3#KC{@-nPzlYmYcd@z1abJ8m^A9-(b}8LlUE&@* ze*RA|=349R`f9v>?bdtc`(GMHW#`x5ktkoj{NlpWs}CRkb(aNXn;To)!XLkR9CvQX zZ*OPY&2`I(0+F~;hl+zB1{B9tuMcA4?9q(x(5@fWgulQ3M&Nz7CcLFLsg!jN7^k_L1WX@N&GnTqmXB`q!aCDH39M%CK$xc=9oQ93mrfaX+UC05oIX|Sk^f@&FTjb zT;y^lI&B}uRNACzw4XS%en7+Q(kR?@#g~zDO_x{u7iG0|bJpIw)5H{v}0y(Oa0YMDQj+}j#Ps6SWY*EXN9N)F_N8*-c z6B{aWb(tb8(+cVk>>+3X((x)CS;yrOPa{k-5;~Oe0JG==iAG4-#Kc5Y*F1zt#YAR@ zD#)x<973^y6~!3Ezn1vWdZag*594ux2YZ;7HMfEk!!Qt5l`V7DUZOB>wAa)2hEQX55MYgU)TH;RzDoOivzMn z-1a_SVBq;s4EmsXP1Os3c~>Ua0v6y*lXB|isxw#>Nu1j?3Lk(Dip~& zaXOk}U`M5+_%!meK1t|e7gPgrpzU&T`1xW#UK%0$yOfCy0)ZlELLh;cChDk4?66PP zR+Y|@VThX;5;@1v3lj(|h#L(xW{n3U4MBhe#@-8$-~I(g=F{=ojg;eNF0!k{-Tq__ zoI4GBu|5Bzb91i6-#NMR%*mD0-igTH**)Gle#36eHBTEq&(-bk8dovqZk*PyB2;?R z`snzubZ_A+<>UGXFK>MN*$*r7z4_apTI*9k-7S57{^GMM7cRWMRI1{0vc853jhEB diff --git a/media/atree/connect_0111_0101.png b/media/atree/connect_0111_0101.png deleted file mode 100644 index 68e417f8b98945e2803f03afd91be63efabfbb6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1615 zcmah}OK;p%6m}q4kWmmSR76pcjQb!|^4l|>v1VE(nQ1a8PDjZI!irw|UVGe(AJ|T2 z60r!8DpV1PYE}rbD1s$aV!?_9?F!TdtU$0LDEl5i)sNpqJ3>BVt4wkNUCPTb3Ofo+lWh!3y?R+A@7t+dSc1WkACdVZ8efWi}0 z>N!ufc_ttFCrf|4ID}33o|IM2@!2O2+g)+EXM?;VcZGBUX7z!ury}$&xFH)QHmNU zCBP*qiXrKStaN0>P*p=!crc|gIK-Z__kYmrD6w!=M|EBG5?Fn2;?57qCi&o$D|hxh zHtTf**`qBaXqR9vciLSPxCDj)^x+8Rol$E{ERARcLvbyO*PNoxmo?4dVOT(|9fTDG zy)ueVLMQE`nABZR4a9-A%fw+9^Lo5FM)tdeiVXsRBK%W+eOdzlzZambeH5rT~00Ckcd+z#914iZx(dv5%!xUbo_Z0K_7xUokwi@-$ z;;;6t`S$U`(Us$KhmYM-{Z(mBekeY)7EX??9xt3&N4Nd^a52M!ukH%yF5ULt z{`u->cMd+ZFMTrq+wc|h!u-9NbItZ^SHA8Z2*(E}((6cjQn-7+u=rwcdWx|w75+3I ReS4k`?^biY@nGrJ;eT3I?2`Zh diff --git a/media/atree/connect_0111_0110.png b/media/atree/connect_0111_0110.png deleted file mode 100644 index 9f6db4fb65dd4e095709d614f3c5db9d9b06584b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1620 zcmah}OK;p%6n04}m4qN97DR!CaY;y{`0aVvnrS=9n z6O75mn!U6VtzEovE1xeGKL2WZ`ejj4-uUU4%L`Y&_~iOB3rIKCH@VBd|Lk(y*#*zu zO1J8377l$0ku&_!BW2Xe0y*n)57mIXe8q&sC{tKQ%j!O& zE=iB3N)sG=QR;;O&uEbo?xtk{^5`b$NA>!EJxF>wfFg1SMY1X>vhTC3Gjx(J?Llxa zms8P+y&q9|gC^l_jOo%I4bt=daDoT1(Qe$zsvuaVE$Ra-fz{N(QY*E3V}NE=H$6Yf zB0%8*D)rp|w0SHa_9sifzc_>q_#Q*{>tsL>!>WdOm*rEdmIbz`MMCTmEBnmps9+RP zUQ`VSi3T>YSab|WBt=s%sD?r?ar->AAW2bxDdPcV$paExMA)PX5f!3pqLJ5i5fz9b znyQ(juBw=bn*I1!Vh>smwTAOyJOp?Q%`sHsY9hi;UNrQgD;BY-ihABqO_!=p&ZPoR zu(cehuSbcxs?j4X# zdVR>%bGAG_>vap^ZV3t6qeSRA@iq#Y6ovuxVHf6|veyI^9Adhv2zv^>Qg9rxSinT2 zs^$`;ft*DUu^XR+ZrVmMtu#S35C_^W6NjJe?Z<^avb{~2*dP!nA`S^8@bXX{C7B)e z;o3^_X)<(iLqj6t=z3uSfdz5>p~kGiV59*E5X0E3zV+D@jLava)vF1|jX%roGu+*e zCc*h;ty-~Xf56u!8+_|%=9#0J2cI5EeCzmdj(>ON!JIpPbLRH^@l8Saur)tBd-&-6 zFA2sYFD;HdJiKz-R^B-`H9q&^)o*|MRXCnHeDw5l?N{wvcZwJ8jgQ{5)~3%r{Cn=> eKb|-ldA`Ekx%*W6N6#?X_^vIjS5FqM-S`){$?Ts1 diff --git a/media/atree/connect_0111_0111.png b/media/atree/connect_0111_0111.png deleted file mode 100644 index dbf77128198a542571436f260045fb20cfa0eaf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1604 zcmah}PiP!v6rX4-HZdUfQbploT0#uW|C!zWhTYaAyGa(ZaY>dMPv)EXzTFI&f1R0R zH#x;1n3jTAyeJ-u)iA3|9-3U2yW*(^No2y_~xUjf5~~UC(`xh1>y9k z*Z%_JmF8-zwj8fsJ^HYC@BVwgJuVbVlB_htv(3h; z?M8u2Xyh=t7sP-Pgz~jsOxzuoA%}Imup<8b-S;B$XhnR-YN(BPk!^b`2MOCaSZlcl zJ1(Z;wX4&U<(>@;0m}&L1%8;?y^5F<+wjcYiimPZwo?%|8%?wrB@CIeDXUVg=k4ht zkRVh}Xxm<|E)7V)s)*ZJ7Tb!_?RMp^AxB9^(J;n}sw=uK0YXam!;JK#FumL-8PHT& z>Ly;Cc~OWsEpejVtRg}k{Q`ry(HOCZ=|BfiMCp-O(PUK#0$z2FPP5t`1P^LC6P>p9 zW2UULG}=vER@-A?c6k_1-BE13oA`Mb)K!?z0)VBEO&g_Jt~Z(^H1E3O1#un$3Xf2k z*M6$aGxhK{dHKWS5H{j_2084L13?VC8o9f?o_e(+@}xG6T#wrMqlO)<8dgEVhFXwJ z%b-%pViuUxaIisKS}G2C>S3CZ&}Ezlm}L)0idx%pREKk`!i(lWD4K=py3E+fY16Nch-6Xx@^&m@tw>Nh^x{ipbL<9@Brp{|C)KB?P~6|6EtR6teG++=CBfgWVW&4V=D* z@>#cu+ixKydyI+$r@KwU4ufR?bJ&M@XP&jGQRWyh92Vwhtx3cvV5wcuEmqVV!!iEAOU)Fm;h%6T)YoUjpY=y*l5T z{n_2R)I=x8GcO;{Jow{SMkh}W=g{q$2Xp?FBN;t85=H;smD$BLJvJa_Ahf8KZ@I}pBl_(AoThl`&yg<$vnT)QsghH>k~Dh~Zb1_7=`ufiLeHB5yo_?(4R_^g+0RDWm|82Cb` dPhnmG)3iLE@4hN>=0M{aJYD@<);T3K0RZFLI_3ZX diff --git a/media/atree/connect_1001_1.png b/media/atree/connect_1001_1.png deleted file mode 100644 index 748a9c041f6970911eed2de6bbacbf80bb92604c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1581 zcmah}J!~9B6yAUX#EBaMl7%Ri4MIlB?9c4p?T_w~oP8Jjkk1_U2~34&XJ^kV-~O<> z_MKgk$bpcC1{y>M1w|B*f(8nt2t)zmrvOnPA-MoirK6(Y&EDR{ku0&&{=B!}`{uoG z-n@6aYs+)9&&&#fFxOsbuESg8_spa4yw>Y~3$K}eqtg(CyYHR-mz)OsRJOjnDBSts z*}uVfvb)-AEhnp&uD?^QTITJ~-}vUrA!%)uq=e9kr7zvF%{xAY~f|Ydw0f zLv2sGd}(gBHgbR=WH}K>;ULPKQC%vC9r)&MMG^~0zEhVrJ6&-pPMK)PhODC2DA>ai zkceU}^?YZ&d9@?~t1fNldEzL_a5$8Qx*Vr{MYC;NQL%zC0tCwTqnwOTlwBB;lr&A2 z(KJZ%AdW;%OWb%juS<}}IKeRKbf)Z4R_XwXC?k?6nye~e$g?icS>D=%;4+sJ(OGXl zVahto;@y<8)*g%U3zKk#Y_npfQq!lZ}*g{P=I z@E>UNP(J)mk^W?H2%GXfgq+mLfgpy{jOi}Vr`@bed{M`XY2Z1gEG zY$03sF!Bt{EXFjoqLB$tJIZnrQO0?ISq^|iw=CWC8AGO4RgpmmLvF=Tk*eyx>uRoz zT|9|@ISruo$l!QBoW}zm({wFU^(>^(iiZrHRFP{EiYPNH7+bE1^{OO#)VUg`A%QIm zLeghS67?mKE9*46?Ye|zwKTf}lKTLK{thB99%iM#7ldphCtNzMS~05?LszYeZDHLg z5wEf|gWlp~nkws%Jy+g=jQ@4uaCjsqO_DR< zQZaT^)6sBG!;Y>whKaybC*ar%{Qduf7Dq{ht2W-(l^}!F_owdifNZcgkGV?cAP|dQ zcL*JqkdZy+Nu`r+lc>*N7(gG6VZjM|tx}6vu4*8=%5-FC20@l-=*V=LN_FDf#P=uh zS?uRSlCnl0R0DCK?FwpY$oFIqWjEfBdfg>jo!kK zbZh>fEA2BkzMFr3;ZVAFarI)Uc7ei=n&QJ40iW*tGzK05OLk#w!UqO)3X+a+bVN>UhH^nC$Gh;J^oVhH001 VBd`43UI8?h!PC{xWt~$(69BG7IHCXm diff --git a/media/atree/connect_1010_1.png b/media/atree/connect_1010_1.png deleted file mode 100644 index 9c8bec9c10cfb3a97ae0a3e115ad695c1ba71d40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1578 zcmah}O=u)V6z;{1U_wCFOOQP@y&|hn{onJ`%eYL{ z+u2%OetB6Cgw&c`1a$KfAJF7SCZb=y71ul>wkf9 zt-IZCY(?9*&h8bz{=QHsz5VB7B&)A}^UpblroGONaOcS*R}hxgz5ZUh*WR{C=*yUf z4wJ`z1SmnM+#W}m9I{k&*uV>_(%(N^NTNrp(py$rZATmIz-ykwZ0DrYCntxbOr_hm zR+lSd8yI|+VsY#bgTx+JrHt5yZ|+tkF@vOsRcWu?6*t0|iKcAIDr$_qV_gCgQLMz& zwR`o=oCK_@bdaWzttg|>NFEt-7!MS!TrMlBuIM@f1WG1BipMBOZcIsXnmS8J>_w>; z1|p}$PI#17CCFo%pdYo{bM_#~bpS<_F^&{XRu$jpS!d`ZZ5%^zp39}^q(6z6(ql<@ z6cg4sW?#|J?>VfA+Sp+CNN2Q+oRGVk= z;eWF9XNyDFobMUrtWFLDG3Gz}Pe}MMYZBkXbSsm4;S;{JQR2^D>{V|C5TuiK?hvOgK%u|AfQF+a_xC-Y?WCAXrxmPX!7}xdR#MG^R>%>%EKZ=e zIGLu(hAxR*c^i@k2{CUw2+c~eH)4j@E!*M`GEaf<6WWx%ECx~&#% zP48>EZD_WxA~4k%IHaCC`9ElOltj2H(|v7v39LSuyYmCG!`@wR<<6leX1#7>GA$v& z$Bat3lN{h+z+f0aA5LN3C421{Wvyho4l2WrBhxKlu5%g3b&{$chqoVM`zF1pDxlD#BZed7d98)h$ zAg~~AHq^K^AB-#j0b&??YZvh@jLa9J=6)gwD=+c;ityRzYv9~$)ocBme~`OtUGbH3 zS=_#Jy5D>#T%3M!?g;why%!s&Z+5%CEbXs+`pNkR*SuP~nSk^Qyc0ZA#2E~!6XTe~G+6tX(>U)DVkIiF|c wDyIDki^^Lr*-2*2kI~9y-N149^TuL%ks6VMyk0UdfEF@%y85}Sb4q9e04+I1>;M1& diff --git a/media/atree/connect_1011_0011.png b/media/atree/connect_1011_0011.png deleted file mode 100644 index a396d8571c0026dc14427b1d4453ab4f84a15c95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1609 zcmah}-D@0G6rW-%G&P{uhae)ugld(}_hfcw*ln}PZj&|HxNMf1K4^1i?w#EsJ0H&6 zWH$jJmQoWC1$_%X^db0AwHU-FAB?^TeNaS@zA1SS1flw;T8fe%+B0%f9H44 z`JHpmetl(Oa^i&vj^ie)i{%EqdG?%r3jW_~wtj@y*;c7m;<%%aF8qVe!TL<-V z^P6jQt+s5tQ6M24IYjCOF`ziEaIF_3cY{#gAuT^F3Xi}2PT+l96yBVvDYbZ>wEe}M zgskqYG~JyI*TTZJtCJIjo(&8Ep@{DVoiMd~MIj@$;hULdfzKdxqbRJ^>im3^5Z;gs zNf9eOe@hd9gy#zh_UuOa`hWyhMWIb;Y|C=D+m*Vy6eTTLwJb|kG+EO`K#1vfNKsD= z)9F6RfTm1RH}PZYMkrQpwq5yI90}SF?ZNwg?106sSxrbs|l@vJ$SkxIhrIjsk z9>j7gI&E&pL~f8Y+Du$h*&-pG9=c<9pJ zs-ugZsUy+IAxE@aB`0DlkF^CzD0B(q0cObu5*5!_X3q3P#c&nT$eTn& zUfvN6>>(>>n#joKhyHIQKC~X{jK{-xFz_h4f^#Z%Mb}ha(O|*kRZ|yvbEs{S;EPML^NM(QTu!MPL{}ANDQ&DYZ5fY&f2ghhEX3*182X(o{us z45*&2TSUi@Pv1X{JlaJGDYZZ~5C_^W6NkS%sK={AWVcP2*dP!nB8~|p@bXw51({v; z@!SgXX)^S2V?!e2=v!d|!3z9_LyfIQgOP+FKmub=IO5pQJ|8dMOgZkt^Xxgp9eg?k z##_~Lsd@QlcYUhPclIRy&coIZ7nSC*e6RiY-j(CSi|v!C-9Ns*d-t<_{`2*vr6pRr z@%`I(UOqlN{P4BoEIyPzmRj{eC={_TBJ@EY1o1&A_#pbEe}E63J3G5>A>hL7%sKO& z?|%2YXU>Psjn(O?b5k70P1o0|Ex7aSI`KUGzt`@354RJY#l|AXeRbp1KX?-2iKMl< z#69@o{GSlaG}qg;)oA_F^$!fg6eXp2|LcnvUw!xIr#JiB8HP?<8=Kt1@4vVlH@W1s zx6-Y~y5)qvgh^;qsqaUC;<)nVeuSM}n({X7ctJ(@>(RFY?~#h|mflbr(K6lf)(&I3 zdAQMb4tJdr5iVbvo+|e(VDM>*`M%!`602VkGGYs!Sy~qO43h3vgsn!CUk+o+Ymz1@ zVy*A(BLPTwz8n+RYE@Tq5{N3oPMSuREcbdnsaKG~xFf5jQb|^jjF1QjF*yiQ+!upn zZa|XLRB7VGUX*%az%yEGhkI#7038E?e$;4;*n=eZ0UVM0IFeOKk$s=3&d^C(+lS&@ z%dzOBeGpN(MU!wZc4%#%2I<_eoH(P}XfN(&yC9BCyVM6*0;{Q`rB>^W<_OJp-SPY= zs{n^bsMK@+^X3^n>`f+rxHy!J_?|%y_sM`@hE)xnJ*KB#tq5#Ui-e9xtn5)yv4agn zL|o8HqE^&h5i8ggQBgB(yI>pCForz!AW3oHP{sqyk_RNE0#%5sJEC4tOi?pbRkTf) zh^D4%4nak?NYSwVjo5?M!`*Q`jE4YEfv5`Bm<|UcQ3Fk)S=1F!RMK_DHi`vP6?o!U zD`D(oI5N-29V$mbN8p*W)?%|>5s;+hVY7=<7ogDJUO>WLlIPo=Pd8J{tWyoWXy_V3 zx`|ZNDCLMZX`DcBF)~$=3ZR}jZ-IEQ5VN*}(rioCa!hd=#_cfdRs^PkXElR&4fr?+ zPH`2d6u1%5$DDNmsc_V;DHau57d7nYqOB4|Bt>c< zSJzBpo5T7fbkiP=>0$?519PD5GIRL({5)P8B6~ZOnGFVkBjT7r0xys4qb#$JeSB|a z`8XQ}xUnITaSXCBfgpmq;ZS4IXfVMwA zw_aUr&;RIb&o=Lz`$*b;|49dZ_tMGM!r7B6GtJYrGuth6dfHg{`{l{W+LiW~4R!=#T&a diff --git a/media/atree/connect_1011_1010.png b/media/atree/connect_1011_1010.png deleted file mode 100644 index d4fc9bd31fc3a1df82e77566d3a3ba415d9c1751..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1616 zcmah}J8T?P6rCtZfsG{u2{IAIFqVZaF`u28ou76Suf1z~W3O54RWKEP^WK~F;GIuq z#@@B7U@Q@!K%z(yA>@uoM3F$Gfg&!DkSL%7Iw&F}L_tMI!F{uzIFcnsn$Nv+?s@l~ zci;V>xwbShc4ds?xQY65bsgS3yDyxF=ew=;_wc&Vt~4qf_vOdq|DuavpG()5=D7zy zzWgT`lg-suZ7E(g4D0IQ`{w`_QSI9_qUG(-X~?@&0<4o#0zxWUp`3a z#=%+(AM9Y82-mJojFoy0Fa$J1d@tyPsnaV9Ik5xZ%qbSIYuh)FBb}iIacYu3aQV z)kMW5s;Dani?*fPqOB;%wu&aD9vSk~!!$!7ri=%eB_Bu>VtAP9mgw0o7WIPWiWb(f zsHv`7L@M$q!o&F26CYX+bx!BQcnI)l0Jn&VMWkWKLo;d8iO;YGBMpjTHO%-^iyaUODgow2rgytnV3kgM8l(eF#Qx;ejJd5f7Yrx?k zC_`10QQ%Tk)lp2RptcICqbZJJh+ry1a727>|No%*QR3k$_4jqzPhs`_k$Z4JHt5Y$ zu7R`T^Lej32=_}!(H@1J?K|-{3fmNh0rX+tlzAjFpue zwKq`b{^KjR+@nv%zv9k+aeVyI3isi|!)a+-x_oo#@)kUHXWzRwb$4#&o4MP6oh&}b YdDox)=kYIi6njbk9sDok<$fvFWs$S*Usa>Q$#q*Jr9K z=}uOeabXle*MmzJ@iPO1x|)T-g~BWx20`2@G6=2&-Ma9;>d#~#U_pKEt#i-2_q_Y= z`;FD5>B+N`9LG)9ma9#8^XxwL7(B1FIzPbcRHss}aNO;WrvAmJ!Jf#POY_{VAJ6>> z#m|y2f4l;}@UfPR|Fe^?bd);*vO& zuot(f)DIItaa`$QKfz>+=6suWf~YL~b>~}w54^JQhEZ4Q$pYOBmiJP+wzt|Mdt1cu zgo_uZCrf=77($w3z8`j@%AX_tlo%V0Hiywp;y-Wa1<)tw+riU?46jLHN5 zp*D}?!~PWMj~0ipG2bJ|QJoA3Vz||qY_oi7)v~}Ab-kDbo?Cp{itii9@kGZkeNneH zO>Ap+Tg1fIJ!BJuXx@mY7G*h(2xUCLECoPfsmLM-YocMz_`Hrp{hA;3QaWBVdh+)fu z5O=7YL>+-=%DR8^Imlr3-LZRkK-TEhW3HjI8}LQ1 zyO<10$nXw@ogFyICXPB3h5_{9z!RLX*NS1OKB1O~uu4Q7wXujz#Skq6Q`=At*f3)h zpT&OO!zrzFKs68t+O80XKR?`$mq*CnCS_uSK%j{DP#_RqK2}FbW{3TFZ6*0683wpx zL!#gqcwqv81#zRH#;oyRq!9>^!q{v6?L7e_^QmO{O2%x1vu$gVA} KR&UL3T>lU4m*+qL diff --git a/media/atree/connect_1100.png b/media/atree/connect_1100.png deleted file mode 100644 index 779aa18c14eeabd9d78dd4d5899bded4d453e607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*Z=?j1DOs^US?(%x_Snx zs_GLiUK0cgF_i@Q1v4;|O+IS@PijQ$OIv&xCLsPWe|Khge%Nih$!?OxY+N==8xgU0?%df7+4-M5^dRDfB_Gh%|lbi}+^fLq&?9h=On8gHX?%o!zDoaA1DUneTk} zobR4{-fgcf&&{5iQRFo*%gag{T}7?UlU@eb|zVO99++gk$fyH(+Qsim~yMY`#)>?L%4Z>>xA zwut2lXHL(}R)#h(1T@3^FzAP=J*)~ju?^qMEem`O$+oJ(Myt&)MhWG0NtYC{G4ywk z03%#T8z(PAgs&Z+|BF-kCqTdfIum=-#KBJvQ&vMMQZ5U{Lsbec7GAh^ioNOaoW zjj7zBX|$aX+Ss9CHa`w`$s{)3PWpKjE|F=U1^`Q8HFdJoa#Ri5nZhe(Zoh($ diff --git a/media/atree/connect_1101.png b/media/atree/connect_1101.png deleted file mode 100644 index 692e29b8a35c23df53acc009a1e474c706b91ec5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*Z=?j1DOs^US?(%x_Snx zs_GLiUK0cgF_i@Q1v4;|O+IS@9eDKUk&u)S zkYo@|;B9SUn9;CNYexPOhRcs~*04O1mOj8ET4Kh|(!k9jwVF+mn@fqaV+EV^ xBCZ*1){8heW?3!f+_8&e5ob#SD;p03!+9GK1D`L}AAxocOLeQ1v1$9t0197X>d~Jovtu+3gkr4$PnT=J$Tz zd%y2}?~T^l%G~S|vkb$`HR{ziyjglrABX4VPWM}QO?NBJ3d4N%_Q`+I4A@gqdu55a zdHcyfz?g5Xc4{lZ>bWadHBILQsc_?q)2E+)>E~Y;m!4gC>ydwGK-^wiXBL0IV>8Um zlGE9UH=3)4<+}pHzDb0k8vu%7O6P|GvbIUgnxyM^W$v%9zu{O1m${4irqm3U$(B>! z3(5N4TF2Vkwu+cLe{OEJG&F$0B{5=$ZqJL1VVO&a4fv*Rkz*4`yj|utnk{zO4+*OZ zsvz;Tp|hiKK*F-65Zgw(dLbhLtITc1abSqzU@#B{Il&LRqFgK%MM)7Ag$D#5?Rqg9 z@?Nx%l4LYh5?P@W#E$Q=lopwOKQ42SN1C7;G@D~~FUoWPMZ_TrL|KqT*QHq}=qRr3 zKya4Jk?5$i8xXNgBEKJ6q_#u6cwrQdt#NG74|_=!*b+&PxB!b_HF><$N~75tqiNM$ z#|@GQPYjFL0cODg61AWqO(HsPnlj>5NrCvh zsqzF9y{G^&HuX{bbD;yRhkBFwP#z3CwpzeNgfK5_D&&Doo7Yv<;_U*KG@@ZulVy&@ zmT|!kT?AX^xTs6S!0U1>Ro1Aq8f8uqq|9vfP;3Jf`rGlaKZr7a$8pJejHq<7mM>^| zRhAW9QHsS;T21jT;X@%>(zqgk*tCjHlh z!+}wZswgJFB`Jy_6%1MF$cmxpMo#9zlty45JNEAXL6f7z!c|K5weCc)`tI1B9gubM z@`NjM_8d0pbpu&x2@%>Mn9H2j7V^3Th5__p3UiLwYehwhXV*je7Q~H)8nwoQk$50L2xHH@c%lj;^TD8gDPowDkJ5XJdGGys zaGH&3rE}&7Yjgh3{YK;No%*Gz+g(l59-4a$uKn3tuN}Kwf8ou;Tm1l+`s7=;6#n|^Yk_y|qVQU-imTxY-Er3sBf53C z*{}|GE!`GwT%VmPbPZteXhQg|*YabdTNF}a1HPGC68IF7>=uRXYK>nBBFbmQtcX#$ z>$VjENO-;w*^W^!t@cU4DhfME5*m`!>2$C=>GY(;LExPi|zT4DzKNl}12dI@@AwK`zyTBTYWqFL2V*9+4K zP3 zku1)ktgdGfp&196X3iq2nNv*S40tMjoDkolj0c!S7f5o{!5Eu)WSO#wvYMtN4cl3y z*$Q!pZ06;RI*5NYa-sD|YcwCmV*`(qRdTwbSdfCLqAXQ2NF%z6G8rdtXVe@p+X8P} z#%d6G1h&leNRvvT-xPSJtg%$96a_`ZeY4ggi33pRZ`ZejPTco5T#s%ggh?l>xjYbK zUDI_XuV(s)+cb)yw-}j>#f&2GOnC#62MGylI|xlnGFBo=k|1gXL8~aREO-{v`&Wg- zVUvWENJ4=ND~f^hhO9JX#ZWXu))1I@01j-|Iru+ldX#v$3cY<@cVk%nVCe22kS)45 z;_5qFE}!>lGjy*Cd| zr&3yKT>IX-Gymt&FXyG&`M2Czb8c>~QJ>k{+dG=Vi;JHxES{X4JpFKD;@uzjFS= YIM@DU=I(Rl@q~@;N_n$%x_syOUjl#Zu>b%7 diff --git a/media/atree/connect_1101_1100.png b/media/atree/connect_1101_1100.png deleted file mode 100644 index 7a41664ef35b40d939ea8dae3124fcbb0641e2f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1615 zcmah}OK;p%6n3ClkdjTwBBB+tTmnJr^=oX8ZOycqWTwfWOc;_8O(iONeSJN#GJaq? znMow1CaP2c3#5t_VhM-^st5>Jbb-1+s4NN#gxDauipmE5Ko^|rc`%hCVabnk{GIQd z``vTTyUn%b>8a7Y}b4hFSdL8zjna{r2bF+{+7#uYRqZxyAyr^|cLd;rMrt<0cpV z)@HWZShYzMh}exBD)xdHP#jmf)Qd6MrWxdaBg3fgzw7=6gXWOzmD-$cb(EW^PH~b4a#b7B(AAekn>QuZfx{ zqgv13RRtj7`BLI~_Ima5fCQ|vu$5)8ElJ&OSL_zVC}~TIWm%G}N~($gfzrJ&!#xzH z=ldiBnkr36;>VdEg*>ChPPCJi1*oH6U=TMNBla*I=m3gHJseAlC`&=Ws?O1AR@;T( zK`keu)7D;0rFEJ{I|-q+T^eTRhv6<6#l|~HCtrn2B-)_?z*5Mjj8ZMv8_f}#t-9?8 zaUKB*k5HNK{in?n^{_vA`NQN8HsX5%Ib0_Lf*5u+B0H>}dbKRDq_!Io-?j5kOExu= zx*C!TE=8K@SO{yfjtp1Ph^IOQy&w;H>S3DUkWj`0%%Tq@gvf=WNi0+>U=3-iqaj?B zJ!I%k!BwzTB#J(ae=YH$^>Amr9>(JWk6yHNolqAkhOQz_@nqy^*h88DH55F>QHdk) zF0n61Nq}L?`~bJ96o+krXUf`@X1y$^A{3jO%?{2yfI@%!p&ND6fxqPkbR)w|I>=Ns zbWKqW(^L(^7$DxDNeaEi$P`&Dr~=QFx1o5D5VN*}(0obuQbKVSC9Npxlm(WSXEFVM z4LBSw&TtiH6u4wnwdJC%s4YdcRnyiC1g1O$N3QSf{U0chNZ_Thb(AD_ z*vHpal1`GLj~g2jIY-|M69_Db8xA#QjRqqPL4X9t-aBjW9l*$ZCSJLka@^_X*ge60 z^zke>q4_JV`Jc)4*}rep>$T=eb>cyLW@bh+GalFy#K^;G4{qlwjp=Io2kvsdmt z`ec4{pt2hbMn{Q=bC?fc6{@thsTG<``>+d bFkR*zA5EXR_sh3$vdLYqtyS+VUO)U7=a1?W diff --git a/media/atree/connect_1101_1101.png b/media/atree/connect_1101_1101.png deleted file mode 100644 index 83756fc25f9eba6acb2b6747c288bad61ccb204f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1604 zcmah}OK2oj6s>#)oDA7$(BKSGR78@+>-u*6=$@G*-ANkKv8PiyaUtf_t5=;OT_39| z=}thHnT_a1(T#{3XXQczvv45_q9E=@1(mpRBcdP*x^>}w)t|{gz=HbRTj!p4?|Jv# z*IHZavolZ5a2z+=*r>JP&9i%Y3ZA!}&e!mo?kqQ#Iqu#Ym;S{Uz@Et3>nq&J{VTtN z@n~z)sjnxSSMR)PeE6}hn=k$NGb%~XeDu%j44SvMwz=!Sp86bjVI^>O@}1_UP2#YG zz1XFtewYA?<0{ws2`2kA=Uv(fqN?!Mr=JUa;8lg2<)+k3R_R`_ah%fa<1L3A?-R=t zu3ep-sq}4N2x*S_e%OsNyI&OwVjI4hTNL;LlJ8fAoo0()jZ?~NC9Nc(dOtW+1R&x0 zO6vJ`yS6qY0jnzP<#}R@Vz1XL_0&?Fc0}2-EKyQKML~c-*-@0^K8mu10m+c2Ml+HI zNgl)z&uFn5ALLa5@)#r-Ce7xUJ<5hUfFfcaC!$=E#4u!87w9anA42djmvhmXbCgiA zO|$qQCA5A>qkLf$?vZh9a*%e5Dm)_6E)4;e!D{k&sr5#)HAb_lJ3*Kf5uorGl?VO< zZ63;p{VCEPEe>I0zK4*bIvEheuxl|nVEHs^Re>#Pdoc+-yZCe!)$+}@t9AN?P+I7&QRmBGGl1R1P;Gzh*Xl4bFD4r=WE^+tF?LUI@4hz=&fP|B*;)LC z?9Tsr>!(wvwevl9zcV*C=d>Mo-kOn?mOkNbzx~$a=JnPiPv4!s`Ne#9@q@`x|ys-li$N uQy)f2xX$VA^_{~TIOp9Prm~kGm>8bV5#=fAXA%M0$l&Sf=d#Wzp$PzZ96k;J diff --git a/media/atree/connect_1110_0110.png b/media/atree/connect_1110_0110.png deleted file mode 100644 index 35a8eb5a63a80251efa8f4f692cd24856b7e71eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1614 zcmah}-D@LN6raWJqFIB&iq;QgnEkL_xifceCdp(-w%az{Hrm9PW^087doyz<9h>=Z zX40g6(o#@YMIU@o7FiYqQCZdpp|GIU2L&G#d{FVlH^C?Wfqn4Y$;Wn~;J|#GGr#k@ z=lt%u=k0oJF`arMB?v;gvQ%!sTjck|eeisv+5Qn;6YbgRtRQ^#{>eM^IM~Oc#^RiC z^XI4j0b{bh++0`;mUB7%!tR^-FTXx}_8BDOSAP5J;@orVkEUPc0db?YB3%6Y4_gq9 z&pFMtc&)l@n7%7h%WpBc?*@PpgyKv;pynov#TIKjUP=1*_P3JgSS9JDY!z37dA8v! z?SyP)r`9xgHqC-1&0I*QihTnZTozNY?{>V%=$E8~*nn^DRwOZj#G55)ty&l7{g8>8 ztjQQH^qnmt0f{IUL(4WAYjhq(ZYU^R8T)MBMtAESBI zZO09g2vB&8iXHo|HuvPi|0L;;7KgAg-#y4tog4^a7-iq=@_Z`glEfD^EZ=l2Bl*ly zm1qQKkXAMqL_Ln<2=ADJ3x}v87pW74XI4ikfsxg zC^j{e&u28%Bp8!SZWRAQ=s@ey&f$DGj|DuK0^C$pWUCn+X?e{?x<&JdSz4~8TV??> zO%g5Bxa5Z}g)MVj+Ga}NwIz`&Ys}UwC5gy*Xx2M4wgC$L?Rb{oi-!KDhFMAzEc&1ii({R4iwRBy!~qNFF4lyzL+~Dan`*8IAq0>HD3M#Dhg1 zGx%49!(q{wmTAm@3ln1CyrGh&N(_=Wa2A1yM_}J_?Ct-9CPztxt2o%#B`1Q_x5w_` z0a;k!mojaJ?ldpHqyngP3z4PK{*Ur54{X>^)tB>q` zLDT8(i?vK+_nlk&`}_JwpFaNV^uff$^H;8X`0KmxrPo*QPhGwJ*!k)2F5_D#QmLya hZv6S=;=zB#wP`yauW=Vbr@ diff --git a/media/atree/connect_1110_1010.png b/media/atree/connect_1110_1010.png deleted file mode 100644 index 4a08b8cbe26455a43da2dec33b0dac1f02efb2cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1610 zcmah}O>7%g5Z*wGTDOsq2oO<_R$E$1cFuas(VXX$n3Y50G;-uwn`XPWbsd4~Dyy$k=~vtUmowdDoo;dd|p z3C47FwZ60*t?GLI+WtGngGY!LuUvWU&QHJ2E!_NifA1CzNNa2B%-kQpxD0c4!K-hi z8e*hDphtdj0vmqDlej? zp0^`&K*F+x*m12|>BfKrtRmM+)5sEpZnw*K6+Vocf@GSeAj*O)BS4^JH%M_01?aEtf0R5t?@0^!zA` z0EI`W)N}vS=81afo2>j{atIsoJ%JqdNr51SRSNA6t*2Znax|&sgtq5c*`ur)s_AGV zGKg*<)iq?)z_N)%#ny1%P$k954|&Q#lH$N7ln0o34@i`Tq7z+qkZxcTsiun&R%H!o zMqaZeMYLsE9LC>@Js3UQ9?Q+$-Sf$Mim7yxt{J+f zijtUDw7fEme1pUZ%oZh+L|&0OmMU*S@gO0lV+WyGOV(mca2m$-Fl-k&nwF(8{dX1k zI2@ef5>5$liLz{oh9${$Nw#F&0>!};hv3lh+};0!W}gxZze4|9SG)wW?~dGq4`iKe zkGTfUw#R0(ZehFMLV|aQ!wsBv3kOXC%K+xE5A#krYZK>HRhJFakQ4=}nr$P~&1=Zi zb&W_OR*2{hi=Ib-x+ndwX`|X3Pza3mXnK|I^w_m+`XRi9~%Cq-AJwAFcbME$6$H&gm(bVrx zy@+-Fbw$rjcDC=HpP9XM>6635!)H#uIG3B9eLnXBcM)$-O-*f=9{v2oJ^c3sbMwcG VAANlCc?)te<)yXK!-dWLe*x9Y<(2>d diff --git a/media/atree/connect_1110_1100.png b/media/atree/connect_1110_1100.png deleted file mode 100644 index de9a81ba185ec167d564ec7948e27968de28282d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1607 zcmah}OK;p%6m}q$nv_9EP^c;-ELROviR>PUwr6gfegr6vD_`wJ*x9BjZ_~CHRD`GBeJ}7HsR-AL4Y?65&@FFyKc?&Z zt1V}L+cAl7bv8F$?peU#(-iYPzY`=@uOei`7JM_eB=8v|-L425jV8Yk#*{CJ1yM$| zp0}$CK*ICon7Gzjb#XufRz=uK)5wyfZnrCTH8G6al46>sB&(9DB0!*IFGz6@1<9p8 z$$+Lx6DRhf)C&Wi(PBH?Nh<>6(NEBi8jTTqkPLJHMWh~%Bt?`Z-)C88=p?P}LhvA$ zQ_)FlFQU>KO~Renp|xEaq?d-_#2Lj#J8>tgf;bZGP#<6ktfq{XTB$sptNu&2#y%KUw<2#UX6O_Z)IqCj){QRyA~XSU&Y?MPQ3sBy>DtWuLNJbg8Bg zq!^BZ3Z`qKlB$`=)i70Q(bfv8Hsq-XNs0r9G9F+SJs@#qhm@#MM5e1?RIp1$WSc5R zhJmT0+jdE}@i6|~*n`%?o$-7a4*?#?qNpo&5m8qwqJp6VkLwsnh9roisjjXT1fDq7 zVi@}vw#@T!n@Um87I>ztHP@_H1XYv=X0wA+7ogDJUO>WbGVr%NpRT8vNvG&VSf(Hs zG*i`zC1ZeikH!h~79&$+QBwt;DQ`jYAR%UL2ccO>){JAn12G8Uj-uf_g{(bXjcZS`zNj6PZvT@yAY&@mke)G*{%>2vD zB)iF}4XCjQ-o;Z1=%Fom@Z`Zrp%%f47x5-|5IiY(5W$1*oBh)i0xrzXyf?r1``-J# zH}Avd+VXT^w!m@RbbY1Pf;-QyvuEJ{cBl6(+|Ksq8}l6Z#m5)_#pl4DN?Xed+=C~t z{0_#Y=4xkYIbOYT@ImR`m!?_X_~{o>Qs(aeb%#N-)>@mp_S?@c$DLd7I_uebW7Q^6 zAYmu!QfUyxfa18y)j^EO7R~rB?RjBU_~Wau1>SS2!VROLG~z|N>8$ke9+z*0C(ogB4XZ!{-pR&~z{;yeNro}e<% zeX7kf`LH*6`s2ePY{K^pa$F|^f*5u!B0DUfdaWw3Lv1G_o@3{auC7spi6S~BLlJd^ z4Y6!dN7O8rYDUS#gji#qdYEQ7B$V+0v*ZDZp;J{am#K&-(M4S|saSRuQ^criD#%bu zu49hl-%LChJ?x*%hw(VT(=|{@H@am}?J7vrO;r~y-2hswmP)FlQA<$--XZq&Cg>!BmpQln?BB5fWmxxp%eAfk-y^ww4GrlooYgThGtosfy$Z+ zY37k{(1tvbL8FjH4HYX? z?>g={K8@V0j}toI1Jytr7`t2?zBuZ~Yhz@8lQOYEAW%enDv&1d@`*YsGW*z1)>e^E zlVONEu_SVip%*p~SP(Z}YRsA}MjCH;%@$;i!?-tL$j9aZ% zvc3J*rI&79IA56hM_PNMpoov&Ke%&njcb2%JnO&i&wli76c-=@W_ci7lIr=s)O^q+l zZr%Amw1tHyVUupXI%}nWwf{U@R`B_uEd%fU?=}rbxQc%<{hcVS@QOG8 Q1<(x)p00i_>zopr0Ay2Av;Y7A diff --git a/media/atree/connect_1111_0011.png b/media/atree/connect_1111_0011.png deleted file mode 100644 index 3229c68cc2effbdc1fc1abca9d9d35044c20bd33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1630 zcmah}OK;p%6m~?5Xrj=m8X!U}j7v&E^7#GKOq<7~$v`HICX+DT&|LdoPn?V&*iL4W z6;u_J1!9530ez1jX6UT4~KwK<0Q_??M=(OIy^(#Fy}bNKyJ ze}geyUuiBb#VfjQUcL3E@zEzUGtcrx@#fEe%+9~C`S|2E4agd+Ys~ClzjzFDcHVEc zvQ}-ycB7z(aO99;FNgufFr{m~7`ZznV;$1=!!q~JXPl&=75A{O9}SuM&hWQE zsZTPXsgTr7{5bQYkfpT9iFUIx2YK`p4B}dC#2%&t9Y7JGhhjl0ib4?3taEglE$%_^ zAeU3oX>&g&LW88yZsL-~JrZV@hT+&9#m2iyC$9p#0_l(dU@5F7jh0%f*6JfPt-9?8 zaUKB*k5HNK-Ph)UeCVG%{o&#eHsX5#IjoZcK@7VRxw|x8iYnOp`~7 z=!Hrcd?*hF9#b?_tQtCxWuow^Ch5GT=njuHQMVjP!kTV! zEOzbdQ4%27GCx3VBE(^vW2v(CT)kT6m8JN0EPbcLmYL}fxqboWGzEfI!V_I zT~ka`Q)N>#tN~(!Bq{V3C6mOWB6BQN-iG8sLPXmRLi3XBg@m9iN}5sBDRVRnmd5n| z)!=Yol%WdB2yltAY>S31$xTVNWz|+y9!zlvjc8*d{Oxpbz^n|CGJ~%jrjl8Uj5;E5Y)j%9*yIdUh%3wd98zQ?~go+IUfg=2Ufi#X4PSjBn z=wUxuTS+)ghCc4ZkjOduUYI~&LELbtQEN09NeBWYF!mn)V*4u?ndjo=8!5v~T%`9H z^Zp0Z;Dkdo*SzwByE$FI+qr$2y?y!U&eL~0&%AbXw*G7D!i`tN=69|2!r>d5`r|j> zwq{h7dH(45_!z(UCp?*p1z~LB&J#y(7pCMFH!dtqe_42Bsy=QoeyUw z*-h|4_r(eUU-YH=pk-eQQt&~1@WFzj5J-Ix#5W}rslN0d@WFFuKbk_of%!ORe&=`3 z`Q3BRM~&6xxtUjH7>1dvtyG%uX6Ze78lE4t+@Ik!=`Pk68RqMcXaB`hU{55?wo?3FwE4F z*IG~4>$eOm^mEt_O_J;S5ug~Rc&!^@Ym20;Nn9@|aZkVdo?|_`#NE!HjR@Z)Nw^(bq`E_b^x`Pow#Kp1cHGXYuq~dni4U*@RujidE!XOeF`8EG zdVZ8efWl)`>N(G}c`hINCrf{{IE0P)oRwpehmJ>sbaNs0rDP#$2;c|f9@vZFY*iY!ymkV2e1 z!bFvkEa`$kbgap;K8k-M_Mr7}`*=Q-#|9q9G_fOzDiR$>MT#Tn$g~w16|kfhG(~k( zP2*VGGH!&ik73I^AG?H)0+(Z{vc_VgR^p_bFf?A{f%k#-vim7yB zA+HwlvX;+FnkXsa5b+j?6X-2UCJH%O;#jJ@0m*}en6@2+W+fTrnBX*wTVdEPaWo5- z#ti<|;c(bE#TA?q;1VRs5L82yTB2k~iXkcpOko5LZO_^LKWKK8Sh$LVeO>VqSbcZw z9v+Z2^6oL$(AoCbtk(@}4N6Gx4uPE=IIT?_xCDj)^x?qcov_!kCTc{NWu$3&5h<$R zAXCT}kS>_EteUEdB~WV+pM*}@!7*8MK{XHu+Ab4^y*%8H*GI_CCZS@3K%fYECQzPZ z`D1kyd3xB7*H+|Dl3{>5HY75RffptaSP(ZFYSbDJMiPJkF^s*3^WGyEnJ-2wZzl{h zdzRi4%-zoyz}c-;7F(Bpu{IVOfA;UZ%HHc8xIexWHD7+nvrqbu?_A-Z9Q7Y_)3<*( zI=a{I_uK6@b8h2Q{OSH-vwV8v)KT^30rtL(&h7VlM+e^hpQg(T=cf+ef6v~!aM+uA x^MW(K|Bo>L>&6SpL|J+I+1)Q@C(pDeCz!^SGlyrV-B0K+uT@to50*B1{{aj?^=$wE diff --git a/media/atree/connect_1111_0110.png b/media/atree/connect_1111_0110.png deleted file mode 100644 index fcef42f80969ec05bce1cc4400865a72c7f4aabc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1633 zcmah}OK%%h6rR42nko>fT||_{WSW*xG4Ju%GqRgFahe##!Ey|w8=C8T?>M7)9?VQ) zCu~YV6r@OOP#1^|f(-~oV$l@~vWXCj78VEvHkGnKLaM|MfCcA{$8IV`Vx)PTGvE2{ zIp00^d|Y2%7#(?WgyXo;>SCn{(>_+8gMgmqzSWlABmgR1@D|K}#h}yDhS(dD5vZjfE5aVq>K|Rrr zuk=YWnhK3^UqzJrv5Sq{yDfvQE))GQS1E zSuQ7{; zbP<_IY3YT6rV=!We?4-c^-$+{K8%L|kAtacsHP>Vc@2w3-m=63#*V0(22l)+nuI_e z1lu=*$V0GYu7}!G4*j;kGiB{^y;>49Ny*H52PF6EH(igeB#23;LdIrJ zFX*{K!8A23L%d0&7Y3Iy9NAFHD%v%`M8 zwxWEJ41L_OA(3+Qy)c2mg1Es@W7cpmQXd3}VC)Ga@4gQs^L)5?E9STh7ukJ^`|R^^ zaCWPea&ziuyf$86JKDX*?_S&gXkzW?)jPLmewjQNyY;s6>*T@e*xrXZqxsY1!-K;^ z?)v^$a6P$y|LBOjI3_n5+H=>{JC`S>p1)MqKA5Q2tJV70?|Ep$5OyF%O`;&7_7G7Hu1iA_$?N~M*V=9Kv)g1L8{EW#tq`=&_S=mW|6x1X zO-{6`n&!l%il9QM<5D+id>;ui^GwV~(N@KOg%SodJ6&SzVl`4t{*= zFEA#IWwW*zEnm5_TP$kow@+{W{2N=~FJFFVcK+SHhtCg5K)SlJM$P{DON*k;%)4eY zZ8ny5JM;_42`yac`VpWgs(iH@A$tR-w1wMlP+^{b_dP?qPKEhUY4D9`0k6ADJ276{ zSuyRM4ZGwpSFenZmb*GI_&7y$*Y|=%?^c+MScf%na}1q9(v1q!Y#8)H7-L#4$OWFQ zb=@tI0TP-n$4*OMtzOGXz^X9oX&UJq*XeW$9jOq;ZB8haN*ph8qR0Y*O}2v+b=e@f z)Fa7hsyMM@H%i?wpb0Iq!p*e8KpwpW{ix9xum?%511Q3EQN#%ap7VW@b%svT+7<-o zxtxkl%LYtTJAz!le`}h2^c5&GSeTiY4H2 zZ2ejo`v{KA^-&vhQP5^+qO3k=)GLfw;B(XPP}%}0^tT&0VJFG`rt9Oi6cOo!qM{ZR zNiB+kB#N4xBR;@!0=-4Z1im1N3{8~RA$gDxk+y@-tR#IQ#wZPAGYq{7L$aVrOz&F* zE{B6sR7EKUE?yLMUeyKB6hs}?GS7m^_ram#wzmHdnq4Ite&yb|F1ZQpzCCc~7i0~8 zdcu`EJ(tdUT}O7WgamD2hsmAxIttnth5__p59Xe7)}kijmSpj)gcX&QWygXuQbbm9 zRD`USRn#!l(~D2SR@y-^o@;|@AP%%$CJsH7pT~3`*;&U#Y!C<(VP6OY!gD9;D0Aep zpRBFSohCyMcVb9n96c{gAg~~=Kh%gd7>qao0b&??J0Blx!N?qmmTn{zHFl0XL)62s zCcv50t8?bmPxh?|gZA!EKDs~o?CwR{d-B=M+4@oQ?2S*R&7-C ldo*(P@$T-_W9l1;qGtbiy=H#><#jU0>$R2Y!ThZ|{{eoN?sNbE diff --git a/media/atree/connect_1111_1001.png b/media/atree/connect_1111_1001.png deleted file mode 100644 index be7c2214c2157cd20e848b965dd2e88656d17ee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1635 zcmah}U1%It6rR+?pM=ngSVC!Gm{P6ao&TMk8Ft%lvT3q58`sU!#wT-U?w!q;ouAH3 zvYR|!qS|Ld*x*YFx|SLzjxyZhGEKX?M{v9!4`%iaF| z;*BPIIeWQ7h`vWX1qh&epnVBeDayV`=l&fDb(e9JV)34#hrw% z>@2t3oekF_!ufO4lck;w3<1qB-wQfnYWK=QPHe-Pxg~+mA=yS*SgkksxhSE$F6yF; z=6n8@DgX)3mlERH&FaMg30P%eJOJDox$Qk260Cc}9z!XfrDdkVikkAg|r|40ThvXIF=MqmV$s~oukuiehY#J zxg3g4TiY>}nlz0z6PM0!(J(tb3@7d=Hr`A+c@@N!Xom&>OJO%mj;G3qYMwz{RW}P%9rDz|G{d1w84oavK9DG`uIUBYL`6+=5Wr<*VUwVu zsp-1uQ3q3V82@78L+jzr!F(7G0UotzxCPyG5$sw(x>9tIsVWqy1qWM1^Atw~9^%>; zqa?s^WPX6#REop4z%ym-N~2a5R8bz7jSkK{fI@%!A&I)_z~Ax%x{_fgonjPhhM3Gu8V#OyKj%&gA1}kuO4s> zoE@Lfd)>xvzl0QTQ6dbS?m7YXr&FRfjH21xj6jU!FfD4M0VFH6B`5qMbJnf4Wy9{)KQYyWj|P3 zNjgl1KJLJf$T|96m_T4b+;FHdYcv>X2m&N9_P+V_9)^+mM7;QN%5hUqv1g3C`Q8jT zw`+BEi+Dzk@z3b2L@9%!w{_0rVeDZUN|9x-&`t#EIUwiw)<5#}ybWXmyx3_=) zKKJz6^z@}mmyaBoc;~gd{_2Uwjo#SO8@KLUUA^bu>Emri-d t-~3ZP{^QzX`q-TQ;N6?IrpAwU#>cp8{?Wft&uFn}UYlR8-kx3C{TE;r^Va|X diff --git a/media/atree/connect_1111_1010.png b/media/atree/connect_1111_1010.png deleted file mode 100644 index 803f74ae9e1ca8984cf94f93f5ffe31dabfe1cdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1633 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 diff --git a/media/atree/connect_1111_1101.png b/media/atree/connect_1111_1101.png deleted file mode 100644 index 4823513e48b009b0e539d97f46cb070420ff260b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1627 zcmah}U1%It6rN~w(-ZHO|Hh0kwln*Q3azoWb`HTBHB*MGW0(P@2og?Zu6-&}?no%I@P z=~{Kkv_n6SozNort{(x4VTu>J5w3P?uahL)jBPT%MS}F~Al$KsvC(GS&Z=;1p0tS%umpA!hg&UFsmNBx-B8iHV9HwT}HYrILH-sSiyo|s2ayQ zws|p(eGEtD`M62=C}?slRo0xTRZ5(c7y4$cjZ+t((BEF*gq@`CZ+JdgNimg9)YO8e z%7ucV%9^OCeZ_v z)*M|E9SdVg9>gc1n|5$aW}2WHhy!hxiNogl=ke?S*=Z3fHV6cYp!)(D9Lpc7qsY_C zez>+Gf0PV8+@T?narC?}fxv>e!BC^ta4?bp1c+hm{qW1HcVT2c8ZEw(FwBWl^ci9H zKbZt4T%wsq?q_>_vNo}I<=p!Idp|aBPaJ2Nx?cdqr=>UX)^!S&q1 z)43dTdc9u1H1g2Y)acdA*Cwu>T)ow5=l=TR`diNNxi3Eb=;LeRS2u6XzJL4R@l*Bk r=HzD|TFm}sVgKIS<-4O#JfiI~&%beUdgr-rhYs?}{Brrm?E3D1S?KYD diff --git a/media/atree/connect_1111_1110.png b/media/atree/connect_1111_1110.png deleted file mode 100644 index 92f9919c29541ad7bbe0fa6e5a1a242411aa6454..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1630 zcmah}O>7%Q6yCOp1Syt~ijc*Rv~1N?85g{4BHm zhs~HbJt!je)x&5m#%#KBQHp&PCb5M_Nm?7KW@{2WNC4kx$x_E4#P~( zx~-LDrFqG;15ZFWXcJ-Jg@9t1^7%oC>@||GZPIc53isz{pL48>E8KI1rqm4QNY`E1 zipcWTV$0rIvklChKbM;+4@_Y2NP^gb*Yjg@P~lQy6V}u%a%>7o)+*de(_-g>h_ITV z2@+o)xEl%wBrIEwuwyROE@mWPRk&`Fgr+F=`+cFW3PIEnWy3H;Nf8x=2LvB)`Ux8F zetdF7lF`&iY)5XGxPi}7TGS5KlL`lUj1u(1W^=;s$C(bGh&Vu@C<~J4c{J-39Vhh- z2+ndj6dkuVLn1DbI9QKtQr{qca&jDw?MZC79`({Huq~1v@c~jE*0A# zhEq^@Sm?awXeHigOC_F2l8T63kR61^JPkiikZ%*p1I&U8Bn1_#2x$_pYes?BN0t*rrIYnS zQ7@=PP1dENDwQ(Cvm}b4w0-~hYM=Kn#{tHi>uJUZ6}H-_CeC+_Tm zERz=wxH4zYWz$|akv%FQMjHfknbYnfze8XcKp&1^?jdKb5?NLa1)75%v!+XkFA)dx zSiz+dF)%41eH0BRcm~m-+t*C%WCY_81_VB-1OB~-?$Imqfh;H^P}8@wO>@}t=sRJH@Wj7Q+0|?R0gR3H^Z)<= diff --git a/media/atree/connect_1111_1111.png b/media/atree/connect_1111_1111.png deleted file mode 100644 index ec468ba17e8ed1430e5c12e69defcdb49bc4980d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1616 zcmah}Pi)&%7geeon2%f3V#q z?Fn`m1tGy1aRAXHRN4V99H>BuiNoMD0SCAsBrb5^Iw8L2I3bfN5$VPLKL5Vo`@Y}% z-uITZu{uBd^en?L^YyiA6P_$x(~rad8?ElQ@R;sa8Wo1Q`|jLBJOlPr(p+6)j_*J7 z2N>tA^;T^)TEBYx&B7<2>3Z?jgI^IZE`Iv=+Z38MH#V8;zyIhm%*>M4+Df+?>!uz0 zJa)nk;Rk*MD26Fr8${UNAt~D-T`wqee|_;K$9hhgds%CUjcA!{dus_w>;1}vq;op3iTbCAa{K|g9V#_U0o>i~)f0~`qwFABa-v(C^-THAx*JeO0^NozkM zLX#xnZfuj<9tqORqj1L_$40wxFRQ|_1=1rvz!KO^8gI2)Z&+hAt-9;^Q5FFTk5Q@T z{-@0&`Oq&}`lHPuY|Qrva#SY;f*58sw0CJf^=g@;o0?8&dybjCYDPgWibVw#HAP0M zE{g~|x{YKoC8s0Vx<*Dk^&m-cU=zv%%)AFAvLx%O;bLTnL_w;m*a#ENK&qw_-FD;x zE)+-cFUB6U9_~%%LwOwFksMVt3dBW432R7|RRdvJBFOC&bX}BPg%HTYvCWk*_Awlp z=i@FBqM*yMR9UlP)ytgBi@9m_aOwgS`r8Yfu%G1qmgker6jSLWT`TCCqARe9nr!5V zmq?sIZ&5Ny<-YUuk%*YhD7o?~mR21=%F8O}KJr&ttP* zH?ch|A;EjZ;c}BvsWE)zv$2 zM#K0dbkjbLNu>*_fjH21nK$AR^Zd!t(Ql7U zZ@e%oPE{_wcX4fyk6U=9xt_T{|kg->Bj&7 diff --git a/media/atree/connectors.png b/media/atree/connectors.png new file mode 100644 index 0000000000000000000000000000000000000000..be2f56a6af1792a5cf11acd4fe37ad79fab86582 GIT binary patch literal 4186 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 N002ovPDHLkV1oaP=ZF9R From 18d9827256afc5da06400cc2632264c7bc0262af Mon Sep 17 00:00:00 2001 From: fin444 Date: Sat, 23 Jul 2022 17:50:06 -0400 Subject: [PATCH 02/19] compress connectors atlas --- media/atree/connectors.png | Bin 4186 -> 1824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/media/atree/connectors.png b/media/atree/connectors.png index be2f56a6af1792a5cf11acd4fe37ad79fab86582..99c9aa90281f2f9e12832bfa655381803af6593c 100644 GIT binary patch delta 1786 zcmV#Fbq_)(KhP+;UXb+45lRDyf7lDI;0>mU(*GW05yd zf1OyNjDY{JHz1PLLRMLCD{rlH&bT^v&igi_Wpu6%IwK&E-6FKf1qk# zxlbKv3jq#m<_sbAEv1(suD3GLh^wh;$k`_RZ>< zW+%vo`;L!f<~g#imG$U!YfjoqH*47ny0=Pe4o{IKN)~k>)kxI5t+Wo<{KXWL+lb zgJTf+IjRm_QvIBo!_Me!N8sqn5s;MB;HI!4Z9%f;7F?&gNj8td+`_*g3bjFzxdnSq zt<4;(Mw(@ywD|oFDA@!|E-9&b2|3pcTNFz59FheP4 zBoaQkX4phLU(dOtV?pk2K@KUbdB^$FZi~gFBUpoxT2iHvNC{+AGg?Nw1Hpei*@MON zP2Lc2MnLfiDXe+N2$Ow3SsGCz>zYAX^a8>s*NnFWQ219C-x`Vu{K^~yAmh}pK&jOV zPFdEo9-78;WZm3GKq{@Df7!lw*@@4lrJP-^B1R%|h`^ywqV&D{7({~8R5L5b|ElOk zi5o{E*hPixM-el8%_rZ}N=H1*MIzW~s+^BRFtv`GN)1vq7m1)#H+9rqVjMZ%u>)hD zIgUh-YB87^jwr@4OsW zI%1g*!8mo-A6|}|vvNE>iQY;5DOs^(9=})Wq++XewOx%PK&EzE{e;k~(VIw3ae>03 z1qN^0=L`;+lStr51y&k?1#{`yyVb=(KwuD4l|-hL8vP;|CzR#TgVA&7tPO(@_K<>_ zb%7}=?$`qwdLRHrf6u^i3X8+clZg;-)Klk$%`6$53()2gC`Z5!e?Vv7GP*JAN^!1Jk&CIF z*sky$h?G*+~6G zWT_6F>WCAOFC%+I*8k8ZKV+rvx$N;J!v)0Dl9~mnWj6T{D}B#pFVVw?H1nREK1NnN zMFzUOQ8~_4f1va2ZR%1Jw5B)=Q++-d=aMoUv8dE|jR_lgLH^33WD5X0Gyve21soV0 zqrYYARa2f1bylTCTwf^oGo}Mrdp-ES?lYbopcn@PS!Y0p2})rvQfl(lje2SX#xQsf0_waL5OOZw$6oy&05tAS1Fju| z_rMYEMfcd?skiE>85jkHfO5Z%5iY*;3JrrYNO4TX2)b;(^Hv9)>X9@podP+VUe$~v zu~+zQND8I)G`e4VBrHk(OvdIrw>3c4FiA{dUO5bzYOT_mf cfu2kK4;uV|P5DYrdjJ3c07*qoM6N<$g8be?&Hw-a delta 4165 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 P00000NkvXXu0mjf$JQTO From 1e699f223bedcc487ed4d5bcdd04a1133ffe32c7 Mon Sep 17 00:00:00 2001 From: fin444 Date: Sat, 23 Jul 2022 18:10:44 -0400 Subject: [PATCH 03/19] atlas atree icons --- js/atree.js | 35 ++++++++++++++----------- media/atree/icons.png | Bin 0 -> 2891 bytes media/atree/node_0.png | Bin 1658 -> 0 bytes media/atree/node_0_blocked.png | Bin 231 -> 0 bytes media/atree/node_0_selected.png | Bin 266 -> 0 bytes media/atree/node_1.png | Bin 1691 -> 0 bytes media/atree/node_1_blocked.png | Bin 254 -> 0 bytes media/atree/node_1_selected.png | Bin 313 -> 0 bytes media/atree/node_2.png | Bin 1719 -> 0 bytes media/atree/node_2_blocked.png | Bin 276 -> 0 bytes media/atree/node_2_selected.png | Bin 328 -> 0 bytes media/atree/node_3.png | Bin 1758 -> 0 bytes media/atree/node_3_blocked.png | Bin 309 -> 0 bytes media/atree/node_3_selected.png | Bin 385 -> 0 bytes media/atree/node_archer.png | Bin 1685 -> 0 bytes media/atree/node_archer_blocked.png | Bin 267 -> 0 bytes media/atree/node_archer_selected.png | Bin 297 -> 0 bytes media/atree/node_assassin.png | Bin 1680 -> 0 bytes media/atree/node_assassin_blocked.png | Bin 260 -> 0 bytes media/atree/node_assassin_selected.png | Bin 291 -> 0 bytes media/atree/node_mage.png | Bin 1682 -> 0 bytes media/atree/node_mage_blocked.png | Bin 263 -> 0 bytes media/atree/node_mage_selected.png | Bin 295 -> 0 bytes media/atree/node_shaman.png | Bin 1685 -> 0 bytes media/atree/node_shaman_blocked.png | Bin 264 -> 0 bytes media/atree/node_shaman_selected.png | Bin 297 -> 0 bytes media/atree/node_warrior.png | Bin 1679 -> 0 bytes media/atree/node_warrior_blocked.png | Bin 261 -> 0 bytes media/atree/node_warrior_selected.png | Bin 294 -> 0 bytes 29 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 media/atree/icons.png delete mode 100644 media/atree/node_0.png delete mode 100644 media/atree/node_0_blocked.png delete mode 100644 media/atree/node_0_selected.png delete mode 100644 media/atree/node_1.png delete mode 100644 media/atree/node_1_blocked.png delete mode 100644 media/atree/node_1_selected.png delete mode 100644 media/atree/node_2.png delete mode 100644 media/atree/node_2_blocked.png delete mode 100644 media/atree/node_2_selected.png delete mode 100644 media/atree/node_3.png delete mode 100644 media/atree/node_3_blocked.png delete mode 100644 media/atree/node_3_selected.png delete mode 100644 media/atree/node_archer.png delete mode 100644 media/atree/node_archer_blocked.png delete mode 100644 media/atree/node_archer_selected.png delete mode 100644 media/atree/node_assassin.png delete mode 100644 media/atree/node_assassin_blocked.png delete mode 100644 media/atree/node_assassin_selected.png delete mode 100644 media/atree/node_mage.png delete mode 100644 media/atree/node_mage_blocked.png delete mode 100644 media/atree/node_mage_selected.png delete mode 100644 media/atree/node_shaman.png delete mode 100644 media/atree/node_shaman_blocked.png delete mode 100644 media/atree/node_shaman_selected.png delete mode 100644 media/atree/node_warrior.png delete mode 100644 media/atree/node_warrior_blocked.png delete mode 100644 media/atree/node_warrior_selected.png diff --git a/js/atree.js b/js/atree.js index e8dc914..7222688 100644 --- a/js/atree.js +++ b/js/atree.js @@ -372,11 +372,11 @@ const atree_validate = new (class extends ComputeNode { const abil = node.ability; if (atree_state.get(abil.id).active) { atree_to_add.push([node, 'not reachable', false]); - atree_state.get(abil.id).img.src = '../media/atree/' + abil.display.icon + '_selected.png'; + atree_state.get(abil.id).img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[abil.display.icon], 2], [9, 3]); } else { atree_not_present.push(abil.id); - atree_state.get(abil.id).img.src = '../media/atree/' + abil.display.icon + '_blocked.png'; + atree_state.get(abil.id).img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[abil.display.icon], 0], [9, 3]); } } @@ -425,7 +425,7 @@ const atree_validate = new (class extends ComputeNode { const node = atree_state.get(node_id); const [success, hard_error, reason] = abil_can_activate(node, atree_state, reachable, archetype_count, ap_left); if (success) { - node.img.src = '../media/atree/'+node.ability.display.icon+'.png'; + node.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[node.ability.display.icon], 1], [9, 3]); } } @@ -973,7 +973,6 @@ function render_AT(UI_elem, list_elem, tree) { const parent_id = parent_abil.id; let connect_elem = document.createElement("div"); - // connect_elem.style = "background-size: cover; width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated;"; connect_elem.style = "width: 112.5%; height: 112.5%; position: absolute; top: -5.55%; left: -5.55%; image-rendering: pixelated; background-image: url('../media/atree/connectors.png'); background-size: 1200% 400%;" // connect up for (let i = ability.display.row - 1; i > parent_abil.display.row; i--) { @@ -1010,16 +1009,8 @@ function render_AT(UI_elem, list_elem, tree) { // create node let node_elem = document.createElement('div'); - let icon = ability.display.icon; - if (icon === undefined) { - icon = "node"; - } - let node_img = document.createElement('img'); - node_img.src = '../media/atree/'+icon+'.png'; - node_img.style = "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1;"; - node_elem.appendChild(node_img); - - node_wrap.img = node_img; + node_wrap.img = make_elem("div", [], {style: "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1; background-image: url('../media/atree/icons.png'); background-size: 900% 300%;"}) + node_elem.appendChild(node_wrap.img); // create hitbox // this is necessary since images exceed the size of their square, but should only be interactible within that square @@ -1242,11 +1233,11 @@ function atree_set_state(node_wrapper, new_state) { } if (new_state) { node_wrapper.active = true; - node_wrapper.elem.children[0].src = "../media/atree/" + icon + "_selected.png"; + node_wrapper.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[icon], 2], [9, 3]); } else { node_wrapper.active = false; - node_wrapper.elem.children[0].src = "../media/atree/" + icon + ".png"; + node_wrapper.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[icon], 1], [9, 3]); } let atree_connectors_map = node_wrapper.all_connectors_ref; for (const parent of node_wrapper.parents) { @@ -1275,6 +1266,18 @@ const atreeConnectorAtlasPositions = { "1011": {"0000": [5, 2], "1011": [6, 2], "1010": [7, 2], "1001": [8, 2], "0011": [9, 2]}, "1111": {"0000": [0, 3], "1111": [1, 3], "1110": [2, 3], "1101": [3, 3], "1100": [4, 3], "1011": [5, 3], "1010": [6, 3], "1001": [7, 3], "0111": [8, 3], "0110": [9, 3], "0101": [10, 3], "0011": [11, 3]} } +// just has the x position, y is based on state +const atreeNodeAtlasPositions = { + "node_0": 0, + "node_1": 1, + "node_2": 2, + "node_3": 3, + "node_archer": 4, + "node_warrior": 5, + "node_mage": 6, + "node_assassin": 7, + "node_shaman": 8 +} function atlasBGPositionCalc(pos, atlasSize) { // https://css-tricks.com/focusing-background-image-precise-location-percentages/ // p = (c + 0.5/z - 0.5) * z/(z - 1) + 0.5 diff --git a/media/atree/icons.png b/media/atree/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..fcbc48d78c0e338190c7ab98299ea428629fd437 GIT binary patch literal 2891 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 b1Up9bmlT*j%G$qw!w?%QN6RvcbFu#eK$%jC literal 0 HcmV?d00001 diff --git a/media/atree/node_0.png b/media/atree/node_0.png deleted file mode 100644 index a3ed2157fb48bc68a7e7d06e918ccf9b37a54200..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1658 zcmah}U1%It6rN}*ZDK0MeP|`b$<)}W%+CL0XWX^fWVgvmHf}egMtsSgnLC>yJ3r1$ zvYR}a(n?wseenkoL@0;|6+!etM10VqV5|7zn;=*y3cmPQiRaG#xP^cNcjn$R-}%nD z-#zzSs;w-JjXXF)QPfyvsZ@tAO~&xu@ZK{T*WfeUC{~LU^~r0ax6u(;55@JxdFtx* ziCeIktSuYm#c+9g`t(DSQ*4%>n}15vpO~939UL57`{~DX#tXM^UjLN1CH0k6YVPJQ z4n-ZAca61Vt-7pRftN*gVB&1s3jsw@`7`YhS(`YaP26z(0`u1wUoo_67ntXiDqjs3 z@P@mz8{yU66~o%yv^1MJ^W@k_zO4g;hZ97%y`~@Q?E;e$>o6x~j-gXXvRPo(sx^8c zh%hZ@aG@(Uiu$2@T$fJ{>7gno%c0caw0E%#J6mmkA=RA*O zoucEUybaFXTn?fys?zYwU4Y#6Zx(nOla1(m~iy@lO54BjS z*7|6&>xS!vsRtN(F`l)$PD? zZ9Sdl6eM6pR#Hf7$SQtuz|=K1T{^9G9a)_%#AqAaOxI{_Aisg389*I&VD2GjE%T}-DVoOS za!@964#7-=Q|CcGi&qpRI#SO+4xFTgB3x{MY9J1jT`CSe(>;$zd&t%XCSrp?EU&Ty z0x>iY2IUKgzwofAqtnBl^_lc%-VC z;`qAp+04fq%jf?<8AJU$qe$ZyF7b~YyXVc%KbU@eYWPB#{kc5$#=Yxzy?puWTPOF5 z-yRPaG8ccyFJF9pf_eDuiOKyxkDo3ViogKZ;dTZ}hn%b$} V|I$D2eRDwCd!@Wmx;npp{$Hz3_s9SM diff --git a/media/atree/node_0_blocked.png b/media/atree/node_0_blocked.png deleted file mode 100644 index 4f423290c615cfdd7f9db2386be22945c368c7b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0L@}@9*H`Wo2utt7o99 zsxB|5c;j4ACQyR0B*-tA!Qt7BG$5zJ)5S5Q;?`0Bt&E2qczC`wG9FslTWEocY?D&&?OMiC*mC(+NB;CbD?`<24N{)+$ZaG|oP~RqN`Brfna*S0MfW|Nr9`FT`KFZ@y-$ zT~m8tNVJ2KmxGgwrIod=o`I^W`t%hU`+({gOM?7@862M7NCR@Fc)B=-RNP8lpv}SS z`gF6DH2VUs85ZmdJ~aEz@tx?}e=nBlN{hgXsI67jP7S6vvL`N>P{w^{ zN<#AbtEtxEI)-MgoIBpk(_L}n;?pL98(WJbAD_F*!I=C$ve$R>@&j8puvNd03fxlE zoylFOU3<4ugHjuguL5 zyFVQJ1B~(VLN!0v)(H;zFFI^KEmTqkx1Y)1Gc974>A71gs3z2m)7Om{zM5Z}D-*t1}5%mKl~~ zIF1Gc?QhrtYSXqq(IM&5sMyy$(+y0=CPP|ObJl_k1$lH5wA@mu&u;r&9Y7JLja()X zXBo>1vyRYykY9)3ZY~F+{pyB`nF{uuHBZC&b!-O{y>MOY$GU4?Gpa(@7~I4bzJJA za8X5fG*efiPeYWkC~7rYLn5LDp_Zai0v~B(jh| z8e&tlz^9V5APG8+bOX^c&vGm$^GHfcz4%8w6Iu^7_vaJx=)j{HhLq$bk>+K@(*jEB zw2X9_mJHz6B^hZc$V1nZla6O0*fP^XbQshJ-$%-jn6a=}8cno_!^j1igVBNw2|6=p$(F`a)UI2<|(kct8fTr9^atf+8eHNh%e3O;C7QrI5Y(M@CH|De%P zBH>DR_O)R8u=+;d-8~>n_|!gE*V#15sMi&wbxQEjI@YPKQ)?i*j$s%;A9i5o0eh{B zNhyJ`K(k3%rUh)&=o$xG&+~dMfhCT|5(v|Y_Z=f>ArI&3pc;q+Z5N3{PIdR=L=V|& z;81K3h-O84pFkAJ?5iWqgok~9ZE5Bp89KOqLn7kncwqv81#!Kh7Fzwmh;0zSgR$rR z{3i<|bI2_`;SsM=I*|kq1r;QSaSVCuV4wtJNswPKgTu2MX+X|&PZ!6Kid%aFwlX?7^0=}e$eJUycK_3ijeA~C zO!?1SU?KTndAIHADN$!@O*RO93uvw9KdaugSNcUq=8}644&9uUmX|Pd)5%*M<_7HV z9@%HyTOqNSclZBnhp?Pm&(^1^X=Lm&`}RrpN=vuvdrOHw4%>WwPJa3G|K>U` zpEb+O*KDuIUkP6ovdWta8l?mcMG_-Kx?`?5FJc2u90QBug8 zR$YEkD8F~>?Md7ZncV$KtLN@z6KTjexp(ss&)ey5&5Bi`{TAFRDqlaj>wVk~2IsWOpnctkvn&5nPuOJhi6Q%qC1tfIx}=M}gAxK~XJ&>sw*u6{1- HoD!MrsBNa{b&di*_nIh zJKsI$JLjHxH9MBt)c#OAK@gkLBgq`x$?$4f5C5;^3rlcoDI_upf|xtH@fK=@^*TS7 z8YIqr)%80py0fGC;gmbNZQEmyJlst)+^)erL(I@>YGIGG_x6Fm_NUO!o}C*Wy!2V< z8RW*siCw>aZxBT5pqZZtCNiUn=2#5U9ThVb%LNoc^bb^Aq)p<0RB^$yg$MV|2NiC*R13Ndh_g55PuyBCLidC|Gr4pyA#0orzW|ktW zkYF-SO=PmC04+C@X|mgXT?bHv ztss|;FdS=HVboQ$9}G`}b3K;3qW%1gi`g9ZoheVl!_(Lfwl>^#t?BDdd8KL=y2j!X zwgBeCZjt6zsdOgWM2A@yOv|l$fWl2wU>g5vb4NVkn`-!t&B3k7cL%bOX9xr_l%%6g zh4G}5aVp$Y(H+gymFlA?8L}?w9L-~cqeUE%X~fAoE%8Fk5JW?kB(1@dw*3Iv8V-4Y znK6L`^P&Wv9C&Gcv?!?tt#X`3%YCsZ7GqIWL~_Ib3D1PqL#4IxggiR%7#fyw1VIRb z6s1KG$YO#Z(;|w=Sc?i=OzopcT~l^Do`s-friBWab?pL0hRQ05Y&uTy3|C)fODHe^ z3jJ-`x>NS+_PlA~@c@O=MWm?2OJY=ra&jLhh;_tM*z=*cLb3?Q2s}lG$}12&NQlC= z1J`Ow%8-Xq;COk*DaENU3NrMmz01Ji&{2SrD8RtQ@w~!G3NPg&oWl1h(J0Ny3fBNT zx@pY(AGCUuNci>F>N;Zju=`BYTt6V=_=Pp5y0K)E)m~SSR!hN0(^#kKMy-hK0)}A# zeOQB;cd4~1t40i|P!s{{v?%KWRNWBhm@M(Sgj8ORpoYKi7(p3%I8gxAKpbehsyJj% zy&k;{WVwh#u|Xi3ljt=9Q6#%2kA5~h>}zxDXYVFM4Yy`UR5@x^m_V=szs67tSIxnQ zZ4khNvA23w8i0|x!5w+pCy0&rhu1pd#K}%DUQ8zw`JOB9AKP*7;q2z&#IuK1Pc2?u z@z>p0Jhbg)b>Qmtwj*zMpSC~w`kBtXUwwW3a~DtQ%jD(* zAAP%}WAis1%cb7QcZp*^zH_MeSFiItb0K*2(vI2LCER-b_{r(Z@3oyN3SGafA5C2T zF12uL`-zE#zfX&=y?A`Ad%5%HD_36Xdi+ewjg=2ZPyI3X0D<=^`B z&3}$8(px@!qisIV%+KC#yY@ithIRJZ{LKZqZI)_n|Dm`q{4k`4$CBp;pFjLBsGcXk diff --git a/media/atree/node_2_blocked.png b/media/atree/node_2_blocked.png deleted file mode 100644 index ca9586e307b7735366af921e8b41eb28c3e6af4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0L@+e|Rk-pFI&fZv$wHub)j#?l*Ve-c%a;j@t<|p|y^c@#Ges#h8 z#AY|K=h(hUKZH~0K7xhy3f#&PjaC)X#2nFXp3`dC?W8$Q&n zkbA%D5bvR_XZJccglTH+`FQiZJr`FIFH??x4wK2|1t03qzkdDGc-y9er^Q+2Z+tp_ zu-V=>*(n_KW!m+_qB7-24;0Mou!~@s`!DcKaMNjqoy>lU%kqOf?9~JO4n;hf!+W%q Yx6duAC`mP{6X-PtPgg&ebxsLQ04|b+MgRZ+ diff --git a/media/atree/node_3.png b/media/atree/node_3.png deleted file mode 100644 index 71f7c673ff0970887861ae592b8cee3af1f35cc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1758 zcmah~YiJZ#6dq$t@e#BXqLpYT6RcX8o#*cCj9KF*yC!S1Nl4aeM8Uf=cXlW4&WtmY z>?X8WrKJxlwNxww1uK*)EtVFsQmNWn?4MTsRi%~+EefL4AJW0gbr;l4YQ`yL*Rxp+EG5TCz0{{fl<_AGa( zr;9j#vGo=h%Ln^1$sW6J#fp`5h;4gr`MRzR-78mbVAw~=`)5kyC4*+%LP?2%cVGt3xu_w-qcG_)AC zDV%20b~nx&y^{_ep6t)4lRH#dqdM0%&+8~Fz)-{w$ArrTa2snR5{t2&158J0=<9`X#uPqK?b-u@twK{zuh@m7bbzL>JxQ$^?(fHoikijXTczYURRJ>DZxeKSfi>=HIK|3hG77G zSb-T&*lWyb91^2pTH*wO76mp-M=@*^mUtY|bvdi4AWS9RwREq9930PqY9J1@T_6s* zs=6PmYsgX_`(lGYG#jC32t<+0j5<0P|FF-jt%G@z3>DmrArWv?yfA^lg1FjH^R4<| z#3l&fz}P##?9etCnRD&l5tkt5KjUAsh^d21!1+fi5znl;bm%><|NOc|$&%afXqnt{ zvyjo>3ja?#!U+*?sNZa7e3n0?>#ZFt+ZsDI=pJ( z8^8b9+nzhUq~w1pE%yNeb2w5_%@XQ diff --git a/media/atree/node_3_blocked.png b/media/atree/node_3_blocked.png deleted file mode 100644 index 544241d16e7015dc3aed3dcf85841809147b3392..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 309 zcmV-50m}Y~P)YaU=`fI?7UGq?i6NDt(g zZePGIOq1{Bwsd#d0`w2$$3OY<56(`Q1kmLOdEIAO)jA3r%EC9YgH|BQ`CujDaMQS*Oq|$&+e`t z^Jmpq?hm8xeHCLfUP`oAYKP=t==4T3WdDAjNLr+51P0bhH;|vu2sfc0`S86e^k9ea f5fOzMmBa!7l8j&6*nxT500000NkvXXu0mjfPVbyv diff --git a/media/atree/node_archer.png b/media/atree/node_archer.png deleted file mode 100644 index 5f00770569034e34ce62fb9c00f00fc342107d87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1685 zcmah}TWB0*6rPv{Y|?0IwFaqioEn=J=eo1A*KmS?DPmXoA-_=f0RA+WPGX-y&+#6cqxvx+>3$G2uq1+HfoqW0D8fpW(C72o; zrcQjb?N>0m^Am;9F>j)`cL&P}_ukX9Yp>Y*w%Ge=Y;gFY#l^jUkbrP%a+(^v^1Vq> zZNpY!CY;GlsJd%&$Z$2xRcsGX6qOpNcu1eaA+6z}<)oRbAAHQvmXT)m#B)N<8^N>I z__B|umnRGQ@|>PDn1Kg7+fx-47;GFOx?-1{K&_;ih**Vh;^rATf`oHvW+s=XM_eD% zGADBaJ6f?8B?d@nI^`RtI+fX7BLOST%!Z+-@_e~m=E^b7^^3fiOeT3j;w6a%1RE?l zA*!%Wu)RuBqsibv_bo59T!$vKNOKp$Gy{256STctuEFjEH61_^zJfen z^of&(lEg}kRd52cvaacDzb407NyKJCNhA#k;W|&&2}0!PnD78IX90<92r`m1jWvvT zf|X@cX0?QgSUIjEK@t=s>xp{&!@dQrhf2-)5FP_~lE@TMLe^O^hWlCBltdQA6_bt2 zhAGLSCWr~(F?4me>)Qyn%(77t^PW>=Xrin-l+UIai4$sOzJx*(pwQozW4Ps@<}X+_ zo(>U_PE_KGq{xaW%Tg>Z0ewXDDE0&BEkY&=TufqUqPz;pgM^5*9fU?DsUto{q3ai1 zx0Gf`Fim2r|8j6R3>2aa3NdgAlB5cXDk%j~P-B8B%B+x3g*wnhT#B?M>@8%)iq&myOYVHiLkR$`FN-Zs)0Drc9A%AUu{2j){*5|OvDC( zSRiZ?h@ttWI#N72?9H{M`1NF{;+lp;#8LIa1Of}<>O+lKjlqZ=5Wt7A*LAY^5sb`@ z-uPnyMRjZ;cMJ9U;cjpq&t`@SeczsbC4I|h^rrn^UX1fssM9MK4+_Tp?T4xPo8H^= z+dr#Z_Q-4M7l#J^PG8@-fT+t$zdUhy?a0}4M;ZUs7w7+a;K2TUEmz+Ca2Kn-8pBe`a{~_MgAH^J&W2xNU3S-4~uC P^FKQ}nK?21)U*ErQV3aLFs&(54Y|c&N3_9ZWnO!Xr#;W05-M2grCOZXLcID zU}8Ly&wu^Uj-_0DHBmM#Ru9U>)|jbl1lj*l`t_eBPUtcP`%R1xq z=47Ejlm8#unKBuE^Lb2TioIu(9=rdy5c}jA&o49y=uViLA=%3CH%n!v>d}qYf$nAS MboFyt=akR{000+aSO5S3 diff --git a/media/atree/node_archer_selected.png b/media/atree/node_archer_selected.png deleted file mode 100644 index e48bb682675a6ecf255e114de3eea3079140df6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvxd5LKS0EjK?Y{Y%t*Qr&RaY6> zHMMKS8#*|7>FOD%s;c*VmBc!NX4zB1xgG7w#O|5njA#h zwD}u2*$cGZ@`@`gyw)V_&~q;#o4Y(ix8wab?~M%8-k+8~6!H7sTmf(Ew_Yl_OGCfh zKd+F&StT{oUuv3+0Q&>RlRU|{3a!sE2^Z|Vd571<{xwr9zs1@+jfp22=iC&T7yM7r zW$$n1n1=P0!Dk+x`(QD-iRoqFdLHJV7pGM0Eq3_KpU!lE_sCz~DPcmBzW(9d@%rcf qMkbDo)&&}F0*hFU+)M)a85kDL^_*3zxN#}a6AYfNelF{r5}E*aDQ!jo diff --git a/media/atree/node_assassin.png b/media/atree/node_assassin.png deleted file mode 100644 index 62ffcd40ad8f1f64add94bdecc635b92a5da4a1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1680 zcmah}YiJx*6rN}zBsCQbN)XX;N^C67&U0rn?dcQ<2p9+{c! zZlb8IzaokWR*I!q#RpQbSZHZ6id7L2tOYf;zpM(Hil9G~{;{Cv&OS^b;KJRRbLKnW zIrqEgp5@8$kFK)Yr+Q4oJvLl1k ziLdVb6^!o5vE1;8H`dd$la2BB+?0&GbBT?0X!Nwt?t9Ho=^lFlch;?`;ZjPZNNH~{dW-^oXkn3Yw zj>$2e9j;nskpU8#PWWa)&!+a&Nx(`nvtj7z99OASVihUo`bAFAG>zj$P83-{u)(4e zqAKeIJ8C3#niLKU-||Asb!bA1^6o;IWT1{(fwq^)G})b?t^+8-RguRDF`lz+Qgwt5 z!r?Lm*K64l9pn~0%w=)lF8Br>E@LO$(Fiwugl4LdbY`-N zCc7?LwiiW!!cA0Y75>xax_Zc$sQgB92y60PhivSV06`2rx=&$nyp(%Y2?i8Zua92yz|?nw;;;H+a%c5F*FGga?>o7Lce?pNIrSWi?fh zSy|Nt7Rg}73knjoxR8%yz7ap+ThMx_w7DL_V-g-sGgQE`DmDtNj0%0MCLzQMT3m{& z1}~eaz|f|l?{R$_!I4=uDq_xaiVRJZ)dwciNk)u8vdEk)p|Ai@=x@t0-AYjR=PVmf zg@{NeC~-wpWLRonA20XS5f5QMfZifx0w0q^h9=7EP&`P8NZUbZv?YDW$0&6Doa>g7 z3`t9qnA*1tTn-b3D1|}{T)ZgiyrPRrPT+NkhZmMtb-n?1O{=i@f6(YE(eO*u&UMrZ zAp2s|UB4hxcz%)*7Zlq zPwZbCY1{tV^RLXbeLo?Ut*=wB9}?d9@dN7R`B!tFy?gHQ%7u5%ZhUv@Q2UiLC!hNL zCbaSCEjPTc7M^a|_3f=)+1BZc>dpmk{pvsMF9=swxogzQ58bD_jvaf8jQ;fSc3sUpG65AtI4E>z>cX`^j?UtKhdtTx4{=7+?+!Zs`Q*KQ4zI|C%;`Qdq zDQ5pauze8?n8xIL&n7)~|IgqC$9KkC6kNDdRHNJ!8Gd*wUC&52X$Cr!!PC{xWt~$( F69DB=U?czl diff --git a/media/atree/node_assassin_selected.png b/media/atree/node_assassin_selected.png deleted file mode 100644 index 7d88a0f319ca9fab906f2fb3cf65217c49ff4a26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvxd5LKS0EjK?Y{Y%t*Qr&RaY6> zHMMKS8#*|7>FOD%s;c*VmpY-4mpUl zX|owjVt=6ZLRqZCg|}7Rpz9FZO3gXNuMd5?Z5Do{UGx6(_-Qrg?v|&Yx#hQAFtYq& z+OPNN24T(f+@4Kto0W4!P(vX)af)t%mJh?{*B-i4K7_oeV)*d8G4ZOx^GZj{xAIDR zSQ1$Kej6Dd4_R1H;i0ggzsjEZYoO;Rp?iWt8YfuH8;aN`sXBf8^^Zm6XI-(v0?P~e j9~4};Q+hHM$T2V&xA{)$o9R*kbUuTptDnm{r-UW|di-oI diff --git a/media/atree/node_mage.png b/media/atree/node_mage.png deleted file mode 100644 index 4086888dee2d7167e7ff1119f7e0d9109a5d50a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1682 zcmah}TWB0*6rMB*G&e=W5=yP()X*51o$Jod4!b6s>}Io?jmu_R(}LK4X8ze7vU71} zlHJ6DZG(s+)C+wPvDKHx3N3;@Y0bL?hsR#wMs5frSGbAE=SSC0Z-oItac-WNxO>|mh~7!F zv=A)hXBEw{B1m^s9I0C_pa>!{UU!kUhyzl^Wz$YlJ70fGk*1!cUW(?~ygP*}=FF;x z=U3-S+UlYf*QxR6hWZnA1sE(GAhK>%ZC|M;sRpqE@1dKa$Oa@>Oi~N^0y*V)m=q&o zgrzfebD5`rgd`K5ZYag{bc+P6BvlClS7Df1trn>X5yvYtTs$6USf1f|8W6O+sG>WXsVj|F z)j(2IkMp!H$|5aB<2o&KJf>wa7Ufk@HVhWEd2+TNAX~#B4=_heAdw7B<0Mw4W4tQR zq7aSIaUqInO%w!KM7*YRd^`R{&xF=P)!q4oJUZ}TIWDlgAVMBeoEBx3qY;NCS_cAM z(_~fD42sk>W!mv91Y2fWsEircE>mQvtdc6^k`y0dTV|n(0t2AX-=?iQHNWLAnHHW8 zP$(TIMI~MmB_S%vaaNXEh~wDvp|?UZj*SSAeW<(w$%BL_Y&!^Tl%z~~7zK`3a-3?C z3bP=?nC7=U91a}?D2)OPTrAHktfcT#iDMN3{!yBZDQp|;=%%sqf6&HJBH@>4?(2-{ z!|E#?ck6)6<5zdNTF$CTHhNt_TC)TnEn}T(IkgJ1%NT|M^kEZb?y=V@##j^#fsTo~ zM2or}r;!}hX_bv(Rf7gzAP~(; z^e%xYlG#;9f(Z}%?%EQ}UNSUsyM{!Aqv?eS1Qx`#hgxWL1|zmX01w9AojVubfRWkf z&YbWGV&L)c>LNb;Xc(O9xpb;D`r}vcB_CO12d8iSav>{ESnn!t4sMV)`$mtR**x~z z>z@rg^JOvn&4cZGTjSc?;QdWu=brZV_3MZFPW-Wc{P#@i^L@QLFTWq%_vgeDXS;_M z%jYkh-k9$>FuHT=+zb2mqcQ1&jlPrDrY`@w)IIi&e@(3wDoJMjc3FJ#^5Kg5o40*T z-gs=aD|zOV`(2kN$G8i%Q$PQ8_tOI>fB5I(Ps3{;Ke+km*#3jVTi@Nd5)S`dW-fho I^3=J10m&*4t^fc4 diff --git a/media/atree/node_mage_blocked.png b/media/atree/node_mage_blocked.png deleted file mode 100644 index 15d26fca4d874e09d5067656110b63df2d929ebd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0L@+3 zHMMKS8#*|7>FOD%s;c*VmjzonjAzN zwD}u2*$cGRUaw#@zZO-%#B)(PC-))WTGog6%a%t7oc{l5ra|%<<8ZIDsV3YL&CGRI z{i~B&TD)-Cw4V#qLdBB!T_*Tl@Z#fhZ{tg_eizR9@Gd{+Dk&y?^Mx~u88?)5wEt^n zpTGFFC3E$fzsLT03H*Gco-$!!L_4pC^HL$T%7#bsWuF@x2&pV!Jc0=>ZC>FVdQ&MBb@0LLtAx&QzG diff --git a/media/atree/node_shaman.png b/media/atree/node_shaman.png deleted file mode 100644 index 4c88259cbccafce83bc00865ce0480fa2ed62668..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1685 zcmah}U2GIp6rN&9w6u*1(JBPS!D36Co!_1Aj=RvGUD_qxy4h~9#6-9=b7wnt=f|1b z?zT|_))(RfK^`br6JIpZDya{U7#|w6M4qaV7aoj}_ydNA`e>9y&+MN|(!@#T=brh_ zchC9Gx#vu7VzjH{=?;pbx-w&_JiO`fZhr`#rwhez;MHCn$_`P~=Vy1^N84fD7UV~V zsmmLCeuqU*ZoH5l^~QU9pJOBZGyC=)JR$U6lX^c928WOKExvvybRhYONow%7TP8(q zAGQipWGXwZ>aHC@hO6O7#r6P2QHjG959u?Q&>AjUPLkRDa*d%aBgwoJ&GK1q1W#LI z3qGD)m?-EAGkV-$4j=03NK{l{urWb&#V$F4T1hfhVimqaGsn%JkIeVCyFc}*kIlv zsKPqI{u)W0CWQmtw>)CG4js}W&7CDl2I8m%XnWaglidmGI)EZv1$mqh;W^t5qpqR@ zlAZ(SdMsO_gTlOrxjYWsSzpKLIqZ=A4R=Ft`g*f|shWkMbGU?UfCaFd(A;V?lg%~J zVb(><_NpGBa1%u=^FM7Kh$sA04ZpEDxHb76KsNFWfgpyOa`o9To=hsqgqx~{t6PRz z{fx^oDT)n^RWws#WzAGrO^FID;&sUoktB$U(BR290YQ$ALmps`SU@sh7@8tT5*t-w z;2#$>7Rjc@nmm#aQcM$za>M^c--6adrPg>t9s_uE1#5x^RTnV^9v;V7P0}Pb7S*Cs zRFoAX7Gr2bSC6>9ji6+fjf$A_oFYSq%Bn-TOp*~Je0`ZKAz}g)`rC30w;a^%1mXQS&^ghm>i1@)Dg$9A3$$~WC95J4$KFmUmrsPc*`Dg}X8C0>=IEI**~4X|ri=KTLbt4E23D^aWK zm=(b8^G$R8fK1}oT1<6g$)c;ht|GmbLV)J5!PJfVG;)d<_y;e|llx^6tNPkvERZDd!1cZzr@A8*_C!RGa^l{PZ5boIa@9((@!J8^e#>cTGRX7_R|#(i5r6 I!zbVR7wbO~KmY&$ diff --git a/media/atree/node_shaman_blocked.png b/media/atree/node_shaman_blocked.png deleted file mode 100644 index 49ae36619f8f987872b68eff7895a6e94d742154..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 264 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0L@+3 zHMMKS8#*|7>FOD%s;c*VmBc!NX4zS7YzBD6-3w)#Q7UI z*+2MNZGXrl@!YC_NlGa1!p()XYxs`*-PtbQEh5zigWyA?s91V z=KH>T&Ueq8S5wpDeLeT~P!!ddn24w0PW#ucPWXQ@lfMeLUHMotMo|~f^!|goVBO)S z$496O-yXaPi~iJ9W^CM^8XSC(4e$>fKJ@T$Vems?@QO4%GFd7e|H~(M>FF72c=N{` zMRkoBnOSc(Ii+E%7(lv}C4oxO1{6g_hAK9~^TeaGByX5e=FiVBGqj;cnPZ_OpR`BG zoH4QJkeS8l3|^eas?H2O*4GoMXrNFe9-=G7g6V3NC{q*G;Mosz3|)hG^HF9tnW9H6 zhtR=5Fu=286{93FAVSj-N6%^L_|b+4tfI`E=h+&^mCNNoSqfNAo)c76<#>@3MHUdO zTQxmYVNG|SF4B;R6Bj#%?HQIy`?4r&EqGA|@~9_Rw3Eq}y6HB207tkAvN<8ZbH$>c zbq(!$VW~w&f(!{kE98AVa#PkN5bRD<2_JUKWRiR^!6i5+ZF6<_>wi-_) zQ!TV#b>1l2H3m4`LU~5+KX2~H$A42xzqvWkT6%XNn|1m?Fhh%5c)`yn5sxzdrkZYH zL)U7LSWyu#2r8=t#T*+{NQgx!C$XxogjF086p>e&ItkPDkco+}1IhsdL`X1)B!U%I z!IHuTLsAf^LYU2laaauLIe{mn$$!Ezp!HCpJs)332OU|!Vpf$gs}dFRAVHl~LJH4{ zga}y9VP4Wh46S4BsO1z99GOu>dBWLdo}qnbwOA?^9;V zqex~vuOE+@W|AW>(B^rK_ z`ngURF6>@yg&QBp40*0C)Cd*~y4LF&!u1kdR3bXl2;wq;Vb(HId~x;+qWy zvAoQ-8N|?BTOAS3|Jd7Wi*P&HP{*|miJC?|3KIxcz-taQf7Kd{!~_Ez7<=b$efc_! z%x-()X_umU_xo1|wf0s&1m8=-E+;d|6hehFoci-7MkaC|{Uis3I=SCI1ic zz2)_}arDSPoKVR{jcYZ?`wQk>MB2sb1&n{oD$Qu)8NE`;!JZ1-VZx? z1YN#!ss2c-O5tGGbN zHMMKS8#*|7>FOD%s;c*Vm80`b2ZyVUwh6ivJ5Tvng7mVKJcoq z;uZggphPAM=5GbooA(QK?2%Mp*5ti>&OvMH2`zn_RrMB+8P_@7c;9e@Q!)0Oe53cd o^=wQj3*HM)o7lZ3civ@bMKj-SS^nB$pbr>4UHx3vIVCg!06v~>IRF3v From de6108f187bc44cd00e3189b3a2685d24d6c5b00 Mon Sep 17 00:00:00 2001 From: fin444 Date: Sat, 23 Jul 2022 18:14:03 -0400 Subject: [PATCH 04/19] remove unnecessary div stacking in atree --- js/atree.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/atree.js b/js/atree.js index 7222688..0033921 100644 --- a/js/atree.js +++ b/js/atree.js @@ -1008,7 +1008,7 @@ function render_AT(UI_elem, list_elem, tree) { } // create node - let node_elem = document.createElement('div'); + let node_elem = document.getElementById("atree-row-" + ability.display.row).children[ability.display.col]; node_wrap.img = make_elem("div", [], {style: "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1; background-image: url('../media/atree/icons.png'); background-size: 900% 300%;"}) node_elem.appendChild(node_wrap.img); @@ -1048,8 +1048,6 @@ function render_AT(UI_elem, list_elem, tree) { delete node_wrap.tooltip_elem; } }); - - document.getElementById("atree-row-" + ability.display.row).children[ability.display.col].appendChild(node_elem); }; atree_render_connection(atree_connectors_map); From ae4b8d44e695782493bf3818cb275d256d35b0fc Mon Sep 17 00:00:00 2001 From: hppeng Date: Sat, 23 Jul 2022 20:20:16 -0700 Subject: [PATCH 05/19] Fix reachable check to use currently reachable set instead of atree selection state --- js/atree.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/atree.js b/js/atree.js index 639eca2..fc05ff5 100644 --- a/js/atree.js +++ b/js/atree.js @@ -310,7 +310,7 @@ function abil_can_activate(atree_node, atree_state, reachable, archetype_count, } let failed_deps = []; for (const dep_id of ability.dependencies) { - if (!atree_state.get(dep_id).active) { failed_deps.push(dep_id) } + if (!reachable.has(dep_id)) { failed_deps.push(dep_id) } } if (failed_deps.length > 0) { const dep_strings = failed_deps.map(i => '"' + atree_state.get(i).ability.display_name + '"'); @@ -318,7 +318,7 @@ function abil_can_activate(atree_node, atree_state, reachable, archetype_count, } let blocking_ids = []; for (const blocker_id of ability.blockers) { - if (atree_state.get(blocker_id).active) { blocking_ids.push(blocker_id); } + if (reachable.has(blocker_id)) { blocking_ids.push(blocker_id); } } if (blocking_ids.length > 0) { const blockers_strings = blocking_ids.map(i => '"' + atree_state.get(i).ability.display_name + '"'); From a009e07587ecd80ebfe9e940d77a01789d5632cb Mon Sep 17 00:00:00 2001 From: fin444 Date: Sun, 24 Jul 2022 17:31:23 -0400 Subject: [PATCH 06/19] atlas for item textures --- builder/index.html | 2 +- builder/index_full.html | 34 ++++++++++++------------- crafter/index.html | 4 +-- dev/index.html | 2 +- items_adv/items_2_help.html | 2 +- js/builder_graph.js | 3 ++- js/crafter.js | 11 ++++++-- js/display.js | 17 ++++++++++--- js/icons.js | 22 ++++++++++------ js/sq2icons.js | 34 ------------------------- media/items/common.png | Bin 0 -> 988 bytes media/items/new.png | Bin 0 -> 27938 bytes media/items/new/generic-armorTome.png | Bin 1745 -> 0 bytes media/items/new/generic-boots.png | Bin 3628 -> 0 bytes media/items/new/generic-bow.png | Bin 3120 -> 0 bytes media/items/new/generic-bracelet.png | Bin 3281 -> 0 bytes media/items/new/generic-chestplate.png | Bin 3411 -> 0 bytes media/items/new/generic-dagger.png | Bin 2876 -> 0 bytes media/items/new/generic-food.png | Bin 521 -> 0 bytes media/items/new/generic-guildTome.png | Bin 1745 -> 0 bytes media/items/new/generic-helmet.png | Bin 3417 -> 0 bytes media/items/new/generic-leggings.png | Bin 3206 -> 0 bytes media/items/new/generic-necklace.png | Bin 3625 -> 0 bytes media/items/new/generic-potion.png | Bin 308 -> 0 bytes media/items/new/generic-relik.png | Bin 3378 -> 0 bytes media/items/new/generic-ring.png | Bin 3770 -> 0 bytes media/items/new/generic-scroll.png | Bin 324 -> 0 bytes media/items/new/generic-spear.png | Bin 2561 -> 0 bytes media/items/new/generic-sword.png | Bin 345 -> 0 bytes media/items/new/generic-wand.png | Bin 2945 -> 0 bytes media/items/new/generic-weaponTome.png | Bin 1745 -> 0 bytes media/items/new/palette.png | Bin 223 -> 0 bytes media/items/old.png | Bin 0 -> 2025 bytes media/items/old/generic-armorTome.png | Bin 1885 -> 0 bytes media/items/old/generic-boots.png | Bin 418 -> 0 bytes media/items/old/generic-bow.png | Bin 588 -> 0 bytes media/items/old/generic-bracelet.png | Bin 677 -> 0 bytes media/items/old/generic-chestplate.png | Bin 484 -> 0 bytes media/items/old/generic-dagger.png | Bin 559 -> 0 bytes media/items/old/generic-food.png | Bin 930 -> 0 bytes media/items/old/generic-guildTome.png | Bin 1885 -> 0 bytes media/items/old/generic-helmet.png | Bin 395 -> 0 bytes media/items/old/generic-leggings.png | Bin 411 -> 0 bytes media/items/old/generic-necklace.png | Bin 682 -> 0 bytes media/items/old/generic-potion.png | Bin 650 -> 0 bytes media/items/old/generic-relik.png | Bin 713 -> 0 bytes media/items/old/generic-ring.png | Bin 622 -> 0 bytes media/items/old/generic-scroll.png | Bin 705 -> 0 bytes media/items/old/generic-spear.png | Bin 596 -> 0 bytes media/items/old/generic-sword.png | Bin 828 -> 0 bytes media/items/old/generic-wand.png | Bin 568 -> 0 bytes media/items/old/generic-weaponTome.png | Bin 1885 -> 0 bytes 52 files changed, 61 insertions(+), 70 deletions(-) delete mode 100644 js/sq2icons.js create mode 100644 media/items/common.png create mode 100644 media/items/new.png delete mode 100644 media/items/new/generic-armorTome.png delete mode 100644 media/items/new/generic-boots.png delete mode 100644 media/items/new/generic-bow.png delete mode 100644 media/items/new/generic-bracelet.png delete mode 100644 media/items/new/generic-chestplate.png delete mode 100644 media/items/new/generic-dagger.png delete mode 100644 media/items/new/generic-food.png delete mode 100644 media/items/new/generic-guildTome.png delete mode 100644 media/items/new/generic-helmet.png delete mode 100644 media/items/new/generic-leggings.png delete mode 100644 media/items/new/generic-necklace.png delete mode 100644 media/items/new/generic-potion.png delete mode 100644 media/items/new/generic-relik.png delete mode 100644 media/items/new/generic-ring.png delete mode 100644 media/items/new/generic-scroll.png delete mode 100644 media/items/new/generic-spear.png delete mode 100644 media/items/new/generic-sword.png delete mode 100644 media/items/new/generic-wand.png delete mode 100644 media/items/new/generic-weaponTome.png delete mode 100644 media/items/new/palette.png create mode 100644 media/items/old.png delete mode 100644 media/items/old/generic-armorTome.png delete mode 100644 media/items/old/generic-boots.png delete mode 100644 media/items/old/generic-bow.png delete mode 100644 media/items/old/generic-bracelet.png delete mode 100644 media/items/old/generic-chestplate.png delete mode 100644 media/items/old/generic-dagger.png delete mode 100644 media/items/old/generic-food.png delete mode 100644 media/items/old/generic-guildTome.png delete mode 100644 media/items/old/generic-helmet.png delete mode 100644 media/items/old/generic-leggings.png delete mode 100644 media/items/old/generic-necklace.png delete mode 100644 media/items/old/generic-potion.png delete mode 100644 media/items/old/generic-relik.png delete mode 100644 media/items/old/generic-ring.png delete mode 100644 media/items/old/generic-scroll.png delete mode 100644 media/items/old/generic-spear.png delete mode 100644 media/items/old/generic-sword.png delete mode 100644 media/items/old/generic-wand.png delete mode 100644 media/items/old/generic-weaponTome.png diff --git a/builder/index.html b/builder/index.html index 37b62f6..eec6e2b 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1,2 +1,2 @@ - WynnBuilder
Join the discord today to suggest new features, submit bug reports, and hangout/talk to devs!
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Level:
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Active boosts
Earth
Thunder
Water
Fire
Air
Curse (Active)
Concentration (Passive)
Offense
Defense
Overall
Input a weapon to see abilities!

Made by hppeng, ferricles, and reschan with Atlas Inc (JavaScript required to function, nothing works without js)

Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.

\ No newline at end of file + WynnBuilder
Join the discord today to suggest new features, submit bug reports, and hangout/talk to devs!
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Level:
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Active boosts
Earth
Thunder
Water
Fire
Air
Curse (Active)
Concentration (Passive)
Offense
Defense
Overall
Input a weapon to see abilities!

Made by hppeng, ferricles, and reschan with Atlas Inc (JavaScript required to function, nothing works without js)

Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.

\ No newline at end of file diff --git a/builder/index_full.html b/builder/index_full.html index fcf508e..4e7d217 100644 --- a/builder/index_full.html +++ b/builder/index_full.html @@ -49,7 +49,7 @@
- +
@@ -76,7 +76,7 @@
- +
@@ -103,7 +103,7 @@
- +
@@ -130,7 +130,7 @@
- +
@@ -156,7 +156,7 @@
- +
@@ -183,7 +183,7 @@
- +
@@ -210,7 +210,7 @@
- +
@@ -237,7 +237,7 @@
- +
@@ -263,7 +263,7 @@
- +
@@ -620,7 +620,7 @@
- +
@@ -644,7 +644,7 @@
- +
@@ -668,7 +668,7 @@
- +
@@ -692,7 +692,7 @@
- +
@@ -716,7 +716,7 @@
- +
@@ -740,7 +740,7 @@
- +
@@ -764,7 +764,7 @@
- +
@@ -1262,7 +1262,7 @@ - + diff --git a/crafter/index.html b/crafter/index.html index 20e4a4c..34cacf7 100644 --- a/crafter/index.html +++ b/crafter/index.html @@ -39,7 +39,7 @@
- +
@@ -283,7 +283,7 @@ - + diff --git a/dev/index.html b/dev/index.html index 0ee432b..9aea30c 100644 --- a/dev/index.html +++ b/dev/index.html @@ -959,7 +959,7 @@
-->
- + diff --git a/items_adv/items_2_help.html b/items_adv/items_2_help.html index 9e89d9d..4736c14 100644 --- a/items_adv/items_2_help.html +++ b/items_adv/items_2_help.html @@ -221,6 +221,6 @@ docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2])); } - + diff --git a/js/builder_graph.js b/js/builder_graph.js index 093f367..ff51b64 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -307,7 +307,8 @@ class WeaponInputDisplayNode extends ComputeNode { const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element const type = item.statMap.get('type'); - this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png'); + this.image.style.backgroundPosition = itemBGPositions[type]; + let dps = get_base_dps(item.statMap); if (isNaN(dps)) { dps = dps[1]; diff --git a/js/crafter.js b/js/crafter.js index 865b740..4c19efe 100644 --- a/js/crafter.js +++ b/js/crafter.js @@ -345,9 +345,16 @@ function toggleMaterial(buttonId) { function updateCraftedImage() { let input = document.getElementById("recipe-choice"); if (all_types.includes(input.value)) { - document.getElementById("recipe-img").src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + input.value.toLowerCase() + ".png"; + let img = document.getElementById("recipe-img"); + if (["potion", "scroll", "food"].includes(input.value.toLowerCase())) { + img.style.backgroundImage = "url('../media/items/common.png')"; + img.style.backgroundSize = "500% 100%"; + } else { + img.style.backgroundImage = "url('../media/items/" + (newIcons ? "new.png')" : "old.png')"); + img.style.backgroundSize = "1200% 100%"; + } + img.style.backgroundPosition = itemBGPositions[input.value.toLowerCase()] } - } /* Reset all fields diff --git a/js/display.js b/js/display.js index cfc064f..48eefb8 100644 --- a/js/display.js +++ b/js/display.js @@ -1,3 +1,7 @@ +const itemBGPositions = {"bow": "0 0", "spear": "9.090909090909088% 0", "wand": "18.181818181818183% 0", "dagger": "27.27272727272727% 0", "relik": "36.36363636363637% 0", + "helmet": "45.45454545454546% 0", "chestplate": "54.54545454545454% 0", "leggings": "63.63636363636363% 0", "boots": "72.72727272727272% 0", + "ring": "81.81818181818181% 0", "bracelet": "90.90909090909092% 0", "necklace": "100% 0", + "potion": "25% 0", "scroll": "50% 0", "food": "75% 0"}; function apply_elemental_format(p_elem, id, suffix) { suffix = (typeof suffix !== 'undefined') ? suffix : ""; @@ -286,11 +290,18 @@ function displayExpandedItem(item, parent_id){ parent_div.appendChild(nolink_row); if (item.has("type")) { - let img = make_elem("img", [], { - src: "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png", + let img = make_elem("div", [], { alt: item.get("type"), - style: " z=index: 1; position: relative;" + style: "z-index: 1; position: relative; image-rendering: pixelated; width: 50%; height: 50%; background-position: " + itemBGPositions[item.get("type")] + ";" }); + if (["potion", "scroll", "food"].includes(item.get("type"))) { + img.style.backgroundImage = "url('../media/items/common.png')"; + img.style.backgroundSize = "500% 100%"; + } else { + img.style.backgroundImage = "url('../media/items/" + (newIcons ? "new.png')" : "old.png')"); + img.style.backgroundSize = "1200% 100%"; + } + let container = make_elem("div"); let bckgrd = make_elem("div", ["col", "px-0", "d-flex", "align-items-center", "justify-content-center", 'scaled-bckgrd'], { // , "no-collapse" diff --git a/js/icons.js b/js/icons.js index a796942..ddf9ef0 100644 --- a/js/icons.js +++ b/js/icons.js @@ -1,33 +1,39 @@ //which icons to use let window_storage = window.localStorage; +console.log(window_storage); icon_state_stored = window_storage.getItem("newicons"); newIcons = true; if (icon_state_stored === "false") {toggleIcons()} + /** Toggle icons on the ENTIRE page. * */ -function toggleIcons() { + function toggleIcons() { newIcons = !newIcons; - let imgs = document.getElementsByTagName("IMG"); + let imgs = document.getElementsByTagName("img"); + let divs = document.getElementsByTagName("div"); let favicon = document.querySelector("link[rel~='icon']"); - let toggleiconbutton = document.getElementById("toggle-icon-button"); if (newIcons) { //switch to new favicon.href = favicon.href.replace("media/icons/old","media/icons/new"); for (const img of imgs) { if (img.src.includes("media/icons/old")) {img.src = img.src.replace("media/icons/old","media/icons/new");} - if (img.src.includes("media/items/old")) {img.src = img.src.replace("media/items/old","media/items/new");} } - toggleiconbutton.textContent = "Use Old Icons"; + for (const div of divs) { + if (div.style.backgroundImage.includes("media/items/old")) {div.style.backgroundImage = div.style.backgroundImage.replace("media/items/old","media/items/new");} + } + //toggleiconbutton.textContent = "Use Old Icons"; window_storage.setItem("newicons","true"); } else { //switch to old favicon.href = favicon.href.replace("media/icons/new","media/icons/old"); for (const img of imgs) { if (img.src.includes("media/icons/new")) {img.src = img.src.replace("media/icons/new","media/icons/old");} - if (img.src.includes("media/items/new")) {img.src = img.src.replace("media/items/new","media/items/old");} } - toggleiconbutton.textContent = "Use New Icons"; + for (const div of divs) { + if (div.style.backgroundImage.includes("media/items/new")) {div.style.backgroundImage = div.style.backgroundImage.replace("media/items/new","media/items/old");} + } + //toggleiconbutton.textContent = "Use New Icons"; window_storage.setItem("newicons","false"); - } + } } \ No newline at end of file diff --git a/js/sq2icons.js b/js/sq2icons.js deleted file mode 100644 index 96b97b8..0000000 --- a/js/sq2icons.js +++ /dev/null @@ -1,34 +0,0 @@ -//which icons to use -let window_storage = window.localStorage; -console.log(window_storage); -icon_state_stored = window_storage.getItem("newicons"); -newIcons = true; -if (icon_state_stored === "false") {toggleIcons()} - - -/** Toggle icons on the ENTIRE page. - * - */ - function toggleIcons() { - newIcons = !newIcons; - let imgs = document.getElementsByTagName("IMG"); - let favicon = document.querySelector("link[rel~='icon']"); - - if (newIcons) { //switch to new - favicon.href = favicon.href.replace("media/icons/old","media/icons/new"); - for (const img of imgs) { - if (img.src.includes("media/icons/old")) {img.src = img.src.replace("media/icons/old","media/icons/new");} - if (img.src.includes("media/items/old")) {img.src = img.src.replace("media/items/old","media/items/new");} - } - //toggleiconbutton.textContent = "Use Old Icons"; - window_storage.setItem("newicons","true"); - } else { //switch to old - favicon.href = favicon.href.replace("media/icons/new","media/icons/old"); - for (const img of imgs) { - if (img.src.includes("media/icons/new")) {img.src = img.src.replace("media/icons/new","media/icons/old");} - if (img.src.includes("media/items/new")) {img.src = img.src.replace("media/items/new","media/items/old");} - } - //toggleiconbutton.textContent = "Use New Icons"; - window_storage.setItem("newicons","false"); - } -} \ No newline at end of file diff --git a/media/items/common.png b/media/items/common.png new file mode 100644 index 0000000000000000000000000000000000000000..417c2ec5e06dcadbf56328fe93deaa18e889a467 GIT binary patch literal 988 zcmV<210(#2P)Px#8FWQhbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X z4i^9b010qNS#tmYE+YT{E+YYWr9XB6009(GOjJcja7+m(7DYxu(bL96L_~FVZzf7L z7#J8aVM6BU+ALK$9x`f!gnh%sxg+8XAC-v%ks5e?%joQ6)Gc6*C(Ex@tfF|Mb4TzE>{*PI?0W*Z}MA z`9XJ0x@$jWIw2o10D(mSpHU@iYimPPXqJ|i+~VP}Y5?o&>pnaQ$X+C&(c*4QKiDxIP zyXULA=YK;ZATt1dLnD1dBV;)td3kx0@?R=Rt?;JV@P&VH461B`%*gbAR;|((tAY@vI>O*@j`*NYyMZ_M6T4B(>6^E`;NW!U#wZP5#lxS1%LP&ETV6pqF%HJhMjlB+|Osx zsfZ8o1JCms)oLngjsXQii3Th29)xkh`QkP5}Sf2q%x7~+nf5( zL*Mn;I~9uKO|+nes~?;7U4I1C&91s#3KoS3T(Ky?$wB5R#NC6uc3pRugUDiCM92Z~ zd#km&9sRl=-gLWHwd*!9UxK*LxsuKqShRF7i0c-3Tn;*P*C5(N0L$~RwOMc0!`gKX z(pEq$nS#^#0XsY<7|RtFp0N~Yh1oH&ZH?}l^3SjZ>CAwewdr^eWllMKbp%H@6UnX& zsx~dohFxemD%*yGWwK1dK?l<26JXUDhw<^zpSJ-SbFr{Hrtbh^u~c=34;T!>!I$}b zUM{CI%q>Uz_vl^$@eTbYWBz4lo9&1)i!?*JoX8Uw6%qND8|ydvBt1tCB?pKA0000< KMNUMnLSTa3v9?73 literal 0 HcmV?d00001 diff --git a/media/items/new.png b/media/items/new.png new file mode 100644 index 0000000000000000000000000000000000000000..9a498f6be28b1b7f073c10734205940b6cf210fd GIT binary patch literal 27938 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 literal 0 HcmV?d00001 diff --git a/media/items/new/generic-armorTome.png b/media/items/new/generic-armorTome.png deleted file mode 100644 index ba0833b630b6caef56323297304536d102d133bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1745 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 KMNUMnLSTaQeJM!* diff --git a/media/items/new/generic-bracelet.png b/media/items/new/generic-bracelet.png deleted file mode 100644 index 4683abe38f0efd21d0e03f96be859c83d8f1447f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3281 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 diff --git a/media/items/new/generic-dagger.png b/media/items/new/generic-dagger.png deleted file mode 100644 index dadc4c1dcd128375b39e172cbd21997dd8e2b6e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2876 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 diff --git a/media/items/new/generic-leggings.png b/media/items/new/generic-leggings.png deleted file mode 100644 index 845fa016a14db630780334f2a19fac5bf5bf60aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3206 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 diff --git a/media/items/new/generic-potion.png b/media/items/new/generic-potion.png deleted file mode 100644 index 53c29d0fe8582613d7ade1dd0b13128b85b1a17e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 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 diff --git a/media/items/new/generic-scroll.png b/media/items/new/generic-scroll.png deleted file mode 100644 index ed4e8fda318cff138650b954a6b56c1f49de55d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|emUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIICa0X`wF?gc)FkM5j0t3f8R)#O^pnc}zW-D1GLD zxax1l{_hKSZf<;0e}a3}R;Oh*f4j82T4bY_;x@z8;5M7_HsibPK}t8ASVFE|cU$^E z@6ek*^?Q1uwt5OuEu}oW+C$Vj7AVPo30>>3mvEkshl0pGABw5<;=4b06w`LVEZ#odBm_eD7NKV|+f`4Gnn T7q4ib`x!i4{an^LB{Ts5F$;ln diff --git a/media/items/new/generic-spear.png b/media/items/new/generic-spear.png deleted file mode 100644 index 89c46ab707c730a0f5801929adcf2db95e8c8e7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2561 zcmV+c3jXzpP)Px&08mU+MMrQ<1OoyQ5fCsiFuJRn`StKsK`U26EC6h0 z0000~KPnX!6;eJa1_T2O2nHb`A-t}gQBhGYA{Ksrehmr+KtMnyCME`HV=EvPx2Tv6 z4Gl{;B{elQM>ZrdBo_c^V*qMpO*$qD3JL&ZUtwWkaBy%51_VSiBD<`c_w(&39uyG` z2qqm900jkkd3jGfC?p#aBN`D!G$TbtMK&oL1_cFLT3P^JSvxEpDJdzorj`f>13peY z_3-NW^zJbw7&t2&02C7t3kOIyBmfBs9UUD&FdwFwih*`&1!`p1&B65V=~+ZB!?mUG z>*O^j8UPj*06{(*6b%pz2N@C!08UC24+<6#3mp~?03IC~7Zm_vT>uRXuc46)3kf

dd?dJexVE{=*05viv9uxo? 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 diff --git a/media/items/new/generic-weaponTome.png b/media/items/new/generic-weaponTome.png deleted file mode 100644 index ba0833b630b6caef56323297304536d102d133bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1745 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 XBdfB_Hut4b00000NkvXXu0mjfq9(Rx diff --git a/media/items/old/generic-boots.png b/media/items/old/generic-boots.png deleted file mode 100644 index e74bc1b85f4be3938ebd76e263365aaf965039ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D%zV?13PLn`LH zz4I{hkb^|S!)+yu=>;stTQ|Sp*wv)Gh5Lz4rv9FwdC!-(J$drRK`;+4XkF=6ZX-+#Y) zy?p=iNbjhx-Cui_t<=0cQC2`$LCwLZVFts-%-{*) ztBb2weZ9d57336{ZN_SA89upU>(?G6QJ7I+WoGr_CRVJqcJu%5n+0U~?$tRxCHQg~ z*eU}E!haSW-L z^LEx=Z)QhL48BL|yy`9QM83au{@3D9cT`GO|Mh%T^||%R%O^`d);+Rz;C0}3xcuGl zfLZ=?`K)>GjvJrbdFp!4|Ce7X=D!fU&zQv!#W3r@@rE;}q)tYd_&tBPbbIyVudPMR zS2}NStjv{s{^88~cd{=xJLrQ{O5IOB@WUwf^!YQYCq7=kJ?Z1GkmX*lb{}uk_uY1; z_(*w!RD)QebiO1=;-TXFl(E&j50Auc>~``FDRh&wYLy zw8Q8;)Dcq0%b4zOKArmVqwuyX@?Q|9nX-MTt>Kdw4wBu~9{yc(#eIZ9rLqU^?>~Ng z(UO1fZTBpI8FXFZfbn_Fus>(Nyg--|Id{I%y1zfoEgkpIcURuWe1%~PgUPy5hWOXw zr|!?ovv`3p^>USY{kr_>d;HPQETIATxTfas1o^zKXYaoso>$rTgD-&j0@H;ZpLWmi no!s+$o7u^a8`9^&Ox^FVGXIg_{QO8@@?h|E^>bP0l+XkKs>2v6 diff --git a/media/items/old/generic-bracelet.png b/media/items/old/generic-bracelet.png deleted file mode 100644 index 3dc5df29370efcdffe37f29d0727777e434b1584..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 677 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-HD>V2by2aSW-L z^LF;d&O;6YZRW>A1!}IdY+;NJ$T`Tx^7gRWnge1g+ZM2DNPN=U_P}<|#M$#x)GwYm zzwxuJrOHj7!{_!J&%SD@oU`cD@xOKdUvy7WseH{^InVu9?Agvm_aE-tCG*8Ti7~$s>{@)dgbC%PeS3a$7n$IZ4u#GtlQY(nsA#;9}s$CKY5 zh}fN-AbUYPC-ZIQy&bH-j<=Sh<>tszGSN}Er^>^Qft-F*@Z#q>7 zGd=4y!Jo+cM4@(F@of%$t9$`$%EVbC&pnOb5yudf(SHoLJeF zpUrfICFc9ayaT1Orq6;c`(cq|^6$LBxA!YJQ|n`!_A*~8J@@#t)HeAK2S9p#lX+KI vr$2wV_CIIY+|M1E2bj4TZZjT!@QJx7^Fz<2gC3iK>5jqE)z4*}Q$iB}??pA& diff --git a/media/items/old/generic-chestplate.png b/media/items/old/generic-chestplate.png deleted file mode 100644 index 0f90a4cc0ab50cbd9398177d0eef7efdea17fd3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 484 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 XBdfB_Hut4b00000NkvXXu0mjfq9(Rx diff --git a/media/items/old/generic-helmet.png b/media/items/old/generic-helmet.png deleted file mode 100644 index c085f9d388281a5d95ccb4cb528cbf29e4cd4f28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 395 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D%zT|HeKLn`LH zz4I`$*+J&m!!0F@;THr;#M(=ySv0MC!0~o@kVf3Y2WMutJ^z`}B=^tall!DQc|cQu z;KQkVLE>`9KYo7mW7oR=xKrCt?5*&RnIg~B!YQEUFoPj+di|&A%jTv=>g|13C=>dA z{<4){E-n3jf{#O3!KcB1@em6~_BYOW>$hKO<4gb5-hVA#`sn5|WowwEu)-VVy$$!H ze63Yuug{fRKesPxBQsDFRQD`j$F29DYx-J$wlTM?Ke;W}_uSk-!FAOH&+xa4}=H diff --git a/media/items/old/generic-necklace.png b/media/items/old/generic-necklace.png deleted file mode 100644 index 6f7d81ac0ac587ba2f01bd7e6d374e7772d36ad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 682 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-HD>U`p|HaSW-L z^LEy5Z)QhSZ zIpUh&zrl_fZ2020j9*QgOu-D;J=`pm20o{oH)7JGs3Ier5DH^H=Omp9j-*qA^Y5Cn-`qxUOV|) z-22<|@$>Gp3tmR<+p5Yp>)mIq2eN8=SJ$w=Hg;e-@Rq^c&2~YIUUQ$@;#;%iC1x|$ zK6tcm{S18CNjio zh&XJ2e%*1o?cbaG{>dr_t%+-%_MYiJ)Q;y2UdQi9z6demw3>dpQE#f(4u+SKUT*`1 z0}dZu^>266ANdcE@DN_buHtz%_{Bc|h0j|Q4*e^B0`u4G0NaLlpSL`Fxlg|P=%E?E zh1Z;C;AGg&d?(}k)hOQndy9=PFL?i%Rbs=|&1+5FzW-g?>Z)A-!rC)fY2vS^YoTge z-ZGX3`}E|$&1wF7_NwXRm&ZK#KR5!zp+SLzNj+YmY}K4ko%Slg#K++2>gTe~DWM4f DsKP?9 diff --git a/media/items/old/generic-potion.png b/media/items/old/generic-potion.png deleted file mode 100644 index 93b4b7e4d6b40aad93961830bb732f2da5c9349f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 650 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Ea{HEjtmSN`)Ym%PG(?Wa`bd@ z45^s&cJ@Yn=RlET@vb~BJti(Cf}GrrJ!@PH_BdW@JaXY;d}5kl^Afhs1dV3BSsGSb zRM|YugEGCNy*d0lunSY*zUGTNGhZ!+t|Ju;|tViy@wr*D}s;w#Md6AH@KCief zQDWb@yhYE`pLK-2<^NF5$O<-h+I7azZ!ea7yqGfQ#qO9Wsr#3oDqGe1f9F0Tmt1~6 z;5XL;mIb^E0>3d_t={(~O6}L%y$)L}+Cw{Ro!)S+l+LP5t?hlG^_+i+ts_+F#yxxj z&mTDG@4a{JmgAQ%N7j_=dGVC{;?d(W7w5+p+0TsGY~#@QP4ob#gS10(wSR$mjqjIN zZa3adm=?k3b8A;T%$U2(M=az2XCFkN7~5b=(&?D?H| zXPDwU%;(Pcb$pv0?2`XJUNE;jR$<@wzt1LI=?~qqp23gd7K2nOzrg;jEB}30|JL$v zU*e)}|C00mtvlWUBS@>vVato@=h-#DJ}vyyb0Pn5dP~kBkOM#STDs0ZGe@rEH8628 Nc)I$ztaD0e0sv6KEQtUB diff --git a/media/items/old/generic-relik.png b/media/items/old/generic-relik.png deleted file mode 100644 index 0593e151aedaab1ad1a735cbf5fcc726ee8267f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 713 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-HD>U~2YsaSW-L z^LEy5@7akW$L!xbcX#nhan8uP61G~?X<m1GD`ttEmf%hJQnY9y*0Jxn@bLwy z{e|5hMHhhdKf88~aV_)T2bHf&grDs0+`d5l@#O{6e=S;Y_uOYcm`jSb@lBBXKljMT z5TWO9Jv?Pf3bwF)Ir7|of%Nf>={B6dKK_)xXAD!EcZb<&?~9+CBY2K^*G^qft+kQ; z#MZz4bDb}Q@^?GT|FetX?yntETP{~RZIer^VFd?5;a>3%+xKkvT2qtD`m6KKdvE@8 zdUedTAz|#l_UN%#nHjUb@3-MT@Y}qsFjn!sY?wL!8XuT_Mcc$by!zQ2!muUA{EcqS zuk|->Nw3m>du;uA;hyyjz6{n3W|7w!qc5g@2nhb|?f?1DlQ-cPKJn;Zxy^Zgdgu<$ z>$X+qFNDDAUdk!h?%&%d_s#0OH~%#E53}bjek}as@5`P#Z68PB1AGm>VfVl8&)WRA z?f7eR%TG3r^W--eK!bTn?Tf3Q7b_oL@Or)-yY6?sa?!`y4q!c1`U>x#sYpJny#2Z9 icM~Ms7ih$+W3ah=LfbM=tOl4i89ZJ6T-G@yGywo^sZ4bM diff --git a/media/items/old/generic-ring.png b/media/items/old/generic-ring.png deleted file mode 100644 index 744ba7f4b669d6b511fae84cf753ec48d669e1eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 622 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-HD>VAAk(aSW-L z^LExoZ>KB*7MdOA32HjtL=YP-Bf9&x6&$jc`_bZPdwtRlC{C;+Z0E;6CeL0d? znzS_N=ha>}HKl|HpC89s?vFSzdnU_$@1;U~R%`xlb@Sf*&rpaVk9|kxFYUv%%!(_1 z9{ixv)6duHuz%Wp2YZ`mQ@&09D3R+m?e|UpE$10H8MZUuxw4jh#??PbC2U`EzsY_1 zch4+i7ypOvY>xi=>zm7V-`!T#^i5KM@j+RG^ljUQcW-?|s&D(T-V#;SOIx@=o>(#edAE3x9d` zifgY+Tfna)2er@elC8_XZKe=}4a zbFX$<+j2}f;e5h+{v|=yPsFc$n!CUiPx%dr3q=RCt{2o6jpmQ547D$kvpJ#7?Xz`~eaQDG?XZx9D?8^BGjZbuyRL&F24y|}34hfLBV zeoe=`M-IRo<(U(#j{tE1w*lPv<^UY?ef>RwFTyeDy6;4nm6aM9c7OjsbU2(x^ytVG z(KR(y&_7J{*;&jpCs-c=;s9;~xY4cy3JVL2+Ex9L=jRuqam-^*LAExxjlP${`UnsQ za2vplb`C(+RyT-_MDmHQs;(sZ^duJeK71a*_aVJ~-GS?WzZ_wG1c(E;4d6yQ2Via9 z-q9M^AL{_qwE%*3LS0=g(SEhQ3hN_49KdY=H`+NMesx21XIC51m?L&~_OmeSdtHFv zt8e(00|MWdNF<03g+5b+^${Qr;5LAppd3Kv(P(s!%(FNqah}KZe75@os1d9SmY3G# zTtEkL0Jj0$WV|1MV^THmXS-j3YY)sZs{I5%td9V30Jj0$_~wB5xkaLzTN;UuonH`* zwYll~-?VPP9Q6A7X7s%j)<=LifZG6W(m6nN?+?%4A0M9rb++K0pXr&2!2Yi5X7nBF zBS0L$Z2&jv9H1I=IIrV8teUq~cYg5vAML@l0@nT>_XM@DJ_5u6+y-!y-Y08nZooED z7vP>-rt5EAu(nt2``_K35?x+VPxb&04~|K@oC|y*4&XL`8|`y}^_AH_^*=($=;Q*P nV`x2ReFTUDxD5~lLEz*AgVB+(1aSW-L z^ET?Bc5|VG8E^2-O( zlMQQ}ZJ7nK7@`@Z=gnqLd6xevbwxkVHzxfnyBgfxln*?utxwiwynpitUjXwBrW+sb zNGb@McHYkZs7qkOqmzlT^V4hC zS1{%<=3IE*aCK?si9dO)#<4$LI_FMRe^C2=`+@W8-o5_i2X*AJ`R5snwyocHZGqgG z^v3!U9%lZYUPgQGG?}@{P$h1MpEJz-`JzJN)$NtR4^$`q+`caF!TFcA;il41cdR?l zX!QBh%(-Xw%(=_1*7oq%z5xC`|8BDO+9yJtVqwSp#7@6{x3}zE=IytyKds$A<4)#_ zHAztED|7W{R_;76ewzQl_sy)oiruwgUflL`%kJ~H&wh(*|0uhFZ3F9ukJ;iAe*dXI zy{82#@UXmke!dw@{l1+UH$Ls(5qJOj+(XbH{K3|&)7defRk$9QOc*>}{an^LB{Ts5 DH_I3` diff --git a/media/items/old/generic-sword.png b/media/items/old/generic-sword.png deleted file mode 100644 index 33174276e8640c111fc4d993576b1c34a4d2027a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 828 zcmeAS@N?(olHy`uVBq!ia0vp^3qY8K8Aw(>tdav#Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD4`PI6XFV_U0huL|Nk!` zA+c}Yz8g1gWM^kjn>NkUOfeHw>ZbQ+fkJ0IT^vI)?!CQ!koS;*fXl@#4%}NFh-EZ_k)rMe zrn3uYI)`;mVvcK>^z?tU&eKf&X>Suxl+Bv{je+rWqu9A%Uq&XTby4E192^xZ9x^a~ zXV6=-`q#69hK6?ba$6aN2mA-mrtiD1Q)4EhkP!P}nbX^?42-Xv6z@#^zxl!Wd&ztP z8!TSz5&ryM+TlP}!ivI}r8*7=COvTOJ>_H5Ruu{qT(EX|mFD`bEBQnPHV8<}J}s@c z#?_IT>7VY4)zSZ?I5;B2b3QXLGG4D@UK(|lBfi#bKG0vPC9V-ADTyViR>?)FK#IZ0 zz{pV7z)087BE-PV%FxKlz);)3z{rktgKw}lS^|`^Gd9& z0)R@384Q>I->r|P#uH?Um6b=0MeiXQq^7fRyaAig^uG zB8jBLH#0Z2q_QBD0qiRMg1mJ5O<{g$=JodA?GMUo2*@?=QM%t;0MUB4u^K)(d!%bX%$K%^U47#f%wn;V)K7+6{u YngI>4ls@kn2Q+}e)78&qol`;+0Ck)ZuK)l5 diff --git a/media/items/old/generic-wand.png b/media/items/old/generic-wand.png deleted file mode 100644 index 2aa60f51b412978b849645082292f667e0a5bf61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 568 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 XBdfB_Hut4b00000NkvXXu0mjfq9(Rx From 60376aa9c5b31d94bcb41831af183b808c0048c0 Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Mon, 25 Jul 2022 15:38:30 +1000 Subject: [PATCH 07/19] Add red outline to errors --- css/sq2bs.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/css/sq2bs.css b/css/sq2bs.css index 59e4e99..a40b162 100644 --- a/css/sq2bs.css +++ b/css/sq2bs.css @@ -102,12 +102,15 @@ input.equipment-input { font-weight: bold; background-color: hsl(0, 0%, 21%) !important; border-radius: 0.375rem !important; - border-color: rgba(33, 37, 41, 1) !important; min-height: calc(1.2 * var(--scaled-fontsize) + 2px); padding: 0rem 0.5rem; font-size: var(--scaled-fontsize); } +input.equipment-input:not(.is-invalid) { + border-color: rgba(33, 37, 41, 1) !important; +} + .my-container { position: fixed; /* Stay in place */ left: var(--sidebar-width); From e47673ef17e6bbcb4d4a933ad11d057e0029ce21 Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Mon, 25 Jul 2022 15:42:51 +1000 Subject: [PATCH 08/19] Add powder input error detection --- js/builder_graph.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index 73caa07..f5956a6 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -445,7 +445,6 @@ class PowderInputNode extends InputNode { compute_func(input_map) { // TODO: haha improve efficiency to O(n) dumb - // also, error handling is missing let input = this.input_field.value.trim(); let powdering = []; let errorederrors = []; @@ -453,12 +452,27 @@ class PowderInputNode extends InputNode { let first = input.slice(0, 2); let powder = powderIDs.get(first); if (powder === undefined) { - return null; + if (first.length > 0) + errorederrors.push(first); + else + break; } else { powdering.push(powder); } input = input.slice(2); } + + if (this.input_field.getAttribute("placeholder") != null) { + let placeholder = this.input_field.getAttribute("placeholder"); + if (placeholder.includes(" slots") && parseInt(placeholder[0]) < powdering.length) + errorederrors.push("Too many powders: " + powdering.length); + } + + if (errorederrors.length) + this.input_field.classList.add("is-invalid"); + else + this.input_field.classList.remove("is-invalid"); + return powdering; } } From 30035dc732195dcf280dee5ae594beac34eda2f6 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 24 Jul 2022 09:05:57 -0700 Subject: [PATCH 09/19] HOTFIX: move atree toggles into same thing that handles sliders so they update when they are toggled --- js/atree.js | 24 +++++++++++++++++------- js/atree_constants.js | 30 +++++++++++++++++++----------- js/atree_constants_min.js | 2 +- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/js/atree.js b/js/atree.js index 639eca2..0b83444 100644 --- a/js/atree.js +++ b/js/atree.js @@ -739,6 +739,20 @@ const atree_scaling = new (class extends ComputeNode { for (const effect of abil.effects) { switch (effect.type) { + case 'raw_stat': + // TODO: toggles... + if (effect.toggle) { + const button = button_map.get(effect.toggle).button; + if (!button.classList.contains("toggleOn")) { continue; } + for (const bonus of effect.bonuses) { + const { type, name, abil = "", value } = bonus; + // TODO: prop + if (type === "stat") { + merge_stat(ret_effects, name, value); + } + } + } + continue; case 'stat_scaling': let total = 0; const {round = true, slider = false, scaling = [0]} = effect; @@ -789,7 +803,6 @@ const atree_stats = new (class extends ComputeNode { compute_func(input_map) { const atree_merged = input_map.get('atree-merged'); - const [slider_map, button_map] = input_map.get('atree-interactive'); let ret_effects = new Map(); for (const [abil_id, abil] of atree_merged.entries()) { @@ -798,11 +811,8 @@ const atree_stats = new (class extends ComputeNode { for (const effect of abil.effects) { switch (effect.type) { case 'raw_stat': - // TODO: toggles... - if (effect.toggle) { - const button = button_map.get(effect.toggle).button; - if (!button.classList.contains("toggleOn")) { continue; } - } + // toggles are handled in atree_scaling. + if (effect.toggle) { continue; } for (const bonus of effect.bonuses) { const { type, name, abil = "", value } = bonus; // TODO: prop @@ -816,7 +826,7 @@ const atree_stats = new (class extends ComputeNode { } return ret_effects; } -})().link_to(atree_merge, 'atree-merged').link_to(atree_make_interactives, 'atree-interactive'); +})().link_to(atree_merge, 'atree-merged'); /** * Construct compute nodes to link builder items and edit IDs to the appropriate display outputs. diff --git a/js/atree_constants.js b/js/atree_constants.js index f6570fa..939ad25 100644 --- a/js/atree_constants.js +++ b/js/atree_constants.js @@ -7665,21 +7665,14 @@ const atrees = { "type": "replace_spell", "name": "Backstab", "base_spell": 3, - "display": "Total Damage", + "display": "Backstab Damage", "parts": [ { - "name": "Per Hit", + "name": "Backstab Damage", "type": "damage", "multipliers": [ 200, 50, 0, 0, 0, 0 ] - }, - { - "name": "Total Damage", - "type": "total", - "hits": { - "Per Hit": 1 - } } ] } @@ -9139,8 +9132,23 @@ const atrees = { "bonuses": [ { "type": "stat", - "name": "dmgMult.Satsujin", - "value": 300 + "name": "damMult.Satsujin:3.Backstab Damage", + "value": 200 + }, + { + "type": "stat", + "name": "damMult.Satsujin:3.Per Hit", + "value": 200 + }, + { + "type": "stat", + "name": "damMult.Satsujin:3.Fatality", + "value": 200 + }, + { + "type": "stat", + "name": "damMult.Satsujin:0.Melee", + "value": 200 } ] } diff --git a/js/atree_constants_min.js b/js/atree_constants_min.js index 8b067f1..8f66739 100644 --- a/js/atree_constants_min.js +++ b/js/atree_constants_min.js @@ -1 +1 @@ -const atrees={"Archer":[{"display_name":"Arrow Shield","desc":"Create a shield around you that deal damage and knockback mobs when triggered. (2 Charges)","parents":[60,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6,"icon":"node_archer"},"properties":{"charges":2,"duration":60,"aoe":5000},"effects":[{"type":"replace_spell","name":"Arrow Shield","cost":30,"base_spell":4,"display":"Total Damage","parts":[{"name":"Shield Damage","type":"damage","multipliers":[90,0,0,0,0,10]},{"name":"Total Damage","type":"total","hits":{"Shield Damage":2}}]}],"id":0},{"display_name":"Escape","desc":"Throw yourself backward to avoid danger. (Hold shift while escaping to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4,"icon":"node_archer"},"properties":{"aoe":0,"range":0},"effects":[{"type":"replace_spell","name":"Escape","cost":25,"base_spell":2,"display":"","parts":[]}],"id":1},{"display_name":"Arrow Bomb","desc":"Throw a long-range arrow that explodes and deal high damage in a large area. (Self-damage for 25% of your DPS)","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_archer"},"properties":{"aoe":4.5,"range":26},"effects":[{"type":"replace_spell","name":"Arrow Bomb","cost":50,"base_spell":3,"spell_type":"damage","display":"Total Damage","parts":[{"name":"Arrow Bomb","type":"damage","multipliers":[160,0,0,0,20,0]},{"name":"Total Damage","type":"total","hits":{"Arrow Bomb":1}}]}],"id":2},{"display_name":"Heart Shatter","desc":"If you hit a mob directly with Arrow Bomb, shatter its heart and deal bonus damage.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Heart Shatter":1}}],"id":3},{"display_name":"Fire Creep","desc":"Arrow Bomb will leak a trail of fire for 6s, Damaging enemies that walk into it every 0.4s.","base_abil":2,"parents":[68,39,5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6,"icon":"node_1"},"properties":{"aoe":0.8,"duration":6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[30,0,0,0,20,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Burn Damage","hits":{"Fire Creep":15}}],"id":4},{"display_name":"Bryophyte Roots","desc":"When you hit an enemy with Arrow Storm, create an area that slows them down and deals damage every 0.4s.","base_abil":7,"archetype":"Trapper","archetype_req":1,"parents":[4,35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":8,"icon":"node_1"},"properties":{"aoe":2,"duration":5},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Bryophyte Roots","cost":0,"multipliers":[40,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Roots Damage","hits":{"Bryophyte Roots":12}}],"id":5},{"display_name":"Nimble String","desc":"Arrow Storm throw out +6 arrows per stream and shoot twice as fast.","base_abil":7,"parents":[36,69],"dependencies":[7],"blockers":[68],"cost":2,"display":{"row":15,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-15,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","hits":{"Single Arrow":6}}],"id":6},{"display_name":"Arrow Storm","desc":"Shoot a stream of 8 arrows, dealing significant damage to close mobs and pushing them back.","parents":[58,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2,"icon":"node_archer"},"properties":{"range":16},"effects":[{"type":"replace_spell","name":"Arrow Storm","cost":40,"base_spell":1,"spell_type":"damage","display":"Total Damage","parts":[{"name":"Single Arrow","multipliers":[30,0,10,0,0,0]},{"name":"Single Stream","hits":{"Single Arrow":8}},{"name":"Total Damage","hits":{"Single Stream":1}}]}],"id":7},{"display_name":"Guardian Angels","desc":"Your protective arrows from Arrow Shield will become sentient bows, dealing damage up to 8 times each to nearby enemies. (Arrow Shield will no longer push nearby enemies)","archetype":"Boltslinger","archetype_req":3,"base_abil":0,"parents":[59,67],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":19,"col":1,"icon":"node_3"},"properties":{"range":4,"duration":60,"shots":8,"charges":2},"effects":[{"type":"replace_spell","name":"Guardian Angels","base_spell":4,"display":"DPS","parts":[{"name":"Single Shot","type":"damage","multipliers":[30,0,0,0,0,10]},{"name":"Single Bow","type":"total","hits":{"Single Shot":8}},{"name":"DPS","type":"total","hits":{"Single Shot":2}},{"name":"Total Damage","type":"total","hits":{"Single Bow":2}}]}],"id":8},{"display_name":"Windy Feet","desc":"When casting Escape, give speed to yourself and nearby allies.","base_abil":1,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":1,"icon":"node_1"},"properties":{"aoe":8,"duration":120},"effects":[],"id":9},{"display_name":"Basaltic Trap","desc":"When you hit the ground with Arrow Bomb, leave a Trap that damages enemies. (Max 2 Traps)","archetype":"Trapper","archetype_req":2,"parents":[5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":8,"icon":"node_3"},"properties":{"aoe":7,"traps":2},"effects":[{"type":"replace_spell","name":"Basaltic Trap","base_spell":7,"display":"Trap Damage","parts":[{"name":"Trap Damage","type":"damage","multipliers":[140,30,0,0,30,0]}]}],"id":10},{"display_name":"Windstorm","desc":"Arrow Storm shoot +1 stream of arrows, and each stream shoots +2 arrows, effectively doubling its damage.","base_abil":7,"parents":[8,33],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":21,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-10,0,-2,0,0,2]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","cost":0,"hits":{"Single Arrow":2}}],"id":11},{"display_name":"Grappling Hook","base_abil":1,"desc":"When casting Escape, throw a hook that pulls you when hitting a block. If you hit an enemy, pull them towards you instead. (Escape will not throw you backward anymore)","archetype":"Trapper","archetype_req":0,"parents":[61,40,33],"dependencies":[],"blockers":[20],"cost":2,"display":{"row":21,"col":5,"icon":"node_2"},"properties":{"range":26},"effects":[],"id":12},{"display_name":"Implosion","desc":"Arrow bomb will pull enemies towards you. If a trap is nearby, it will pull them towards it instead. Increase Heart Shatter's damage.","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[12,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[40,0,0,0,0,0]}],"id":13},{"display_name":"Twain's Arc","desc":"When you have 2+ Focus, holding shift will summon the Twain's Arc. Charge it up to shoot a destructive long-range beam. (Damage is dealt as Main Attack Damage)","archetype":"Sharpshooter","archetype_req":4,"parents":[62,64],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":25,"col":4,"icon":"node_2"},"properties":{"range":64,"focusReq":2},"effects":[{"type":"replace_spell","name":"Twain's Arc","base_spell":5,"scaling":"melee","use_atkspd":false,"display":"Single Shot","parts":[{"name":"Single Shot","type":"damage","multipliers":[200,0,0,0,0,0]}]}],"id":14},{"display_name":"Fierce Stomp","desc":"When using Escape, hold shift to quickly drop down and deal damage.","archetype":"Boltslinger","archetype_req":0,"base_abil":1,"parents":[42,64],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Fierce Stomp","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","cost":0,"hits":{"Fierce Stomp":1},"display":"Stomp Damage"}],"id":15},{"display_name":"Scorched Earth","desc":"Fire Creep become much stronger.","archetype":"Sharpshooter","archetype_req":0,"parents":[14],"dependencies":[4],"blockers":[],"cost":1,"display":{"row":26,"col":5,"icon":"node_1"},"properties":{"duration":2,"aoe":0.4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[10,0,0,0,5,0]}],"id":16},{"display_name":"Leap","desc":"When you double tap jump, leap foward. (2s Cooldown)","archetype":"Boltslinger","archetype_req":5,"parents":[42,55],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_1"},"properties":{"cooldown":2},"effects":[],"id":17},{"display_name":"Shocking Bomb","desc":"Arrow Bomb will not be affected by gravity, and all damage conversions become Thunder.","archetype":"Sharpshooter","archetype_req":5,"base_abil":2,"parents":[14,44,55],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":28,"col":4,"icon":"node_1"},"properties":{"gravity":0},"effects":[{"type":"convert_spell_conv","target_part":"all","base_spell":3,"conversion":"Thunder"}],"id":18},{"display_name":"Mana Trap","desc":"Your Traps will give you 2.85 Mana per second when you stay close to them.","archetype":"Trapper","archetype_req":5,"base_abil":10,"parents":[43,44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":8,"icon":"node_3"},"properties":{"range":16,"manaRegen":2.85},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":10}],"id":19},{"display_name":"Escape Artist","desc":"When casting Escape, release 120 arrows towards the ground.","base_abil":1,"parents":[46,17],"dependencies":[],"blockers":[12],"cost":2,"display":{"row":31,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Per Arrow","multipliers":[20,0,10,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Max Damage (Escape Artist)","hits":{"Per Arrow":120},"display":"Max Damage (Escape Artist)"}],"id":20},{"display_name":"Initiator","desc":"If you do not damage an enemy for 5s or more, your next sucessful hit will deal +50% damage and add +1 Focus.","archetype":"Sharpshooter","archetype_req":5,"parents":[18,44,47],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":31,"col":5,"icon":"node_2"},"properties":{},"effects":[],"id":21},{"display_name":"Call of the Hound","desc":"Arrow Shield summon a Hound that will attack and drag aggressive enemies towards your traps.","archetype":"Trapper","archetype_req":0,"base_abil":0,"parents":[21,47],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_2"},"properties":{},"effects":[{"type":"replace_spell","name":"Call of the Hound","base_spell":8,"display":"DPS","parts":[{"name":"Single Hit","multipliers":[40,0,0,0,0,0]},{"name":"DPS","hits":{"Single Hit":4}}]}],"id":22},{"display_name":"Arrow Hurricane","desc":"Arrow Storm will shoot +2 stream of arrows.","archetype":"Boltslinger","archetype_req":8,"base_abil":7,"parents":[48,20],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":33,"col":0,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":2}}],"id":23},{"display_name":"Geyser Stomp","desc":"Fierce Stomp will create geysers, dealing more damage and vertical knockback.","base_abil":1,"parents":[56],"dependencies":[15],"blockers":[],"cost":2,"display":{"row":37,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Geyser Stomp","multipliers":[0,0,0,50,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","hits":{"Geyser Stomp":1}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":15,"name":"aoe","value":1}]}],"id":24},{"display_name":"Crepuscular Ray","desc":"If you have 5 Focus, casting Arrow Storm will make you levitate and shoot 20 homing arrows per second until you run out of Focus. While in that state, you will lose 1 Focus per second.","archetype":"Sharpshooter","archetype_req":10,"parents":[49],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":37,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Crepuscular Ray","base_spell":6,"display":"DPS","parts":[{"name":"Single Arrow","multipliers":[20,0,0,5,0,0]},{"name":"DPS","hits":{"Single Arrow":20}},{"name":"Total Damage","hits":{"DPS":7}}]}],"id":25},{"display_name":"Grape Bomb","desc":"Arrow bomb will throw 3 additional smaller bombs when exploding.","base_abil":2,"parents":[51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7,"icon":"node_2"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Grape Bomb","multipliers":[30,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Grape Bomb":3}}],"id":26},{"display_name":"Tangled Traps","desc":"Your Traps will be connected by a rope that deals damage to enemies every 0.2s.","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[26],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":38,"col":6,"icon":"node_1"},"properties":{"attackSpeed":0.2},"effects":[{"type":"add_spell_prop","base_spell":7,"target_part":"Line Damage Tick","multipliers":[20,0,0,0,0,20]},{"type":"add_spell_prop","base_spell":7,"target_part":"DPS","hits":{"Line Damage Tick":5}}],"id":27},{"display_name":"Snow Storm","desc":"Enemies near you will be slowed down.","parents":[24,63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":2,"icon":"node_2"},"properties":{"range":2.5,"slowness":0.3},"effects":[],"id":28},{"display_name":"All-Seeing Panoptes","desc":"Your bows from Guardian Angels become all-seeing, increasing their range, damage and letting them shoot up to +5 times each.","archetype":"Boltslinger","archetype_req":11,"base_abil":0,"parents":[28],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":40,"col":1,"icon":"node_3"},"properties":{"range":8,"shots":5},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Shot","multipliers":[0,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Shot":5}}],"id":29},{"display_name":"Minefield","desc":"Allow you to place +6 Traps, but with reduced damage and range.","archetype":"Trapper","archetype_req":10,"base_abil":10,"parents":[26,53],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":40,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":7,"target_part":"Trap Damage","cost":0,"multipliers":[-80,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"aoe","value":-2},{"type":"prop","abil":10,"name":"traps","value":6}]}],"id":30},{"display_name":"Bow Proficiency I","desc":"Improve your Main Attack's damage and range when using a bow.","base_abil":999,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[5,0,0,0,0,0]}],"id":31},{"display_name":"Cheaper Arrow Bomb","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-10}],"id":32},{"display_name":"Cheaper Arrow Storm","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[12,11,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":21,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":33},{"display_name":"Cheaper Escape","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[7,0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":34},{"display_name":"Earth Mastery","base_abil":998,"desc":"Increases your base damage from all Earth attacks","archetype":"Trapper","archetype_req":0,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":35},{"display_name":"Thunder Mastery","base_abil":998,"desc":"Increases your base damage from all Thunder attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7,39,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":36},{"display_name":"Water Mastery","base_abil":998,"desc":"Increases your base damage from all Water attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[34,36,39],"dependencies":[],"blockers":[],"cost":1,"display":{"row":14,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":37},{"display_name":"Air Mastery","base_abil":998,"desc":"Increases base damage from all Air attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":38},{"display_name":"Fire Mastery","base_abil":998,"desc":"Increases base damage from all Fire attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[36,0,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":39},{"display_name":"More Shields","desc":"Give +2 charges to Arrow Shield.","base_abil":0,"parents":[12,10],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":21,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Shield Damage":2,"Single Bow":2}},{"type":"add_spell_prop","base_spell":4,"target_part":"DPS","behavior":"modify","hits":{"Single Shot":2}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"charges","value":2}]}],"id":40},{"display_name":"Stormy Feet","desc":"Windy Feet will last longer and add more speed.","archetype":"Boltslinger","base_abil":1,"parents":[11],"dependencies":[9],"blockers":[],"cost":1,"display":{"row":23,"col":1,"icon":"node_0"},"properties":{"duration":60},"effects":[],"id":41},{"display_name":"Refined Gunpowder","desc":"Increase the damage of Arrow Bomb.","base_abil":2,"parents":[11,64],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","multipliers":[50,0,0,0,0,0]}],"id":42},{"display_name":"More Traps","desc":"Increase the maximum amount of active Traps you can have by +2.","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[54],"dependencies":[10],"blockers":[],"cost":1,"display":{"row":26,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"traps","value":2}]}],"id":43},{"display_name":"Better Arrow Shield","desc":"Arrow Shield will gain additional area of effect, knockback and damage.","archetype":"Sharpshooter","archetype_req":0,"base_abil":0,"parents":[19,18,14],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":28,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Shield","behavior":"modify","multipliers":[40,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"behavior":"modify","name":"aoe","value":1}]}],"id":44},{"display_name":"Better Leap","desc":"Reduce leap's cooldown by 1s.","archetype":"Boltslinger","archetype_req":0,"base_abil":17,"parents":[17,55],"dependencies":[17],"blockers":[],"cost":1,"display":{"row":29,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":17,"name":"cooldown","value":-1}]}],"id":45},{"display_name":"Better Guardian Angels","desc":"Your Guardian Angels can shoot +4 arrows before disappearing.","archetype":"Boltslinger","archetype_req":0,"base_abil":0,"parents":[20,55],"dependencies":[8],"blockers":[],"cost":1,"display":{"row":31,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Shot":4}}],"id":46},{"display_name":"Cheaper Arrow Storm (2)","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[21,19],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":47},{"display_name":"Precise Shot","desc":"+30% Critical Hit Damage","parents":[46,49,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":48},{"display_name":"Cheaper Arrow Shield","desc":"Reduce the Mana cost of Arrow Shield.","base_abil":0,"parents":[48,21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":49},{"display_name":"Rocket Jump","desc":"Arrow Bomb's self-damage will knockback you farther away.","base_abil":2,"parents":[47,21],"dependencies":[2],"blockers":[],"cost":1,"display":{"row":33,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":50},{"display_name":"Cheaper Escape (2)","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[22,70],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":51},{"display_name":"Stronger Hook","desc":"Increase your Grappling Hook's range, speed and strength.","archetype":"Trapper","archetype_req":5,"base_abil":1,"parents":[51],"dependencies":[12],"blockers":[],"cost":1,"display":{"row":35,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":12,"name":"range","value":8}]}],"id":52},{"display_name":"Cheaper Arrow Bomb (2)","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[63,30],"dependencies":[],"blockers":[],"cost":1,"display":{"row":40,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":53},{"display_name":"Bouncing Bomb","desc":"Arrow Bomb will bounce once when hitting a block or enemy","base_abil":2,"parents":[40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7,"icon":"node_2"},"properties":{},"effects":[],"id":54},{"display_name":"Homing Shots","desc":"Your Main Attack arrows will follow nearby enemies and not be affected by gravity","archetype":"Sharpshooter","archetype_req":2,"base_abil":999,"parents":[17,18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_2"},"properties":{},"effects":[],"id":55},{"display_name":"Shrapnel Bomb","desc":"Arrow Bomb's explosion will fling 15 shrapnel, dealing damage in a large area","archetype":"Boltslinger","archetype_req":8,"base_abil":2,"parents":[23,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Shrapnel Bomblet","multipliers":[40,0,0,0,20,0]}],"id":56},{"display_name":"Elusive","desc":"If you do not get hit for 8+ seconds, become immune to self-damage and remove Arrow Storm's recoil. (Dodging counts as not getting hit)","archetype":"Boltslinger","archetype_req":0,"parents":[24],"dependencies":[],"blockers":[],"cost":2,"display":{"row":38,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":57},{"display_name":"Double Shots","desc":"Double Main Attack arrows, but they deal -30% damage per arrow (harder to hit far enemies)","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[60],"cost":1,"display":{"row":7,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-30,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":2},"display":"Total Damage"}],"id":58},{"display_name":"Triple Shots","desc":"Triple Main Attack arrows, but they deal -20% damage per arrow","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[69,67],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-20,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":1},"display":"Total Damage"}],"id":59},{"display_name":"Power Shots","desc":"Main Attack arrows have increased speed and knockback","archetype":"Sharpshooter","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[58],"cost":1,"display":{"row":7,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":60},{"display_name":"Focus","desc":"When hitting an aggressive mob 5+ blocks away, gain +1 Focus (Max 3). Resets if you miss once","archetype":"Sharpshooter","archetype_req":2,"parents":[68],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","name":"damMult.Focus"},"scaling":[40],"slider_max":3}],"id":61},{"display_name":"More Focus","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"base_abil":61,"parents":[33,12],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","slider_max":2,"output":{"type":"stat","name":"damMult.Focus"},"scaling":[-5]}],"id":62},{"display_name":"More Focus (2)","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"base_abil":61,"parents":[25,28],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":39,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","slider_max":2,"output":{"type":"stat","name":"damMult.Focus"},"scaling":[-5]}],"id":63},{"display_name":"Traveler","desc":"For every 1% Walk Speed you have from items, gain +1 Raw Spell Damage (Max 100)","parents":[42,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"spd"}],"output":{"type":"stat","name":"sdRaw"},"scaling":[1],"max":100}],"id":64},{"display_name":"Patient Hunter","desc":"Your Traps will deal +20% more damage for every second they are active (Max +80%)","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[40],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":8,"icon":"node_1"},"properties":{"max":80},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","slider_max":4,"output":{"type":"stat","name":"damMult.Basaltic:7.Trap Damage"},"slider_step":1,"scaling":[20]}],"id":65},{"display_name":"Stronger Patient Hunter","desc":"Add +80% Max Damage to Patient Hunter","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[26],"dependencies":[65],"blockers":[],"cost":1,"display":{"row":38,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","slider_max":4},{"type":"raw_stat","bonuses":[{"type":"prop","abil":65,"name":"max","value":80}]}],"id":66},{"display_name":"Frenzy","desc":"Every time you hit an enemy, briefly gain +6% Walk Speed (Max 200%). Decay -40% of the bonus every second","archetype":"Boltslinger","archetype_req":0,"parents":[59,6],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","output":{"type":"stat","name":"spd"},"scaling":[6],"max":160}],"id":67},{"display_name":"Phantom Ray","desc":"Condense Arrow Storm into a single ray that damages enemies 10 times per second","base_abil":7,"parents":[37,4],"dependencies":[7],"blockers":[11,6,23],"cost":2,"display":{"row":16,"col":4,"icon":"node_2"},"properties":{"range":16},"effects":[{"type":"replace_spell","name":"Phantom Ray","base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[25,0,5,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Arrow":16}}]},{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":68},{"display_name":"Arrow Rain","desc":"When Arrow Shield loses its last charge, unleash 150 arrows raining down on enemies","base_abil":0,"parents":[6,38],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":15,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Per Arrow)","multipliers":[80,0,0,0,0,60]},{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Total)","hits":{"Arrow Rain (Per Arrow)":150}}],"id":69},{"display_name":"Decimator","desc":"Phantom Ray will increase its damage by 10% everytime you do not miss with it (Max 70%)","archetype":"Sharpshooter","archetype_req":0,"base_abil":7,"parents":[49,51],"dependencies":[68],"blockers":[],"cost":2,"display":{"row":34,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Phantom Ray hits","slider_max":7,"output":{"type":"stat","name":"damMult.Decimator:1.Single Arrow"},"scaling":[10]}],"id":70}],"Warrior":[{"display_name":"Bash","desc":"Violently bash the ground, dealing high damage in a large area","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_warrior"},"properties":{"aoe":4,"range":3},"effects":[{"type":"replace_spell","name":"Bash","cost":45,"base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Hit","type":"damage","multipliers":[130,20,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Hit":1}}]}],"id":0},{"display_name":"Spear Proficiency 1","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee","multipliers":[5,0,0,0,0,0]}],"id":1},{"display_name":"Cheaper Bash","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":2},{"display_name":"Double Bash","desc":"Bash will hit a second time at a farther range","parents":[1],"base_abil":0,"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{"range":3},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","cost":0,"hits":{"Single Hit":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","cost":0,"multipliers":[-50,0,0,0,0,0]}],"id":3},{"display_name":"Charge","desc":"Charge forward at high speed (hold shift to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_warrior"},"properties":{},"effects":[{"type":"replace_spell","name":"Charge","cost":25,"base_spell":2,"spell_type":"damage","scaling":"spell","display":"","parts":[]}],"id":4},{"display_name":"Heavy Impact","desc":"After using Charge, violently crash down into the ground and deal damage","base_abil":4,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Heavy Impact","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Heavy Impact":1}}],"id":5},{"display_name":"Vehement","desc":"For every 1% or 1 Raw Main Attack Damage you have from items, gain +2% Walk Speed (Max 20%)","archetype":"Fallen","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[7],"cost":1,"display":{"row":6,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"mdPct"},{"type":"stat","name":"mdRaw"}],"output":{"type":"stat","name":"spd"},"scaling":[2,2],"max":20}],"id":6},{"display_name":"Tougher Skin","desc":"Harden your skin and become permanently +5% more resistant. For every 1% or 1 Raw Heath Regen you have from items, gain +10 Health (Max 100)","archetype":"Paladin","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[6],"cost":1,"display":{"row":6,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"defMult.Base","value":5}]},{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hprRaw"},{"type":"stat","name":"hprPct"}],"output":{"type":"stat","name":"hpBonus"},"scaling":[10,10],"max":100}],"id":7},{"display_name":"Uppercut","desc":"Rocket enemies in the air and deal massive damage","parents":[6,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":2,"icon":"node_warrior"},"properties":{"aoe":3,"range":5},"effects":[{"type":"replace_spell","name":"Uppercut","cost":45,"base_spell":3,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Uppercut","multipliers":[200,40,40,0,0,0]},{"name":"Total Damage","hits":{"Uppercut":1}}]}],"id":8},{"display_name":"Cheaper Charge","desc":"Reduce the Mana cost of Charge","base_abil":4,"parents":[8,10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":9},{"display_name":"War Scream","desc":"Emit a terrorizing roar that deals damage, pull nearby enemies, and add damage resistance to yourself and allies","parents":[7,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":6,"icon":"node_warrior"},"properties":{"duration":30,"aoe":12,"defense_bonus":10},"effects":[{"type":"replace_spell","name":"War Scream","cost":35,"base_spell":4,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"War Scream","multipliers":[50,0,0,0,50,0]},{"name":"Total Damage","hits":{"War Scream":1}}]}],"id":10},{"display_name":"Earth Mastery","base_abil":998,"desc":"Increases base damage from all Earth attacks","archetype":"Fallen","archetype_req":0,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":11},{"display_name":"Thunder Mastery","base_abil":998,"desc":"Increases base damage from all Thunder attacks","archetype":"Fallen","archetype_req":0,"parents":[8,14,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":12},{"display_name":"Water Mastery","base_abil":998,"desc":"Increases base damage from all Water attacks","archetype":"Battle Monk","archetype_req":0,"parents":[9,12,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":11,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":13},{"display_name":"Air Mastery","base_abil":998,"desc":"Increases base damage from all Air attacks","archetype":"Battle Monk","archetype_req":0,"parents":[10,12,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":14},{"display_name":"Fire Mastery","base_abil":998,"desc":"Increases base damage from all Fire attacks","archetype":"Paladin","archetype_req":0,"parents":[10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":15},{"display_name":"Quadruple Bash","desc":"Bash will hit 4 times at an even larger range","archetype":"Fallen","archetype_req":0,"base_abil":0,"parents":[11,17],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":0,"icon":"node_1"},"properties":{"range":6},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Hit":2}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[-20,0,0,0,0,0]}],"id":16},{"display_name":"Fireworks","desc":"Mobs hit by Uppercut will explode mid-air and receive additional damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[12,16],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fireworks","multipliers":[80,0,20,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Fireworks":1}}],"id":17},{"display_name":"Half-Moon Swipe","desc":"Uppercut will deal a footsweep attack at a longer and wider angle. All elemental conversions become Water","archetype":"Battle Monk","archetype_req":1,"base_abil":8,"parents":[13],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":13,"col":4,"icon":"node_1"},"properties":{"range":4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":-10,"multipliers":[-70,0,0,30,0,0]}],"id":18},{"display_name":"Flyby Jab","desc":"Damage enemies in your way when using Charge","base_abil":4,"parents":[14,20],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":6,"icon":"node_1"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flyby Jab","multipliers":[20,0,0,0,0,40]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Flyby Jab":1}}],"id":19},{"display_name":"Flaming Uppercut","desc":"Uppercut will light mobs on fire, dealing damage every 0.6 seconds","archetype":"Paladin","archetype_req":0,"base_abil":8,"parents":[15,19],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":12,"col":8,"icon":"node_1"},"properties":{"duration":3,"tick":0.6},"effects":[{"type":"replace_spell","name":"Flaming Uppercut","base_spell":8,"display":"DPS","parts":[{"name":"Damage Tick","multipliers":[0,0,0,0,50,0]},{"name":"DPS","hits":{"Damage Tick":1.6666666666666667}},{"name":"Total Damage","hits":{"Damage Tick":5}}]}],"id":20},{"display_name":"Iron Lungs","desc":"War Scream deals more damage","archetype":"Paladin","archetype_req":0,"base_abil":10,"parents":[19,20],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"War Scream","cost":0,"multipliers":[30,0,0,0,0,30]}],"id":21},{"display_name":"Generalist","desc":"After casting 3 different spells in a row, your next spell will cost 5 mana","archetype":"Battle Monk","archetype_req":3,"parents":[23],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":2,"icon":"node_3"},"properties":{},"effects":[],"id":22},{"display_name":"Counter","desc":"When dodging a nearby enemy attack, get 30% chance to instantly attack back","archetype":"Battle Monk","archetype_req":0,"parents":[18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":4,"icon":"node_1"},"properties":{"chance":30},"effects":[{"type":"replace_spell","name":"Counter","base_spell":5,"display":"Counter Damage","parts":[{"name":"Counter Damage","multipliers":[60,0,20,0,0,20]}]}],"id":23},{"display_name":"Mantle of the Bovemists","desc":"When casting War Scream, create a holy shield around you that reduces all incoming damage by 70% for 3 hits (20s cooldown)","archetype":"Paladin","archetype_req":3,"parents":[21],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":15,"col":7,"icon":"node_3"},"properties":{"mantle_charge":3},"effects":[{"type":"raw_stat","toggle":"Activate Mantle","bonuses":[{"type":"stat","name":"defMult.Mantle","value":70}]}],"id":24},{"display_name":"Bak'al's Grasp","desc":"After casting War Scream, become Corrupted (15s Cooldown). You cannot heal while in that state. While Corrupted, every 2% of Health you lose will add +4 Raw Damage to your attacks (Max 120)","archetype":"Fallen","archetype_req":2,"parents":[16,17],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":16,"col":1,"icon":"node_3"},"properties":{"cooldown":15},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","slider_max":100,"slider_step":1,"output":{"type":"stat","name":"damRaw"},"max":120,"scaling":[2]}],"id":25},{"display_name":"Spear Proficiency 2","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[25,27],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee","multipliers":[5,0,0,0,0,0]}],"id":26},{"display_name":"Cheaper Uppercut","desc":"Reduce the Mana Cost of Uppercut","base_abil":8,"parents":[26,28,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":27},{"display_name":"Aerodynamics","desc":"During Charge, you can steer and change direction","archetype":"Battle Monk","archetype_req":0,"base_abil":4,"parents":[27,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":28},{"display_name":"Provoke","desc":"Mobs damaged by War Scream will target only you for at least 5s. Reduce the Mana cost of War Scream","base_abil":10,"parents":[28,24],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":29},{"display_name":"Precise Strikes","desc":"+30% Critical Hit Damage","parents":[27,26],"dependencies":[],"blockers":[],"cost":1,"display":{"row":18,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":30},{"display_name":"Air Shout","desc":"War Scream will fire a projectile that can go through walls and deal damage multiple times","base_abil":10,"parents":[28,29],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":18,"col":6,"icon":"node_1"},"properties":{"attackRate":2},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Air Shout","multipliers":[40,0,0,0,0,10]}],"id":31},{"display_name":"Enraged Blow","desc":"While Corriupted, every 1% of Health you lose will increase your damage by +3% (Max 300%)","archetype":"Fallen","archetype_req":0,"base_abil":25,"parents":[26],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":20,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider_name":"Corrupted","slider":true,"output":{"type":"stat","name":"damMult.Enraged"},"scaling":[3]}],"id":32},{"display_name":"Flying Kick","desc":"When using Charge, mobs hit will halt your momentum and get knocked back","archetype":"Battle Monk","archetype_req":1,"base_abil":4,"parents":[27,34],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":3,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick","multipliers":[150,0,0,20,0,30]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Flying Kick":1},"display":"Flying Kick Max Damage"}],"id":33},{"display_name":"Stronger Mantle","desc":"Add +2 additional charges to Mantle of the Bovemists","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[35,33],"dependencies":[24],"blockers":[],"cost":1,"display":{"row":20,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":24,"name":"mantle_charge","value":2}]}],"id":34},{"display_name":"Manachism","desc":"If you receive a hit that's less than 5% of your max HP, gain 10 Mana (1s Cooldown)","archetype":"Paladin","archetype_req":3,"parents":[34,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":8,"icon":"node_2"},"properties":{"cooldown":1},"effects":[],"id":35},{"display_name":"Boiling Blood","desc":"Bash leaves a trail of boiling blood behind its first explosion, slowing down and damaging enemies above it every 0.4 seconds","base_abil":0,"parents":[32,37],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Boiling Blood","cost":0,"multipliers":[25,0,0,0,5,0]}],"id":36},{"display_name":"Ragnarokkr","desc":"War Scream become deafening, increasing its duration and giving damage bonus to players","archetype":"Fallen","archetype_req":0,"base_abil":10,"parents":[36,33],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_2"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":10},{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"duration","value":90}]}],"id":37},{"display_name":"Ambidextrous","desc":"Increase your chance to attack with Counter by +30%","base_abil":23,"parents":[33,34,39],"dependencies":[23],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":23,"name":"chance","value":30}]}],"id":38},{"display_name":"Burning Heart","desc":"For every 100 Health Bonus you have from item IDs, gain +2% Fire Damage (Max 100%)","archetype":"Paladin","archetype_req":0,"parents":[38,40],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hpBonus"}],"output":{"type":"stat","name":"fDamPct"},"scaling":[0.02],"max":100}],"id":39},{"display_name":"Stronger Bash","desc":"Increase the damage of Bash","base_abil":0,"parents":[39,35],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[30,0,0,0,0,0]}],"id":40},{"display_name":"Intoxicating Blood","desc":"After leaving Corrupted, gain 2% of the health lost back for each enemy killed while Corrupted","archetype":"Fallen","archetype_req":5,"base_abil":25,"parents":[37,36],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":23,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":41},{"display_name":"Comet","desc":"After being hit by Fireworks, enemies will crash into the ground and receive more damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[37],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":24,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Comet","cost":0,"multipliers":[80,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"hits":{"Comet":1}}],"id":42},{"display_name":"Collide","desc":"Mobs thrown into walls from Flying Kick will explode and receive additonal damage","archetype":"Battle Monk","archetype_req":4,"base_abil":4,"parents":[38,39],"dependencies":[33],"blockers":[],"cost":2,"display":{"row":23,"col":5,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Collide","cost":0,"multipliers":[150,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Collide":1}}],"id":43},{"display_name":"Rejuvenating Skin","desc":"Regain back 30% of the damage you take as healing over 30s","archetype":"Paladin","archetype_req":5,"parents":[39,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":23,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":44},{"display_name":"Uncontainable Corruption","desc":"Reduce the cooldown of Bak'al's Grasp by -5s, and increase the raw damage gained for every 2% of health lost by +1","base_abil":25,"parents":[36,46],"dependencies":[25],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"stat","name":"damRaw"},"scaling":[0.5]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":25,"name":"cooldown","value":-5}]}],"id":45},{"display_name":"Radiant Devotee","desc":"For every 4% Reflection you have from items, gain +1/5s Mana Regen (Max 10/5s)","archetype":"Battle Monk","archetype_req":1,"parents":[47,45],"dependencies":[],"blockers":[],"cost":1,"display":{"row":26,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","inputs":[{"type":"stat","name":"ref"}],"output":{"type":"stat","name":"mr"},"scaling":[0.25],"max":10}],"id":46},{"display_name":"Whirlwind Strike","desc":"Uppercut will create a strong gust of air, launching you upward with enemies (Hold shift to stay grounded)","archetype":"Battle Monk","archetype_req":5,"base_abil":8,"parents":[38,46],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":26,"col":4,"icon":"node_1"},"properties":{"range":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","multipliers":[0,0,0,0,0,50]}],"id":47},{"display_name":"Mythril Skin","desc":"Gain +5% Base Resistance and become immune to knockback","archetype":"Paladin","archetype_req":6,"parents":[44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"defMult.Base","value":5}]}],"id":48},{"display_name":"Armour Breaker","desc":"While Corrupted, losing 30% Health will make your next Uppercut destroy enemies' defense, rendering them weaker to damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[45,46],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":27,"col":1,"icon":"node_2"},"properties":{"duration":8},"effects":[{"type":"raw_stat","toggle":"Activate Armor Breaker","bonuses":[{"type":"stat","name":"damMult.ArmorBreaker","value":30}]}],"id":49},{"display_name":"Shield Strike","desc":"When your Mantle of the Bovemist loses all charges, deal damage around you for each Mantle individually lost","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[48,51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Shield Strike","base_spell":6,"display":"Damage per Shield","parts":[{"name":"Damage per Shield","multipliers":[60,0,20,0,0,0]}]}],"id":50},{"display_name":"Sparkling Hope","desc":"Everytime you heal 5% of your max health, deal damage to all nearby enemies","archetype":"Paladin","archetype_req":0,"parents":[48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":8,"icon":"node_2"},"properties":{"aoe":6},"effects":[{"type":"replace_spell","name":"Sparkling Hope","base_spell":7,"display":"Damage Tick","parts":[{"name":"Damage Tick","multipliers":[10,0,5,0,0,0]}]}],"id":51},{"display_name":"Massive Bash","desc":"While Corrupted, every 3% Health you lose will add +1 AoE to Bash (Max 10)","archetype":"Fallen","archetype_req":8,"base_abil":25,"parents":[53,45],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"prop","abil":0,"name":"aoe"},"scaling":[0.3333333333333333],"max":10}],"id":52},{"display_name":"Tempest","desc":"War Scream will ripple the ground and deal damage 3 times in a large area","archetype":"Battle Monk","archetype_req":0,"base_abil":10,"parents":[52,54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_1"},"properties":{"aoe":16},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest","multipliers":[30,10,0,0,0,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest Total Damage","hits":{"Tempest":3}},{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Tempest":3}}],"id":53},{"display_name":"Spirit of the Rabbit","desc":"Reduce the Mana cost of Charge and increase your Walk Speed by +20%","archetype":"Battle Monk","archetype_req":5,"base_abil":4,"parents":[53,47],"dependencies":[],"blockers":[],"cost":1,"display":{"row":28,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5},{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":20}]}],"id":54},{"display_name":"Massacre","desc":"While Corrupted, if your effective attack speed is Slow or lower, hitting an enemy with your Main Attack will add +1% to your Corrupted bar","archetype":"Fallen","archetype_req":5,"base_abil":999,"parents":[53,52],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":55},{"display_name":"Axe Kick","desc":"Increase the damage of Uppercut, but also increase its mana cost","base_abil":8,"parents":[53,54],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":10,"multipliers":[100,0,0,0,0,0]}],"id":56},{"display_name":"Radiance","desc":"Bash will buff your allies' positive IDs. (15s Cooldown)","archetype":"Paladin","archetype_req":2,"base_abil":0,"parents":[54,58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":5,"icon":"node_2"},"properties":{"cooldown":15},"effects":[],"id":57},{"display_name":"Cheaper Bash 2","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[57,50,51],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":58},{"display_name":"Cheaper War Scream","desc":"Reduce the Mana cost of War Scream","base_abil":10,"parents":[52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":59},{"display_name":"Discombobulate","desc":"Every time you hit an enemy, briefly increase your elemental damage dealt to them by +3 (Additive, Max +80). This bonus decays -5 every second","archetype":"Battle Monk","archetype_req":11,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":2,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","slider_max":27,"output":[{"type":"stat","name":"eDamAddMin"},{"type":"stat","name":"eDamAddMax"},{"type":"stat","name":"tDamAddMin"},{"type":"stat","name":"tDamAddMax"},{"type":"stat","name":"wDamAddMin"},{"type":"stat","name":"wDamAddMax"},{"type":"stat","name":"fDamAddMin"},{"type":"stat","name":"fDamAddMax"},{"type":"stat","name":"aDamAddMin"},{"type":"stat","name":"aDamAddMax"}],"scaling":[3],"max":80}],"id":60},{"display_name":"Thunderclap","desc":"Bash will cast at the player's position and gain additional AoE.\n\n All elemental conversions become Thunder","archetype":"Battle Monk","archetype_req":8,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"convert_spell_conv","target_part":"all","base_spell":1,"conversion":"Thunder"},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"aoe","value":3}]}],"id":61},{"display_name":"Cyclone","desc":"After casting War Scream, envelop yourself with a vortex that damages nearby enemies every 0.5s","archetype":"Battle Monk","archetype_req":0,"parents":[54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":4,"icon":"node_1"},"properties":{"aoe":4,"duration":20},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone","multipliers":[10,0,0,0,5,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone Total Damage","hits":{"Cyclone":40}}],"id":62},{"display_name":"Second Chance","desc":"When you receive a fatal blow, survive and regain 30% of your Health (10m Cooldown)","archetype":"Paladin","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":63},{"display_name":"Blood Pact","desc":"If you do not have enough mana to cast a spell, spend health instead (0.6% health per mana)","archetype":"Fallen","archetype_req":10,"parents":[59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_3"},"properties":{"health_cost":0.6},"effects":[],"id":64},{"display_name":"Brink of Madness","desc":"If your health is 25% full or less, gain +40% Resistance","parents":[64,66],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":4,"icon":"node_2"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Brink","bonuses":[{"type":"stat","name":"defMult.Brink","value":40}]}],"id":65},{"display_name":"Cheaper Uppercut 2","desc":"Reduce the Mana cost of Uppercut","base_abil":8,"parents":[63,65],"dependencies":[],"blockers":[],"cost":1,"display":{"row":35,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":66},{"display_name":"Martyr","desc":"When you receive a fatal blow, all nearby allies become invincible","archetype":"Paladin","archetype_req":0,"parents":[63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":8,"icon":"node_1"},"properties":{"duration":3,"aoe":12},"effects":[],"id":67},{"display_name":"Haemorrhage","desc":"Reduce Blood Pact's health cost. (0.3% health per mana)","archetype":"Fallen","archetype_req":0,"base_abil":64,"parents":[64],"dependencies":[64],"blockers":[],"cost":2,"display":{"row":35,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":64,"name":"health_cost","value":-0.3}]}],"id":68}],"Mage":[{"display_name":"Meteor","desc":"Summon a slow but powerful meteor from the sky, dealing massive damage in a large area","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_mage"},"properties":{"aoe":5,"range":18},"effects":[{"type":"replace_spell","name":"Meteor","cost":55,"base_spell":3,"display":"Total Damage","parts":[{"name":"Meteor Damage","multipliers":[300,100,0,0,0,0]},{"name":"Total Damage","hits":{"Meteor Damage":1}}]}],"id":0},{"display_name":"Teleport","desc":"Instantly teleport in the direction you're facing","parents":[4],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_mage"},"properties":{"range":12},"effects":[{"type":"replace_spell","name":"Teleport","cost":25,"base_spell":2,"display":"","parts":[]}],"id":1},{"display_name":"Heal","desc":"Heal yourself and nearby allies in a large area around you. (When healing an ally, you cannot heal more than 30% of their max health)","parents":[14,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":2,"icon":"node_mage"},"properties":{"aoe":5},"effects":[{"type":"replace_spell","name":"Heal","cost":35,"base_spell":1,"display":"Heal","parts":[{"name":"Heal","power":0.1}]}],"id":2},{"display_name":"Ice Snake","desc":"Summon a fast-moving ice snake that reduces your enemies' speed and damage them.","parents":[13,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":6,"icon":"node_mage"},"properties":{"range":18,"effects":40,"duration":3},"effects":[{"type":"replace_spell","name":"Ice Snake","cost":35,"base_spell":4,"display":"Ice Snake Damage","parts":[{"name":"Ice Snake Damage","multipliers":[70,0,0,30,0,0]}]}],"id":3},{"display_name":"Shooting Star","desc":"Drastically increase the speed of your Meteor ability.","base_abil":3,"parents":[5],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":4},{"display_name":"Wand Proficiency I","desc":"Improve your Main Attack's damage and range when using a wand.","base_abil":999,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":5},{"display_name":"Cheaper Meteor","desc":"Reduce the Mana cost of Meteor.","base_abil":0,"parents":[5],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-10}],"id":6},{"display_name":"Earth Mastery","base_abil":998,"desc":"Increases your base damage from all Earth attacks","archetype":"Arcanist","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":7},{"display_name":"Thunder Mastery","base_abil":998,"desc":"Increases your base damage from all Thunder attacks","archetype":"Riftwalker","archetype_req":0,"parents":[2,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":8},{"display_name":"Water Mastery","base_abil":998,"desc":"Increases your base damage from all Water attacks","archetype":"Light Bender","archetype_req":0,"parents":[12,8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":11,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":9},{"display_name":"Air Mastery","base_abil":998,"desc":"Increases base damage from all Air attacks","archetype":"Riftwalker","archetype_req":0,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":10},{"display_name":"Fire Mastery","base_abil":998,"desc":"Increases base damage from all Fire attacks","archetype":"Arcanist","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":11},{"display_name":"Cheaper Teleport","desc":"Reduce the Mana cost of Teleport.","base_abil":1,"parents":[2,3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":12},{"display_name":"Wisdom","desc":"For every 2% or 2 Raw Spell Damage you have from items, gain +1/5s mana regen (Max 5/5s)","archetype":"Arcanist","archetype_req":0,"parents":[1],"dependencies":[],"blockers":[14],"cost":1,"display":{"row":6,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"sdPct"},{"type":"stat","name":"sdRaw"}],"output":{"type":"stat","name":"mr"},"scaling":[0.5,0.5],"max":5}],"id":13},{"display_name":"Wand Proficiency II","desc":"Improve your Main Attack's damage and range when using a wand.","archetype":"Riftwalker","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":14},{"display_name":"Wind Slash","desc":"When using Teleport, slash through the air and deal damage to enemies you pierce.","archetype":"Riftwalker","base_abil":1,"parents":[10,16],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":12,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","target_part":"Wind Slash","base_spell":2,"multipliers":[50,0,0,0,0,50]},{"type":"add_spell_prop","target_part":"Total Damage","base_spell":2,"display":"Total Damage","hits":{"Wind Slash":1}}],"id":15},{"display_name":"Thunderstorm","desc":"After casting Meteor, summon 3 lightning strikes and deal additional damage","base_abil":0,"parents":[15,8],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":12,"col":2,"icon":"node_1"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","target_part":"Lightning Damage","base_spell":3,"multipliers":[30,0,15,0,0,0]},{"type":"add_spell_prop","target_part":"Total Damage","base_spell":3,"hits":{"Lightning Damage":3}}],"id":16},{"display_name":"Stronger Meteor","desc":"Increase the damage of Meteor.","base_abil":0,"archetype":"Arcanist","archetype_req":2,"parents":[18],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Meteor Damage","behavior":"modify","multipliers":[30,90,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Lightning Damage","behavior":"modify","multipliers":[30,90,0,0,0,0]}],"id":17},{"display_name":"Burning Sigil","desc":"Meteor will leave a sigil that damages enemies every 0.4s.","base_abil":0,"parents":[11,7],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":7,"icon":"node_1"},"properties":{"aoe":7,"duration":8},"effects":[{"type":"replace_spell","name":"Burning Sigil","base_spell":6,"display":"DPS","parts":[{"name":"Tick Damage","multipliers":[15,0,0,0,25,0]},{"name":"DPS","hits":{"Tick Damage":2.5}},{"name":"Total Burn Damage","hits":{"Tick Damage":20}}]}],"id":18},{"display_name":"Sunshower","desc":"Heal emit a strong light, damaging nearby enemies.","archetype":"Light Bender","archetype_req":0,"base_abil":2,"parents":[9],"dependencies":[2],"blockers":[22],"cost":2,"display":{"row":13,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Sunshower Damage","multipliers":[70,0,0,30,0,0]}],"id":19},{"display_name":"Windsweeper","desc":"Your Main Attack will add +1 Winded to enemies you hit. (Max 5, 0.5s cooldown) Ice Snake will deal additional damage to enemies for every Winded they have","archetype":"Riftwalker","archetype_req":3,"parents":[15,16],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":15,"col":1,"icon":"node_3"},"properties":{"max":5},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":{"type":"stat","name":"nConvBase:4.Ice Snake Damage"},"scaling":[20],"slider_step":1,"slider_max":5},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":{"type":"stat","name":"wConvBase:4.Ice Snake Damage"},"scaling":[10]}],"id":20},{"display_name":"Ophanim","desc":"When casting Meteor, instead summon 2 orbs of light with 200 Health that will attack when you use your Main Attack. When they damage an enemy, they lose 20% of their Health. They can be healed back.","archetype":"Light Bender","archetype_req":2,"parents":[19],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":4,"icon":"node_3"},"properties":{"health":200},"effects":[{"type":"replace_spell","name":"Ophanim","base_spell":3,"display":"Per Melee (max)","parts":[{"name":"Per Orb","multipliers":[50,0,30,20,0,0]},{"name":"Per Melee (max)","hits":{"Per Orb":2}}]},{"type":"add_spell_prop","base_spell":3,"cost":30}],"id":21},{"display_name":"Arcane Transfer","desc":"Meteor and Ice Snake will add +5 Mana to a Mana Bank for every aggressive enemy you hit. Heal will now transfer the content of your Mana Bank into usable Mana instead of healing.","archetype":"Arcanist","archetype_req":2,"parents":[18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":7,"icon":"node_3"},"properties":{"bank":90},"effects":[{"type":"replace_spell","name":"Arcane Transfer","base_spell":1,"parts":[],"display":""}],"id":22},{"display_name":"Cheaper Heal","desc":"Reduce the Mana cost of Heal.","base_abil":2,"parents":[20,24],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":23},{"display_name":"Purification","desc":"Heal and Arcane Transfer will purify you of all negative effects and fire. (3s Cooldown)","base_abil":1,"parents":[21,23,25],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":4,"icon":"node_2"},"properties":{},"effects":[],"id":24},{"display_name":"Sentient Snake","desc":"Ice Snake will follow the direction you're facing, allowing you to control it.","base_abil":3,"parents":[22,24],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":17,"col":6,"icon":"node_1"},"properties":{},"effects":[],"id":25},{"display_name":"Eye Piercer","desc":"Teleport will blind enemies, confusing them for a short amount of time.","base_abil":1,"parents":[23],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":18,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":26},{"display_name":"Breathless","desc":"Meteor will deal additional damage to enemies for every Winded they have.","base_abil":20,"archetype":"Riftwalker","archetype_req":0,"parents":[23,24],"dependencies":[20],"blockers":[],"cost":2,"display":{"row":18,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"nConvBase:3.Meteor Damage"},{"type":"stat","name":"nConvBase:3.Meteor Damage"},{"type":"stat","name":"nConvBase:3.Lightning Damage"}],"scaling":[15]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"eConvBase:3.Meteor Damage"},{"type":"stat","name":"eConvBase:3.Per Orb"},{"type":"stat","name":"eConvBase:3.Lightning Damage"}],"scaling":[10]}],"id":27},{"display_name":"Larger Heal","desc":"Increase your Heal's range.","base_abil":1,"archetype":"Light Bender","archetype_req":0,"parents":[24,25],"dependencies":[2],"blockers":[22],"cost":1,"display":{"row":18,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":2,"name":"aoe","value":2}]}],"id":28},{"display_name":"Larger Mana Bank","desc":"Increase your maximum Mana Bank by +30.","base_abil":1,"archetype":"Arcanist","archetype_req":0,"parents":[25],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":18,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":22,"name":"bank","value":30}]}],"id":29},{"display_name":"Cheaper Ice Snake","desc":"Reduce the Mana cost of Ice Snake.","base_abil":3,"parents":[26,32],"dependencies":[],"blockers":[],"cost":1,"display":{"row":20,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":30},{"display_name":"Cheaper Teleport II","desc":"Reduce the Mana cost of Teleport.","base_abil":1,"parents":[32,24],"dependencies":[],"blockers":[],"cost":1,"display":{"row":20,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":31},{"display_name":"Fortitude","desc":"After healing 120% of your max health within 10s, apply a damage bonus to each player you've healed. (15s Cooldown)","base_abil":2,"archetype":"Light Bender","archetype_req":0,"parents":[30,31],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":2,"icon":"node_2"},"properties":{"duration":5},"effects":[],"id":32},{"display_name":"Pyrokinesis","desc":"When your Mana Bank reaches 30, your Main Attack will stop and explode when it hits an enemy. (Damage is dealt as Main Attack Damage)","base_abil":4,"archetype":"Arcanist","archetype_req":4,"parents":[25],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":7,"icon":"node_2"},"properties":{},"__TODO":"replace_spell pyrokinesis damage","effects":[],"id":33},{"display_name":"Seance","desc":"For every 5/3s Lifesteal you have from items, gain 1% Spell Damage (Max 50%)","archetype":"","archetype_req":0,"parents":[33,36],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"ls"}],"output":{"type":"stat","name":"sdPct"},"scaling":[0.2],"max":50}],"id":34},{"display_name":"Blink","desc":"Teleport will trigger 2 times in quick successions","base_abil":1,"archetype":"Riftwalker","archetype_req":0,"parents":[32,30],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":21,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":1,"name":"range","value":-4}]},{"type":"add_spell_prop","behavior":"modify","target_part":"Total Damage","base_spell":2,"hits":{"Wind Slash":1,"Explosion Damage":1}}],"id":35},{"display_name":"Snake Nest","desc":"Ice Snake will summon 3 snakes.","base_abil":3,"parents":[34,31,40],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":22,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":36},{"display_name":"Arcane Restoration","desc":"Pyrokinesis will add +1 Mana every 1s to your Mana Bank when hitting an aggressive enemy.","base_abil":999,"archetype":"Arcanist","archetype_req":0,"parents":[34,36],"dependencies":[33],"blockers":[],"cost":2,"display":{"row":23,"col":6,"icon":"node_1"},"properties":{"duration":4},"effects":[],"id":37},{"display_name":"Fluid Healing","desc":"For every 1% Water Damage Bonus you have, buff Heal's healing power by +0.3%.","archetype":"Light Bender","archetype_req":0,"base_abil":2,"parents":[40,39],"dependencies":[],"blockers":[],"cost":2,"display":{"row":23,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"round":false,"inputs":[{"type":"stat","name":"wDamPct"}],"output":{"type":"stat","name":"healPct"},"scaling":[0.3]}],"id":38},{"display_name":"Transonic Warp","desc":"Teleport will deal additional damage to enemies for every Winded they have.","base_abil":20,"archetype":"Riftwalker","archetype_req":5,"parents":[30],"dependencies":[3,20],"blockers":[],"cost":2,"display":{"row":23,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"nConvBase:2.Wind Slash"},{"type":"stat","name":"nConvBase:2.Explosion Damage"}],"scaling":[20]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"tConvBase:2.Wind Slash"},{"type":"stat","name":"tConvBase:2.Explosion Damage"}],"scaling":[5]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"aConvBase:2.Wind Slash"},{"type":"stat","name":"aConvBase:2.Explosion Damage"}],"scaling":[5]}],"id":39},{"display_name":"Healthier Ophanim I","desc":"Increase the health of your orbs from Ophanim by +800 and reduce the damage they take when hitting an enemy by -5%.","archetype":"Light Bender","archetype_req":0,"base_abil":21,"parents":[32,31],"dependencies":[21],"blockers":[],"cost":1,"display":{"row":22,"col":3,"icon":"node_0"},"properties":{},"effects":[],"id":40},{"display_name":"Orphion's Pulse","desc":"Heal will trigger 2 more times, increasing the overall healing.","archetype":"Light Bender","base_abil":2,"parents":[40,36],"dependencies":[2],"blockers":[22],"cost":2,"display":{"row":23,"col":4,"icon":"node_1"},"properties":{"aoe":5},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Second and Third Pulses","power":0.15},{"type":"add_spell_prop","base_spell":1,"display":"Total Heal","target_part":"Total Heal","hits":{"Heal":1,"Second and Third Pulses":2}}],"id":41},{"display_name":"Diffusion","desc":"If you kill an enemy with Winded on them, the leftover Winded will spread to nearby enemies.","archetype":"Riftwalker","archetype_req":6,"base_abil":20,"parents":[39,38],"dependencies":[20],"blockers":[],"cost":2,"display":{"row":25,"col":1,"icon":"node_3"},"properties":{"aoe":5},"effects":[],"id":42},{"display_name":"Lightweaver","desc":"After healing 50% of your max health within 10s, summon a rotating orb that damages all enemies it touches for 20s. (Max 3 Orbs)","archetype":"Light Bender","archetype_req":7,"parents":[41],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Lightweaver","base_spell":5,"display":"Orb Damage","parts":[{"name":"Single Orb","type":"damage","multipliers":[30,0,0,0,20,0]},{"name":"Orb Damage","type":"total","hits":{"Single Orb":3}}]}],"id":43},{"display_name":"Arcane Speed","desc":"After casting Heal or Arcane Transfer, gain +80% speed for 3s. (8s Cooldown)","base_abil":2,"parents":[43,45],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":25,"col":6,"icon":"node_1"},"properties":{},"effects":[],"id":44},{"display_name":"Larger Mana Bank II","desc":"Increase your maximum Mana Bank by +30.","base_abil":1,"archetype":"Arcanist","archetype_req":0,"parents":[34,44],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":25,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":45},{"display_name":"Psychokinesis","desc":"Meteor will launch directly from you as a slow projectile.","base_abil":3,"archetype":"Arcanist","archetype_req":5,"parents":[45,44],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":26,"col":7,"icon":"node_1"},"properties":{"range":20},"effects":[],"id":46},{"display_name":"More Winded","desc":"Incrase your maximum Winded by +5.","base_abil":20,"archetype":"Riftwalker","archetype_req":0,"parents":[42],"dependencies":[20],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":20,"name":"max","value":5}]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","slider_max":5}],"id":47},{"display_name":"Cheaper Ice Snake II","desc":"Reduce the Mana cost of Ice Snake.","base_abil":3,"parents":[42,52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":27,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":48},{"display_name":"Cheaper Meteor II","desc":"Reduce the Mana cost of Meteor.","base_abil":0,"parents":[52,43,44],"dependencies":[],"blockers":[],"cost":1,"display":{"row":27,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":49},{"display_name":"Chaos Explosion","desc":"When your Mana Bank reaches 120, casting Arcane Transfer will rapidly unleash the last 3 spells you've cast in order.","base_abil":22,"archetype":"Arcanist","archetype_req":8,"parents":[45],"dependencies":[22],"blockers":[],"cost":2,"display":{"row":27,"col":8,"icon":"node_3"},"properties":{},"effects":[],"id":50},{"display_name":"Arcane Power","desc":"Meteor and Ice Snake will add +2 Mana to your Mana Bank for each aggressive mob you hit.","base_abil":22,"archetype":"Arcanist","archetype_req":0,"parents":[56],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":29,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":51},{"display_name":"Explosive Entrance","desc":"Deal Damage in an area on the location you Teleport to.","base_abil":1,"parents":[48,49],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":27,"col":3,"icon":"node_1"},"properties":{"aoe":3},"effects":[{"type":"add_spell_prop","target_part":"Explosion Damage","base_spell":2,"multipliers":[50,0,0,0,30,0]},{"type":"add_spell_prop","behavior":"modify","target_part":"Total Damage","base_spell":2,"hits":{"Explosion Damage":1}}],"id":52},{"display_name":"Gust","desc":"Ice Snake will add +1 Winded to enemies and deal more damage.","base_abil":3,"archetype":"Riftwalker","archetype_req":7,"parents":[48,52],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","target_part":"Ice Snake Damage","base_spell":4,"multipliers":[0,0,0,0,0,20]}],"id":53},{"display_name":"Time Dilation","desc":"When sprinting, create an area that increases the speed of all allies the longer they run in it. (Step out or stop running to cancel)","archetype":"Riftwalker","archetype_req":7,"parents":[48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_2"},"properties":{},"effects":[],"id":54},{"display_name":"Better Ophanim","desc":"Increase your maximum orbs from Ophanim by +1.","archetype":"Light Bender","archetype_req":0,"base_abil":21,"parents":[52,49],"dependencies":[21],"blockers":[],"cost":1,"display":{"row":28,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Per Melee (max)","hits":{"Per Orb":1}}],"id":55},{"display_name":"Arctic Snake","desc":"Ice Snake will freeze enemies completely for 2s.","base_abil":3,"parents":[50],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":28,"col":7,"icon":"node_1"},"properties":{},"effects":[],"id":56},{"display_name":"Devitalize","desc":"Enemies will deal -2% damage for every Winded they have.","base_abil":20,"archetype":"Riftwalker","archetype_req":5,"parents":[58,59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":57},{"display_name":"More Winded II","desc":"Incrase your maximum Winded by +5.","base_abil":20,"archetype":"Riftwalker","archetype_req":0,"parents":[54,59],"dependencies":[20],"blockers":[],"cost":1,"display":{"row":31,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":20,"name":"max","value":5}]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","slider_max":5}],"id":58},{"display_name":"Dynamic Faith","desc":"For every 2% Sprint you have from items, gain +1% Thunder Damage (Max 100%)","parents":[58,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"sprint"}],"output":{"type":"stat","name":"tDamPct"},"scaling":[0.5],"max":100}],"id":59},{"display_name":"Divination","desc":"Increase your maximum orbs from Ophanim by +3 and reduce their damage.","base_abil":21,"archetype":"Light Bender","archetype_req":0,"parents":[59,61],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":32,"col":3,"icon":"node_2"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Per Orb","multipliers":[-30,0,-10,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Per Melee (max)","hits":{"Per Orb":3}}],"id":60},{"display_name":"Healthier Ophanim II","desc":"Increase the health of your orbs from Ophanim by +3000.","base_abil":21,"archetype":"Light Bender","archetype_req":0,"parents":[55],"dependencies":[40],"blockers":[],"cost":1,"display":{"row":31,"col":4,"icon":"node_0"},"properties":{},"effects":[],"id":61},{"display_name":"Sunflare","desc":"After healing 400% of your max health within 10s, your next Heal will make every nearby ally temporarily immune.","archetype":"Light Bender","archetype_req":12,"base_abil":2,"parents":[61],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":5,"icon":"node_3"},"properties":{"aoe":12,"duration":5},"effects":[],"id":62},{"display_name":"Larger Mana Bank III","desc":"Increase your maximum Mana Bank by +30.","archetype":"Arcanist","archetype_req":0,"base_abil":22,"parents":[56],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":31,"col":7,"icon":"node_0"},"properties":{},"effects":[],"id":63},{"display_name":"Arcane Overflow","desc":"Arcane Transfer will allow you to overflow your mana over its maximum limits.","archetype":"Arcanist","archetype_req":12,"base_abil":22,"parents":[63],"dependencies":[22],"blockers":[],"cost":2,"display":{"row":33,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":64},{"display_name":"Memory Recollection","desc":"Chaos Explosion will cast +2 spells.","archetype":"Arcanist","archetype_req":0,"base_abil":22,"parents":[64],"dependencies":[50],"blockers":[],"cost":1,"display":{"row":34,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":65},{"display_name":"Manastorm","desc":"If you have more than 100 Mana, casting a spell will give you +10 mana over 5s.","archetype":"Arcanist","archetype_req":1,"parents":[69,64,62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":66},{"display_name":"Better Lightweaver","desc":"Increase your Max Orbs by +2.","archetype":"Light Bender","archetype_req":0,"base_abil":43,"parents":[69,66],"dependencies":[43],"blockers":[],"cost":1,"display":{"row":35,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","target_part":"Orb Damage","base_spell":5,"hits":{"Single Orb":2}}],"id":67},{"display_name":"Timelock","desc":"Holding shift and casting Heal will absorb all Winded on nearby enemies and make you Timelocked. While Timelocked, your mana will not be depleted and you become immovable from outside forces. Enemies will recieve Winded damage from all absorbed stacks. (Max 30)","archetype":"Riftwalker","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":0,"icon":"node_3"},"properties":{},"effects":[],"id":68},{"display_name":"Cheaper Heal II","desc":"Reduce the Mana cost of Heal.","base_abil":2,"parents":[68,66],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":69}],"Assassin":[{"display_name":"Spin Attack","desc":"Slash rapidly around you, damaging enemies in a large area.","archetype":"","archetype_req":0,"parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Spin Attack","cost":45,"base_spell":1,"spell_type":"damage","scaling":"spell","use_atkspd":true,"display":"Spin Attack","parts":[{"name":"Spin Attack","type":"damage","multipliers":[120,0,30,0,0,0]}]}],"id":0},{"display_name":"Dagger Proficiency I","desc":"Increase your speed by +5% and improve your Main Attack’s damage when using a dagger.","archetype":"","archetype_req":0,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":5},{"type":"stat","name":"mdPct","value":5}]}],"id":1},{"display_name":"Cheaper Spin Attack","desc":"Reduce the Mana cost of Spin Attack.","archetype":"","archetype_req":0,"base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":2},{"display_name":"Double Spin","desc":"Spin Attack will activate a second time with a larger area of effect.","archetype":"","archetype_req":0,"base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Spin Attack":1},"display":"Total Damage"}],"id":3},{"display_name":"Poisoned Blade","desc":"For every 2% or 2 Raw Main Attack Damage you have from items, gain +5/3s Poison Damage (Max 50/3s)","archetype":"Shadestepper","archetype_req":0,"parents":[5],"dependencies":[],"blockers":[6],"cost":1,"display":{"row":7,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"mdPct"},{"type":"stat","name":"mdRaw"}],"output":[{"type":"stat","name":"poison"}],"scaling":[2.5,2.5],"max":50}],"id":4},{"display_name":"Dash","desc":"Dash in the direction you're facing.","archetype":"","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Dash","cost":20,"base_spell":2,"display":"Total Damage","parts":[{"name":"None","type":"damage","multipliers":[0,0,0,0,0,0]}]}],"id":5},{"display_name":"Double Slice","desc":"Your Main Attack will attack twice, but deal -40% damage per hit.","archetype":"Acrobat","archetype_req":0,"base_abil":999,"parents":[5],"dependencies":[],"blockers":[4],"cost":1,"display":{"row":7,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee","multipliers":[-40,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"display":"Total Damage","target_part":"Total Damage","hits":{"Melee":2}}],"id":6},{"display_name":"Smoke Bomb","desc":"Throw a bomb that slouly emits smoke, damaging all enemies in it every 0.5s.","archetype":"","archetype_req":0,"parents":[4,8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Smoke Bomb","cost":40,"base_spell":4,"display":"Total Damage","parts":[{"name":"Per Tick","type":"damage","multipliers":[25,5,0,0,0,5]},{"name":"Per Bomb","type":"total","hits":{"Per Tick":10}},{"name":"Total Damage","type":"total","hits":{"Per Bomb":1}}]}],"id":7},{"display_name":"Cheaper Dash","desc":"Reduce the Mana cost of Dash","archetype":"","archetype_req":0,"base_abil":5,"parents":[7,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":8},{"display_name":"Multihit","desc":"Unleash a rapid flurry of 8 hits to enemies facing you, dealing overwhelming damage","archetype":"","archetype_req":0,"parents":[6,8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Multihit","cost":45,"base_spell":3,"display":"Total Damage","parts":[{"name":"Per Hit","type":"damage","multipliers":[25,0,0,10,0,0]},{"name":"Total Damage","type":"total","hits":{"Per Hit":8}}]}],"id":9},{"display_name":"Earth Mastery","desc":"Increases base damage from all Earth attacks","archetype":"Shadestepper","archetype_req":0,"base_abil":998,"parents":[7,11],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":10},{"display_name":"Thunder Mastery","desc":"Increases base damage from all Thunder attacks","archetype":"Shadestepper","archetype_req":0,"base_abil":998,"parents":[10,7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":11},{"display_name":"Fire Mastery","desc":"Increases base damage from all Fire attacks","archetype":"Trickster","archetype_req":0,"base_abil":998,"parents":[8,13],"dependencies":[],"blockers":[],"cost":1,"display":{"row":14,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":12},{"display_name":"Water Mastery","desc":"Increases base damage from all Water attacks","archetype":"Acrobat","archetype_req":0,"base_abil":998,"parents":[8,9,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":13},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Acrobat","archetype_req":0,"base_abil":998,"parents":[13,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":14},{"display_name":"Backstab","desc":"Multihit will deal a single devastating hit. If you strike the enemy from behind, deal double damage","archetype":"Shadestepper","archetype_req":2,"base_abil":9,"parents":[10,11],"dependencies":[9],"blockers":[44,16],"cost":2,"display":{"row":15,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Backstab","base_spell":3,"display":"Total Damage","parts":[{"name":"Per Hit","type":"damage","multipliers":[200,50,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Per Hit":1}}]}],"id":15},{"display_name":"Fatality","desc":"Multihit will deal an additional final slash","archetype":"","archetype_req":0,"base_abil":9,"parents":[13,14],"dependencies":[9],"blockers":[15],"cost":2,"display":{"row":15,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fatality","multipliers":[100,0,0,0,0,50]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Fatality":1}}],"id":16},{"display_name":"Vanish","desc":"Dash will vanish you into the shadows and make you invisible to enemies (5s Cooldown). You cannot heal or gain mana while in that state (Attack or get hit to cancel)","archetype":"","archetype_req":0,"base_abil":5,"parents":[15,18],"dependencies":[5],"blockers":[],"cost":2,"display":{"row":16,"col":2,"icon":"node_2"},"properties":{"duration":5,"cooldown":5},"effects":[],"id":17},{"display_name":"Sticky Bomb","desc":"Smoke Bomb will stick to enemies and deal additional damage","archetype":"Trickster","archetype_req":0,"base_abil":7,"parents":[17,12],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[0,0,0,0,10,0]}],"id":18},{"display_name":"Righting Reflex","desc":"When you hold shift while airborne, slowly glide and become immune to fall damage (Max 5s)","archetype":"Acrobat","archetype_req":0,"parents":[16],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6,"icon":"node_2"},"properties":{},"effects":[],"id":19},{"display_name":"Surprise Strike","desc":"While using Vanish, your next attack will deal +60% more damage for a single hit only","archetype":"Shadestepper","archetype_req":3,"base_abil":5,"parents":[17],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":19,"col":2,"icon":"node_3"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Surprise Strike","bonuses":[{"type":"stat","name":"damMult.SurpriseStrike","value":60}]}],"id":20},{"display_name":"Mirror Image","desc":"After leaving Vanish, summon 3 Clones that will follow you and protect you. (20s Cooldown)","archetype":"Trickster","archetype_req":2,"base_abil":5,"parents":[18],"dependencies":[17],"blockers":[22],"cost":2,"display":{"row":19,"col":4,"icon":"node_3"},"properties":{"clone":3},"effects":[{"type":"raw_stat","toggle":"Activate Clones","bonuses":[{"type":"stat","name":"defMult.Clone","value":80}]}],"id":21},{"display_name":"Lacerate","desc":"Spin Attack will lunge you forward, deal 3 strikes and lunge you forward again.","archetype":"Acrobat","archetype_req":2,"base_abil":0,"parents":[16],"dependencies":[],"blockers":[21],"cost":2,"display":{"row":19,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Lacerate","base_spell":1,"display":"Total Damage","parts":[{"name":"Per Hit","type":"damage","multipliers":[50,0,0,10,0,20]},{"name":"Total Damage","type":"total","hits":{"Per Hit":3}}]}],"id":22},{"display_name":"Silent Killer","desc":"After killing an enemy, reset Vanish's cooldown","archetype":"","archetype_req":0,"base_abil":5,"parents":[20],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":20,"col":1,"icon":"node_2"},"properties":{},"effects":[],"id":23},{"display_name":"Shenanigans","desc":"For every 2% Stealing you have from items, gain +1/3s Mana Steal (Max 8/3s)","archetype":"Trickster","archetype_req":0,"parents":[21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":20,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"eSteal"}],"output":[{"type":"stat","name":"ms"}],"scaling":[0.5],"max":8}],"id":24},{"display_name":"Wall of Smoke","desc":"Smoke Bomb will throw +2 bombs, damaging more often in a larger area","archetype":"","archetype_req":0,"base_abil":7,"parents":[22],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":20,"col":8,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Per Bomb":2}},{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[-20,0,0,0,0,0]}],"id":25},{"display_name":"Better Smoke Bomb","desc":"Increase the range and area of effect of Smoke Bomb","archetype":"","archetype_req":0,"base_abil":7,"parents":[23,27],"dependencies":[7],"blockers":[],"cost":1,"display":{"row":22,"col":0,"icon":"node_0"},"properties":{},"effects":[],"id":26},{"display_name":"Shadow Travel","desc":"Vanish will increase your speed by +100%","archetype":"Shadestepper","archetype_req":0,"base_abil":5,"parents":[26,23,28],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_1"},"properties":{},"effects":[],"id":27},{"display_name":"Cheaper Multihit","desc":"Reduce the Mana cost of Multihit","archetype":"","archetype_req":0,"base_abil":9,"parents":[24,27,29],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":28},{"display_name":"Dagger Proficiency II","desc":"Increase your Main Attack's range and add +5 raw damage to all attacks","archetype":"","archetype_req":0,"base_abil":999,"parents":[28,25],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"damRaw","value":5}]}],"id":29},{"display_name":"Last Laugh","desc":"When losing a Clone, it will cast Spin Attack before dying","archetype":"Trickster","archetype_req":3,"base_abil":5,"parents":[27,28],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":23,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":30},{"display_name":"Cheaper Smoke Bomb","desc":"Reduce the Mana cost of Smoke Bomb","archetype":"","archetype_req":0,"base_abil":7,"parents":[26,32],"dependencies":[7],"blockers":[],"cost":1,"display":{"row":25,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":31},{"display_name":"Blazing Powder","desc":"Spin Attack will blind enemies and deal additional damage","archetype":"","archetype_req":0,"base_abil":0,"parents":[31,27,28],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":3,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Spin Attack","multipliers":[0,0,0,0,20,0]}],"id":32},{"display_name":"Weightless","desc":"When you hit an enemy while airborne, gain +0.5 Mana (1.25+ blocks off the ground to be airborne)","archetype":"Acrobat","archetype_req":4,"parents":[28,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7,"icon":"node_2"},"properties":{},"effects":[],"id":33},{"display_name":"Black Hole","desc":"Smoke Bomb will pull nearby enemies","archetype":"","archetype_req":0,"base_abil":7,"parents":[31,32],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":34},{"display_name":"Sandbagging","desc":"Anytime you get hit for less than 5% of your max hp, reduce your abilities cooldown by -2s. (1s Cooldown)","archetype":"Trickster","archetype_req":0,"parents":[32,36],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":35},{"display_name":"Hop","desc":"When you double tap jump, leap forward. (2s Cooldown)","archetype":"Acrobat","archetype_req":0,"parents":[35,33],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":6,"icon":"node_1"},"properties":{"cooldown":2},"effects":[],"id":36},{"display_name":"Dancing Blade","desc":"Deal damage to mobs you Dash through","archetype":"","archetype_req":0,"base_abil":5,"parents":[33],"dependencies":[5],"blockers":[],"cost":2,"display":{"row":26,"col":8,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Dancing Blade","multipliers":[80,0,0,0,0,20],"display":"Dancing Blade"}],"id":37},{"display_name":"Violent Vortex","desc":"If you deal more damage than 2x of your max health in a single hit, deal 20% of the damage to other nearby enemies","archetype":"Shadestepper","archetype_req":0,"parents":[31],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Violent Vortex","base_spell":5,"display":"Total Damage","parts":[{"name":"Total Damage","type":"damage","multipliers":[0,0,0,0,0,0]}]}],"id":38},{"display_name":"Delirious Gas","desc":"While inside Smoke Bomb, increase your damage by +40% and gain Lure for 20s","archetype":"Trickster","archetype_req":4,"base_abil":7,"parents":[35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":27,"col":3,"icon":"node_2"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Delirious Gas","bonuses":[{"type":"stat","name":"damMult.DeliriousGas","value":40}]}],"id":39},{"display_name":"Marked","desc":"Smoke Bomb will add +1 Mark to enemies it hits. (Max 5, 0.5s Cooldown) Marked enemies will take +10% damage for each mark they have.","archetype":"Shadestepper","archetype_req":5,"parents":[38],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":1,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Marked","slider_step":1,"slider_max":5,"output":[{"type":"stat","name":"damMult.Marked"}],"scaling":[10]}],"id":40},{"display_name":"Echo","desc":"Your Clones will mimic your spells and abilities. While they are active, deal -60% damage.","archetype":"Trickster","archetype_req":6,"base_abil":5,"parents":[35,42],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":28,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Clones","bonuses":[{"type":"stat","name":"damMult.Echo","value":-60}]}],"id":41},{"display_name":"Shurikens","desc":"After using Dash, your next Main Attack will throw 3 shurikens","archetype":"Acrobat","archetype_req":0,"base_abil":5,"parents":[41,43],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":6,"icon":"node_2"},"properties":{},"effects":[{"type":"replace_spell","name":"Shurikens","base_spell":6,"display":"Total Damage","parts":[{"name":"Per Shuriken","type":"damage","multipliers":[90,0,0,0,10,0]},{"name":"Total Damage","type":"total","hits":{"Per Shuriken":3}}]}],"id":42},{"display_name":"Far Reach","desc":"Increase the range of Multihit","archetype":"","archetype_req":0,"base_abil":9,"parents":[37,42],"dependencies":[],"blockers":[],"cost":1,"display":{"row":28,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":43},{"display_name":"Stronger Multihit","desc":"Increases Multihit's amount of hits by +3","archetype":"","archetype_req":0,"base_abil":9,"parents":[41,42],"dependencies":[],"blockers":[15],"cost":1,"display":{"row":29,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Per Hit":3}}],"id":44},{"display_name":"Psithurism","desc":"Increase your Walk Speed by +20% and your Jump Height by +1","archetype":"Acrobat","archetype_req":5,"parents":[42,43],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":20},{"type":"stat","name":"jh","value":1}]}],"id":45},{"display_name":"Ambush","desc":"Increase Surprise Strike's damage by +40%","archetype":"Shadestepper","archetype_req":4,"base_abil":5,"parents":[40],"dependencies":[20],"blockers":[],"cost":1,"display":{"row":31,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Surprise Strike","bonuses":[{"type":"stat","name":"damMult.SurpriseStrike","value":40}]}],"id":46},{"display_name":"Cheaper Dash 2","desc":"Reduce the Mana cost of Dash","archetype":"","archetype_req":0,"base_abil":5,"parents":[41],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":47},{"display_name":"Parry","desc":"After dodging damage, if you cast a spell within 1.5s, it will be free. (3s Cooldown)","archetype":"Acrobat","archetype_req":5,"parents":[49],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":6,"icon":"node_2"},"properties":{},"effects":[],"id":48},{"display_name":"Cheaper Spin Attack 2","desc":"Reduce the Mana cost of Spin Attack","archetype":"","archetype_req":0,"base_abil":0,"parents":[43,48],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":49},{"display_name":"Death Magnet","desc":"After leaving Vanish, pull all nearby Marked mobs towards you","archetype":"Shadestepper","archetype_req":5,"base_abil":5,"parents":[51,46],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":33,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":50},{"display_name":"Cheaper Multihit 2","desc":"Reduce the Mana cost of Multihit","archetype":"","archetype_req":0,"base_abil":9,"parents":[50,46,52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":51},{"display_name":"Hoodwink","desc":"When hitting enemies with Spin Attack, shorten the duration of your negative effects by 30% and transfer it onto enemies Lure can be transferred to the feeble minded. (Bosses and special enemies are immune)","archetype":"Trickster","archetype_req":1,"base_abil":0,"parents":[51,47,53],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":33,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":52},{"display_name":"Choke Bomb","desc":"Smoke Bomb will slow down enemies while in the smoke","archetype":"Trickster","archetype_req":0,"base_abil":7,"parents":[52,54,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":33,"col":6,"icon":"node_1"},"properties":{},"effects":[],"id":53},{"display_name":"Wall Jump","desc":"Reduce Hop's cooldown by 1s. When you Hop into a wall, bounce backward. (Hold shift to cancel)","archetype":"Acrobat","archetype_req":5,"parents":[53,49],"dependencies":[36],"blockers":[],"cost":2,"display":{"row":33,"col":8,"icon":"node_1"},"properties":{},"effects":[],"id":54},{"display_name":"Fatal Spin","desc":"Spin Attack will add +1 Mark to all enemies it hits and gain additional area of effect","archetype":"Shadestepper","archetype_req":8,"base_abil":0,"parents":[50,51],"dependencies":[40],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":55},{"display_name":"Stronger Lacerate","desc":"Lacerate will deal +1 slash","archetype":"Acrobat","archetype_req":0,"base_abil":0,"parents":[53,54],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":34,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Per Hit":1}}],"id":56},{"display_name":"Stronger Vortex","desc":"If you deal more damage than 3x of your max health in a single hit, deal 60% of the damage to other nearby enemies","archetype":"Shadestepper","archetype_req":4,"parents":[55],"dependencies":[38],"blockers":[],"cost":1,"display":{"row":35,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"replace_spell","name":"Violent Vortex","base_spell":5,"display":"Total Damage","parts":[{"name":"Total Damage","type":"damage","multipliers":[0,0,0,0,0,0]}]}],"id":57},{"display_name":"Harvester","desc":"After killing an enemy, gain +5 Mana for each leftover Marks it had","archetype":"Shadestepper","archetype_req":0,"parents":[55,59],"dependencies":[40],"blockers":[],"cost":2,"display":{"row":37,"col":1,"icon":"node_2"},"properties":{},"effects":[],"id":58},{"display_name":"Cheaper Smoke Bomb 2","desc":"Reduce the Mana cost of Smoke Bomb","archetype":"","archetype_req":0,"base_abil":7,"parents":[58,52,60],"dependencies":[7],"blockers":[],"cost":1,"display":{"row":37,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":59},{"display_name":"Blade Fury","desc":"Multihit will be easier to aim and enemies hit will stay locked in front of you","archetype":"Acrobat","archetype_req":0,"base_abil":9,"parents":[56,59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7,"icon":"node_1"},"properties":{},"effects":[],"id":60},{"display_name":"More Marks","desc":"Add +2 max Marks","archetype":"Shadestepper","archetype_req":0,"base_abil":40,"parents":[58,59],"dependencies":[40],"blockers":[],"cost":1,"display":{"row":38,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Marked","slider_max":2}],"id":61},{"display_name":"Stronger Clones","desc":"Improve your damage while your Clones are active by +20%","archetype":"Trickster","archetype_req":7,"base_abil":5,"parents":[59,60],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":38,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Clones","bonuses":[{"type":"stat","name":"damMult.Echo","value":20}]}],"id":62},{"display_name":"Ricochets","desc":"When hitting an enemy with your Shurikens, they will bounce to the nearest enemy","archetype":"Acrobat","archetype_req":6,"base_abil":5,"parents":[60],"dependencies":[42],"blockers":[],"cost":2,"display":{"row":38,"col":8,"icon":"node_1"},"properties":{},"effects":[],"id":63},{"display_name":"Satsujin","desc":"If an enemy has 3 Marks and 70% of their health or more, your next Multihit or Main Attack will deal triple damage. (30s Cooldown, per enemy)","archetype":"Shadestepper","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":1,"icon":"node_3"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Satsujin","bonuses":[{"type":"stat","name":"dmgMult.Satsujin","value":300}]}],"id":64},{"display_name":"Forbidden Art","desc":"Summon +3 additional Clones. (+20s Cooldown)","archetype":"Trickster","archetype_req":8,"base_abil":5,"parents":[59],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":39,"col":4,"icon":"node_2"},"properties":{},"effects":[],"id":65},{"display_name":"Diversion","desc":"Anytime a Lured enemy gets killed, every nearby ally gets +40% health as extra overflowing health. (3s Cooldown). Decay -4% of the bonus every second.","archetype":"Trickster","archetype_req":11,"base_abil":7,"parents":[65],"dependencies":[39],"blockers":[],"cost":2,"display":{"row":40,"col":5,"icon":"node_3"},"properties":{},"effects":[],"id":66},{"display_name":"Jasmine Bloom","desc":"After spending 40 Mana, bloom an area under you that damages enemies below it every 0.4s After every bloom, reset the duration and increase the radius (Max 10 Blocks)","archetype":"Acrobat","archetype_req":12,"parents":[60],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Jasmine Bloom","base_spell":7,"display":"Per Hit","parts":[{"name":"Per Hit","type":"damage","multipliers":[60,5,0,15,0,0]}]}],"id":67},{"display_name":"Better Ricochets","desc":"Add +1 Max Bounce to Ricochets","archetype":"Acrobat","archetype_req":0,"base_abil":5,"parents":[67],"dependencies":[63],"blockers":[],"cost":1,"display":{"row":40,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":68},{"display_name":"Devour","desc":"Harvester will give +5 Mana","archetype":"Shadestepper","archetype_req":0,"parents":[64],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":41,"col":0,"icon":"node_0"},"properties":{},"effects":[],"id":69},{"display_name":"Better Marked","desc":"Increase Marked's damage bonus by +5%","archetype":"","archetype_req":0,"base_abil":40,"parents":[64],"dependencies":[],"blockers":[],"cost":1,"display":{"row":41,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Marked","output":[{"type":"stat","name":"damMult.Marked"}],"scaling":[5]}],"id":70}]} \ No newline at end of file +const atrees={"Archer":[{"display_name":"Arrow Shield","desc":"Create a shield around you that deal damage and knockback mobs when triggered. (2 Charges)","parents":[60,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6,"icon":"node_archer"},"properties":{"charges":2,"duration":60,"aoe":5000},"effects":[{"type":"replace_spell","name":"Arrow Shield","cost":30,"base_spell":4,"display":"Total Damage","parts":[{"name":"Shield Damage","type":"damage","multipliers":[90,0,0,0,0,10]},{"name":"Total Damage","type":"total","hits":{"Shield Damage":2}}]}],"id":0},{"display_name":"Escape","desc":"Throw yourself backward to avoid danger. (Hold shift while escaping to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4,"icon":"node_archer"},"properties":{"aoe":0,"range":0},"effects":[{"type":"replace_spell","name":"Escape","cost":25,"base_spell":2,"display":"","parts":[]}],"id":1},{"display_name":"Arrow Bomb","desc":"Throw a long-range arrow that explodes and deal high damage in a large area. (Self-damage for 25% of your DPS)","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_archer"},"properties":{"aoe":4.5,"range":26},"effects":[{"type":"replace_spell","name":"Arrow Bomb","cost":50,"base_spell":3,"spell_type":"damage","display":"Total Damage","parts":[{"name":"Arrow Bomb","type":"damage","multipliers":[160,0,0,0,20,0]},{"name":"Total Damage","type":"total","hits":{"Arrow Bomb":1}}]}],"id":2},{"display_name":"Heart Shatter","desc":"If you hit a mob directly with Arrow Bomb, shatter its heart and deal bonus damage.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Heart Shatter":1}}],"id":3},{"display_name":"Fire Creep","desc":"Arrow Bomb will leak a trail of fire for 6s, Damaging enemies that walk into it every 0.4s.","base_abil":2,"parents":[68,39,5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6,"icon":"node_1"},"properties":{"aoe":0.8,"duration":6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[30,0,0,0,20,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Burn Damage","hits":{"Fire Creep":15}}],"id":4},{"display_name":"Bryophyte Roots","desc":"When you hit an enemy with Arrow Storm, create an area that slows them down and deals damage every 0.4s.","base_abil":7,"archetype":"Trapper","archetype_req":1,"parents":[4,35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":8,"icon":"node_1"},"properties":{"aoe":2,"duration":5},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Bryophyte Roots","cost":0,"multipliers":[40,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Roots Damage","hits":{"Bryophyte Roots":12}}],"id":5},{"display_name":"Nimble String","desc":"Arrow Storm throw out +6 arrows per stream and shoot twice as fast.","base_abil":7,"parents":[36,69],"dependencies":[7],"blockers":[68],"cost":2,"display":{"row":15,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-15,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","hits":{"Single Arrow":6}}],"id":6},{"display_name":"Arrow Storm","desc":"Shoot a stream of 8 arrows, dealing significant damage to close mobs and pushing them back.","parents":[58,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2,"icon":"node_archer"},"properties":{"range":16},"effects":[{"type":"replace_spell","name":"Arrow Storm","cost":40,"base_spell":1,"spell_type":"damage","display":"Total Damage","parts":[{"name":"Single Arrow","multipliers":[30,0,10,0,0,0]},{"name":"Single Stream","hits":{"Single Arrow":8}},{"name":"Total Damage","hits":{"Single Stream":1}}]}],"id":7},{"display_name":"Guardian Angels","desc":"Your protective arrows from Arrow Shield will become sentient bows, dealing damage up to 8 times each to nearby enemies. (Arrow Shield will no longer push nearby enemies)","archetype":"Boltslinger","archetype_req":3,"base_abil":0,"parents":[59,67],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":19,"col":1,"icon":"node_3"},"properties":{"range":4,"duration":60,"shots":8,"charges":2},"effects":[{"type":"replace_spell","name":"Guardian Angels","base_spell":4,"display":"DPS","parts":[{"name":"Single Shot","type":"damage","multipliers":[30,0,0,0,0,10]},{"name":"Single Bow","type":"total","hits":{"Single Shot":8}},{"name":"DPS","type":"total","hits":{"Single Shot":2}},{"name":"Total Damage","type":"total","hits":{"Single Bow":2}}]}],"id":8},{"display_name":"Windy Feet","desc":"When casting Escape, give speed to yourself and nearby allies.","base_abil":1,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":1,"icon":"node_1"},"properties":{"aoe":8,"duration":120},"effects":[],"id":9},{"display_name":"Basaltic Trap","desc":"When you hit the ground with Arrow Bomb, leave a Trap that damages enemies. (Max 2 Traps)","archetype":"Trapper","archetype_req":2,"parents":[5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":8,"icon":"node_3"},"properties":{"aoe":7,"traps":2},"effects":[{"type":"replace_spell","name":"Basaltic Trap","base_spell":7,"display":"Trap Damage","parts":[{"name":"Trap Damage","type":"damage","multipliers":[140,30,0,0,30,0]}]}],"id":10},{"display_name":"Windstorm","desc":"Arrow Storm shoot +1 stream of arrows, and each stream shoots +2 arrows, effectively doubling its damage.","base_abil":7,"parents":[8,33],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":21,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-10,0,-2,0,0,2]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","cost":0,"hits":{"Single Arrow":2}}],"id":11},{"display_name":"Grappling Hook","base_abil":1,"desc":"When casting Escape, throw a hook that pulls you when hitting a block. If you hit an enemy, pull them towards you instead. (Escape will not throw you backward anymore)","archetype":"Trapper","archetype_req":0,"parents":[61,40,33],"dependencies":[],"blockers":[20],"cost":2,"display":{"row":21,"col":5,"icon":"node_2"},"properties":{"range":26},"effects":[],"id":12},{"display_name":"Implosion","desc":"Arrow bomb will pull enemies towards you. If a trap is nearby, it will pull them towards it instead. Increase Heart Shatter's damage.","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[12,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[40,0,0,0,0,0]}],"id":13},{"display_name":"Twain's Arc","desc":"When you have 2+ Focus, holding shift will summon the Twain's Arc. Charge it up to shoot a destructive long-range beam. (Damage is dealt as Main Attack Damage)","archetype":"Sharpshooter","archetype_req":4,"parents":[62,64],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":25,"col":4,"icon":"node_2"},"properties":{"range":64,"focusReq":2},"effects":[{"type":"replace_spell","name":"Twain's Arc","base_spell":5,"scaling":"melee","use_atkspd":false,"display":"Single Shot","parts":[{"name":"Single Shot","type":"damage","multipliers":[200,0,0,0,0,0]}]}],"id":14},{"display_name":"Fierce Stomp","desc":"When using Escape, hold shift to quickly drop down and deal damage.","archetype":"Boltslinger","archetype_req":0,"base_abil":1,"parents":[42,64],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Fierce Stomp","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","cost":0,"hits":{"Fierce Stomp":1},"display":"Stomp Damage"}],"id":15},{"display_name":"Scorched Earth","desc":"Fire Creep become much stronger.","archetype":"Sharpshooter","archetype_req":0,"parents":[14],"dependencies":[4],"blockers":[],"cost":1,"display":{"row":26,"col":5,"icon":"node_1"},"properties":{"duration":2,"aoe":0.4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[10,0,0,0,5,0]}],"id":16},{"display_name":"Leap","desc":"When you double tap jump, leap foward. (2s Cooldown)","archetype":"Boltslinger","archetype_req":5,"parents":[42,55],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_1"},"properties":{"cooldown":2},"effects":[],"id":17},{"display_name":"Shocking Bomb","desc":"Arrow Bomb will not be affected by gravity, and all damage conversions become Thunder.","archetype":"Sharpshooter","archetype_req":5,"base_abil":2,"parents":[14,44,55],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":28,"col":4,"icon":"node_1"},"properties":{"gravity":0},"effects":[{"type":"convert_spell_conv","target_part":"all","base_spell":3,"conversion":"Thunder"}],"id":18},{"display_name":"Mana Trap","desc":"Your Traps will give you 2.85 Mana per second when you stay close to them.","archetype":"Trapper","archetype_req":5,"base_abil":10,"parents":[43,44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":8,"icon":"node_3"},"properties":{"range":16,"manaRegen":2.85},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":10}],"id":19},{"display_name":"Escape Artist","desc":"When casting Escape, release 120 arrows towards the ground.","base_abil":1,"parents":[46,17],"dependencies":[],"blockers":[12],"cost":2,"display":{"row":31,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Per Arrow","multipliers":[20,0,10,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Max Damage (Escape Artist)","hits":{"Per Arrow":120},"display":"Max Damage (Escape Artist)"}],"id":20},{"display_name":"Initiator","desc":"If you do not damage an enemy for 5s or more, your next sucessful hit will deal +50% damage and add +1 Focus.","archetype":"Sharpshooter","archetype_req":5,"parents":[18,44,47],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":31,"col":5,"icon":"node_2"},"properties":{},"effects":[],"id":21},{"display_name":"Call of the Hound","desc":"Arrow Shield summon a Hound that will attack and drag aggressive enemies towards your traps.","archetype":"Trapper","archetype_req":0,"base_abil":0,"parents":[21,47],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_2"},"properties":{},"effects":[{"type":"replace_spell","name":"Call of the Hound","base_spell":8,"display":"DPS","parts":[{"name":"Single Hit","multipliers":[40,0,0,0,0,0]},{"name":"DPS","hits":{"Single Hit":4}}]}],"id":22},{"display_name":"Arrow Hurricane","desc":"Arrow Storm will shoot +2 stream of arrows.","archetype":"Boltslinger","archetype_req":8,"base_abil":7,"parents":[48,20],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":33,"col":0,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":2}}],"id":23},{"display_name":"Geyser Stomp","desc":"Fierce Stomp will create geysers, dealing more damage and vertical knockback.","base_abil":1,"parents":[56],"dependencies":[15],"blockers":[],"cost":2,"display":{"row":37,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Geyser Stomp","multipliers":[0,0,0,50,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","hits":{"Geyser Stomp":1}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":15,"name":"aoe","value":1}]}],"id":24},{"display_name":"Crepuscular Ray","desc":"If you have 5 Focus, casting Arrow Storm will make you levitate and shoot 20 homing arrows per second until you run out of Focus. While in that state, you will lose 1 Focus per second.","archetype":"Sharpshooter","archetype_req":10,"parents":[49],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":37,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Crepuscular Ray","base_spell":6,"display":"DPS","parts":[{"name":"Single Arrow","multipliers":[20,0,0,5,0,0]},{"name":"DPS","hits":{"Single Arrow":20}},{"name":"Total Damage","hits":{"DPS":7}}]}],"id":25},{"display_name":"Grape Bomb","desc":"Arrow bomb will throw 3 additional smaller bombs when exploding.","base_abil":2,"parents":[51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7,"icon":"node_2"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Grape Bomb","multipliers":[30,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Grape Bomb":3}}],"id":26},{"display_name":"Tangled Traps","desc":"Your Traps will be connected by a rope that deals damage to enemies every 0.2s.","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[26],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":38,"col":6,"icon":"node_1"},"properties":{"attackSpeed":0.2},"effects":[{"type":"add_spell_prop","base_spell":7,"target_part":"Line Damage Tick","multipliers":[20,0,0,0,0,20]},{"type":"add_spell_prop","base_spell":7,"target_part":"DPS","hits":{"Line Damage Tick":5}}],"id":27},{"display_name":"Snow Storm","desc":"Enemies near you will be slowed down.","parents":[24,63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":2,"icon":"node_2"},"properties":{"range":2.5,"slowness":0.3},"effects":[],"id":28},{"display_name":"All-Seeing Panoptes","desc":"Your bows from Guardian Angels become all-seeing, increasing their range, damage and letting them shoot up to +5 times each.","archetype":"Boltslinger","archetype_req":11,"base_abil":0,"parents":[28],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":40,"col":1,"icon":"node_3"},"properties":{"range":8,"shots":5},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Shot","multipliers":[0,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Shot":5}}],"id":29},{"display_name":"Minefield","desc":"Allow you to place +6 Traps, but with reduced damage and range.","archetype":"Trapper","archetype_req":10,"base_abil":10,"parents":[26,53],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":40,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":7,"target_part":"Trap Damage","cost":0,"multipliers":[-80,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"aoe","value":-2},{"type":"prop","abil":10,"name":"traps","value":6}]}],"id":30},{"display_name":"Bow Proficiency I","desc":"Improve your Main Attack's damage and range when using a bow.","base_abil":999,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[5,0,0,0,0,0]}],"id":31},{"display_name":"Cheaper Arrow Bomb","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-10}],"id":32},{"display_name":"Cheaper Arrow Storm","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[12,11,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":21,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":33},{"display_name":"Cheaper Escape","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[7,0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":34},{"display_name":"Earth Mastery","base_abil":998,"desc":"Increases your base damage from all Earth attacks","archetype":"Trapper","archetype_req":0,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":35},{"display_name":"Thunder Mastery","base_abil":998,"desc":"Increases your base damage from all Thunder attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7,39,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":36},{"display_name":"Water Mastery","base_abil":998,"desc":"Increases your base damage from all Water attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[34,36,39],"dependencies":[],"blockers":[],"cost":1,"display":{"row":14,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":37},{"display_name":"Air Mastery","base_abil":998,"desc":"Increases base damage from all Air attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":38},{"display_name":"Fire Mastery","base_abil":998,"desc":"Increases base damage from all Fire attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[36,0,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":39},{"display_name":"More Shields","desc":"Give +2 charges to Arrow Shield.","base_abil":0,"parents":[12,10],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":21,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Shield Damage":2,"Single Bow":2}},{"type":"add_spell_prop","base_spell":4,"target_part":"DPS","behavior":"modify","hits":{"Single Shot":2}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"charges","value":2}]}],"id":40},{"display_name":"Stormy Feet","desc":"Windy Feet will last longer and add more speed.","archetype":"Boltslinger","base_abil":1,"parents":[11],"dependencies":[9],"blockers":[],"cost":1,"display":{"row":23,"col":1,"icon":"node_0"},"properties":{"duration":60},"effects":[],"id":41},{"display_name":"Refined Gunpowder","desc":"Increase the damage of Arrow Bomb.","base_abil":2,"parents":[11,64],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","multipliers":[50,0,0,0,0,0]}],"id":42},{"display_name":"More Traps","desc":"Increase the maximum amount of active Traps you can have by +2.","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[54],"dependencies":[10],"blockers":[],"cost":1,"display":{"row":26,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"traps","value":2}]}],"id":43},{"display_name":"Better Arrow Shield","desc":"Arrow Shield will gain additional area of effect, knockback and damage.","archetype":"Sharpshooter","archetype_req":0,"base_abil":0,"parents":[19,18,14],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":28,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Shield","behavior":"modify","multipliers":[40,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"behavior":"modify","name":"aoe","value":1}]}],"id":44},{"display_name":"Better Leap","desc":"Reduce leap's cooldown by 1s.","archetype":"Boltslinger","archetype_req":0,"base_abil":17,"parents":[17,55],"dependencies":[17],"blockers":[],"cost":1,"display":{"row":29,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":17,"name":"cooldown","value":-1}]}],"id":45},{"display_name":"Better Guardian Angels","desc":"Your Guardian Angels can shoot +4 arrows before disappearing.","archetype":"Boltslinger","archetype_req":0,"base_abil":0,"parents":[20,55],"dependencies":[8],"blockers":[],"cost":1,"display":{"row":31,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Shot":4}}],"id":46},{"display_name":"Cheaper Arrow Storm (2)","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[21,19],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":47},{"display_name":"Precise Shot","desc":"+30% Critical Hit Damage","parents":[46,49,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":48},{"display_name":"Cheaper Arrow Shield","desc":"Reduce the Mana cost of Arrow Shield.","base_abil":0,"parents":[48,21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":49},{"display_name":"Rocket Jump","desc":"Arrow Bomb's self-damage will knockback you farther away.","base_abil":2,"parents":[47,21],"dependencies":[2],"blockers":[],"cost":1,"display":{"row":33,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":50},{"display_name":"Cheaper Escape (2)","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[22,70],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":51},{"display_name":"Stronger Hook","desc":"Increase your Grappling Hook's range, speed and strength.","archetype":"Trapper","archetype_req":5,"base_abil":1,"parents":[51],"dependencies":[12],"blockers":[],"cost":1,"display":{"row":35,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":12,"name":"range","value":8}]}],"id":52},{"display_name":"Cheaper Arrow Bomb (2)","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[63,30],"dependencies":[],"blockers":[],"cost":1,"display":{"row":40,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":53},{"display_name":"Bouncing Bomb","desc":"Arrow Bomb will bounce once when hitting a block or enemy","base_abil":2,"parents":[40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7,"icon":"node_2"},"properties":{},"effects":[],"id":54},{"display_name":"Homing Shots","desc":"Your Main Attack arrows will follow nearby enemies and not be affected by gravity","archetype":"Sharpshooter","archetype_req":2,"base_abil":999,"parents":[17,18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_2"},"properties":{},"effects":[],"id":55},{"display_name":"Shrapnel Bomb","desc":"Arrow Bomb's explosion will fling 15 shrapnel, dealing damage in a large area","archetype":"Boltslinger","archetype_req":8,"base_abil":2,"parents":[23,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Shrapnel Bomblet","multipliers":[40,0,0,0,20,0]}],"id":56},{"display_name":"Elusive","desc":"If you do not get hit for 8+ seconds, become immune to self-damage and remove Arrow Storm's recoil. (Dodging counts as not getting hit)","archetype":"Boltslinger","archetype_req":0,"parents":[24],"dependencies":[],"blockers":[],"cost":2,"display":{"row":38,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":57},{"display_name":"Double Shots","desc":"Double Main Attack arrows, but they deal -30% damage per arrow (harder to hit far enemies)","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[60],"cost":1,"display":{"row":7,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-30,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":2},"display":"Total Damage"}],"id":58},{"display_name":"Triple Shots","desc":"Triple Main Attack arrows, but they deal -20% damage per arrow","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[69,67],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-20,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":1},"display":"Total Damage"}],"id":59},{"display_name":"Power Shots","desc":"Main Attack arrows have increased speed and knockback","archetype":"Sharpshooter","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[58],"cost":1,"display":{"row":7,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":60},{"display_name":"Focus","desc":"When hitting an aggressive mob 5+ blocks away, gain +1 Focus (Max 3). Resets if you miss once","archetype":"Sharpshooter","archetype_req":2,"parents":[68],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","name":"damMult.Focus"},"scaling":[40],"slider_max":3}],"id":61},{"display_name":"More Focus","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"base_abil":61,"parents":[33,12],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","slider_max":2,"output":{"type":"stat","name":"damMult.Focus"},"scaling":[-5]}],"id":62},{"display_name":"More Focus (2)","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"base_abil":61,"parents":[25,28],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":39,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","slider_max":2,"output":{"type":"stat","name":"damMult.Focus"},"scaling":[-5]}],"id":63},{"display_name":"Traveler","desc":"For every 1% Walk Speed you have from items, gain +1 Raw Spell Damage (Max 100)","parents":[42,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"spd"}],"output":{"type":"stat","name":"sdRaw"},"scaling":[1],"max":100}],"id":64},{"display_name":"Patient Hunter","desc":"Your Traps will deal +20% more damage for every second they are active (Max +80%)","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[40],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":8,"icon":"node_1"},"properties":{"max":80},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","slider_max":4,"output":{"type":"stat","name":"damMult.Basaltic:7.Trap Damage"},"slider_step":1,"scaling":[20]}],"id":65},{"display_name":"Stronger Patient Hunter","desc":"Add +80% Max Damage to Patient Hunter","archetype":"Trapper","archetype_req":0,"base_abil":10,"parents":[26],"dependencies":[65],"blockers":[],"cost":1,"display":{"row":38,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","slider_max":4},{"type":"raw_stat","bonuses":[{"type":"prop","abil":65,"name":"max","value":80}]}],"id":66},{"display_name":"Frenzy","desc":"Every time you hit an enemy, briefly gain +6% Walk Speed (Max 200%). Decay -40% of the bonus every second","archetype":"Boltslinger","archetype_req":0,"parents":[59,6],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","output":{"type":"stat","name":"spd"},"scaling":[6],"max":160}],"id":67},{"display_name":"Phantom Ray","desc":"Condense Arrow Storm into a single ray that damages enemies 10 times per second","base_abil":7,"parents":[37,4],"dependencies":[7],"blockers":[11,6,23],"cost":2,"display":{"row":16,"col":4,"icon":"node_2"},"properties":{"range":16},"effects":[{"type":"replace_spell","name":"Phantom Ray","base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[25,0,5,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Arrow":16}}]},{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":68},{"display_name":"Arrow Rain","desc":"When Arrow Shield loses its last charge, unleash 150 arrows raining down on enemies","base_abil":0,"parents":[6,38],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":15,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Per Arrow)","multipliers":[80,0,0,0,0,60]},{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Total)","hits":{"Arrow Rain (Per Arrow)":150}}],"id":69},{"display_name":"Decimator","desc":"Phantom Ray will increase its damage by 10% everytime you do not miss with it (Max 70%)","archetype":"Sharpshooter","archetype_req":0,"base_abil":7,"parents":[49,51],"dependencies":[68],"blockers":[],"cost":2,"display":{"row":34,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Phantom Ray hits","slider_max":7,"output":{"type":"stat","name":"damMult.Decimator:1.Single Arrow"},"scaling":[10]}],"id":70}],"Warrior":[{"display_name":"Bash","desc":"Violently bash the ground, dealing high damage in a large area","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_warrior"},"properties":{"aoe":4,"range":3},"effects":[{"type":"replace_spell","name":"Bash","cost":45,"base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Hit","type":"damage","multipliers":[130,20,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Hit":1}}]}],"id":0},{"display_name":"Spear Proficiency 1","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee","multipliers":[5,0,0,0,0,0]}],"id":1},{"display_name":"Cheaper Bash","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":2},{"display_name":"Double Bash","desc":"Bash will hit a second time at a farther range","parents":[1],"base_abil":0,"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{"range":3},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","cost":0,"hits":{"Single Hit":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","cost":0,"multipliers":[-50,0,0,0,0,0]}],"id":3},{"display_name":"Charge","desc":"Charge forward at high speed (hold shift to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_warrior"},"properties":{},"effects":[{"type":"replace_spell","name":"Charge","cost":25,"base_spell":2,"spell_type":"damage","scaling":"spell","display":"","parts":[]}],"id":4},{"display_name":"Heavy Impact","desc":"After using Charge, violently crash down into the ground and deal damage","base_abil":4,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Heavy Impact","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Heavy Impact":1}}],"id":5},{"display_name":"Vehement","desc":"For every 1% or 1 Raw Main Attack Damage you have from items, gain +2% Walk Speed (Max 20%)","archetype":"Fallen","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[7],"cost":1,"display":{"row":6,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"mdPct"},{"type":"stat","name":"mdRaw"}],"output":{"type":"stat","name":"spd"},"scaling":[2,2],"max":20}],"id":6},{"display_name":"Tougher Skin","desc":"Harden your skin and become permanently +5% more resistant. For every 1% or 1 Raw Heath Regen you have from items, gain +10 Health (Max 100)","archetype":"Paladin","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[6],"cost":1,"display":{"row":6,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"defMult.Base","value":5}]},{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hprRaw"},{"type":"stat","name":"hprPct"}],"output":{"type":"stat","name":"hpBonus"},"scaling":[10,10],"max":100}],"id":7},{"display_name":"Uppercut","desc":"Rocket enemies in the air and deal massive damage","parents":[6,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":2,"icon":"node_warrior"},"properties":{"aoe":3,"range":5},"effects":[{"type":"replace_spell","name":"Uppercut","cost":45,"base_spell":3,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Uppercut","multipliers":[200,40,40,0,0,0]},{"name":"Total Damage","hits":{"Uppercut":1}}]}],"id":8},{"display_name":"Cheaper Charge","desc":"Reduce the Mana cost of Charge","base_abil":4,"parents":[8,10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":9},{"display_name":"War Scream","desc":"Emit a terrorizing roar that deals damage, pull nearby enemies, and add damage resistance to yourself and allies","parents":[7,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":6,"icon":"node_warrior"},"properties":{"duration":30,"aoe":12,"defense_bonus":10},"effects":[{"type":"replace_spell","name":"War Scream","cost":35,"base_spell":4,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"War Scream","multipliers":[50,0,0,0,50,0]},{"name":"Total Damage","hits":{"War Scream":1}}]}],"id":10},{"display_name":"Earth Mastery","base_abil":998,"desc":"Increases base damage from all Earth attacks","archetype":"Fallen","archetype_req":0,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":11},{"display_name":"Thunder Mastery","base_abil":998,"desc":"Increases base damage from all Thunder attacks","archetype":"Fallen","archetype_req":0,"parents":[8,14,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":12},{"display_name":"Water Mastery","base_abil":998,"desc":"Increases base damage from all Water attacks","archetype":"Battle Monk","archetype_req":0,"parents":[9,12,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":11,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":13},{"display_name":"Air Mastery","base_abil":998,"desc":"Increases base damage from all Air attacks","archetype":"Battle Monk","archetype_req":0,"parents":[10,12,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":14},{"display_name":"Fire Mastery","base_abil":998,"desc":"Increases base damage from all Fire attacks","archetype":"Paladin","archetype_req":0,"parents":[10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":15},{"display_name":"Quadruple Bash","desc":"Bash will hit 4 times at an even larger range","archetype":"Fallen","archetype_req":0,"base_abil":0,"parents":[11,17],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":0,"icon":"node_1"},"properties":{"range":6},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Hit":2}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[-20,0,0,0,0,0]}],"id":16},{"display_name":"Fireworks","desc":"Mobs hit by Uppercut will explode mid-air and receive additional damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[12,16],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fireworks","multipliers":[80,0,20,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Fireworks":1}}],"id":17},{"display_name":"Half-Moon Swipe","desc":"Uppercut will deal a footsweep attack at a longer and wider angle. All elemental conversions become Water","archetype":"Battle Monk","archetype_req":1,"base_abil":8,"parents":[13],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":13,"col":4,"icon":"node_1"},"properties":{"range":4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":-10,"multipliers":[-70,0,0,30,0,0]}],"id":18},{"display_name":"Flyby Jab","desc":"Damage enemies in your way when using Charge","base_abil":4,"parents":[14,20],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":6,"icon":"node_1"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flyby Jab","multipliers":[20,0,0,0,0,40]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Flyby Jab":1}}],"id":19},{"display_name":"Flaming Uppercut","desc":"Uppercut will light mobs on fire, dealing damage every 0.6 seconds","archetype":"Paladin","archetype_req":0,"base_abil":8,"parents":[15,19],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":12,"col":8,"icon":"node_1"},"properties":{"duration":3,"tick":0.6},"effects":[{"type":"replace_spell","name":"Flaming Uppercut","base_spell":8,"display":"DPS","parts":[{"name":"Damage Tick","multipliers":[0,0,0,0,50,0]},{"name":"DPS","hits":{"Damage Tick":1.6666666666666667}},{"name":"Total Damage","hits":{"Damage Tick":5}}]}],"id":20},{"display_name":"Iron Lungs","desc":"War Scream deals more damage","archetype":"Paladin","archetype_req":0,"base_abil":10,"parents":[19,20],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"War Scream","cost":0,"multipliers":[30,0,0,0,0,30]}],"id":21},{"display_name":"Generalist","desc":"After casting 3 different spells in a row, your next spell will cost 5 mana","archetype":"Battle Monk","archetype_req":3,"parents":[23],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":2,"icon":"node_3"},"properties":{},"effects":[],"id":22},{"display_name":"Counter","desc":"When dodging a nearby enemy attack, get 30% chance to instantly attack back","archetype":"Battle Monk","archetype_req":0,"parents":[18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":4,"icon":"node_1"},"properties":{"chance":30},"effects":[{"type":"replace_spell","name":"Counter","base_spell":5,"display":"Counter Damage","parts":[{"name":"Counter Damage","multipliers":[60,0,20,0,0,20]}]}],"id":23},{"display_name":"Mantle of the Bovemists","desc":"When casting War Scream, create a holy shield around you that reduces all incoming damage by 70% for 3 hits (20s cooldown)","archetype":"Paladin","archetype_req":3,"parents":[21],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":15,"col":7,"icon":"node_3"},"properties":{"mantle_charge":3},"effects":[{"type":"raw_stat","toggle":"Activate Mantle","bonuses":[{"type":"stat","name":"defMult.Mantle","value":70}]}],"id":24},{"display_name":"Bak'al's Grasp","desc":"After casting War Scream, become Corrupted (15s Cooldown). You cannot heal while in that state. While Corrupted, every 2% of Health you lose will add +4 Raw Damage to your attacks (Max 120)","archetype":"Fallen","archetype_req":2,"parents":[16,17],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":16,"col":1,"icon":"node_3"},"properties":{"cooldown":15},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","slider_max":100,"slider_step":1,"output":{"type":"stat","name":"damRaw"},"max":120,"scaling":[2]}],"id":25},{"display_name":"Spear Proficiency 2","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[25,27],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee","multipliers":[5,0,0,0,0,0]}],"id":26},{"display_name":"Cheaper Uppercut","desc":"Reduce the Mana Cost of Uppercut","base_abil":8,"parents":[26,28,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":27},{"display_name":"Aerodynamics","desc":"During Charge, you can steer and change direction","archetype":"Battle Monk","archetype_req":0,"base_abil":4,"parents":[27,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":28},{"display_name":"Provoke","desc":"Mobs damaged by War Scream will target only you for at least 5s. Reduce the Mana cost of War Scream","base_abil":10,"parents":[28,24],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":29},{"display_name":"Precise Strikes","desc":"+30% Critical Hit Damage","parents":[27,26],"dependencies":[],"blockers":[],"cost":1,"display":{"row":18,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":30},{"display_name":"Air Shout","desc":"War Scream will fire a projectile that can go through walls and deal damage multiple times","base_abil":10,"parents":[28,29],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":18,"col":6,"icon":"node_1"},"properties":{"attackRate":2},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Air Shout","multipliers":[40,0,0,0,0,10]}],"id":31},{"display_name":"Enraged Blow","desc":"While Corriupted, every 1% of Health you lose will increase your damage by +3% (Max 300%)","archetype":"Fallen","archetype_req":0,"base_abil":25,"parents":[26],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":20,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider_name":"Corrupted","slider":true,"output":{"type":"stat","name":"damMult.Enraged"},"scaling":[3]}],"id":32},{"display_name":"Flying Kick","desc":"When using Charge, mobs hit will halt your momentum and get knocked back","archetype":"Battle Monk","archetype_req":1,"base_abil":4,"parents":[27,34],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":3,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick","multipliers":[150,0,0,20,0,30]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Flying Kick":1},"display":"Flying Kick Max Damage"}],"id":33},{"display_name":"Stronger Mantle","desc":"Add +2 additional charges to Mantle of the Bovemists","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[35,33],"dependencies":[24],"blockers":[],"cost":1,"display":{"row":20,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":24,"name":"mantle_charge","value":2}]}],"id":34},{"display_name":"Manachism","desc":"If you receive a hit that's less than 5% of your max HP, gain 10 Mana (1s Cooldown)","archetype":"Paladin","archetype_req":3,"parents":[34,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":8,"icon":"node_2"},"properties":{"cooldown":1},"effects":[],"id":35},{"display_name":"Boiling Blood","desc":"Bash leaves a trail of boiling blood behind its first explosion, slowing down and damaging enemies above it every 0.4 seconds","base_abil":0,"parents":[32,37],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Boiling Blood","cost":0,"multipliers":[25,0,0,0,5,0]}],"id":36},{"display_name":"Ragnarokkr","desc":"War Scream become deafening, increasing its duration and giving damage bonus to players","archetype":"Fallen","archetype_req":0,"base_abil":10,"parents":[36,33],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_2"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":10},{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"duration","value":90}]}],"id":37},{"display_name":"Ambidextrous","desc":"Increase your chance to attack with Counter by +30%","base_abil":23,"parents":[33,34,39],"dependencies":[23],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":23,"name":"chance","value":30}]}],"id":38},{"display_name":"Burning Heart","desc":"For every 100 Health Bonus you have from item IDs, gain +2% Fire Damage (Max 100%)","archetype":"Paladin","archetype_req":0,"parents":[38,40],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hpBonus"}],"output":{"type":"stat","name":"fDamPct"},"scaling":[0.02],"max":100}],"id":39},{"display_name":"Stronger Bash","desc":"Increase the damage of Bash","base_abil":0,"parents":[39,35],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[30,0,0,0,0,0]}],"id":40},{"display_name":"Intoxicating Blood","desc":"After leaving Corrupted, gain 2% of the health lost back for each enemy killed while Corrupted","archetype":"Fallen","archetype_req":5,"base_abil":25,"parents":[37,36],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":23,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":41},{"display_name":"Comet","desc":"After being hit by Fireworks, enemies will crash into the ground and receive more damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[37],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":24,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Comet","cost":0,"multipliers":[80,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"hits":{"Comet":1}}],"id":42},{"display_name":"Collide","desc":"Mobs thrown into walls from Flying Kick will explode and receive additonal damage","archetype":"Battle Monk","archetype_req":4,"base_abil":4,"parents":[38,39],"dependencies":[33],"blockers":[],"cost":2,"display":{"row":23,"col":5,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Collide","cost":0,"multipliers":[150,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Collide":1}}],"id":43},{"display_name":"Rejuvenating Skin","desc":"Regain back 30% of the damage you take as healing over 30s","archetype":"Paladin","archetype_req":5,"parents":[39,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":23,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":44},{"display_name":"Uncontainable Corruption","desc":"Reduce the cooldown of Bak'al's Grasp by -5s, and increase the raw damage gained for every 2% of health lost by +1","base_abil":25,"parents":[36,46],"dependencies":[25],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"stat","name":"damRaw"},"scaling":[0.5]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":25,"name":"cooldown","value":-5}]}],"id":45},{"display_name":"Radiant Devotee","desc":"For every 4% Reflection you have from items, gain +1/5s Mana Regen (Max 10/5s)","archetype":"Battle Monk","archetype_req":1,"parents":[47,45],"dependencies":[],"blockers":[],"cost":1,"display":{"row":26,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","inputs":[{"type":"stat","name":"ref"}],"output":{"type":"stat","name":"mr"},"scaling":[0.25],"max":10}],"id":46},{"display_name":"Whirlwind Strike","desc":"Uppercut will create a strong gust of air, launching you upward with enemies (Hold shift to stay grounded)","archetype":"Battle Monk","archetype_req":5,"base_abil":8,"parents":[38,46],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":26,"col":4,"icon":"node_1"},"properties":{"range":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","multipliers":[0,0,0,0,0,50]}],"id":47},{"display_name":"Mythril Skin","desc":"Gain +5% Base Resistance and become immune to knockback","archetype":"Paladin","archetype_req":6,"parents":[44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"defMult.Base","value":5}]}],"id":48},{"display_name":"Armour Breaker","desc":"While Corrupted, losing 30% Health will make your next Uppercut destroy enemies' defense, rendering them weaker to damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[45,46],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":27,"col":1,"icon":"node_2"},"properties":{"duration":8},"effects":[{"type":"raw_stat","toggle":"Activate Armor Breaker","bonuses":[{"type":"stat","name":"damMult.ArmorBreaker","value":30}]}],"id":49},{"display_name":"Shield Strike","desc":"When your Mantle of the Bovemist loses all charges, deal damage around you for each Mantle individually lost","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[48,51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Shield Strike","base_spell":6,"display":"Damage per Shield","parts":[{"name":"Damage per Shield","multipliers":[60,0,20,0,0,0]}]}],"id":50},{"display_name":"Sparkling Hope","desc":"Everytime you heal 5% of your max health, deal damage to all nearby enemies","archetype":"Paladin","archetype_req":0,"parents":[48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":8,"icon":"node_2"},"properties":{"aoe":6},"effects":[{"type":"replace_spell","name":"Sparkling Hope","base_spell":7,"display":"Damage Tick","parts":[{"name":"Damage Tick","multipliers":[10,0,5,0,0,0]}]}],"id":51},{"display_name":"Massive Bash","desc":"While Corrupted, every 3% Health you lose will add +1 AoE to Bash (Max 10)","archetype":"Fallen","archetype_req":8,"base_abil":25,"parents":[53,45],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"prop","abil":0,"name":"aoe"},"scaling":[0.3333333333333333],"max":10}],"id":52},{"display_name":"Tempest","desc":"War Scream will ripple the ground and deal damage 3 times in a large area","archetype":"Battle Monk","archetype_req":0,"base_abil":10,"parents":[52,54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_1"},"properties":{"aoe":16},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest","multipliers":[30,10,0,0,0,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest Total Damage","hits":{"Tempest":3}},{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Tempest":3}}],"id":53},{"display_name":"Spirit of the Rabbit","desc":"Reduce the Mana cost of Charge and increase your Walk Speed by +20%","archetype":"Battle Monk","archetype_req":5,"base_abil":4,"parents":[53,47],"dependencies":[],"blockers":[],"cost":1,"display":{"row":28,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5},{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":20}]}],"id":54},{"display_name":"Massacre","desc":"While Corrupted, if your effective attack speed is Slow or lower, hitting an enemy with your Main Attack will add +1% to your Corrupted bar","archetype":"Fallen","archetype_req":5,"base_abil":999,"parents":[53,52],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":55},{"display_name":"Axe Kick","desc":"Increase the damage of Uppercut, but also increase its mana cost","base_abil":8,"parents":[53,54],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":10,"multipliers":[100,0,0,0,0,0]}],"id":56},{"display_name":"Radiance","desc":"Bash will buff your allies' positive IDs. (15s Cooldown)","archetype":"Paladin","archetype_req":2,"base_abil":0,"parents":[54,58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":5,"icon":"node_2"},"properties":{"cooldown":15},"effects":[],"id":57},{"display_name":"Cheaper Bash 2","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[57,50,51],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":58},{"display_name":"Cheaper War Scream","desc":"Reduce the Mana cost of War Scream","base_abil":10,"parents":[52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":59},{"display_name":"Discombobulate","desc":"Every time you hit an enemy, briefly increase your elemental damage dealt to them by +3 (Additive, Max +80). This bonus decays -5 every second","archetype":"Battle Monk","archetype_req":11,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":2,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","slider_max":27,"output":[{"type":"stat","name":"eDamAddMin"},{"type":"stat","name":"eDamAddMax"},{"type":"stat","name":"tDamAddMin"},{"type":"stat","name":"tDamAddMax"},{"type":"stat","name":"wDamAddMin"},{"type":"stat","name":"wDamAddMax"},{"type":"stat","name":"fDamAddMin"},{"type":"stat","name":"fDamAddMax"},{"type":"stat","name":"aDamAddMin"},{"type":"stat","name":"aDamAddMax"}],"scaling":[3],"max":80}],"id":60},{"display_name":"Thunderclap","desc":"Bash will cast at the player's position and gain additional AoE.\n\n All elemental conversions become Thunder","archetype":"Battle Monk","archetype_req":8,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"convert_spell_conv","target_part":"all","base_spell":1,"conversion":"Thunder"},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"aoe","value":3}]}],"id":61},{"display_name":"Cyclone","desc":"After casting War Scream, envelop yourself with a vortex that damages nearby enemies every 0.5s","archetype":"Battle Monk","archetype_req":0,"parents":[54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":4,"icon":"node_1"},"properties":{"aoe":4,"duration":20},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone","multipliers":[10,0,0,0,5,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone Total Damage","hits":{"Cyclone":40}}],"id":62},{"display_name":"Second Chance","desc":"When you receive a fatal blow, survive and regain 30% of your Health (10m Cooldown)","archetype":"Paladin","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":63},{"display_name":"Blood Pact","desc":"If you do not have enough mana to cast a spell, spend health instead (0.6% health per mana)","archetype":"Fallen","archetype_req":10,"parents":[59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_3"},"properties":{"health_cost":0.6},"effects":[],"id":64},{"display_name":"Brink of Madness","desc":"If your health is 25% full or less, gain +40% Resistance","parents":[64,66],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":4,"icon":"node_2"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Brink","bonuses":[{"type":"stat","name":"defMult.Brink","value":40}]}],"id":65},{"display_name":"Cheaper Uppercut 2","desc":"Reduce the Mana cost of Uppercut","base_abil":8,"parents":[63,65],"dependencies":[],"blockers":[],"cost":1,"display":{"row":35,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":66},{"display_name":"Martyr","desc":"When you receive a fatal blow, all nearby allies become invincible","archetype":"Paladin","archetype_req":0,"parents":[63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":8,"icon":"node_1"},"properties":{"duration":3,"aoe":12},"effects":[],"id":67},{"display_name":"Haemorrhage","desc":"Reduce Blood Pact's health cost. (0.3% health per mana)","archetype":"Fallen","archetype_req":0,"base_abil":64,"parents":[64],"dependencies":[64],"blockers":[],"cost":2,"display":{"row":35,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":64,"name":"health_cost","value":-0.3}]}],"id":68}],"Mage":[{"display_name":"Meteor","desc":"Summon a slow but powerful meteor from the sky, dealing massive damage in a large area","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_mage"},"properties":{"aoe":5,"range":18},"effects":[{"type":"replace_spell","name":"Meteor","cost":55,"base_spell":3,"display":"Total Damage","parts":[{"name":"Meteor Damage","multipliers":[300,100,0,0,0,0]},{"name":"Total Damage","hits":{"Meteor Damage":1}}]}],"id":0},{"display_name":"Teleport","desc":"Instantly teleport in the direction you're facing","parents":[4],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_mage"},"properties":{"range":12},"effects":[{"type":"replace_spell","name":"Teleport","cost":25,"base_spell":2,"display":"","parts":[]}],"id":1},{"display_name":"Heal","desc":"Heal yourself and nearby allies in a large area around you. (When healing an ally, you cannot heal more than 30% of their max health)","parents":[14,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":2,"icon":"node_mage"},"properties":{"aoe":5},"effects":[{"type":"replace_spell","name":"Heal","cost":35,"base_spell":1,"display":"Heal","parts":[{"name":"Heal","power":0.1}]}],"id":2},{"display_name":"Ice Snake","desc":"Summon a fast-moving ice snake that reduces your enemies' speed and damage them.","parents":[13,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":6,"icon":"node_mage"},"properties":{"range":18,"effects":40,"duration":3},"effects":[{"type":"replace_spell","name":"Ice Snake","cost":35,"base_spell":4,"display":"Ice Snake Damage","parts":[{"name":"Ice Snake Damage","multipliers":[70,0,0,30,0,0]}]}],"id":3},{"display_name":"Shooting Star","desc":"Drastically increase the speed of your Meteor ability.","base_abil":3,"parents":[5],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":4},{"display_name":"Wand Proficiency I","desc":"Improve your Main Attack's damage and range when using a wand.","base_abil":999,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":5},{"display_name":"Cheaper Meteor","desc":"Reduce the Mana cost of Meteor.","base_abil":0,"parents":[5],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-10}],"id":6},{"display_name":"Earth Mastery","base_abil":998,"desc":"Increases your base damage from all Earth attacks","archetype":"Arcanist","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":7},{"display_name":"Thunder Mastery","base_abil":998,"desc":"Increases your base damage from all Thunder attacks","archetype":"Riftwalker","archetype_req":0,"parents":[2,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":8},{"display_name":"Water Mastery","base_abil":998,"desc":"Increases your base damage from all Water attacks","archetype":"Light Bender","archetype_req":0,"parents":[12,8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":11,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":9},{"display_name":"Air Mastery","base_abil":998,"desc":"Increases base damage from all Air attacks","archetype":"Riftwalker","archetype_req":0,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":10},{"display_name":"Fire Mastery","base_abil":998,"desc":"Increases base damage from all Fire attacks","archetype":"Arcanist","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":11},{"display_name":"Cheaper Teleport","desc":"Reduce the Mana cost of Teleport.","base_abil":1,"parents":[2,3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":12},{"display_name":"Wisdom","desc":"For every 2% or 2 Raw Spell Damage you have from items, gain +1/5s mana regen (Max 5/5s)","archetype":"Arcanist","archetype_req":0,"parents":[1],"dependencies":[],"blockers":[14],"cost":1,"display":{"row":6,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"sdPct"},{"type":"stat","name":"sdRaw"}],"output":{"type":"stat","name":"mr"},"scaling":[0.5,0.5],"max":5}],"id":13},{"display_name":"Wand Proficiency II","desc":"Improve your Main Attack's damage and range when using a wand.","archetype":"Riftwalker","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":14},{"display_name":"Wind Slash","desc":"When using Teleport, slash through the air and deal damage to enemies you pierce.","archetype":"Riftwalker","base_abil":1,"parents":[10,16],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":12,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","target_part":"Wind Slash","base_spell":2,"multipliers":[50,0,0,0,0,50]},{"type":"add_spell_prop","target_part":"Total Damage","base_spell":2,"display":"Total Damage","hits":{"Wind Slash":1}}],"id":15},{"display_name":"Thunderstorm","desc":"After casting Meteor, summon 3 lightning strikes and deal additional damage","base_abil":0,"parents":[15,8],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":12,"col":2,"icon":"node_1"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","target_part":"Lightning Damage","base_spell":3,"multipliers":[30,0,15,0,0,0]},{"type":"add_spell_prop","target_part":"Total Damage","base_spell":3,"hits":{"Lightning Damage":3}}],"id":16},{"display_name":"Stronger Meteor","desc":"Increase the damage of Meteor.","base_abil":0,"archetype":"Arcanist","archetype_req":2,"parents":[18],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Meteor Damage","behavior":"modify","multipliers":[30,90,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Lightning Damage","behavior":"modify","multipliers":[30,90,0,0,0,0]}],"id":17},{"display_name":"Burning Sigil","desc":"Meteor will leave a sigil that damages enemies every 0.4s.","base_abil":0,"parents":[11,7],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":7,"icon":"node_1"},"properties":{"aoe":7,"duration":8},"effects":[{"type":"replace_spell","name":"Burning Sigil","base_spell":6,"display":"DPS","parts":[{"name":"Tick Damage","multipliers":[15,0,0,0,25,0]},{"name":"DPS","hits":{"Tick Damage":2.5}},{"name":"Total Burn Damage","hits":{"Tick Damage":20}}]}],"id":18},{"display_name":"Sunshower","desc":"Heal emit a strong light, damaging nearby enemies.","archetype":"Light Bender","archetype_req":0,"base_abil":2,"parents":[9],"dependencies":[2],"blockers":[22],"cost":2,"display":{"row":13,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Sunshower Damage","multipliers":[70,0,0,30,0,0]}],"id":19},{"display_name":"Windsweeper","desc":"Your Main Attack will add +1 Winded to enemies you hit. (Max 5, 0.5s cooldown) Ice Snake will deal additional damage to enemies for every Winded they have","archetype":"Riftwalker","archetype_req":3,"parents":[15,16],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":15,"col":1,"icon":"node_3"},"properties":{"max":5},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":{"type":"stat","name":"nConvBase:4.Ice Snake Damage"},"scaling":[20],"slider_step":1,"slider_max":5},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":{"type":"stat","name":"wConvBase:4.Ice Snake Damage"},"scaling":[10]}],"id":20},{"display_name":"Ophanim","desc":"When casting Meteor, instead summon 2 orbs of light with 200 Health that will attack when you use your Main Attack. When they damage an enemy, they lose 20% of their Health. They can be healed back.","archetype":"Light Bender","archetype_req":2,"parents":[19],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":4,"icon":"node_3"},"properties":{"health":200},"effects":[{"type":"replace_spell","name":"Ophanim","base_spell":3,"display":"Per Melee (max)","parts":[{"name":"Per Orb","multipliers":[50,0,30,20,0,0]},{"name":"Per Melee (max)","hits":{"Per Orb":2}}]},{"type":"add_spell_prop","base_spell":3,"cost":30}],"id":21},{"display_name":"Arcane Transfer","desc":"Meteor and Ice Snake will add +5 Mana to a Mana Bank for every aggressive enemy you hit. Heal will now transfer the content of your Mana Bank into usable Mana instead of healing.","archetype":"Arcanist","archetype_req":2,"parents":[18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":7,"icon":"node_3"},"properties":{"bank":90},"effects":[{"type":"replace_spell","name":"Arcane Transfer","base_spell":1,"parts":[],"display":""}],"id":22},{"display_name":"Cheaper Heal","desc":"Reduce the Mana cost of Heal.","base_abil":2,"parents":[20,24],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":23},{"display_name":"Purification","desc":"Heal and Arcane Transfer will purify you of all negative effects and fire. (3s Cooldown)","base_abil":1,"parents":[21,23,25],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":4,"icon":"node_2"},"properties":{},"effects":[],"id":24},{"display_name":"Sentient Snake","desc":"Ice Snake will follow the direction you're facing, allowing you to control it.","base_abil":3,"parents":[22,24],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":17,"col":6,"icon":"node_1"},"properties":{},"effects":[],"id":25},{"display_name":"Eye Piercer","desc":"Teleport will blind enemies, confusing them for a short amount of time.","base_abil":1,"parents":[23],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":18,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":26},{"display_name":"Breathless","desc":"Meteor will deal additional damage to enemies for every Winded they have.","base_abil":20,"archetype":"Riftwalker","archetype_req":0,"parents":[23,24],"dependencies":[20],"blockers":[],"cost":2,"display":{"row":18,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"nConvBase:3.Meteor Damage"},{"type":"stat","name":"nConvBase:3.Meteor Damage"},{"type":"stat","name":"nConvBase:3.Lightning Damage"}],"scaling":[15]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"eConvBase:3.Meteor Damage"},{"type":"stat","name":"eConvBase:3.Per Orb"},{"type":"stat","name":"eConvBase:3.Lightning Damage"}],"scaling":[10]}],"id":27},{"display_name":"Larger Heal","desc":"Increase your Heal's range.","base_abil":1,"archetype":"Light Bender","archetype_req":0,"parents":[24,25],"dependencies":[2],"blockers":[22],"cost":1,"display":{"row":18,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":2,"name":"aoe","value":2}]}],"id":28},{"display_name":"Larger Mana Bank","desc":"Increase your maximum Mana Bank by +30.","base_abil":1,"archetype":"Arcanist","archetype_req":0,"parents":[25],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":18,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":22,"name":"bank","value":30}]}],"id":29},{"display_name":"Cheaper Ice Snake","desc":"Reduce the Mana cost of Ice Snake.","base_abil":3,"parents":[26,32],"dependencies":[],"blockers":[],"cost":1,"display":{"row":20,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":30},{"display_name":"Cheaper Teleport II","desc":"Reduce the Mana cost of Teleport.","base_abil":1,"parents":[32,24],"dependencies":[],"blockers":[],"cost":1,"display":{"row":20,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":31},{"display_name":"Fortitude","desc":"After healing 120% of your max health within 10s, apply a damage bonus to each player you've healed. (15s Cooldown)","base_abil":2,"archetype":"Light Bender","archetype_req":0,"parents":[30,31],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":2,"icon":"node_2"},"properties":{"duration":5},"effects":[],"id":32},{"display_name":"Pyrokinesis","desc":"When your Mana Bank reaches 30, your Main Attack will stop and explode when it hits an enemy. (Damage is dealt as Main Attack Damage)","base_abil":4,"archetype":"Arcanist","archetype_req":4,"parents":[25],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":7,"icon":"node_2"},"properties":{},"__TODO":"replace_spell pyrokinesis damage","effects":[],"id":33},{"display_name":"Seance","desc":"For every 5/3s Lifesteal you have from items, gain 1% Spell Damage (Max 50%)","archetype":"","archetype_req":0,"parents":[33,36],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"ls"}],"output":{"type":"stat","name":"sdPct"},"scaling":[0.2],"max":50}],"id":34},{"display_name":"Blink","desc":"Teleport will trigger 2 times in quick successions","base_abil":1,"archetype":"Riftwalker","archetype_req":0,"parents":[32,30],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":21,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":1,"name":"range","value":-4}]},{"type":"add_spell_prop","behavior":"modify","target_part":"Total Damage","base_spell":2,"hits":{"Wind Slash":1,"Explosion Damage":1}}],"id":35},{"display_name":"Snake Nest","desc":"Ice Snake will summon 3 snakes.","base_abil":3,"parents":[34,31,40],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":22,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":36},{"display_name":"Arcane Restoration","desc":"Pyrokinesis will add +1 Mana every 1s to your Mana Bank when hitting an aggressive enemy.","base_abil":999,"archetype":"Arcanist","archetype_req":0,"parents":[34,36],"dependencies":[33],"blockers":[],"cost":2,"display":{"row":23,"col":6,"icon":"node_1"},"properties":{"duration":4},"effects":[],"id":37},{"display_name":"Fluid Healing","desc":"For every 1% Water Damage Bonus you have, buff Heal's healing power by +0.3%.","archetype":"Light Bender","archetype_req":0,"base_abil":2,"parents":[40,39],"dependencies":[],"blockers":[],"cost":2,"display":{"row":23,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"round":false,"inputs":[{"type":"stat","name":"wDamPct"}],"output":{"type":"stat","name":"healPct"},"scaling":[0.3]}],"id":38},{"display_name":"Transonic Warp","desc":"Teleport will deal additional damage to enemies for every Winded they have.","base_abil":20,"archetype":"Riftwalker","archetype_req":5,"parents":[30],"dependencies":[3,20],"blockers":[],"cost":2,"display":{"row":23,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"nConvBase:2.Wind Slash"},{"type":"stat","name":"nConvBase:2.Explosion Damage"}],"scaling":[20]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"tConvBase:2.Wind Slash"},{"type":"stat","name":"tConvBase:2.Explosion Damage"}],"scaling":[5]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","output":[{"type":"stat","name":"aConvBase:2.Wind Slash"},{"type":"stat","name":"aConvBase:2.Explosion Damage"}],"scaling":[5]}],"id":39},{"display_name":"Healthier Ophanim I","desc":"Increase the health of your orbs from Ophanim by +800 and reduce the damage they take when hitting an enemy by -5%.","archetype":"Light Bender","archetype_req":0,"base_abil":21,"parents":[32,31],"dependencies":[21],"blockers":[],"cost":1,"display":{"row":22,"col":3,"icon":"node_0"},"properties":{},"effects":[],"id":40},{"display_name":"Orphion's Pulse","desc":"Heal will trigger 2 more times, increasing the overall healing.","archetype":"Light Bender","base_abil":2,"parents":[40,36],"dependencies":[2],"blockers":[22],"cost":2,"display":{"row":23,"col":4,"icon":"node_1"},"properties":{"aoe":5},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Second and Third Pulses","power":0.15},{"type":"add_spell_prop","base_spell":1,"display":"Total Heal","target_part":"Total Heal","hits":{"Heal":1,"Second and Third Pulses":2}}],"id":41},{"display_name":"Diffusion","desc":"If you kill an enemy with Winded on them, the leftover Winded will spread to nearby enemies.","archetype":"Riftwalker","archetype_req":6,"base_abil":20,"parents":[39,38],"dependencies":[20],"blockers":[],"cost":2,"display":{"row":25,"col":1,"icon":"node_3"},"properties":{"aoe":5},"effects":[],"id":42},{"display_name":"Lightweaver","desc":"After healing 50% of your max health within 10s, summon a rotating orb that damages all enemies it touches for 20s. (Max 3 Orbs)","archetype":"Light Bender","archetype_req":7,"parents":[41],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Lightweaver","base_spell":5,"display":"Orb Damage","parts":[{"name":"Single Orb","type":"damage","multipliers":[30,0,0,0,20,0]},{"name":"Orb Damage","type":"total","hits":{"Single Orb":3}}]}],"id":43},{"display_name":"Arcane Speed","desc":"After casting Heal or Arcane Transfer, gain +80% speed for 3s. (8s Cooldown)","base_abil":2,"parents":[43,45],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":25,"col":6,"icon":"node_1"},"properties":{},"effects":[],"id":44},{"display_name":"Larger Mana Bank II","desc":"Increase your maximum Mana Bank by +30.","base_abil":1,"archetype":"Arcanist","archetype_req":0,"parents":[34,44],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":25,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":45},{"display_name":"Psychokinesis","desc":"Meteor will launch directly from you as a slow projectile.","base_abil":3,"archetype":"Arcanist","archetype_req":5,"parents":[45,44],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":26,"col":7,"icon":"node_1"},"properties":{"range":20},"effects":[],"id":46},{"display_name":"More Winded","desc":"Incrase your maximum Winded by +5.","base_abil":20,"archetype":"Riftwalker","archetype_req":0,"parents":[42],"dependencies":[20],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":20,"name":"max","value":5}]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","slider_max":5}],"id":47},{"display_name":"Cheaper Ice Snake II","desc":"Reduce the Mana cost of Ice Snake.","base_abil":3,"parents":[42,52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":27,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":48},{"display_name":"Cheaper Meteor II","desc":"Reduce the Mana cost of Meteor.","base_abil":0,"parents":[52,43,44],"dependencies":[],"blockers":[],"cost":1,"display":{"row":27,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":49},{"display_name":"Chaos Explosion","desc":"When your Mana Bank reaches 120, casting Arcane Transfer will rapidly unleash the last 3 spells you've cast in order.","base_abil":22,"archetype":"Arcanist","archetype_req":8,"parents":[45],"dependencies":[22],"blockers":[],"cost":2,"display":{"row":27,"col":8,"icon":"node_3"},"properties":{},"effects":[],"id":50},{"display_name":"Arcane Power","desc":"Meteor and Ice Snake will add +2 Mana to your Mana Bank for each aggressive mob you hit.","base_abil":22,"archetype":"Arcanist","archetype_req":0,"parents":[56],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":29,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":51},{"display_name":"Explosive Entrance","desc":"Deal Damage in an area on the location you Teleport to.","base_abil":1,"parents":[48,49],"dependencies":[1],"blockers":[],"cost":2,"display":{"row":27,"col":3,"icon":"node_1"},"properties":{"aoe":3},"effects":[{"type":"add_spell_prop","target_part":"Explosion Damage","base_spell":2,"multipliers":[50,0,0,0,30,0]},{"type":"add_spell_prop","behavior":"modify","target_part":"Total Damage","base_spell":2,"hits":{"Explosion Damage":1}}],"id":52},{"display_name":"Gust","desc":"Ice Snake will add +1 Winded to enemies and deal more damage.","base_abil":3,"archetype":"Riftwalker","archetype_req":7,"parents":[48,52],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","target_part":"Ice Snake Damage","base_spell":4,"multipliers":[0,0,0,0,0,20]}],"id":53},{"display_name":"Time Dilation","desc":"When sprinting, create an area that increases the speed of all allies the longer they run in it. (Step out or stop running to cancel)","archetype":"Riftwalker","archetype_req":7,"parents":[48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_2"},"properties":{},"effects":[],"id":54},{"display_name":"Better Ophanim","desc":"Increase your maximum orbs from Ophanim by +1.","archetype":"Light Bender","archetype_req":0,"base_abil":21,"parents":[52,49],"dependencies":[21],"blockers":[],"cost":1,"display":{"row":28,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Per Melee (max)","hits":{"Per Orb":1}}],"id":55},{"display_name":"Arctic Snake","desc":"Ice Snake will freeze enemies completely for 2s.","base_abil":3,"parents":[50],"dependencies":[3],"blockers":[],"cost":2,"display":{"row":28,"col":7,"icon":"node_1"},"properties":{},"effects":[],"id":56},{"display_name":"Devitalize","desc":"Enemies will deal -2% damage for every Winded they have.","base_abil":20,"archetype":"Riftwalker","archetype_req":5,"parents":[58,59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":57},{"display_name":"More Winded II","desc":"Incrase your maximum Winded by +5.","base_abil":20,"archetype":"Riftwalker","archetype_req":0,"parents":[54,59],"dependencies":[20],"blockers":[],"cost":1,"display":{"row":31,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":20,"name":"max","value":5}]},{"type":"stat_scaling","slider":true,"slider_name":"Winded","slider_max":5}],"id":58},{"display_name":"Dynamic Faith","desc":"For every 2% Sprint you have from items, gain +1% Thunder Damage (Max 100%)","parents":[58,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"sprint"}],"output":{"type":"stat","name":"tDamPct"},"scaling":[0.5],"max":100}],"id":59},{"display_name":"Divination","desc":"Increase your maximum orbs from Ophanim by +3 and reduce their damage.","base_abil":21,"archetype":"Light Bender","archetype_req":0,"parents":[59,61],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":32,"col":3,"icon":"node_2"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Per Orb","multipliers":[-30,0,-10,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Per Melee (max)","hits":{"Per Orb":3}}],"id":60},{"display_name":"Healthier Ophanim II","desc":"Increase the health of your orbs from Ophanim by +3000.","base_abil":21,"archetype":"Light Bender","archetype_req":0,"parents":[55],"dependencies":[40],"blockers":[],"cost":1,"display":{"row":31,"col":4,"icon":"node_0"},"properties":{},"effects":[],"id":61},{"display_name":"Sunflare","desc":"After healing 400% of your max health within 10s, your next Heal will make every nearby ally temporarily immune.","archetype":"Light Bender","archetype_req":12,"base_abil":2,"parents":[61],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":5,"icon":"node_3"},"properties":{"aoe":12,"duration":5},"effects":[],"id":62},{"display_name":"Larger Mana Bank III","desc":"Increase your maximum Mana Bank by +30.","archetype":"Arcanist","archetype_req":0,"base_abil":22,"parents":[56],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":31,"col":7,"icon":"node_0"},"properties":{},"effects":[],"id":63},{"display_name":"Arcane Overflow","desc":"Arcane Transfer will allow you to overflow your mana over its maximum limits.","archetype":"Arcanist","archetype_req":12,"base_abil":22,"parents":[63],"dependencies":[22],"blockers":[],"cost":2,"display":{"row":33,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":64},{"display_name":"Memory Recollection","desc":"Chaos Explosion will cast +2 spells.","archetype":"Arcanist","archetype_req":0,"base_abil":22,"parents":[64],"dependencies":[50],"blockers":[],"cost":1,"display":{"row":34,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":65},{"display_name":"Manastorm","desc":"If you have more than 100 Mana, casting a spell will give you +10 mana over 5s.","archetype":"Arcanist","archetype_req":1,"parents":[69,64,62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":66},{"display_name":"Better Lightweaver","desc":"Increase your Max Orbs by +2.","archetype":"Light Bender","archetype_req":0,"base_abil":43,"parents":[69,66],"dependencies":[43],"blockers":[],"cost":1,"display":{"row":35,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","target_part":"Orb Damage","base_spell":5,"hits":{"Single Orb":2}}],"id":67},{"display_name":"Timelock","desc":"Holding shift and casting Heal will absorb all Winded on nearby enemies and make you Timelocked. While Timelocked, your mana will not be depleted and you become immovable from outside forces. Enemies will recieve Winded damage from all absorbed stacks. (Max 30)","archetype":"Riftwalker","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":0,"icon":"node_3"},"properties":{},"effects":[],"id":68},{"display_name":"Cheaper Heal II","desc":"Reduce the Mana cost of Heal.","base_abil":2,"parents":[68,66],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":69}],"Assassin":[{"display_name":"Spin Attack","desc":"Slash rapidly around you, damaging enemies in a large area.","archetype":"","archetype_req":0,"parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Spin Attack","cost":45,"base_spell":1,"spell_type":"damage","scaling":"spell","use_atkspd":true,"display":"Spin Attack","parts":[{"name":"Spin Attack","type":"damage","multipliers":[120,0,30,0,0,0]}]}],"id":0},{"display_name":"Dagger Proficiency I","desc":"Increase your speed by +5% and improve your Main Attack’s damage when using a dagger.","archetype":"","archetype_req":0,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":5},{"type":"stat","name":"mdPct","value":5}]}],"id":1},{"display_name":"Cheaper Spin Attack","desc":"Reduce the Mana cost of Spin Attack.","archetype":"","archetype_req":0,"base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":2},{"display_name":"Double Spin","desc":"Spin Attack will activate a second time with a larger area of effect.","archetype":"","archetype_req":0,"base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Spin Attack":1},"display":"Total Damage"}],"id":3},{"display_name":"Poisoned Blade","desc":"For every 2% or 2 Raw Main Attack Damage you have from items, gain +5/3s Poison Damage (Max 50/3s)","archetype":"Shadestepper","archetype_req":0,"parents":[5],"dependencies":[],"blockers":[6],"cost":1,"display":{"row":7,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"mdPct"},{"type":"stat","name":"mdRaw"}],"output":[{"type":"stat","name":"poison"}],"scaling":[2.5,2.5],"max":50}],"id":4},{"display_name":"Dash","desc":"Dash in the direction you're facing.","archetype":"","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Dash","cost":20,"base_spell":2,"display":"Total Damage","parts":[{"name":"None","type":"damage","multipliers":[0,0,0,0,0,0]}]}],"id":5},{"display_name":"Double Slice","desc":"Your Main Attack will attack twice, but deal -40% damage per hit.","archetype":"Acrobat","archetype_req":0,"base_abil":999,"parents":[5],"dependencies":[],"blockers":[4],"cost":1,"display":{"row":7,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee","multipliers":[-40,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"display":"Total Damage","target_part":"Total Damage","hits":{"Melee":2}}],"id":6},{"display_name":"Smoke Bomb","desc":"Throw a bomb that slouly emits smoke, damaging all enemies in it every 0.5s.","archetype":"","archetype_req":0,"parents":[4,8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Smoke Bomb","cost":40,"base_spell":4,"display":"Total Damage","parts":[{"name":"Per Tick","type":"damage","multipliers":[25,5,0,0,0,5]},{"name":"Per Bomb","type":"total","hits":{"Per Tick":10}},{"name":"Total Damage","type":"total","hits":{"Per Bomb":1}}]}],"id":7},{"display_name":"Cheaper Dash","desc":"Reduce the Mana cost of Dash","archetype":"","archetype_req":0,"base_abil":5,"parents":[7,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":8},{"display_name":"Multihit","desc":"Unleash a rapid flurry of 8 hits to enemies facing you, dealing overwhelming damage","archetype":"","archetype_req":0,"parents":[6,8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6,"icon":"node_assassin"},"properties":{},"effects":[{"type":"replace_spell","name":"Multihit","cost":45,"base_spell":3,"display":"Total Damage","parts":[{"name":"Per Hit","type":"damage","multipliers":[25,0,0,10,0,0]},{"name":"Total Damage","type":"total","hits":{"Per Hit":8}}]}],"id":9},{"display_name":"Earth Mastery","desc":"Increases base damage from all Earth attacks","archetype":"Shadestepper","archetype_req":0,"base_abil":998,"parents":[7,11],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":10},{"display_name":"Thunder Mastery","desc":"Increases base damage from all Thunder attacks","archetype":"Shadestepper","archetype_req":0,"base_abil":998,"parents":[10,7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":11},{"display_name":"Fire Mastery","desc":"Increases base damage from all Fire attacks","archetype":"Trickster","archetype_req":0,"base_abil":998,"parents":[8,13],"dependencies":[],"blockers":[],"cost":1,"display":{"row":14,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":12},{"display_name":"Water Mastery","desc":"Increases base damage from all Water attacks","archetype":"Acrobat","archetype_req":0,"base_abil":998,"parents":[8,9,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":13},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Acrobat","archetype_req":0,"base_abil":998,"parents":[13,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":14},{"display_name":"Backstab","desc":"Multihit will deal a single devastating hit. If you strike the enemy from behind, deal double damage","archetype":"Shadestepper","archetype_req":2,"base_abil":9,"parents":[10,11],"dependencies":[9],"blockers":[44,16],"cost":2,"display":{"row":15,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Backstab","base_spell":3,"display":"Backstab Damage","parts":[{"name":"Backstab Damage","type":"damage","multipliers":[200,50,0,0,0,0]}]}],"id":15},{"display_name":"Fatality","desc":"Multihit will deal an additional final slash","archetype":"","archetype_req":0,"base_abil":9,"parents":[13,14],"dependencies":[9],"blockers":[15],"cost":2,"display":{"row":15,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fatality","multipliers":[100,0,0,0,0,50]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Fatality":1}}],"id":16},{"display_name":"Vanish","desc":"Dash will vanish you into the shadows and make you invisible to enemies (5s Cooldown). You cannot heal or gain mana while in that state (Attack or get hit to cancel)","archetype":"","archetype_req":0,"base_abil":5,"parents":[15,18],"dependencies":[5],"blockers":[],"cost":2,"display":{"row":16,"col":2,"icon":"node_2"},"properties":{"duration":5,"cooldown":5},"effects":[],"id":17},{"display_name":"Sticky Bomb","desc":"Smoke Bomb will stick to enemies and deal additional damage","archetype":"Trickster","archetype_req":0,"base_abil":7,"parents":[17,12],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[0,0,0,0,10,0]}],"id":18},{"display_name":"Righting Reflex","desc":"When you hold shift while airborne, slowly glide and become immune to fall damage (Max 5s)","archetype":"Acrobat","archetype_req":0,"parents":[16],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6,"icon":"node_2"},"properties":{},"effects":[],"id":19},{"display_name":"Surprise Strike","desc":"While using Vanish, your next attack will deal +60% more damage for a single hit only","archetype":"Shadestepper","archetype_req":3,"base_abil":5,"parents":[17],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":19,"col":2,"icon":"node_3"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Surprise Strike","bonuses":[{"type":"stat","name":"damMult.SurpriseStrike","value":60}]}],"id":20},{"display_name":"Mirror Image","desc":"After leaving Vanish, summon 3 Clones that will follow you and protect you. (20s Cooldown)","archetype":"Trickster","archetype_req":2,"base_abil":5,"parents":[18],"dependencies":[17],"blockers":[22],"cost":2,"display":{"row":19,"col":4,"icon":"node_3"},"properties":{"clone":3},"effects":[{"type":"raw_stat","toggle":"Activate Clones","bonuses":[{"type":"stat","name":"defMult.Clone","value":80}]}],"id":21},{"display_name":"Lacerate","desc":"Spin Attack will lunge you forward, deal 3 strikes and lunge you forward again.","archetype":"Acrobat","archetype_req":2,"base_abil":0,"parents":[16],"dependencies":[],"blockers":[21],"cost":2,"display":{"row":19,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Lacerate","base_spell":1,"display":"Total Damage","parts":[{"name":"Per Hit","type":"damage","multipliers":[50,0,0,10,0,20]},{"name":"Total Damage","type":"total","hits":{"Per Hit":3}}]}],"id":22},{"display_name":"Silent Killer","desc":"After killing an enemy, reset Vanish's cooldown","archetype":"","archetype_req":0,"base_abil":5,"parents":[20],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":20,"col":1,"icon":"node_2"},"properties":{},"effects":[],"id":23},{"display_name":"Shenanigans","desc":"For every 2% Stealing you have from items, gain +1/3s Mana Steal (Max 8/3s)","archetype":"Trickster","archetype_req":0,"parents":[21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":20,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"eSteal"}],"output":[{"type":"stat","name":"ms"}],"scaling":[0.5],"max":8}],"id":24},{"display_name":"Wall of Smoke","desc":"Smoke Bomb will throw +2 bombs, damaging more often in a larger area","archetype":"","archetype_req":0,"base_abil":7,"parents":[22],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":20,"col":8,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Per Bomb":2}},{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[-20,0,0,0,0,0]}],"id":25},{"display_name":"Better Smoke Bomb","desc":"Increase the range and area of effect of Smoke Bomb","archetype":"","archetype_req":0,"base_abil":7,"parents":[23,27],"dependencies":[7],"blockers":[],"cost":1,"display":{"row":22,"col":0,"icon":"node_0"},"properties":{},"effects":[],"id":26},{"display_name":"Shadow Travel","desc":"Vanish will increase your speed by +100%","archetype":"Shadestepper","archetype_req":0,"base_abil":5,"parents":[26,23,28],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_1"},"properties":{},"effects":[],"id":27},{"display_name":"Cheaper Multihit","desc":"Reduce the Mana cost of Multihit","archetype":"","archetype_req":0,"base_abil":9,"parents":[24,27,29],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":28},{"display_name":"Dagger Proficiency II","desc":"Increase your Main Attack's range and add +5 raw damage to all attacks","archetype":"","archetype_req":0,"base_abil":999,"parents":[28,25],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"damRaw","value":5}]}],"id":29},{"display_name":"Last Laugh","desc":"When losing a Clone, it will cast Spin Attack before dying","archetype":"Trickster","archetype_req":3,"base_abil":5,"parents":[27,28],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":23,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":30},{"display_name":"Cheaper Smoke Bomb","desc":"Reduce the Mana cost of Smoke Bomb","archetype":"","archetype_req":0,"base_abil":7,"parents":[26,32],"dependencies":[7],"blockers":[],"cost":1,"display":{"row":25,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":31},{"display_name":"Blazing Powder","desc":"Spin Attack will blind enemies and deal additional damage","archetype":"","archetype_req":0,"base_abil":0,"parents":[31,27,28],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":3,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Spin Attack","multipliers":[0,0,0,0,20,0]}],"id":32},{"display_name":"Weightless","desc":"When you hit an enemy while airborne, gain +0.5 Mana (1.25+ blocks off the ground to be airborne)","archetype":"Acrobat","archetype_req":4,"parents":[28,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7,"icon":"node_2"},"properties":{},"effects":[],"id":33},{"display_name":"Black Hole","desc":"Smoke Bomb will pull nearby enemies","archetype":"","archetype_req":0,"base_abil":7,"parents":[31,32],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":34},{"display_name":"Sandbagging","desc":"Anytime you get hit for less than 5% of your max hp, reduce your abilities cooldown by -2s. (1s Cooldown)","archetype":"Trickster","archetype_req":0,"parents":[32,36],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":35},{"display_name":"Hop","desc":"When you double tap jump, leap forward. (2s Cooldown)","archetype":"Acrobat","archetype_req":0,"parents":[35,33],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":6,"icon":"node_1"},"properties":{"cooldown":2},"effects":[],"id":36},{"display_name":"Dancing Blade","desc":"Deal damage to mobs you Dash through","archetype":"","archetype_req":0,"base_abil":5,"parents":[33],"dependencies":[5],"blockers":[],"cost":2,"display":{"row":26,"col":8,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Dancing Blade","multipliers":[80,0,0,0,0,20],"display":"Dancing Blade"}],"id":37},{"display_name":"Violent Vortex","desc":"If you deal more damage than 2x of your max health in a single hit, deal 20% of the damage to other nearby enemies","archetype":"Shadestepper","archetype_req":0,"parents":[31],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Violent Vortex","base_spell":5,"display":"Total Damage","parts":[{"name":"Total Damage","type":"damage","multipliers":[0,0,0,0,0,0]}]}],"id":38},{"display_name":"Delirious Gas","desc":"While inside Smoke Bomb, increase your damage by +40% and gain Lure for 20s","archetype":"Trickster","archetype_req":4,"base_abil":7,"parents":[35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":27,"col":3,"icon":"node_2"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Delirious Gas","bonuses":[{"type":"stat","name":"damMult.DeliriousGas","value":40}]}],"id":39},{"display_name":"Marked","desc":"Smoke Bomb will add +1 Mark to enemies it hits. (Max 5, 0.5s Cooldown) Marked enemies will take +10% damage for each mark they have.","archetype":"Shadestepper","archetype_req":5,"parents":[38],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":1,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Marked","slider_step":1,"slider_max":5,"output":[{"type":"stat","name":"damMult.Marked"}],"scaling":[10]}],"id":40},{"display_name":"Echo","desc":"Your Clones will mimic your spells and abilities. While they are active, deal -60% damage.","archetype":"Trickster","archetype_req":6,"base_abil":5,"parents":[35,42],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":28,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Clones","bonuses":[{"type":"stat","name":"damMult.Echo","value":-60}]}],"id":41},{"display_name":"Shurikens","desc":"After using Dash, your next Main Attack will throw 3 shurikens","archetype":"Acrobat","archetype_req":0,"base_abil":5,"parents":[41,43],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":6,"icon":"node_2"},"properties":{},"effects":[{"type":"replace_spell","name":"Shurikens","base_spell":6,"display":"Total Damage","parts":[{"name":"Per Shuriken","type":"damage","multipliers":[90,0,0,0,10,0]},{"name":"Total Damage","type":"total","hits":{"Per Shuriken":3}}]}],"id":42},{"display_name":"Far Reach","desc":"Increase the range of Multihit","archetype":"","archetype_req":0,"base_abil":9,"parents":[37,42],"dependencies":[],"blockers":[],"cost":1,"display":{"row":28,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":43},{"display_name":"Stronger Multihit","desc":"Increases Multihit's amount of hits by +3","archetype":"","archetype_req":0,"base_abil":9,"parents":[41,42],"dependencies":[],"blockers":[15],"cost":1,"display":{"row":29,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Per Hit":3}}],"id":44},{"display_name":"Psithurism","desc":"Increase your Walk Speed by +20% and your Jump Height by +1","archetype":"Acrobat","archetype_req":5,"parents":[42,43],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":20},{"type":"stat","name":"jh","value":1}]}],"id":45},{"display_name":"Ambush","desc":"Increase Surprise Strike's damage by +40%","archetype":"Shadestepper","archetype_req":4,"base_abil":5,"parents":[40],"dependencies":[20],"blockers":[],"cost":1,"display":{"row":31,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Surprise Strike","bonuses":[{"type":"stat","name":"damMult.SurpriseStrike","value":40}]}],"id":46},{"display_name":"Cheaper Dash 2","desc":"Reduce the Mana cost of Dash","archetype":"","archetype_req":0,"base_abil":5,"parents":[41],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":47},{"display_name":"Parry","desc":"After dodging damage, if you cast a spell within 1.5s, it will be free. (3s Cooldown)","archetype":"Acrobat","archetype_req":5,"parents":[49],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":6,"icon":"node_2"},"properties":{},"effects":[],"id":48},{"display_name":"Cheaper Spin Attack 2","desc":"Reduce the Mana cost of Spin Attack","archetype":"","archetype_req":0,"base_abil":0,"parents":[43,48],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":49},{"display_name":"Death Magnet","desc":"After leaving Vanish, pull all nearby Marked mobs towards you","archetype":"Shadestepper","archetype_req":5,"base_abil":5,"parents":[51,46],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":33,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":50},{"display_name":"Cheaper Multihit 2","desc":"Reduce the Mana cost of Multihit","archetype":"","archetype_req":0,"base_abil":9,"parents":[50,46,52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":51},{"display_name":"Hoodwink","desc":"When hitting enemies with Spin Attack, shorten the duration of your negative effects by 30% and transfer it onto enemies Lure can be transferred to the feeble minded. (Bosses and special enemies are immune)","archetype":"Trickster","archetype_req":1,"base_abil":0,"parents":[51,47,53],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":33,"col":4,"icon":"node_1"},"properties":{},"effects":[],"id":52},{"display_name":"Choke Bomb","desc":"Smoke Bomb will slow down enemies while in the smoke","archetype":"Trickster","archetype_req":0,"base_abil":7,"parents":[52,54,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":33,"col":6,"icon":"node_1"},"properties":{},"effects":[],"id":53},{"display_name":"Wall Jump","desc":"Reduce Hop's cooldown by 1s. When you Hop into a wall, bounce backward. (Hold shift to cancel)","archetype":"Acrobat","archetype_req":5,"parents":[53,49],"dependencies":[36],"blockers":[],"cost":2,"display":{"row":33,"col":8,"icon":"node_1"},"properties":{},"effects":[],"id":54},{"display_name":"Fatal Spin","desc":"Spin Attack will add +1 Mark to all enemies it hits and gain additional area of effect","archetype":"Shadestepper","archetype_req":8,"base_abil":0,"parents":[50,51],"dependencies":[40],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":55},{"display_name":"Stronger Lacerate","desc":"Lacerate will deal +1 slash","archetype":"Acrobat","archetype_req":0,"base_abil":0,"parents":[53,54],"dependencies":[22],"blockers":[],"cost":1,"display":{"row":34,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Per Hit":1}}],"id":56},{"display_name":"Stronger Vortex","desc":"If you deal more damage than 3x of your max health in a single hit, deal 60% of the damage to other nearby enemies","archetype":"Shadestepper","archetype_req":4,"parents":[55],"dependencies":[38],"blockers":[],"cost":1,"display":{"row":35,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"replace_spell","name":"Violent Vortex","base_spell":5,"display":"Total Damage","parts":[{"name":"Total Damage","type":"damage","multipliers":[0,0,0,0,0,0]}]}],"id":57},{"display_name":"Harvester","desc":"After killing an enemy, gain +5 Mana for each leftover Marks it had","archetype":"Shadestepper","archetype_req":0,"parents":[55,59],"dependencies":[40],"blockers":[],"cost":2,"display":{"row":37,"col":1,"icon":"node_2"},"properties":{},"effects":[],"id":58},{"display_name":"Cheaper Smoke Bomb 2","desc":"Reduce the Mana cost of Smoke Bomb","archetype":"","archetype_req":0,"base_abil":7,"parents":[58,52,60],"dependencies":[7],"blockers":[],"cost":1,"display":{"row":37,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":59},{"display_name":"Blade Fury","desc":"Multihit will be easier to aim and enemies hit will stay locked in front of you","archetype":"Acrobat","archetype_req":0,"base_abil":9,"parents":[56,59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7,"icon":"node_1"},"properties":{},"effects":[],"id":60},{"display_name":"More Marks","desc":"Add +2 max Marks","archetype":"Shadestepper","archetype_req":0,"base_abil":40,"parents":[58,59],"dependencies":[40],"blockers":[],"cost":1,"display":{"row":38,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Marked","slider_max":2}],"id":61},{"display_name":"Stronger Clones","desc":"Improve your damage while your Clones are active by +20%","archetype":"Trickster","archetype_req":7,"base_abil":5,"parents":[59,60],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":38,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Clones","bonuses":[{"type":"stat","name":"damMult.Echo","value":20}]}],"id":62},{"display_name":"Ricochets","desc":"When hitting an enemy with your Shurikens, they will bounce to the nearest enemy","archetype":"Acrobat","archetype_req":6,"base_abil":5,"parents":[60],"dependencies":[42],"blockers":[],"cost":2,"display":{"row":38,"col":8,"icon":"node_1"},"properties":{},"effects":[],"id":63},{"display_name":"Satsujin","desc":"If an enemy has 3 Marks and 70% of their health or more, your next Multihit or Main Attack will deal triple damage. (30s Cooldown, per enemy)","archetype":"Shadestepper","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":1,"icon":"node_3"},"properties":{},"effects":[{"type":"raw_stat","toggle":"Activate Satsujin","bonuses":[{"type":"stat","name":"damMult.Satsujin:3.Backstab Damage","value":200},{"type":"stat","name":"damMult.Satsujin:3.Per Hit","value":200},{"type":"stat","name":"damMult.Satsujin:3.Fatality","value":200},{"type":"stat","name":"damMult.Satsujin:0.Melee","value":200}]}],"id":64},{"display_name":"Forbidden Art","desc":"Summon +3 additional Clones. (+20s Cooldown)","archetype":"Trickster","archetype_req":8,"base_abil":5,"parents":[59],"dependencies":[21],"blockers":[],"cost":2,"display":{"row":39,"col":4,"icon":"node_2"},"properties":{},"effects":[],"id":65},{"display_name":"Diversion","desc":"Anytime a Lured enemy gets killed, every nearby ally gets +40% health as extra overflowing health. (3s Cooldown). Decay -4% of the bonus every second.","archetype":"Trickster","archetype_req":11,"base_abil":7,"parents":[65],"dependencies":[39],"blockers":[],"cost":2,"display":{"row":40,"col":5,"icon":"node_3"},"properties":{},"effects":[],"id":66},{"display_name":"Jasmine Bloom","desc":"After spending 40 Mana, bloom an area under you that damages enemies below it every 0.4s After every bloom, reset the duration and increase the radius (Max 10 Blocks)","archetype":"Acrobat","archetype_req":12,"parents":[60],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Jasmine Bloom","base_spell":7,"display":"Per Hit","parts":[{"name":"Per Hit","type":"damage","multipliers":[60,5,0,15,0,0]}]}],"id":67},{"display_name":"Better Ricochets","desc":"Add +1 Max Bounce to Ricochets","archetype":"Acrobat","archetype_req":0,"base_abil":5,"parents":[67],"dependencies":[63],"blockers":[],"cost":1,"display":{"row":40,"col":8,"icon":"node_0"},"properties":{},"effects":[],"id":68},{"display_name":"Devour","desc":"Harvester will give +5 Mana","archetype":"Shadestepper","archetype_req":0,"parents":[64],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":41,"col":0,"icon":"node_0"},"properties":{},"effects":[],"id":69},{"display_name":"Better Marked","desc":"Increase Marked's damage bonus by +5%","archetype":"","archetype_req":0,"base_abil":40,"parents":[64],"dependencies":[],"blockers":[],"cost":1,"display":{"row":41,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Marked","output":[{"type":"stat","name":"damMult.Marked"}],"scaling":[5]}],"id":70}]} \ No newline at end of file From 74b1d480faeffc839b90880504beab3f9fbf502b Mon Sep 17 00:00:00 2001 From: hppeng Date: Mon, 25 Jul 2022 08:58:41 -0700 Subject: [PATCH 10/19] Don't update the ability tree when NONE weapon is selected so u can delete weapon and switch to the same class weapon and keep tree --- js/atree.js | 1 - js/builder_graph.js | 1 + js/computation_graph.js | 11 ++++------- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/js/atree.js b/js/atree.js index 4a9d492..519349b 100644 --- a/js/atree.js +++ b/js/atree.js @@ -199,7 +199,6 @@ const atree_node = new (class extends ComputeNode { const atree_render = new (class extends ComputeNode { constructor() { super('builder-atree-render'); - this.fail_cb = true; this.UI_elem = document.getElementById("atree-ui"); this.list_elem = document.getElementById("atree-header"); } diff --git a/js/builder_graph.js b/js/builder_graph.js index b2ed8cb..0ae6fbd 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -429,6 +429,7 @@ class PlayerClassNode extends ValueCheckComputeNode { compute_func(input_map) { if (input_map.size !== 1) { throw "PlayerClassNode accepts exactly one input (build)"; } const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + if (build.weapon.statMap.has('NONE')) { return null; } return wep_to_class.get(build.weapon.statMap.get('type')); } } diff --git a/js/computation_graph.js b/js/computation_graph.js index d144e29..bd46036 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -135,7 +135,7 @@ class ComputeNode { } class ValueCheckComputeNode extends ComputeNode { - constructor(name) { super(name); } + constructor(name) { super(name); this.valid_val = null; } /** * Request update of this compute node. Pushes updates to children, @@ -154,14 +154,11 @@ class ValueCheckComputeNode extends ComputeNode { calc_inputs.set(this.input_translation.get(input.name), input.value); } let val = this.compute_func(calc_inputs); - if (val !== this.value) { - super.mark_dirty(2); - } - else { - console.log("soft update"); + if (val !== null) { + if (val !== this.valid_val) { super.mark_dirty(2); } // don't mark dirty if NULL (no update) + this.valid_val = val; } this.value = val; - this.dirty = 0; for (const child of this.children) { child.mark_input_clean(this.name, this.value); From 9d5339b2550b8ff51e82ad9bb4ff7621be06db69 Mon Sep 17 00:00:00 2001 From: fin444 Date: Mon, 25 Jul 2022 12:00:43 -0400 Subject: [PATCH 11/19] potential fix for atree image misalignment --- js/atree.js | 24 +++++++++++++----------- media/atree/connectors.png | Bin 1824 -> 4539 bytes media/atree/icons.png | Bin 2891 -> 9996 bytes 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/js/atree.js b/js/atree.js index 0033921..e43e743 100644 --- a/js/atree.js +++ b/js/atree.js @@ -372,11 +372,11 @@ const atree_validate = new (class extends ComputeNode { const abil = node.ability; if (atree_state.get(abil.id).active) { atree_to_add.push([node, 'not reachable', false]); - atree_state.get(abil.id).img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[abil.display.icon], 2], [9, 3]); + atree_state.get(abil.id).img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[abil.display.icon], 2], atreeNodeAtlasSize); } else { atree_not_present.push(abil.id); - atree_state.get(abil.id).img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[abil.display.icon], 0], [9, 3]); + atree_state.get(abil.id).img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[abil.display.icon], 0], atreeNodeAtlasSize); } } @@ -425,7 +425,7 @@ const atree_validate = new (class extends ComputeNode { const node = atree_state.get(node_id); const [success, hard_error, reason] = abil_can_activate(node, atree_state, reachable, archetype_count, ap_left); if (success) { - node.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[node.ability.display.icon], 1], [9, 3]); + node.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[node.ability.display.icon], 1], atreeNodeAtlasSize); } } @@ -973,7 +973,7 @@ function render_AT(UI_elem, list_elem, tree) { const parent_id = parent_abil.id; let connect_elem = document.createElement("div"); - connect_elem.style = "width: 112.5%; height: 112.5%; position: absolute; top: -5.55%; left: -5.55%; image-rendering: pixelated; background-image: url('../media/atree/connectors.png'); background-size: 1200% 400%;" + connect_elem.style = "width: 112.5%; height: 112.5%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); image-rendering: pixelated; background-image: url('../media/atree/connectors.png'); background-size: 1700% 500%;" // connect up for (let i = ability.display.row - 1; i > parent_abil.display.row; i--) { const coord = i + "," + ability.display.col; @@ -1009,7 +1009,7 @@ function render_AT(UI_elem, list_elem, tree) { // create node let node_elem = document.getElementById("atree-row-" + ability.display.row).children[ability.display.col]; - node_wrap.img = make_elem("div", [], {style: "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1; background-image: url('../media/atree/icons.png'); background-size: 900% 300%;"}) + node_wrap.img = make_elem("div", [], {style: "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1; background-image: url('../media/atree/icons.png'); background-size: 1700% 500%;"}) node_elem.appendChild(node_wrap.img); // create hitbox @@ -1231,11 +1231,11 @@ function atree_set_state(node_wrapper, new_state) { } if (new_state) { node_wrapper.active = true; - node_wrapper.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[icon], 2], [9, 3]); + node_wrapper.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[icon], 2], atreeNodeAtlasSize); } else { node_wrapper.active = false; - node_wrapper.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[icon], 1], [9, 3]); + node_wrapper.img.style.backgroundPosition = atlasBGPositionCalc([atreeNodeAtlasPositions[icon], 1], atreeNodeAtlasSize); } let atree_connectors_map = node_wrapper.all_connectors_ref; for (const parent of node_wrapper.parents) { @@ -1264,6 +1264,7 @@ const atreeConnectorAtlasPositions = { "1011": {"0000": [5, 2], "1011": [6, 2], "1010": [7, 2], "1001": [8, 2], "0011": [9, 2]}, "1111": {"0000": [0, 3], "1111": [1, 3], "1110": [2, 3], "1101": [3, 3], "1100": [4, 3], "1011": [5, 3], "1010": [6, 3], "1001": [7, 3], "0111": [8, 3], "0110": [9, 3], "0101": [10, 3], "0011": [11, 3]} } +const atreeConnectorAtlasSize = [17, 5] // just has the x position, y is based on state const atreeNodeAtlasPositions = { "node_0": 0, @@ -1276,6 +1277,7 @@ const atreeNodeAtlasPositions = { "node_assassin": 7, "node_shaman": 8 } +const atreeNodeAtlasSize = [17, 5] function atlasBGPositionCalc(pos, atlasSize) { // https://css-tricks.com/focusing-background-image-precise-location-percentages/ // p = (c + 0.5/z - 0.5) * z/(z - 1) + 0.5 @@ -1293,7 +1295,7 @@ function atree_render_connection(atree_connectors_map) { let connector_elem = connector_info.connector; set_connector_type(connector_info); connector_info.highlight = [0, 0, 0, 0]; - connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[connector_info.type]["0000"], [12, 4]); + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[connector_info.type]["0000"], atreeConnectorAtlasSize); let target_elem = document.getElementById("atree-row-" + i.split(",")[0]).children[i.split(",")[1]]; if (target_elem.children.length != 0) { // janky special case... @@ -1345,15 +1347,15 @@ function atree_set_edge(atree_connectors_map, parent, child, state) { for (let i = 0; i < 4; i++) { render += highlight_state[i] === 0 ? "0" : "1"; } - connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][render], [12, 4]); + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][render], atreeConnectorAtlasSize); continue; } else { // lol bad overloading, [0] is just the whole state highlight_state[0] += state_delta; if (highlight_state[0] > 0) { - connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][ctype], [12, 4]); + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][ctype], atreeConnectorAtlasSize); } else { - connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype]["0000"], [12, 4]); + connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype]["0000"], atreeConnectorAtlasSize); } } } diff --git a/media/atree/connectors.png b/media/atree/connectors.png index 99c9aa90281f2f9e12832bfa655381803af6593c..ba0f6c9567d002b64f755a67f659f4b923ebe779 100644 GIT binary patch literal 4539 zcmbtXhdUcw)Q@_#_TD2!5nF94c2ub}_6}ae-ZM6B)oKN)U9_sy3PEY5)|=WRR8dqB zp;WE3s8wJ3eSgAt?{n{SpYuHT+;g6De!p{WhWR~XCVGB)006*batC4w08lAW&W^NK zDZ3W;!flEm)z{F_+{DmOI3z6C%QpZ407&L0k0O|PMaD@Ge?<+4w;w9rf1U1 zTlS0EO9rOe3Yp1Y+?QZ7$;^7W;ux~;f&IQhY^Z-wFHD=5tZ_?Is`F09tgLB&P@m*PD*}d?j zY!~rhrCty_L2;=$T|K=PH0iSSnBU*$g$@P?7Cs_sFNq*Q~#!TKYC*3SFT|u z%dKU<>t?HDO_U<0SNc6w@PylH1c%znsLpnRlKz!!YOH#9FPt}2RwP9XGW7bzV|rPL z@5~*=+{%lhw4|W}(6BhVp7C}zjr9wIu=ACqeVkM4P0dw_RCm3(tiYtlfIj$hhmM^+ zkHw&Jz#s1wQ~$GiCbtdruUMxnL8^qs-u_4@^l|d2klTt$A>(lFxk{=56;k(Q@CemK zhaRD1(aHaM4U68RPRDL4X6BSBQAAVRdmOH{BZ<&!l4VogM3%TPE4gkpC_`9f(uXLj*!fO#GP@`)b*Z8OzuQyZsl~6={5k~eAhQWb> zh@c37VHo1y2!y9_v~Pruu(1i$+%AQg2LNEnGlA$^BNukySUDOs7gXv5! zYC_<0x0Mat2Ch8KU&b-7{9U;t$fm3t4o~PbMA4Qy1%vvLW)4lp5Xds7JIWmyr4#=Q z{Hx=&*4xUd;ZPNpK?vkSXn#mxP*)l0Y%jy(^jP=LR}o>&_Ks!f?U!t977?z)2ZoT% zrh=7u52p8ph{hEYHsiW>KoFY%kCBu+6#08OUi2SfPe5}U#|zF^h@Su$WQgP@HLzAp za7O}9ou2OO8nHlhvez;)eB;iR#1@xsbX#Wx@~m97^JwXjS5UYGMIz5X>;qm80am8u zjQ}X~p}()2&7|&x^AO%jO6Q|=v>w^&3S1(|%bZReR3jR6(G-uFa(eiz8b7RcQXsaO?LzjU#B@h|Yt(o=L_ z#G?Xq)ozQ2GpFaRL2q1K8}PyPhxl^Q8d_0-5HRwyNX!w(ksVPvo|&hne~F>zOOm!oo@Bvw3< zMrq;V#IG%jZzCLxR@N(92~}TD_Cu9!X;!-w*qmZCw3vu=Y`|%{jKMNkPynck5*dfU z(sqOW(4>>?a1akq7F{b0sWlsz}YT$sGooxc;Z z`e&D0IngGyA&B+eKaEO7pNfZVFUG8|KcI^2$A+O$=Vf4m40X$Hm{r&aRbm3UxYNBw ztt((%I_lz3;B-0ZQn|Fy_X}p3kB4SzUJ7CG1+m*H-4U6DsK!lqC$nln=JEZr#64lQ)iDUIvFHIgjs0GJHCAPtP2t!aXAx~2xEt9_xCPBqwvzg`IYNHM55o z<*UMPQU0xu>z&Jwq|IOpiCOs+ak^q|Vf1S@wu_U&J(nw<_nmu>8y{L)-<68tK6DZh zJn!f<-ESQ6LZ(ZEiIjnS18lrFIyhr8*QdDVJp{dz^@p5=g!kU6ZplWF<3nR&rslsU zs$(0cX%1MSBuBXPzBuBC{iM2&K@76&ajKas@zgyc?0jwGzWp*(>X+UFRVCUkF(-bx#PE_9;;Slw>2V*#z^{wxf4VekqkAxUi6?jFRHS47R+{(X8xjkZn$^gROkw z?=L6qF69!L*OGIn!t2VdY*)kXt2+ox8?fPDqB6LyJHOH!N~Wo~EShe=TjICvvH! za8W^8)H8kfjkhm_ZRl_iR+|bKV}>z#$T-v>#uHaUG9Fy}uKZ&wwvwxO?~&0Tt;OY1 zHCg^Shlgji<&yf_s!TCnWp-=oK^P&y3H#+LBzJIesY%)c{r<+;!QRP&($BmB3HGt0 z;v+20qJ;(6fy|ilLLG!hNhD%@IKW9m&4#gaq=W~isb7QM*_h77!WYocZ{9mAIc7FF zjrSGdEwh0ZIS0RaS^ZX{Y`i3ZQAl3Lu-(zMut=s=I=-~_bAXJz=a13}iAS;QxT_Nv zOMzQ{IZ)T@uHD)u^w4p=S&7H;<(QDN84j6;#`OFZt50^wVD32c z;%`;Hd4T`ugEheg-0vKtsFd&wgX(v-kUeLv47eR9lYft9jV#dOT1;Q+Eb1Mt#vG8j zB5OFzn%+I!|Lj6n17{1!Zcx(g&7_YS zyqYPSkFVJ)E}ZGW-9Jlv}P1+u?M_{=-o@?fpzPi zIY^>(>Px@eM&N!)I6Ea)NtO^5m#UiA=OfdCDUi&NA+#h=jKfw6^r6{o&X?ketD&!d z;-d^m^YSYh^bS5J!DnaLcq;2GO3{UhU91QZK_YVDyoRbN-yu&yK2n~lJ7Mc-AilH4 zcMGaYW7j_c(4?(3}kzTEB5{w+Qd|7;SAgH88qruOR0k3L+e0R)p z{Ti}lb5oUm@szso=Xuw7^ymll&aREKRpB_g4vqzv7}^OtG})a2I!UXCdOJf4Le84Y z_kQsb6*b3C_W`i|k9q0OW+z0V{<~dX>B9r7i&7=63Q&E_P|x%}?Kau^u$|RDh|lge z{O^dN_oXOx0W~)Z=Kic}&x@Z)7p)xl8})^hN^W5@3MQ_;O8bnoY-6wwbo0K<`?r&t z=O=A8X*Kn%8eB{CHN*jE`EDBTzv5gLdYsTW-%;NrRN8alfdbL607R$@%=1$~+APhT z$ZXCw>i5x&8HZ8bm)$K~FNgiN7C2!eQB*MVA+?UL_mFzuVu0Gof4goK_%G-%#m!nI z2@*&gy2aA)aZN8!fY5tiUM|kT7L%_cwP)2BT1Cl4dD69=D~|G>ff0f<)*htt7dqK+vziudKc9K$$6w*J~nyx$F2jxN4{p>9_V}S z9aaigfx$5e32jy+IemDOatohy>#h0=*>*Tl`z z<)Xb$gLm%Ek`SU?X)bF;w_1Sy5yUr-uk)a`NO-RzMesQ*uD&Ok#>KHESBQ(+oXKH``P-c!i8#$KyhrEg9Wk=PvL>}@q^G5uN?1~pN(n|e4@hen6-^Egm zieWVrnr;q+52xQSji=WVYzCSh-T20odY9$E?bm_3;ttjPSyyiydNd$y2(W8=S zJjjVhE9nM4zP3}s!qO}(a-(D^E<7n(;P83lRuLyS-Mjl>Q`>TM!`BHX#&iDBQPz|F z2k&GAjInSP8pBFh%k<`26jy*@?Hc+Gs>`}U* delta 1808 zcmV+r2k-d1BcKjGiBL{Q4GJ0x0000DNk~Le0002k0000;1Oos70A^?=od5s;1ZP1_ zK>z@;j|==^1poj50drDELIAGL9O;wn0VjU|32;bRa{vG?BLDy{BLR4&KXw2B02okA zR7FQ{OeG~JH>|ZbHaI?lhb=BJU)JA1LQ2cMA2R>|00DGTPE!Ct=GbNc00v)4L_t(o z!?jsWZ`(K&G@$5SAvSs^1f3mFg0HniXRi@tV(&u0^A;}PzrXL3w)Nr4bxH)y_+62PMlo?pDN=T|(9gS>N1p zRMy$@VC^cYge)l|S^AcFdfH==H&B0_SfPx7|FAb8lGQ?1S#K+Et#i(}I(N?dHlt;9 zt`0gQAj`b}K(Zcdpa+p0N_SyPD(_{M?oh(7UE9eD1scbDpZRv zCiHR5EQY^PzhKO!l?To1;B4e-vrWq@V)7cFZ$Or5a}Kyd$Q?YGwfI`VgSmf3-;eGm zJTCl^mIi9DL#GpJbVXYX)7S+}2x@K16G=-Zd)*P$cMA(y!%{W?&J|JE(p4Xqk7IYD z1+!Rlwm&oadg4-l3Yl%QlRYDN4=@sctK~Z!XhtyhPv~o{?`&!5ljw9^y6ZPF?JP6H zM~stx;}rboSy;m+NL(YGoYG1ie9cT*y4r}GpPW{L7ZBO5YM89cB_94$9yAwoK zN~UvufaB73@f|Xe>@X~LI$cFolk7VXKvaXf&=yYpN+DbQT*$TS1N^1^J zktIqNbs*J9)V$=)59t4}r6XaCkyIm##Pcr4cF_NdZ2Q#E6J(>cO6Px<@!4i|M5`lS zz$o=J+2|V|!8rAGWcr>)@bzR}Cg+1=5cxT(4qa0HoSMVV=xs;f=*kh0l+@s+upw=L6Nxydrz&+9IHl}WuUb9{SGME1WYa|sd))G*9=<}PZDZW z;TvG~l3ueOA{i^>3#xxQMqV&ODQ6@SKDlPtL_A;5xuau2?ruR2DXe+N`O|KT#iS!x zgOOTNrIAPpWK=U+M!N&Se?8fQ#q&+x5O797@d+ucdB+HoeLq)Lmj6Io`1YW1l&WM38KLA9=ZF{pK}ytdL{zP3DUiiSHdbP9B-Z zr1)+}n!BBo<79v2fFl^EmWl7Y99KGGnGeA@b=MzWj+?V`JU)ruN&P8Vv1A^Djy0#X&$|5L1;z zrj#1}A{Zx><6r#MF|S z1*v5=`4KC9&t)&s!-q8Uo}4~LRy;)py1Y?2&Q*V)^X+ZwQWLbMI1E#LJ{aecG90m} z)Od{v8+bwf%A#Zo06R1Q;Fkp)7#yT~b(CHx>kRx=3IQwi20e96&e7cfx(~krVycQ60F5bsp{T=``?oI=)9fSA45$;9z*x;$R>ZutR1%-fezm5?uzVr$WgEB~Q zOvMPgY`*hW2c7DXG%cM1Ih$V9j3cpE_-#lErS>$sUwb4nabq@0#gpU`PyLYZo=EBc yRxPs04_WDZE_;bhe#lDSbJ? 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 Date: Mon, 25 Jul 2022 13:52:20 -0400 Subject: [PATCH 12/19] compress new atlases --- media/atree/connectors.png | Bin 4539 -> 1912 bytes media/atree/icons.png | Bin 9996 -> 3229 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/media/atree/connectors.png b/media/atree/connectors.png index ba0f6c9567d002b64f755a67f659f4b923ebe779..b68ce89276dc7cd25c30b4ac0cf53a3e68351935 100644 GIT binary patch delta 1875 zcmV-Z2dwzJBlr%G7$yV*0002VILCDW000DMK}|sb0I`n?{9y$E0004VQb$4nuFf3k zlk5Q}e*g(^NLh0L04^f{04^f|c%?sf0000OP)t-sM{rCwHaI?lhb1K^H>|ZSE-+u# z-#|i2{5K^%00001bW%=J06^y0W&i*Nw@E}nRA}DqT1}7QI20r;<&J|Qt`ie_uLBmE zdt~L@A+q+~ihN#X4*dOnUp5Va9s(J9SHc=ie^+PT)yYHD5ja7AEdoO?rN z9o9e(VzkWkfm)I~Cx4P&X7FR)HZu2qI5X#*aqY%AYi;d)XRU$u14pygcX8CK7&0GYd7? zp$Y7Zu&^zLiAW9Xp;oobI?Zliwwe>K9}3p9gcU6S>~rFyGM&F2K0@;yt((G{z4~S7 zX2lhM7iOw8JGDpf>fuU!kINnHi|J5iK;#z;q?Bt^rs&htgym^IZjWiBm?nHIe{niy zyo;t;`L4p9xZ;w`R}AuY#eGUY{2>5@l#-8g>aOn|w~-{?cfjmTW=MEIWD=O&^el33 zH}smvy~;*ryOT&0BHNqxvm2W_!|e3Q;UY8pKxR!VGu?z|eA4FOsW1C;Gs{EG@^Q?P zz$~hdG(@6?OJ?qb>myp)Q-v{-e}>5T%JEY|)04qIFq=M(#~Eh5B}*@_=0lqb!>cg- z0rXOjW!Bp)BIu{Sh*{V?Blu!wjS<ttV;%2 z-3jpTTyy894#IiGGPQ&&e-rp=Gy`D9sh^%wlL?%mlw&by3&)XlaU21isN4uQXEHZC zJ^*KO*!U2!5{W{7W_BW=JNS|Xk! z`L-sY^42)^*2;P*f50<{sazs=Yg%*)U87LI`c4-JK$$V{egP?{S=X4N;)V@9OH&1) z?igrJVX+@$&P1|qf=^p%1{MLx)&-=n0JR&LpfoX5vSc^ZR~)f=;rN2Dqs*CTT4O3L zBAY>HiCaa27i!k85ZTX!8BAGC!vm-m9I2Ti)QrOn-O#<7e=Ak6Pm3<>0qDOlMKu{j z;3qS~j~NZGGQEfq`tks3#$g6>8{JR>Sr4ca(5RWAug02849;_r8@hg*1bbiUw-hecz{*mkb4W;H9qYiFZWEUyXiD_jrBUn zo*m7(y~cVSWY3P~++HJ72T9pKsB)j}*`Dp$A)f8op6%J5?b-f6?OzGTOgrkkoF@PP N002ovPDHLkV1n;!b}0Y= literal 4539 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 diff --git a/media/atree/icons.png b/media/atree/icons.png index 8d78c344830496c93c37086e914baf3ff2471425..d812ac418a4bc7639a98d0787c4dbf59243bd73c 100644 GIT binary patch literal 3229 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 Mw{@|pwf4L6AGS;QCIA2c literal 9996 zcmeI2by!sI+V9uUC?%+rGy;lBNC^_s2na|s(%oGn3?m?n(t;u&B@GgiN_TgMgfxsa zLkuzR;`iHopZ)H0&il{#_gr(Wb? ze4j={0PY2|C$<0p^43;HMqNophS|l<*~-?z5&*bj{A0uwTcqx^8IL@dyHDn&?%JuI z`~dl$=+VnIRy{7qw>k_ek1hf^NR{4)Wh@xD?3#byTv+^85LCcS1V@O7KDk$|_ztUl z?V;IIk?oF}*HJazh{0UmF_LiHd@I^KAC=x!owfz;K$>7CNbmFBQL2qLlBXRL;(mU0 zVfznu+yf$w?mF`~a#9ZP&6XgAkvYN-lMf_ZKYwI@d-BDnH|;v5q?@7FX@vWb=^N{i z2o<{&5tRqG#q_pKbojLv?=2#~+Qso$80v^PyXtTYj#qOAm~s^tX8wHPPUHH3eH`?@TO5Wc4nCLHR^x~mZ<8>zS_RQqn3an7p0B?Y@Uo%Hx-XxVXg z<|!O#`u(fEVw__f3&na)0W853(nSBZlU-k*jNJq`GiC6^&v`C0s$UJOGT54`<7>rt z!3*i4rTHOnf`FlL8Uo5};x+Sz?$R*=B<5|Q8c{mro(OJq-T^v;>UQ6XT#rp$aQL^V z3$Czr;=3#@x(6~B&10OwH-A8WQp=l!T@M2g>LSZw^+gG!!aZNBzI&Wl-WvOyoTA=P zCeA6}8ezXwJKht<(S1K(LvscIkWu8OUa;ouja4ij`Z_d~LK?Spg+OYDBw3#MCYkT> z#zu0n%_m)*+}KV#D6^!v_*98oDcS?0;=~xlwPy=$YNm1oQ+)XN!7n@%&w}`eQBjwR9a%fZA^B_B zgX$ZuLE}gMG~wteQ7Gp=3>;lM(yvcJc?LpqbxFOAnC2Yuh*oi1N9f?N7jWTk{58W| z?S85~>(-ZtyWfs!h11}sC@Se|zYrpiAwl0IZn?<(rl3ojc93tqPQ_Y%qeZHfx1?eF zHiy+GzS~ddO32*7xQ4SliIoK)qiK)UOp>GOFB#Xc z$aswTW429$*Rz+w(S)&$Rw@WQIdLM;V6EHDsvcC#)uPHXRBfclx$V&R6bpP>ce7zIV`7i-u zJCvMNol)wJ`6k`Tg`^bGJph-LBdxX+XK#ek5;4;knbF-|W-UB(yT2{qWn`C}NzsaJ zZ^hFH^^TDrnrQX$7?r2?w3Dxkgyx)6AL-1TWWXq2{=Q~|E+7ybVbwEkRdTKp@WU^Z%RmLlXJB0He#g9$1i=J`Lpp(HKIypwwn>la!(*k< zv=<4y!qZoYX%?|deVav-8M6+XIbyDKB>(E9ATD~mQ{gXP%AAw{cS$6-)_GRSP+D*5 zz0#TZVnYA&m6+tk#fQ^Dbf}k#>G#>jqnv52+ZJ_b8v4G#&exRL?^fhAX^Hq)`rIdC zcvZD5^^Bpn0v!!huz&U!?WWuvuUpqyY&V?!4zhdh;9!MksH#?2qj>DJRi@#;Hab7q zOO*JvhK~lNiQ+jQmBgl6kJ$_6YKF8b`zP4FGbf3VFXLx|S2nEoIIkVXLNQ=0$A9r^ zS`T9(bUMt)u7r4J@1*5di!Oa%IQ4jn`KrVD;grT`($P{p8^g<27JkyKKEAWLRacq^ z+eGoOqac7~n`x#~>qD8~l5V&Kztqo4nO0uj8k-_t5;OGmZ zV;?Jpo8oz{Xpki0>d!RxV3cPqr|(Kg1V3|^qLmg#6SLqaLhcoRQy-zN?uvdu1gN}F znG_XI5bE+UnUFs{FH?K}CCmU#=B04LaWpY2UA`_kevtx}rxWm$+w@zvjm1+|L**Tx zD#@{sHZNSN!t0-H45fya1i4_f1dk)sX=79vPO`$*b_@vot5m=mR$k6flFaKXSVEc3M ztWkSH-o}@nbSA0QrxxFQojj|0EMOt_4@xyHVZh|eJc?VZnTDT#!&cVr>fw- zk#jkBhu0d6p|ntjf-2MA`E9+#{;{T{84Zm9;o#We%}4EUM?>$7DPe!xAo&bmnW;~G zn^e*^QrmXYVDR=|ymqZvD@Coc#TJ#+xXEt$#oS9s(ku|;{l%rA>|Wr=Ndid_(W+eYroI+%*)up)X+*G-J-&@A|yDrJp%WKdyM zbq!KyUaLNJNt&DEmP=6S+--JxkTq;OyLF_|0OjBsNw@m&n=8NYyQr!Ag@y^ZP#u#0 z>ipRs<>8sLOyG|3HYWXpHyrw%c!sms`qk(BC@FVAri5rT8RfY!OzJZfuNxN`@#~d$ z#G81qP~PFVK_YCQ?M;eXmLmt z`9&mgL)?kQ(`!?nM8>nZzk;>D)JbS}VpPb)3$|F4WAglHDKgHH;c*;~U@mTV)pja6 zWst|^(A|iLrIi|&mCe1;fQbe5z=LgZ?5VT4X418j1Sioo0XZXwQ}@K^n!1UH2W4}< zByNvIFXTP@Wjevah?;2g!gIygm7p7Nmw&d+&H+L zB8h>|cnRk$=KkaU*JQ+laOXYul(_pGP0Cc+)-u3kZ^($xauRw{QNPlyT=}AToHKY3 zaq}tE6w!)Q^ujIK>D+xxv_ardpu&v4u6b3Sr7J=Gam9TO#8>`I@!iIsvyA)iB={d- z`1i+XZBfyq%tK9mVwT|OGkiuPPo^aD3une4Rd6tZ%tjSNYLl@fL_5-Wg6Yiv=9vyH z^Coqr#MQY45~=L|iKrKozK^R_yjwnYz~kNun`d0=Z2Uztzpk^Dw7r!Bq`2vyhRTwVvbfc z;_CLykr6TDp$A~$OD?7Wv!-{nyzs&4CxrnRSfA8I0bVij^c2+286}9DKw9_M{Baid zRJ2Nl;gB2aYDb?2-#_WHh~DI5ec8j+Qs5N~iddaIiE&lP&AsQTac7Zmvm>&Qqh3ChB^w zB+)K|Q;PSBXTm~65M1*?Y-N#1**?#o9x|{bzjPX&t|tc(4{D8)HF9>G&*_dSKDDtG zLp15lZ?b9v3|Rzb_ZHGSg-F}`ipDlBNM2oGQEjRm_Ak|b?>?HX03sg<_LJ>>EdI@`?yD6A(oBj!xJ-7!U&^HLh?^$8*;h!b)Z48{tVD0Ufi!&gjyE_ z0?<$Xed7TW#O_@lZ;zwY?F!hUn__x`ok zmq(2(Vivyy-Zlkg=|R3R-z4Q)GJXAr@0~}b0xiHNH3T3sc`)}W!nPGDl|yw@kxXb^ z4le(PflM&t74Vc$HNAx_4D_G3g z`k!B6!q`8g=NnAtH|UZQLBktRYwmsUI8f^i&4KZj6Iqe&1HE1ZcfAIbMC$>-AKzR^ z3snz*IB|&Kl+X1Z?iM+RjzIy$Y3a=uTj%y9pR1zRYtphU33{PPMeeP<>XiHrYp)Oc zlk8yl0Nfo#0M{xFWcv6mcc5E<6ae!p8hA-r+B+gYW0uh*d}2C$JNR&cExYjBSJ5@* z^4|+lV;hKt4Q-j__QH|`vDC}IMbH|Nk-l^H0>rm(oi zZkou&JwpJTv<;{&^A}-92DQh_EuJ7M1kJDOO=@soIkfz{~Kl+@}%EpN2>! zL|K`~Yo`onlUhGp7AGEQ-T#_L0`5Kzi5MD(f{Eu*RaHO|IygHQIpjjakfq~`9(cr) zXm_7;qH-`N*1;W{^JdX~T=!ueGQ5d%=GZ~UxPkFTOGyGbAMQ&6Npy0E!^WRlYfQI? zL!+OMen7>lObx_%@Y~|7*GSovA;~QWfJsN#TL|29`XQOCnzSdq$+wL)n}DeCho|4Z~h> zP4503bToVxZPRD7sb&FfAsH2U z(1UPHY}D=_;re&R#gM5kxvvEtVymF^yE!F?S$YPH&^#HZ_@R(yyBhh?2 zx$DN!69H8Nd#U>cn-8~UaFN>%m0m6E1Y^$kj{k!5kSoIbkUmIG8|yA1>P(jQghHx3 z-BOhR54d#VOK0rK%k9VQbu&a(TYa~N#Rok;`0hd7@^cD{>;FB=aZQ!LQS#7p>1|A= z9r?Y`HO$&e;QcVl`e3`{=Ghr<@qaV zySpn*{TyDYPW||fT8t^$9tBtAhhfY$CPz<45qq?07M6NjfwT!K_gl1#^uMP!1_&Fj z*{JunwdDlZ_P~Kgs;UP2$4qAJZudgrKtgYe@+^-$YV^#4u=iN{2AG^ae!AQt?{O** zo*(XXt-wSkAnR;!@hzT=ONR`@CzWCosAyY;K=*2(djQMM+UA)nfU|yidP}Yj#X) z5y;-os6W(6h0R8|l?zl&do=@kW#}WYe9rXa4h<0g6!M(yHY54fDR$x!N4K6_Rk6agmBPObJ)AER}5|CPNm8@ik-s-wOF)g;nlNQ@wD0ik=FR?f)OPikP_ zz$1|~;S+_S{>&b@F6ZWnXU%bDz^i%*qfcc=IGq9}x-TCHGD}EgiW*L#`4P~{2I$Oj zdccBwzb8DiK}0+w{AXH7aN+yYI8HfVW<3`vL1W{)6qFvUCxGQ1aJ?;l*xs~uRTdr%vp z>Ue)aY)6EfHs;OD!KN=y<@pm;q#e~k;#L(jftgRTKkxG!3(6ReY;!Ldj&YNL%jOra z7_bx7>9SS5&gL`~xiNa^wp{ZOgQ+IqONUQjjRq%F2X)3$DfUgGCz1`&y`jcoyy0wDZ*$ijzbt!gwg#X!fbrK%$i}C0`Ee{(N95Iuc>)RWneq9*@5JJ6v> zum_^uucT^BR@cUM`hPvZfk4@g15TOYczBG2sb!#4ISi`q1q+qzf_eOUS<-YFo?ET-VuPrzlM!_)W5$l zoU93G;BK0qYXABKyKNfB=&Lr1E6>loKfS`X^CUviXo6#;qW;%N(5W~1j8lmBY3J(~ zs=EJc)Nn3b6MVw~RfTL7#M2|Y<jvbeSWtBqiug+4A!)|`mq*S1wkE}^R zOuH>~qgh&`&V|F%o6!r!_M041JAU}$CpDJ6kB$#b4tcO7S2_og&C2`<$L?~?v=v)3 z_FJv?f+Gq@&8R>EQ}5L7R!{bis8WbP6h)Svs915SfRl@`zv;?%Ry>V- ziKX~@qku1OcQ8{DOIh`!z94R_fiQf}1b6Wc>60M7IXD%V<}cV5tBLs}L?S=_gsUVx zD;IER$-!beeqh?})nfW7p6i70@h7yVL`AY4ojETTW=;4mZV5ZKvzaF5Dv*LGXd~oh zdec=vj_yWF8rtt0d5p(%(fP_NQ_W~{jrG*6GQ8)?RTM?SBR%A0;mRTGF+w(3kIG;F zOP|>On?7;%n!B}y{+B{2N7G6k>N0Y_tVR;VK@OCSopEOYSRT_;z4uwv=|$+_DPPo+ zsJ{@HmSPr9QGHY_w@J@ z>c%}+y&W9gf94RHQil)Znlj^N_XGgD|B&t*Jr9KsdZOia(&xDi{LeP*Z=4UFRr4e#S8e)mA+luDqb?i*q&qLv z7Mz;Y$&m*;jPibdMA%&(Y&2F1})|3w{p3nm1xIG#K$S%+eZAsMA2_(Put138{ehZG2@*_#ej*W#3uFaXkJxN znA_L08B=Bchf?W2T6l$ONU48JbY)dwk%h5@3MRW)MOSneTz{<^LAz;qb1Ek=`Lh+h zvE?%LSJAgiqWGz>5 z9Il&s^z{e03!q*Gfa~w@fyR_jI+x#)g?Z`R&RBA-ny34Oh&d(hrCn!jX9%#`^YTPe zJ0PM66%Z&pk1zP}vB})D$HD|TEtsDY9CL7Y&*{6f5!lsn(vQSS)-7Xyi{Vc~#I%v91-XRT^s4)p_ucnS%soY!eGIY#Kh$ra5`bGsh&Dm-VG!Zg2xAj@ zY=Pr~`&mz17<3|)qL~CK-Cy_F=S-Ddh04+rd;|wFyY2ZQC)4jMxp|~(t*!~Z4&>$) zR){8}`+GW@{5loY;9s>N9ulXNV75U3?4eHBR@p;Rol`HZ_}u+9ejIl?o-RK{tBk41 zxBY6Ve|gjKoPxB8%P8S#NMybjpuP&8!v`iU<%Qz#hdfuJ`S>WnDz4zpj2Jxg=ltb3v96rY1n<##X6vejz6Y%c?#sKaZasXs^PY+eJj?7-JlJMq8DjUC{k$U z{jxK=#C;A7E(8mY3M-RY`9nKt7WdXf)OTU3LmAM>ea@;rf z5F4s=GgUc#GMy!xa~_L7L``LPy|ubBgCqqdHr|M2;U1scsjRnPh})P6JgTDQah&ZM zPA{jGyXW#~B~gMUQ;HPnU|sixQ6sDi^iZ2x!2XSti+15XR2Vg=xe9ufHe!zVrwNi? zjAS2)l0AD*L-195JQuYrM!N2|f0z69?!K$#3(%K`Njd};e1@0woB_4J%amlH1A*%` zKz#($;_#NcO=9^tLQ7{1I$cFbF+1h36{19vKi@pty)ZiVf0G+xi~c^arIvSJwqR;-=i!N z1P_KE{dGrLDG30d_Sm&`dwjDJKSULex7vho>0VJ8azy0YMzlqft^SnI(=jv`sI2r? zTUy9CO%o?I3|59j!BB- zLFv}hXxM0)_dZ=>gyCoL{nhMJvcRGlExf~i=&jdYcab4ll<}3i3$4kv_FD=!b~8TQ zsudA+P)<;@;b1pD;LI=*-zv6d+$V*jHVV*;8;z)6n;R9NN_nvK+xfx~Gkip=f{4dp zbd2k^VHapmE&(RcYxL17ic*Z>ZCcT&Kfel$++$T3e($#O0}Er`A?lj1pV#S<$-{%=LcgOJw@@5UoQi6a~hW92ijC8^k1akJYX5&jc+!s z5IhEdsz((x%*6cO56WQv4@Nu85Su#OQnx1+kE#6=ymq1UKUdk4LUPcd=6lYflRT$s z#ijH-9FDj+4JfdjgRMbAxkdx>uTg_-KAqL%#qv*KLegZCGvLhrerJiEc}NxKc$3S0 z(QV+CzRUDYTP>)^EXjF)*V|=Y7Z!|J_Iz z32#f3>8|aDt+#W=p1RWA6k0G|f$N{%IR{v_0R@9^Cna1xE0k%zgwH zkt7y#h**3@lhEh0Vh;Z&7L(#z6}x(v+`rC-dYqZzE_U{z>-^RBhK78GfO66g$H3NL z#kJxqRU(Yt{w+9S>SSr=G5$cM&&+3^RN^mi> Date: Tue, 26 Jul 2022 13:44:16 -0400 Subject: [PATCH 13/19] resolve comments on PR #172 --- builder/index_full.html | 34 ++++++++++++++--------------- css/sq2bs.css | 13 ++++++++++++ js/atree.js | 4 ++-- js/icons.js | 46 +++++++++++++++------------------------- media/atree/icons.png | Bin 3229 -> 2891 bytes 5 files changed, 49 insertions(+), 48 deletions(-) diff --git a/builder/index_full.html b/builder/index_full.html index 4e7d217..e6e6252 100644 --- a/builder/index_full.html +++ b/builder/index_full.html @@ -49,7 +49,7 @@

-
+
@@ -76,7 +76,7 @@
-
+
@@ -103,7 +103,7 @@
-
+
@@ -130,7 +130,7 @@
-
+
@@ -156,7 +156,7 @@
-
+
@@ -183,7 +183,7 @@
-
+
@@ -210,7 +210,7 @@
-
+
@@ -237,7 +237,7 @@
-
+
@@ -263,8 +263,8 @@
-
-
+
+background-image: url('../media/items/new.png');
@@ -620,7 +620,7 @@
-
+
@@ -644,7 +644,7 @@
-
+
@@ -668,7 +668,7 @@
-
+
@@ -692,7 +692,7 @@
-
+
@@ -716,7 +716,7 @@
-
+
@@ -740,7 +740,7 @@
-
+
@@ -764,7 +764,7 @@
-
+
diff --git a/css/sq2bs.css b/css/sq2bs.css index 59e4e99..1519a35 100644 --- a/css/sq2bs.css +++ b/css/sq2bs.css @@ -460,3 +460,16 @@ a:hover { .ferricles{ color: #5be553; } + +.item-display-new-toggleable { + image-rendering: pixelated; + background-size: 1200% 100%; + aspect-ratio: 1/1; +} +.tome-image { + background-image: url('../media/items/common.png'); + image-rendering: pixelated; + background-size: 500% 100%; + background-position: 100% 0; + aspect-ratio: 1/1; +} \ No newline at end of file diff --git a/js/atree.js b/js/atree.js index e43e743..3d385ef 100644 --- a/js/atree.js +++ b/js/atree.js @@ -1009,7 +1009,7 @@ function render_AT(UI_elem, list_elem, tree) { // create node let node_elem = document.getElementById("atree-row-" + ability.display.row).children[ability.display.col]; - node_wrap.img = make_elem("div", [], {style: "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1; background-image: url('../media/atree/icons.png'); background-size: 1700% 500%;"}) + node_wrap.img = make_elem("div", [], {style: "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1; background-image: url('../media/atree/icons.png'); background-size: 900% 300%;"}) node_elem.appendChild(node_wrap.img); // create hitbox @@ -1277,7 +1277,7 @@ const atreeNodeAtlasPositions = { "node_assassin": 7, "node_shaman": 8 } -const atreeNodeAtlasSize = [17, 5] +const atreeNodeAtlasSize = [9, 3] function atlasBGPositionCalc(pos, atlasSize) { // https://css-tricks.com/focusing-background-image-precise-location-percentages/ // p = (c + 0.5/z - 0.5) * z/(z - 1) + 0.5 diff --git a/js/icons.js b/js/icons.js index ddf9ef0..76088b4 100644 --- a/js/icons.js +++ b/js/icons.js @@ -1,39 +1,27 @@ -//which icons to use let window_storage = window.localStorage; -console.log(window_storage); -icon_state_stored = window_storage.getItem("newicons"); newIcons = true; -if (icon_state_stored === "false") {toggleIcons()} - +if (window_storage.getItem("newicons") === "false") { + toggleIcons(); +} /** Toggle icons on the ENTIRE page. * */ - function toggleIcons() { +function toggleIcons() { newIcons = !newIcons; - let imgs = document.getElementsByTagName("img"); - let divs = document.getElementsByTagName("div"); - let favicon = document.querySelector("link[rel~='icon']"); + window_storage.setItem("newicons", newIcons.toString()); + let newOrOld = (newIcons ? "new" : "old"); - if (newIcons) { //switch to new - favicon.href = favicon.href.replace("media/icons/old","media/icons/new"); - for (const img of imgs) { - if (img.src.includes("media/icons/old")) {img.src = img.src.replace("media/icons/old","media/icons/new");} - } - for (const div of divs) { - if (div.style.backgroundImage.includes("media/items/old")) {div.style.backgroundImage = div.style.backgroundImage.replace("media/items/old","media/items/new");} - } - //toggleiconbutton.textContent = "Use Old Icons"; - window_storage.setItem("newicons","true"); - } else { //switch to old - favicon.href = favicon.href.replace("media/icons/new","media/icons/old"); - for (const img of imgs) { - if (img.src.includes("media/icons/new")) {img.src = img.src.replace("media/icons/new","media/icons/old");} - } - for (const div of divs) { - if (div.style.backgroundImage.includes("media/items/new")) {div.style.backgroundImage = div.style.backgroundImage.replace("media/items/new","media/items/old");} - } - //toggleiconbutton.textContent = "Use New Icons"; - window_storage.setItem("newicons","false"); + let imgs = document.getElementsByTagName("img"); + let divs = document.getElementsByClassName("item-display-new-toggleable"); + let favicon = document.querySelector("link[rel~='icon']"); + favicon.href = favicon.href.replace("media/icons/" + (newIcons ? "old" : "new"), "media/icons/" + newOrOld); + for (const img of imgs) { + // if doesn't contain, replace() does nothing + img.src = img.src.replace("media/icons/" + (newIcons ? "old" : "new"), "media/icons/" + newOrOld); + } + for (let i = 0; i < divs.length; i++) { + console.log(divs.item(i)) + divs.item(i).style.backgroundImage = "url('../media/items/" + (newIcons ? "new" : "old") + ".png')"; } } \ No newline at end of file diff --git a/media/atree/icons.png b/media/atree/icons.png index d812ac418a4bc7639a98d0787c4dbf59243bd73c..fcbc48d78c0e338190c7ab98299ea428629fd437 100644 GIT binary patch delta 2668 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 Date: Tue, 26 Jul 2022 13:46:28 -0400 Subject: [PATCH 14/19] update builder/index.html from builder/index_full.html --- builder/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/index.html b/builder/index.html index eec6e2b..3c29e78 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1,2 +1,2 @@ - WynnBuilder
Join the discord today to suggest new features, submit bug reports, and hangout/talk to devs!
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Level:
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Active boosts
Earth
Thunder
Water
Fire
Air
Curse (Active)
Concentration (Passive)
Offense
Defense
Overall
Input a weapon to see abilities!

Made by hppeng, ferricles, and reschan with Atlas Inc (JavaScript required to function, nothing works without js)

Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.

\ No newline at end of file + WynnBuilder
Join the discord today to suggest new features, submit bug reports, and hangout/talk to devs!
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
background-image: url('../media/items/new.png');
0
0
Level:
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Active boosts
Earth
Thunder
Water
Fire
Air
Curse (Active)
Concentration (Passive)
Offense
Defense
Overall
Input a weapon to see abilities!

Made by hppeng, ferricles, and reschan with Atlas Inc (JavaScript required to function, nothing works without js)

Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.

\ No newline at end of file From 883f6602f044b94c8499b091f595e3604e690b2b Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Wed, 27 Jul 2022 06:48:27 +0930 Subject: [PATCH 15/19] Fix PowderInputNode style --- js/builder_graph.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index f5956a6..1d7d509 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -452,10 +452,11 @@ class PowderInputNode extends InputNode { let first = input.slice(0, 2); let powder = powderIDs.get(first); if (powder === undefined) { - if (first.length > 0) + if (first.length > 0) { errorederrors.push(first); - else + } else { break; + } } else { powdering.push(powder); } @@ -468,10 +469,11 @@ class PowderInputNode extends InputNode { errorederrors.push("Too many powders: " + powdering.length); } - if (errorederrors.length) + if (errorederrors.length) { this.input_field.classList.add("is-invalid"); - else + } else { this.input_field.classList.remove("is-invalid"); + } return powdering; } From 75e1a87a4403e7d124cf4099ad343559deb69e48 Mon Sep 17 00:00:00 2001 From: hppeng Date: Tue, 26 Jul 2022 23:18:55 -0700 Subject: [PATCH 16/19] Tweak graph flow for weapon powdering unjankify: Move powder application out of weapon input node. Item input node now produces an unpowdered item. Powder input node reads this, and uses it to update `slots` message and error. ItemPowderingNode collects from these two nodes and produces a powdered item. --- js/builder_graph.js | 208 ++++++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 94 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index 1d7d509..bf69e71 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -121,7 +121,7 @@ class PowderSpecialDisplayNode extends ComputeNode { /** * Node for getting an item's stats from an item input field. * - * Signature: ItemInputNode(powdering: Optional[list[powder]]) => Item | null + * Signature: ItemInputNode() => Item | null */ class ItemInputNode extends InputNode { /** @@ -143,8 +143,6 @@ class ItemInputNode extends InputNode { } compute_func(input_map) { - const powdering = input_map.get('powdering'); - // built on the assumption of no one will type in CI/CR letter by letter let item_text = this.input_field.value; if (!item_text) { @@ -158,10 +156,6 @@ class ItemInputNode extends InputNode { else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); } if (item) { - if (powdering !== undefined) { - const max_slots = item.statMap.get('slots'); - item.statMap.set('powders', powdering.slice(0, max_slots)); - } let type_match; if (this.category == 'weapon') { type_match = item.statMap.get('category') == 'weapon'; @@ -169,12 +163,6 @@ class ItemInputNode extends InputNode { type_match = item.statMap.get('type') == this.none_item.statMap.get('type'); } if (type_match) { - if (item.statMap.get('category') == 'armor') { - applyArmorPowders(item.statMap); - } - else if (item.statMap.get('category') == 'weapon') { - apply_weapon_powders(item.statMap); - } return item; } } @@ -205,6 +193,31 @@ class ItemInputNode extends InputNode { } } +/** + * Node for updating item input fields from parsed items. + * + * Signature: ItemInputDisplayNode(item: Item, powdering: List[powder]) => Item + */ +class ItemPowderingNode extends ComputeNode { + constructor(name) { super(name); } + + compute_func(input_map) { + const powdering = input_map.get('powdering'); + const item = {}; + item.statMap = new Map(input_map.get('item').statMap); // TODO: performance + + const max_slots = item.statMap.get('slots'); + item.statMap.set('powders', powdering.slice(0, max_slots)); + if (item.statMap.get('category') == 'armor') { + applyArmorPowders(item.statMap); + } + else if (item.statMap.get('category') == 'weapon') { + apply_weapon_powders(item.statMap); + } + return item; + } +} + /** * Node for updating item input fields from parsed items. * @@ -217,7 +230,6 @@ class ItemInputDisplayNode extends ComputeNode { this.input_field = document.getElementById(eq+"-choice"); this.health_field = document.getElementById(eq+"-health"); this.level_field = document.getElementById(eq+"-lv"); - this.powder_field = document.getElementById(eq+"-powder"); // possibly None this.image = item_image; this.fail_cb = true; } @@ -242,18 +254,11 @@ class ItemInputDisplayNode extends ComputeNode { this.input_field.classList.add("is-invalid"); return null; } - if (this.powder_field && item.statMap.has('powders')) { - this.powder_field.placeholder = "powders"; - } if (item.statMap.has('NONE')) { return null; } - if (this.powder_field && item.statMap.has('powders')) { - this.powder_field.placeholder = item.statMap.get('slots') + ' slots'; - } - const tier = item.statMap.get('tier'); this.input_field.classList.add(tier); if (this.health_field) { @@ -374,39 +379,39 @@ class URLUpdateNode extends ComputeNode { * Create a "build" object from a set of equipments. * Returns a new Build object, or null if all items are NONE items. * - * Signature: BuildAssembleNode(helmet-input: Item, - * chestplate-input: Item, - * leggings-input: Item, - * boots-input: Item, - * ring1-input: Item, - * ring2-input: Item, - * bracelet-input: Item, - * necklace-input: Item, - * weapon-input: Item, - * level-input: int) => Build | null + * Signature: BuildAssembleNode(helmet: Item, + * chestplate: Item, + * leggings: Item, + * boots: Item, + * ring1: Item, + * ring2: Item, + * bracelet: Item, + * necklace: Item, + * weapon: Item, + * level: int) => Build | null */ class BuildAssembleNode extends ComputeNode { constructor() { super("builder-make-build"); } compute_func(input_map) { let equipments = [ - input_map.get('helmet-input'), - input_map.get('chestplate-input'), - input_map.get('leggings-input'), - input_map.get('boots-input'), - input_map.get('ring1-input'), - input_map.get('ring2-input'), - input_map.get('bracelet-input'), - input_map.get('necklace-input'), - input_map.get('weaponTome1-input'), - input_map.get('weaponTome2-input'), - input_map.get('armorTome1-input'), - input_map.get('armorTome2-input'), - input_map.get('armorTome3-input'), - input_map.get('armorTome4-input'), - input_map.get('guildTome1-input') + input_map.get('helmet'), + input_map.get('chestplate'), + input_map.get('leggings'), + input_map.get('boots'), + input_map.get('ring1'), + input_map.get('ring2'), + input_map.get('bracelet'), + input_map.get('necklace'), + input_map.get('weaponTome1'), + input_map.get('weaponTome2'), + input_map.get('armorTome1'), + input_map.get('armorTome2'), + input_map.get('armorTome3'), + input_map.get('armorTome4'), + input_map.get('guildTome1') ]; - let weapon = input_map.get('weapon-input'); + let weapon = input_map.get('weapon'); let level = parseInt(input_map.get('level-input')); if (isNaN(level)) { level = 106; @@ -437,13 +442,24 @@ class PlayerClassNode extends ValueCheckComputeNode { * Read an input field and parse into a list of powderings. * Every two characters makes one powder. If parsing fails, NULL is returned. * - * Signature: PowderInputNode() => List[powder] | null + * Signature: PowderInputNode(item: Item) => List[powder] | null */ class PowderInputNode extends InputNode { - constructor(name, input_field) { super(name, input_field); } + constructor(name, input_field) { super(name, input_field); this.fail_cb = true; } compute_func(input_map) { + if (input_map.size !== 1) { throw "PowderInputNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + if (item === null) { + this.input_field.placeholder = 'powders'; + return []; + } + + if (item.statMap.has('slots')) { + this.input_field.placeholder = item.statMap.get('slots') + ' slots'; + } + // TODO: haha improve efficiency to O(n) dumb let input = this.input_field.value.trim(); let powdering = []; @@ -464,9 +480,9 @@ class PowderInputNode extends InputNode { } if (this.input_field.getAttribute("placeholder") != null) { - let placeholder = this.input_field.getAttribute("placeholder"); - if (placeholder.includes(" slots") && parseInt(placeholder[0]) < powdering.length) + if (item.statMap.get('slots') < powdering.length) { errorederrors.push("Too many powders: " + powdering.length); + } } if (errorederrors.length) { @@ -941,9 +957,11 @@ class SumNumberInputNode extends InputNode { } let item_nodes = []; +let item_nodes_map = new Map(); let powder_nodes = []; let edit_input_nodes = []; let skp_inputs = []; +let equip_inputs = []; let build_node; let stat_agg_node; let edit_agg_node; @@ -952,63 +970,65 @@ let atree_graph_creator; function builder_graph_init() { // Phase 1/3: Set up item input, propagate updates, etc. - // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). - for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) { - let input_field = document.getElementById(eq+"-choice"); - let item_image = document.getElementById(eq+"-img"); - - let item_input = new ItemInputNode(eq+'-input', input_field, none_item); - item_nodes.push(item_input); - new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); - new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input); - //new PrintNode(eq+'-debug').link_to(item_input); - //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); - } - for (const [eq, none_item] of zip2(tome_fields, [none_tomes[0], none_tomes[0], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[2]])) { - let input_field = document.getElementById(eq+"-choice"); - let item_image = document.getElementById(eq+"-img"); - - let item_input = new ItemInputNode(eq+'-input', input_field, none_item); - item_nodes.push(item_input); - new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); - } - - // weapon image changer node. - let weapon_image = document.getElementById("weapon-img"); - let weapon_dps = document.getElementById("weapon-dps"); - new WeaponInputDisplayNode('weapon-type', weapon_image, weapon_dps).link_to(item_nodes[8]); - // Level input node. let level_input = new InputNode('level-input', document.getElementById('level-choice')); - - // linking to atree verification - atree_validate.link_to(level_input, 'level'); // "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display. build_node = new BuildAssembleNode(); for (const input of item_nodes) { - build_node.link_to(input); } build_node.link_to(level_input); let build_encode_node = new BuildEncodeNode(); build_encode_node.link_to(build_node, 'build'); - let url_update_node = new URLUpdateNode(); - url_update_node.link_to(build_encode_node, 'build-str'); + // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). + for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) { + let input_field = document.getElementById(eq+"-choice"); + let item_image = document.getElementById(eq+"-img"); - - for (const input of powder_inputs) { - let powder_node = new PowderInputNode(input, document.getElementById(input)); - powder_nodes.push(powder_node); - build_encode_node.link_to(powder_node, input); + let item_input = new ItemInputNode(eq+'-input', input_field, none_item); + equip_inputs.push(item_input); + if (powder_inputs.includes(eq+'-powder')) { // TODO: fragile + const powder_name = eq+'-powder'; + let powder_node = new PowderInputNode(powder_name, document.getElementById(powder_name)) + .link_to(item_input, 'item'); + powder_nodes.push(powder_node); + build_encode_node.link_to(powder_node, powder_name); + let item_powdering = new ItemPowderingNode(eq+'-powder-apply') + .link_to(powder_node, 'powdering').link_to(item_input, 'item'); + item_input = item_powdering; + } + item_nodes.push(item_input); + item_nodes_map.set(eq, item_input); + new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); + new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input); + //new PrintNode(eq+'-debug').link_to(item_input); + //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); + build_node.link_to(item_input, eq); } - item_nodes[0].link_to(powder_nodes[0], 'powdering'); - item_nodes[1].link_to(powder_nodes[1], 'powdering'); - item_nodes[2].link_to(powder_nodes[2], 'powdering'); - item_nodes[3].link_to(powder_nodes[3], 'powdering'); - item_nodes[8].link_to(powder_nodes[4], 'powdering'); + for (const [eq, none_item] of zip2(tome_fields, [none_tomes[0], none_tomes[0], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[2]])) { + let input_field = document.getElementById(eq+"-choice"); + let item_image = document.getElementById(eq+"-img"); + + let item_input = new ItemInputNode(eq+'-input', input_field, none_item); + equip_inputs.push(item_input); + item_nodes.push(item_input); + new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); + build_node.link_to(item_input, eq); + } + + // weapon image changer node. + let weapon_image = document.getElementById("weapon-img"); + let weapon_dps = document.getElementById("weapon-dps"); + new WeaponInputDisplayNode('weapon-type', weapon_image, weapon_dps).link_to(item_nodes[8]); + + // linking to atree verification + atree_validate.link_to(level_input, 'level'); + + let url_update_node = new URLUpdateNode(); + url_update_node.link_to(build_encode_node, 'build-str'); // Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage @@ -1053,7 +1073,7 @@ function builder_graph_init() { // --------------------------------------------------------------- // Trigger the update cascade for build! // --------------------------------------------------------------- - for (const input_node of item_nodes.concat(powder_nodes)) { + for (const input_node of equip_inputs) { input_node.update(); } armor_powder_node.update(); From d7d45465cd237983577703e071de37352721cd73 Mon Sep 17 00:00:00 2001 From: fin444 Date: Wed, 27 Jul 2022 08:08:06 -0400 Subject: [PATCH 17/19] remove console.log --- js/icons.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/icons.js b/js/icons.js index 76088b4..f933fc4 100644 --- a/js/icons.js +++ b/js/icons.js @@ -21,7 +21,6 @@ function toggleIcons() { img.src = img.src.replace("media/icons/" + (newIcons ? "old" : "new"), "media/icons/" + newOrOld); } for (let i = 0; i < divs.length; i++) { - console.log(divs.item(i)) divs.item(i).style.backgroundImage = "url('../media/items/" + (newIcons ? "new" : "old") + ".png')"; } } \ No newline at end of file From e8adcdd4a65e4b50449ec89b8cf3a4ccb112a9a5 Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Wed, 27 Jul 2022 22:18:44 +0930 Subject: [PATCH 18/19] Detect powder slots based on space position --- js/builder_graph.js | 1889 ++++++++++++++++++++++++------------------- 1 file changed, 1064 insertions(+), 825 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index 1d7d509..b44964c 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1,121 +1,167 @@ let armor_powder_node = new (class extends ComputeNode { - constructor() { super('builder-armor-powder-input'); } + constructor() { + super("builder-armor-powder-input"); + } - compute_func(input_map) { - let damage_boost = 0; - let def_boost = 0; - let statMap = new Map(); - for (const [e, elem] of zip2(skp_elements, skp_order)) { - let val = parseInt(document.getElementById(elem+"_boost_armor").value); - statMap.set(e+'DamPct', val); - } - return statMap; + compute_func(input_map) { + let damage_boost = 0; + let def_boost = 0; + let statMap = new Map(); + for (const [e, elem] of zip2(skp_elements, skp_order)) { + let val = parseInt(document.getElementById(elem + "_boost_armor").value); + statMap.set(e + "DamPct", val); } + return statMap; + } })(); let boosts_node = new (class extends ComputeNode { - constructor() { super('builder-boost-input'); } + constructor() { + super("builder-boost-input"); + } - compute_func(input_map) { - let damage_boost = 0; - let def_boost = 0; - for (const [key, value] of damageMultipliers) { - let elem = document.getElementById(key + "-boost") - if (elem.classList.contains("toggleOn")) { - damage_boost += value; - if (key === "warscream") { def_boost += .10 } - if (key === "vanish") { def_boost += .15 } - } + compute_func(input_map) { + let damage_boost = 0; + let def_boost = 0; + for (const [key, value] of damageMultipliers) { + let elem = document.getElementById(key + "-boost"); + if (elem.classList.contains("toggleOn")) { + damage_boost += value; + if (key === "warscream") { + def_boost += 0.1; } - let res = new Map(); - res.set('damMult.Potion', 100*damage_boost); - res.set('defMult.Potion', 100*def_boost); - return res; + if (key === "vanish") { + def_boost += 0.15; + } + } } + let res = new Map(); + res.set("damMult.Potion", 100 * damage_boost); + res.set("defMult.Potion", 100 * def_boost); + return res; + } })().update(); /* Updates all spell boosts -*/ + */ function update_boosts(buttonId) { - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { - elem.classList.remove("toggleOn"); - } else { - elem.classList.add("toggleOn"); - } - boosts_node.mark_dirty().update(); + let elem = document.getElementById(buttonId); + if (elem.classList.contains("toggleOn")) { + elem.classList.remove("toggleOn"); + } else { + elem.classList.add("toggleOn"); + } + boosts_node.mark_dirty().update(); } -let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"]; +let specialNames = [ + "Quake", + "Chain Lightning", + "Curse", + "Courage", + "Wind Prison", +]; let powder_special_input = new (class extends ComputeNode { - constructor() { super('builder-powder-special-input'); } + constructor() { + super("builder-powder-special-input"); + } - compute_func(input_map) { - let powder_specials = []; // [ [special, power], [special, power]] - for (const sName of specialNames) { - for (let i = 1;i < 6; i++) { - if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) { - let powder_special = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))]; - powder_specials.push([powder_special, i]); - break; - } - } + compute_func(input_map) { + let powder_specials = []; // [ [special, power], [special, power]] + for (const sName of specialNames) { + for (let i = 1; i < 6; i++) { + if ( + document + .getElementById(sName.replace(" ", "_") + "-" + i) + .classList.contains("toggleOn") + ) { + let powder_special = + powderSpecialStats[specialNames.indexOf(sName.replace("_", " "))]; + powder_specials.push([powder_special, i]); + break; } - return powder_specials; + } } + return powder_specials; + } })(); function updatePowderSpecials(buttonId) { - let prefix = (buttonId).split("-")[0].replace(' ', '_') + '-'; - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { elem.classList.remove("toggleOn"); } - else { - for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off - //name is same, power is i - const elem2 = document.getElementById(prefix + i); - if(elem2.classList.contains("toggleOn")) { elem2.classList.remove("toggleOn"); } - } - //toggle the pressed button on - elem.classList.add("toggleOn"); + let prefix = buttonId.split("-")[0].replace(" ", "_") + "-"; + let elem = document.getElementById(buttonId); + if (elem.classList.contains("toggleOn")) { + elem.classList.remove("toggleOn"); + } else { + for (let i = 1; i < 6; i++) { + //toggle all pressed buttons of the same powder special off + //name is same, power is i + const elem2 = document.getElementById(prefix + i); + if (elem2.classList.contains("toggleOn")) { + elem2.classList.remove("toggleOn"); + } } - powder_special_input.mark_dirty().update(); + //toggle the pressed button on + elem.classList.add("toggleOn"); + } + powder_special_input.mark_dirty().update(); } class PowderSpecialCalcNode extends ComputeNode { - constructor() { super('builder-powder-special-apply'); } + constructor() { + super("builder-powder-special-apply"); + } - compute_func(input_map) { - const powder_specials = input_map.get('powder-specials'); - let stats = new Map(); - for (const [special, power] of powder_specials) { - if (special["weaponSpecialEffects"].has("Damage Boost")) { - let name = special["weaponSpecialName"]; - if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost - stats.set("sdPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); - stats.set("mdPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); - stats.set("poisonPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); - } else if (name === "Wind Prison") { - stats.set("aDamPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); - } - } + compute_func(input_map) { + const powder_specials = input_map.get("powder-specials"); + let stats = new Map(); + for (const [special, power] of powder_specials) { + if (special["weaponSpecialEffects"].has("Damage Boost")) { + let name = special["weaponSpecialName"]; + if (name === "Courage" || name === "Curse") { + //courage and curse are is universal damage boost + stats.set( + "sdPct", + special.weaponSpecialEffects.get("Damage Boost")[power - 1] + ); + stats.set( + "mdPct", + special.weaponSpecialEffects.get("Damage Boost")[power - 1] + ); + stats.set( + "poisonPct", + special.weaponSpecialEffects.get("Damage Boost")[power - 1] + ); + } else if (name === "Wind Prison") { + stats.set( + "aDamPct", + special.weaponSpecialEffects.get("Damage Boost")[power - 1] + ); } - return stats; + } } + return stats; + } } class PowderSpecialDisplayNode extends ComputeNode { - // TODO: Refactor this entirely to be adding more spells to the spell list - constructor() { - super('builder-powder-special-display'); - this.fail_cb = true; - } + // TODO: Refactor this entirely to be adding more spells to the spell list + constructor() { + super("builder-powder-special-display"); + this.fail_cb = true; + } - compute_func(input_map) { - const powder_specials = input_map.get('powder-specials'); - const stats = input_map.get('stats'); - const weapon = input_map.get('build').weapon; - displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap, true); - } + compute_func(input_map) { + const powder_specials = input_map.get("powder-specials"); + const stats = input_map.get("stats"); + const weapon = input_map.get("build").weapon; + displayPowderSpecials( + document.getElementById("powder-special-stats"), + powder_specials, + stats, + weapon.statMap, + true + ); + } } /** @@ -124,85 +170,95 @@ class PowderSpecialDisplayNode extends ComputeNode { * Signature: ItemInputNode(powdering: Optional[list[powder]]) => Item | null */ class ItemInputNode extends InputNode { - /** - * Make an item stat pulling compute node. - * - * @param name: Name of this node. - * @param item_input_field: Input field (html element) to listen for item names from. - * @param none_item: Item object to use as the "none" for this field. - */ - constructor(name, item_input_field, none_item) { - super(name, item_input_field); - this.none_item = new Item(none_item); - this.category = this.none_item.statMap.get('category'); - if (this.category == 'armor' || this.category == 'weapon') { - this.none_item.statMap.set('powders', []); - apply_weapon_powders(this.none_item.statMap); // Needed to put in damagecalc zeros - } - this.none_item.statMap.set('NONE', true); + /** + * Make an item stat pulling compute node. + * + * @param name: Name of this node. + * @param item_input_field: Input field (html element) to listen for item names from. + * @param none_item: Item object to use as the "none" for this field. + */ + constructor(name, item_input_field, none_item) { + super(name, item_input_field); + this.none_item = new Item(none_item); + this.category = this.none_item.statMap.get("category"); + if (this.category == "armor" || this.category == "weapon") { + this.none_item.statMap.set("powders", []); + apply_weapon_powders(this.none_item.statMap); // Needed to put in damagecalc zeros + } + this.none_item.statMap.set("NONE", true); + } + + compute_func(input_map) { + const powdering = input_map.get("powdering"); + + // built on the assumption of no one will type in CI/CR letter by letter + let item_text = this.input_field.value; + if (!item_text) { + return this.none_item; } - compute_func(input_map) { - const powdering = input_map.get('powdering'); - - // built on the assumption of no one will type in CI/CR letter by letter - let item_text = this.input_field.value; - if (!item_text) { - return this.none_item; - } - - let item; - if (item_text.slice(0, 3) == "CI-") { item = getCustomFromHash(item_text); } - else if (item_text.slice(0, 3) == "CR-") { item = getCraftFromHash(item_text); } - else if (itemMap.has(item_text)) { item = new Item(itemMap.get(item_text)); } - else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); } - - if (item) { - if (powdering !== undefined) { - const max_slots = item.statMap.get('slots'); - item.statMap.set('powders', powdering.slice(0, max_slots)); - } - let type_match; - if (this.category == 'weapon') { - type_match = item.statMap.get('category') == 'weapon'; - } else { - type_match = item.statMap.get('type') == this.none_item.statMap.get('type'); - } - if (type_match) { - if (item.statMap.get('category') == 'armor') { - applyArmorPowders(item.statMap); - } - else if (item.statMap.get('category') == 'weapon') { - apply_weapon_powders(item.statMap); - } - return item; - } - } - else if (this.none_item.statMap.get('category') === 'weapon' && item_text.startsWith("Morph-")) { - let replace_items = [ "Morph-Stardust", - "Morph-Steel", - "Morph-Iron", - "Morph-Gold", - "Morph-Topaz", - "Morph-Emerald", - "Morph-Amethyst", - "Morph-Ruby", - item_text.substring(6) - ] - - for (const [i, x] of zip2(equipment_inputs, replace_items)) { setValue(i, x); } - - for (const node of item_nodes) { - if (node !== this) { - // save a tiny bit of compute - calcSchedule(node, 10); - } - } - // Needed to push the weapon node's updates forward - return this.compute_func(input_map); - } - return null; + let item; + if (item_text.slice(0, 3) == "CI-") { + item = getCustomFromHash(item_text); + } else if (item_text.slice(0, 3) == "CR-") { + item = getCraftFromHash(item_text); + } else if (itemMap.has(item_text)) { + item = new Item(itemMap.get(item_text)); + } else if (tomeMap.has(item_text)) { + item = new Item(tomeMap.get(item_text)); } + + if (item) { + if (powdering !== undefined) { + const max_slots = item.statMap.get("slots"); + item.statMap.set("powders", powdering.slice(0, max_slots)); + } + let type_match; + if (this.category == "weapon") { + type_match = item.statMap.get("category") == "weapon"; + } else { + type_match = + item.statMap.get("type") == this.none_item.statMap.get("type"); + } + if (type_match) { + if (item.statMap.get("category") == "armor") { + applyArmorPowders(item.statMap); + } else if (item.statMap.get("category") == "weapon") { + apply_weapon_powders(item.statMap); + } + return item; + } + } else if ( + this.none_item.statMap.get("category") === "weapon" && + item_text.startsWith("Morph-") + ) { + let replace_items = [ + "Morph-Stardust", + "Morph-Steel", + "Morph-Iron", + "Morph-Gold", + "Morph-Topaz", + "Morph-Emerald", + "Morph-Amethyst", + "Morph-Ruby", + item_text.substring(6), + ]; + + for (const [i, x] of zip2(equipment_inputs, replace_items)) { + setValue(i, x); + } + + for (const node of item_nodes) { + if (node !== this) { + // save a tiny bit of compute + calcSchedule(node, 10); + } + } + // Needed to push the weapon node's updates forward + return this.compute_func(input_map); + } + return null; + } } /** @@ -211,62 +267,85 @@ class ItemInputNode extends InputNode { * Signature: ItemInputDisplayNode(item: Item) => null */ class ItemInputDisplayNode extends ComputeNode { + constructor(name, eq, item_image) { + super(name); + this.input_field = document.getElementById(eq + "-choice"); + this.health_field = document.getElementById(eq + "-health"); + this.level_field = document.getElementById(eq + "-lv"); + this.powder_field = document.getElementById(eq + "-powder"); // possibly None + this.image = item_image; + this.fail_cb = true; + } - constructor(name, eq, item_image) { - super(name); - this.input_field = document.getElementById(eq+"-choice"); - this.health_field = document.getElementById(eq+"-health"); - this.level_field = document.getElementById(eq+"-lv"); - this.powder_field = document.getElementById(eq+"-powder"); // possibly None - this.image = item_image; - this.fail_cb = true; + compute_func(input_map) { + if (input_map.size !== 1) { + throw "ItemInputDisplayNode accepts exactly one input (item)"; + } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + this.input_field.classList.remove( + "text-light", + "is-invalid", + "Normal", + "Unique", + "Rare", + "Legendary", + "Fabled", + "Mythic", + "Set", + "Crafted", + "Custom" + ); + this.input_field.classList.add("text-light"); + this.image.classList.remove( + "Normal-shadow", + "Unique-shadow", + "Rare-shadow", + "Legendary-shadow", + "Fabled-shadow", + "Mythic-shadow", + "Set-shadow", + "Crafted-shadow", + "Custom-shadow" + ); + + if (this.health_field) { + // Doesn't exist for weapons. + this.health_field.textContent = "0"; + } + if (this.level_field) { + // Doesn't exist for tomes. + this.level_field.textContent = "0"; + } + if (!item) { + this.input_field.classList.add("is-invalid"); + return null; + } + if (this.powder_field && item.statMap.has("powders")) { + this.powder_field.placeholder = "powders"; } - compute_func(input_map) { - if (input_map.size !== 1) { throw "ItemInputDisplayNode accepts exactly one input (item)"; } - const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - - this.input_field.classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set', 'Crafted', 'Custom'); - this.input_field.classList.add("text-light"); - this.image.classList.remove('Normal-shadow', 'Unique-shadow', 'Rare-shadow', 'Legendary-shadow', 'Fabled-shadow', 'Mythic-shadow', 'Set-shadow', 'Crafted-shadow', 'Custom-shadow'); - - if (this.health_field) { - // Doesn't exist for weapons. - this.health_field.textContent = "0"; - } - if (this.level_field) { - // Doesn't exist for tomes. - this.level_field.textContent = "0"; - } - if (!item) { - this.input_field.classList.add("is-invalid"); - return null; - } - if (this.powder_field && item.statMap.has('powders')) { - this.powder_field.placeholder = "powders"; - } - - if (item.statMap.has('NONE')) { - return null; - } - - if (this.powder_field && item.statMap.has('powders')) { - this.powder_field.placeholder = item.statMap.get('slots') + ' slots'; - } - - const tier = item.statMap.get('tier'); - this.input_field.classList.add(tier); - if (this.health_field) { - // Doesn't exist for weapons. - this.health_field.textContent = item.statMap.get('hp'); - } - if (this.level_field) { - // Doesn't exist for tomes. - this.level_field.textContent = item.statMap.get('lvl'); - } - this.image.classList.add(tier + "-shadow"); - return null; + if (item.statMap.has("NONE")) { + return null; } + + if (this.powder_field && item.statMap.has("powders")) { + this.powder_field.placeholder = item.statMap.get("slots") + " slots"; + } + + const tier = item.statMap.get("tier"); + this.input_field.classList.add(tier); + if (this.health_field) { + // Doesn't exist for weapons. + this.health_field.textContent = item.statMap.get("hp"); + } + if (this.level_field) { + // Doesn't exist for tomes. + this.level_field.textContent = item.statMap.get("lvl"); + } + this.image.classList.add(tier + "-shadow"); + return null; + } } /** @@ -275,18 +354,20 @@ class ItemInputDisplayNode extends ComputeNode { * Signature: ItemDisplayNode(item: Item) => null */ class ItemDisplayNode extends ComputeNode { - constructor(name, target_elem) { - super(name); - this.target_elem = target_elem; - } + constructor(name, target_elem) { + super(name); + this.target_elem = target_elem; + } - compute_func(input_map) { - if (input_map.size !== 1) { throw "ItemInputDisplayNode accepts exactly one input (item)"; } - const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - - displayExpandedItem(item.statMap, this.target_elem); - collapse_element("#"+this.target_elem); + compute_func(input_map) { + if (input_map.size !== 1) { + throw "ItemInputDisplayNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + displayExpandedItem(item.statMap, this.target_elem); + collapse_element("#" + this.target_elem); + } } /** @@ -295,26 +376,30 @@ class ItemDisplayNode extends ComputeNode { * Signature: WeaponInputDisplayNode(item: Item) => null */ class WeaponInputDisplayNode extends ComputeNode { + constructor(name, image_field, dps_field) { + super(name); + this.image = image_field; + this.dps_field = dps_field; + } - constructor(name, image_field, dps_field) { - super(name); - this.image = image_field; - this.dps_field = dps_field; + compute_func(input_map) { + if (input_map.size !== 1) { + throw "WeaponDisplayNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - compute_func(input_map) { - if (input_map.size !== 1) { throw "WeaponDisplayNode accepts exactly one input (item)"; } - const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - - const type = item.statMap.get('type'); - this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png'); - let dps = get_base_dps(item.statMap); - if (isNaN(dps)) { - dps = dps[1]; - if (isNaN(dps)) dps = 0; - } - this.dps_field.textContent = Math.round(dps); + const type = item.statMap.get("type"); + this.image.setAttribute( + "src", + "../media/items/new/generic-" + type + ".png" + ); + let dps = get_base_dps(item.statMap); + if (isNaN(dps)) { + dps = dps[1]; + if (isNaN(dps)) dps = 0; } + this.dps_field.textContent = Math.round(dps); + } } /** @@ -328,31 +413,33 @@ class WeaponInputDisplayNode extends ComputeNode { * weapon-powder: List[powder]) => str */ class BuildEncodeNode extends ComputeNode { - constructor() { super("builder-encode"); } + constructor() { + super("builder-encode"); + } - compute_func(input_map) { - const build = input_map.get('build'); - const atree = input_map.get('atree'); - const atree_state = input_map.get('atree-state'); - let powders = [ - input_map.get('helmet-powder'), - input_map.get('chestplate-powder'), - input_map.get('leggings-powder'), - input_map.get('boots-powder'), - input_map.get('weapon-powder') - ]; - const skillpoints = [ - input_map.get('str'), - input_map.get('dex'), - input_map.get('int'), - input_map.get('def'), - input_map.get('agi') - ]; - // TODO: grr global state for copy button.. - player_build = build; - build_powders = powders; - return encodeBuild(build, powders, skillpoints, atree, atree_state); - } + compute_func(input_map) { + const build = input_map.get("build"); + const atree = input_map.get("atree"); + const atree_state = input_map.get("atree-state"); + let powders = [ + input_map.get("helmet-powder"), + input_map.get("chestplate-powder"), + input_map.get("leggings-powder"), + input_map.get("boots-powder"), + input_map.get("weapon-powder"), + ]; + const skillpoints = [ + input_map.get("str"), + input_map.get("dex"), + input_map.get("int"), + input_map.get("def"), + input_map.get("agi"), + ]; + // TODO: grr global state for copy button.. + player_build = build; + build_powders = powders; + return encodeBuild(build, powders, skillpoints, atree, atree_state); + } } /** @@ -361,13 +448,17 @@ class BuildEncodeNode extends ComputeNode { * Signature: URLUpdateNode(build_str: str) => null */ class URLUpdateNode extends ComputeNode { - constructor() { super("builder-url-update"); } + constructor() { + super("builder-url-update"); + } - compute_func(input_map) { - if (input_map.size !== 1) { throw "URLUpdateNode accepts exactly one input (build_str)"; } - const [build_str] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - location.hash = build_str; + compute_func(input_map) { + if (input_map.size !== 1) { + throw "URLUpdateNode accepts exactly one input (build_str)"; } + const [build_str] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + location.hash = build_str; + } } /** @@ -386,51 +477,57 @@ class URLUpdateNode extends ComputeNode { * level-input: int) => Build | null */ class BuildAssembleNode extends ComputeNode { - constructor() { super("builder-make-build"); } + constructor() { + super("builder-make-build"); + } - compute_func(input_map) { - let equipments = [ - input_map.get('helmet-input'), - input_map.get('chestplate-input'), - input_map.get('leggings-input'), - input_map.get('boots-input'), - input_map.get('ring1-input'), - input_map.get('ring2-input'), - input_map.get('bracelet-input'), - input_map.get('necklace-input'), - input_map.get('weaponTome1-input'), - input_map.get('weaponTome2-input'), - input_map.get('armorTome1-input'), - input_map.get('armorTome2-input'), - input_map.get('armorTome3-input'), - input_map.get('armorTome4-input'), - input_map.get('guildTome1-input') - ]; - let weapon = input_map.get('weapon-input'); - let level = parseInt(input_map.get('level-input')); - if (isNaN(level)) { - level = 106; - } - - let all_none = weapon.statMap.has('NONE'); - for (const item of equipments) { - all_none = all_none && item.statMap.has('NONE'); - } - if (all_none && !location.hash) { - return null; - } - return new Build(level, equipments, weapon); + compute_func(input_map) { + let equipments = [ + input_map.get("helmet-input"), + input_map.get("chestplate-input"), + input_map.get("leggings-input"), + input_map.get("boots-input"), + input_map.get("ring1-input"), + input_map.get("ring2-input"), + input_map.get("bracelet-input"), + input_map.get("necklace-input"), + input_map.get("weaponTome1-input"), + input_map.get("weaponTome2-input"), + input_map.get("armorTome1-input"), + input_map.get("armorTome2-input"), + input_map.get("armorTome3-input"), + input_map.get("armorTome4-input"), + input_map.get("guildTome1-input"), + ]; + let weapon = input_map.get("weapon-input"); + let level = parseInt(input_map.get("level-input")); + if (isNaN(level)) { + level = 106; } + + let all_none = weapon.statMap.has("NONE"); + for (const item of equipments) { + all_none = all_none && item.statMap.has("NONE"); + } + if (all_none && !location.hash) { + return null; + } + return new Build(level, equipments, weapon); + } } class PlayerClassNode extends ValueCheckComputeNode { - constructor(name) { super(name); } + constructor(name) { + super(name); + } - compute_func(input_map) { - if (input_map.size !== 1) { throw "PlayerClassNode accepts exactly one input (build)"; } - const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - return wep_to_class.get(build.weapon.statMap.get('type')); + compute_func(input_map) { + if (input_map.size !== 1) { + throw "PlayerClassNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + return wep_to_class.get(build.weapon.statMap.get("type")); + } } /** @@ -440,43 +537,48 @@ class PlayerClassNode extends ValueCheckComputeNode { * Signature: PowderInputNode() => List[powder] | null */ class PowderInputNode extends InputNode { + constructor(name, input_field) { + super(name, input_field); + } - constructor(name, input_field) { super(name, input_field); } - - compute_func(input_map) { - // TODO: haha improve efficiency to O(n) dumb - let input = this.input_field.value.trim(); - let powdering = []; - let errorederrors = []; - while (input) { - let first = input.slice(0, 2); - let powder = powderIDs.get(first); - if (powder === undefined) { - if (first.length > 0) { - errorederrors.push(first); - } else { - break; - } - } else { - powdering.push(powder); - } - input = input.slice(2); - } - - if (this.input_field.getAttribute("placeholder") != null) { - let placeholder = this.input_field.getAttribute("placeholder"); - if (placeholder.includes(" slots") && parseInt(placeholder[0]) < powdering.length) - errorederrors.push("Too many powders: " + powdering.length); - } - - if (errorederrors.length) { - this.input_field.classList.add("is-invalid"); + compute_func(input_map) { + // TODO: haha improve efficiency to O(n) dumb + let input = this.input_field.value.trim(); + let powdering = []; + let errorederrors = []; + while (input) { + let first = input.slice(0, 2); + let powder = powderIDs.get(first); + if (powder === undefined) { + if (first.length > 0) { + errorederrors.push(first); } else { - this.input_field.classList.remove("is-invalid"); + break; } - - return powdering; + } else { + powdering.push(powder); + } + input = input.slice(2); } + + if (this.input_field.getAttribute("placeholder") != null) { + let placeholder = this.input_field.getAttribute("placeholder"); + if ( + placeholder.includes(" slots") && + parseInt(placeholder.substring(0, placeholder.indexOf(" "))) < + powdering.length + ) + errorederrors.push("Too many powders: " + powdering.length); + } + + if (errorederrors.length) { + this.input_field.classList.add("is-invalid"); + } else { + this.input_field.classList.remove("is-invalid"); + } + + return powdering; + } } /** @@ -487,62 +589,68 @@ class PowderInputNode extends InputNode { * Signature: SpellSelectNode(build: Build) => [Spell, SpellParts] */ class SpellSelectNode extends ComputeNode { - constructor(spell) { - super("builder-spell"+spell.base_spell+"-select"); - this.spell = spell; - } + constructor(spell) { + super("builder-spell" + spell.base_spell + "-select"); + this.spell = spell; + } - compute_func(input_map) { - const build = input_map.get('build'); - let stats = build.statMap; - // TODO: apply major ids... DOOM..... + compute_func(input_map) { + const build = input_map.get("build"); + let stats = build.statMap; + // TODO: apply major ids... DOOM..... - return [this.spell, this.spell.parts]; - } + return [this.spell, this.spell.parts]; + } } /* * Get all defensive stats for this build. */ function getDefenseStats(stats) { - let defenseStats = []; - let def_pct = skillPointsToPercentage(stats.get('def')) * skillpoint_final_mult[3]; - let agi_pct = skillPointsToPercentage(stats.get('agi')) * skillpoint_final_mult[4]; - //total hp - let totalHp = stats.get("hp") + stats.get("hpBonus"); - if (totalHp < 5) totalHp = 5; - defenseStats.push(totalHp); - //EHP - let ehp = [totalHp, totalHp]; - let defMult = (2 - stats.get("classDef")); - for (const [k, v] of stats.get("defMult").entries()) { - defMult *= (1 - v/100); - } - // newehp = oldehp / [0.1 * A(x) + (1 - A(x)) * (1 - D(x))] - ehp[0] = ehp[0] / (0.1*agi_pct + (1-agi_pct) * (1-def_pct)); - ehp[0] /= defMult; - // ehp[0] /= (1-def_pct)*(1-agi_pct)*defMult; - ehp[1] /= (1-def_pct)*defMult; - defenseStats.push(ehp); - //HPR - let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); - defenseStats.push(totalHpr); - //EHPR - let ehpr = [totalHpr, totalHpr]; - ehpr[0] /= (1-def_pct)*(1-agi_pct)*defMult; - ehpr[1] /= (1-def_pct)*defMult; - defenseStats.push(ehpr); - //skp stats - defenseStats.push([ def_pct*100, agi_pct*100]); - //eledefs - TODO POWDERS - let eledefs = [0, 0, 0, 0, 0]; - for(const i in skp_elements){ //kinda jank but ok - eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.); - } - defenseStats.push(eledefs); - - //[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]] - return defenseStats; + let defenseStats = []; + let def_pct = + skillPointsToPercentage(stats.get("def")) * skillpoint_final_mult[3]; + let agi_pct = + skillPointsToPercentage(stats.get("agi")) * skillpoint_final_mult[4]; + //total hp + let totalHp = stats.get("hp") + stats.get("hpBonus"); + if (totalHp < 5) totalHp = 5; + defenseStats.push(totalHp); + //EHP + let ehp = [totalHp, totalHp]; + let defMult = 2 - stats.get("classDef"); + for (const [k, v] of stats.get("defMult").entries()) { + defMult *= 1 - v / 100; + } + // newehp = oldehp / [0.1 * A(x) + (1 - A(x)) * (1 - D(x))] + ehp[0] = ehp[0] / (0.1 * agi_pct + (1 - agi_pct) * (1 - def_pct)); + ehp[0] /= defMult; + // ehp[0] /= (1-def_pct)*(1-agi_pct)*defMult; + ehp[1] /= (1 - def_pct) * defMult; + defenseStats.push(ehp); + //HPR + let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct") / 100); + defenseStats.push(totalHpr); + //EHPR + let ehpr = [totalHpr, totalHpr]; + ehpr[0] /= (1 - def_pct) * (1 - agi_pct) * defMult; + ehpr[1] /= (1 - def_pct) * defMult; + defenseStats.push(ehpr); + //skp stats + defenseStats.push([def_pct * 100, agi_pct * 100]); + //eledefs - TODO POWDERS + let eledefs = [0, 0, 0, 0, 0]; + for (const i in skp_elements) { + //kinda jank but ok + eledefs[i] = rawToPct( + stats.get(skp_elements[i] + "Def"), + stats.get(skp_elements[i] + "DefPct") / 100 + ); + } + defenseStats.push(eledefs); + + //[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]] + return defenseStats; } /** @@ -554,101 +662,115 @@ function getDefenseStats(stats) { * spell-info: [Spell, SpellParts]) => List[SpellDamage] */ class SpellDamageCalcNode extends ComputeNode { - constructor(spell_num) { - super("builder-spell"+spell_num+"-calc"); - } + constructor(spell_num) { + super("builder-spell" + spell_num + "-calc"); + } - compute_func(input_map) { - const weapon = input_map.get('build').weapon.statMap; - const spell_info = input_map.get('spell-info'); - const spell = spell_info[0]; - const spell_parts = spell_info[1]; - const stats = input_map.get('stats'); - const skillpoints = [ - stats.get('str'), - stats.get('dex'), - stats.get('int'), - stats.get('def'), - stats.get('agi') + compute_func(input_map) { + const weapon = input_map.get("build").weapon.statMap; + const spell_info = input_map.get("spell-info"); + const spell = spell_info[0]; + const spell_parts = spell_info[1]; + const stats = input_map.get("stats"); + const skillpoints = [ + stats.get("str"), + stats.get("dex"), + stats.get("int"), + stats.get("def"), + stats.get("agi"), + ]; + let spell_results = []; + let spell_result_map = new Map(); + const use_speed = "use_atkspd" in spell ? spell.use_atkspd : true; + const use_spell = "scaling" in spell ? spell.scaling === "spell" : true; + + // TODO: move preprocessing to separate node/node chain + for (const part of spell_parts) { + let spell_result; + if ("multipliers" in part) { + // damage type spell + let results = calculateSpellDamage( + stats, + weapon, + part.multipliers, + use_spell, + !use_speed, + spell.base_spell + "." + part.name + ); + spell_result = { + type: "damage", + normal_min: results[2].map((x) => x[0]), + normal_max: results[2].map((x) => x[1]), + normal_total: results[0], + crit_min: results[2].map((x) => x[2]), + crit_max: results[2].map((x) => x[3]), + crit_total: results[1], + }; + } else if ("power" in part) { + // TODO: wynn2 formula + let _heal_amount = + part.power * getDefenseStats(stats)[0] * (stats.get("healPct") / 100); + spell_result = { + type: "heal", + heal_amount: _heal_amount, + }; + } else { + continue; + } + spell_result.name = part.name; + spell_results.push(spell_result); + spell_result_map.set(part.name, spell_result); + } + for (const part of spell_parts) { + if ("hits" in part) { + let spell_result = { + normal_min: [0, 0, 0, 0, 0, 0], + normal_max: [0, 0, 0, 0, 0, 0], + normal_total: [0, 0], + crit_min: [0, 0, 0, 0, 0, 0], + crit_max: [0, 0, 0, 0, 0, 0], + crit_total: [0, 0], + heal_amount: 0, + }; + const dam_res_keys = [ + "normal_min", + "normal_max", + "normal_total", + "crit_min", + "crit_max", + "crit_total", ]; - let spell_results = [] - let spell_result_map = new Map(); - const use_speed = (('use_atkspd' in spell) ? spell.use_atkspd : true); - const use_spell = (('scaling' in spell) ? spell.scaling === 'spell' : true); - - // TODO: move preprocessing to separate node/node chain - for (const part of spell_parts) { - let spell_result; - if ('multipliers' in part) { // damage type spell - let results = calculateSpellDamage(stats, weapon, part.multipliers, use_spell, !use_speed, spell.base_spell + '.' + part.name); - spell_result = { - type: "damage", - normal_min: results[2].map(x => x[0]), - normal_max: results[2].map(x => x[1]), - normal_total: results[0], - crit_min: results[2].map(x => x[2]), - crit_max: results[2].map(x => x[3]), - crit_total: results[1], - } - } else if ('power' in part) { - // TODO: wynn2 formula - let _heal_amount = (part.power * getDefenseStats(stats)[0] * (stats.get('healPct')/100)); - spell_result = { - type: "heal", - heal_amount: _heal_amount - } + for (const [subpart_name, hits] of Object.entries(part.hits)) { + const subpart = spell_result_map.get(subpart_name); + if (!subpart) { + continue; + } + if (spell_result.type) { + if (subpart.type !== spell_result.type) { + throw "SpellCalc total subpart type mismatch"; } - else { - continue; + } else { + spell_result.type = subpart.type; + } + if (spell_result.type === "damage") { + for (const key of dam_res_keys) { + for (let i in spell_result.normal_min) { + spell_result[key][i] += subpart[key][i] * hits; + } } - spell_result.name = part.name; - spell_results.push(spell_result); - spell_result_map.set(part.name, spell_result); + } else { + spell_result.heal_amount += subpart.heal_amount; + } } - for (const part of spell_parts) { - if ('hits' in part) { - let spell_result = { - normal_min: [0, 0, 0, 0, 0, 0], - normal_max: [0, 0, 0, 0, 0, 0], - normal_total: [0, 0], - crit_min: [0, 0, 0, 0, 0, 0], - crit_max: [0, 0, 0, 0, 0, 0], - crit_total: [0, 0], - heal_amount: 0 - } - const dam_res_keys = ['normal_min', 'normal_max', 'normal_total', 'crit_min', 'crit_max', 'crit_total']; - for (const [subpart_name, hits] of Object.entries(part.hits)) { - const subpart = spell_result_map.get(subpart_name); - if (!subpart) { continue; } - if (spell_result.type) { - if (subpart.type !== spell_result.type) { - throw "SpellCalc total subpart type mismatch"; - } - } - else { - spell_result.type = subpart.type; - } - if (spell_result.type === 'damage') { - for (const key of dam_res_keys) { - for (let i in spell_result.normal_min) { - spell_result[key][i] += subpart[key][i] * hits; - } - } - } - else { - spell_result.heal_amount += subpart.heal_amount; - } - } - spell_result.name = part.name; - spell_results.push(spell_result); - spell_result_map.set(part.name, spell_result); - } - } - return spell_results; + spell_result.name = part.name; + spell_results.push(spell_result); + spell_result_map.set(part.name, spell_result); + } } + return spell_results; + } } - /** * Display spell damage from spell parts. * Currently kinda janky / TODO while we rework the internal rep. of spells. @@ -658,22 +780,29 @@ class SpellDamageCalcNode extends ComputeNode { * spell-damage: List[SpellDamage]) => null */ class SpellDisplayNode extends ComputeNode { - constructor(spell_num) { - super("builder-spell"+spell_num+"-display"); - this.spell_idx = spell_num; - } + constructor(spell_num) { + super("builder-spell" + spell_num + "-display"); + this.spell_idx = spell_num; + } - compute_func(input_map) { - const stats = input_map.get('stats'); - const spell_info = input_map.get('spell-info'); - const damages = input_map.get('spell-damage'); - const spell = spell_info[0]; + compute_func(input_map) { + const stats = input_map.get("stats"); + const spell_info = input_map.get("spell-info"); + const damages = input_map.get("spell-damage"); + const spell = spell_info[0]; - const i = this.spell_idx; - let parent_elem = document.getElementById("spell"+i+"-info"); - let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); - displaySpellDamage(parent_elem, overallparent_elem, stats, spell, i, damages); - } + const i = this.spell_idx; + let parent_elem = document.getElementById("spell" + i + "-info"); + let overallparent_elem = document.getElementById("spell" + i + "-infoAvg"); + displaySpellDamage( + parent_elem, + overallparent_elem, + stats, + spell, + i, + damages + ); + } } /** @@ -682,20 +811,35 @@ class SpellDisplayNode extends ComputeNode { * Signature: BuildDisplayNode(build: Build) => null */ class BuildDisplayNode extends ComputeNode { - constructor() { super("builder-stats-display"); } + constructor() { + super("builder-stats-display"); + } - compute_func(input_map) { - const build = input_map.get('build'); - const stats = input_map.get('stats'); - displayBuildStats('overall-stats', build, build_all_display_commands, stats); - displayBuildStats("offensive-stats", build, build_offensive_display_commands, stats); - displaySetBonuses("set-info", build); - // TODO: move weapon out? - displayDefenseStats(document.getElementById("defensive-stats"), stats); + compute_func(input_map) { + const build = input_map.get("build"); + const stats = input_map.get("stats"); + displayBuildStats( + "overall-stats", + build, + build_all_display_commands, + stats + ); + displayBuildStats( + "offensive-stats", + build, + build_offensive_display_commands, + stats + ); + displaySetBonuses("set-info", build); + // TODO: move weapon out? + displayDefenseStats(document.getElementById("defensive-stats"), stats); - displayPoisonDamage(document.getElementById("build-poison-stats"), build); - displayEquipOrder(document.getElementById("build-order"), build.equip_order); - } + displayPoisonDamage(document.getElementById("build-poison-stats"), build); + displayEquipOrder( + document.getElementById("build-order"), + build.equip_order + ); + } } /** @@ -705,103 +849,150 @@ class BuildDisplayNode extends ComputeNode { * Signature: DisplayBuildWarningNode(build: Build, str: int, dex: int, int: int, def: int, agi: int) => null */ class DisplayBuildWarningsNode extends ComputeNode { - constructor() { super("builder-show-warnings"); } + constructor() { + super("builder-show-warnings"); + } - compute_func(input_map) { - const build = input_map.get('build'); - const min_assigned = build.base_skillpoints; - const base_totals = build.total_skillpoints; - const skillpoints = [ - input_map.get('str'), - input_map.get('dex'), - input_map.get('int'), - input_map.get('def'), - input_map.get('agi') - ]; - let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."]; - let total_assigned = 0; - for (let i in skp_order){ //big bren - const assigned = skillpoints[i] - base_totals[i] + min_assigned[i] - setText(skp_order[i] + "-skp-base", "Original: " + base_totals[i]); - setText(skp_order[i] + "-skp-assign", "Assign: " + assigned); - setValue(skp_order[i] + "-skp", skillpoints[i]); - let linebreak = document.createElement("br"); - linebreak.classList.add("itemp"); - setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100*skillpoint_final_mult[i]).toFixed(1).concat(skp_effects[i])); - document.getElementById(skp_order[i]+"-warnings").textContent = '' - if (assigned > 100) { - let skp_warning = document.createElement("p"); - skp_warning.classList.add("warning", "small-text"); - skp_warning.textContent += "Cannot assign " + assigned + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually."; - document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning); - } - total_assigned += assigned; - } - - let summarybox = document.getElementById("summary-box"); - summarybox.textContent = ""; - let skpRow = document.createElement("p"); - - let remainingSkp = document.createElement("p"); - remainingSkp.classList.add("scaled-font"); - let remainingSkpTitle = document.createElement("b"); - remainingSkpTitle.textContent = "Assigned " + total_assigned + " skillpoints. Remaining skillpoints: "; - let remainingSkpContent = document.createElement("b"); - remainingSkpContent.textContent = "" + (levelToSkillPoints(build.level) - total_assigned); - remainingSkpContent.classList.add(levelToSkillPoints(build.level) - total_assigned < 0 ? "negative" : "positive"); - - remainingSkp.appendChild(remainingSkpTitle); - remainingSkp.appendChild(remainingSkpContent); - - summarybox.append(skpRow); - summarybox.append(remainingSkp); - if(total_assigned > levelToSkillPoints(build.level)){ - let skpWarning = document.createElement("span"); - //skpWarning.classList.add("itemp"); - skpWarning.classList.add("warning"); - skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!"; - let skpCount = document.createElement("p"); - skpCount.classList.add("warning"); - skpCount.textContent = "For level " + (build.level>101 ? "101+" : build.level) + ", there are only " + levelToSkillPoints(build.level) + " skill points available."; - summarybox.append(skpWarning); - summarybox.append(skpCount); - } - let lvlWarning; - for (const item of build.items) { - let item_lvl; - if (item.statMap.get("crafted")) { - //item_lvl = item.get("lvlLow") + "-" + item.get("lvl"); - item_lvl = item.statMap.get("lvlLow"); - } - else { - item_lvl = item.statMap.get("lvl"); - } - - if (build.level < item_lvl) { - if (!lvlWarning) { - lvlWarning = document.createElement("p"); - lvlWarning.classList.add("itemp"); lvlWarning.classList.add("warning"); - lvlWarning.textContent = "WARNING: A level " + build.level + " player cannot use some piece(s) of this build." - } - let baditem = document.createElement("p"); - baditem.classList.add("nocolor"); baditem.classList.add("itemp"); - baditem.textContent = item.statMap.get("displayName") + " requires level " + item_lvl + " to use."; - lvlWarning.appendChild(baditem); - } - } - if(lvlWarning){ - summarybox.append(lvlWarning); - } - for (const [setName, count] of build.activeSetCounts) { - const bonus = sets.get(setName).bonuses[count-1]; - if (bonus["illegal"]) { - let setWarning = document.createElement("p"); - setWarning.classList.add("itemp"); setWarning.classList.add("warning"); - setWarning.textContent = "WARNING: illegal item combination: " + setName - summarybox.append(setWarning); - } - } + compute_func(input_map) { + const build = input_map.get("build"); + const min_assigned = build.base_skillpoints; + const base_totals = build.total_skillpoints; + const skillpoints = [ + input_map.get("str"), + input_map.get("dex"), + input_map.get("int"), + input_map.get("def"), + input_map.get("agi"), + ]; + let skp_effects = [ + "% more damage dealt.", + "% chance to crit.", + "% spell cost reduction.", + "% less damage taken.", + "% chance to dodge.", + ]; + let total_assigned = 0; + for (let i in skp_order) { + //big bren + const assigned = skillpoints[i] - base_totals[i] + min_assigned[i]; + setText(skp_order[i] + "-skp-base", "Original: " + base_totals[i]); + setText(skp_order[i] + "-skp-assign", "Assign: " + assigned); + setValue(skp_order[i] + "-skp", skillpoints[i]); + let linebreak = document.createElement("br"); + linebreak.classList.add("itemp"); + setText( + skp_order[i] + "-skp-pct", + ( + skillPointsToPercentage(skillpoints[i]) * + 100 * + skillpoint_final_mult[i] + ) + .toFixed(1) + .concat(skp_effects[i]) + ); + document.getElementById(skp_order[i] + "-warnings").textContent = ""; + if (assigned > 100) { + let skp_warning = document.createElement("p"); + skp_warning.classList.add("warning", "small-text"); + skp_warning.textContent += + "Cannot assign " + + assigned + + " skillpoints in " + + ["Strength", "Dexterity", "Intelligence", "Defense", "Agility"][i] + + " manually."; + document + .getElementById(skp_order[i] + "-warnings") + .appendChild(skp_warning); + } + total_assigned += assigned; } + + let summarybox = document.getElementById("summary-box"); + summarybox.textContent = ""; + let skpRow = document.createElement("p"); + + let remainingSkp = document.createElement("p"); + remainingSkp.classList.add("scaled-font"); + let remainingSkpTitle = document.createElement("b"); + remainingSkpTitle.textContent = + "Assigned " + total_assigned + " skillpoints. Remaining skillpoints: "; + let remainingSkpContent = document.createElement("b"); + remainingSkpContent.textContent = + "" + (levelToSkillPoints(build.level) - total_assigned); + remainingSkpContent.classList.add( + levelToSkillPoints(build.level) - total_assigned < 0 + ? "negative" + : "positive" + ); + + remainingSkp.appendChild(remainingSkpTitle); + remainingSkp.appendChild(remainingSkpContent); + + summarybox.append(skpRow); + summarybox.append(remainingSkp); + if (total_assigned > levelToSkillPoints(build.level)) { + let skpWarning = document.createElement("span"); + //skpWarning.classList.add("itemp"); + skpWarning.classList.add("warning"); + skpWarning.textContent = + "WARNING: Too many skillpoints need to be assigned!"; + let skpCount = document.createElement("p"); + skpCount.classList.add("warning"); + skpCount.textContent = + "For level " + + (build.level > 101 ? "101+" : build.level) + + ", there are only " + + levelToSkillPoints(build.level) + + " skill points available."; + summarybox.append(skpWarning); + summarybox.append(skpCount); + } + let lvlWarning; + for (const item of build.items) { + let item_lvl; + if (item.statMap.get("crafted")) { + //item_lvl = item.get("lvlLow") + "-" + item.get("lvl"); + item_lvl = item.statMap.get("lvlLow"); + } else { + item_lvl = item.statMap.get("lvl"); + } + + if (build.level < item_lvl) { + if (!lvlWarning) { + lvlWarning = document.createElement("p"); + lvlWarning.classList.add("itemp"); + lvlWarning.classList.add("warning"); + lvlWarning.textContent = + "WARNING: A level " + + build.level + + " player cannot use some piece(s) of this build."; + } + let baditem = document.createElement("p"); + baditem.classList.add("nocolor"); + baditem.classList.add("itemp"); + baditem.textContent = + item.statMap.get("displayName") + + " requires level " + + item_lvl + + " to use."; + lvlWarning.appendChild(baditem); + } + } + if (lvlWarning) { + summarybox.append(lvlWarning); + } + for (const [setName, count] of build.activeSetCounts) { + const bonus = sets.get(setName).bonuses[count - 1]; + if (bonus["illegal"]) { + let setWarning = document.createElement("p"); + setWarning.classList.add("itemp"); + setWarning.classList.add("warning"); + setWarning.textContent = + "WARNING: illegal item combination: " + setName; + summarybox.append(setWarning); + } + } + } } /** @@ -810,17 +1001,19 @@ class DisplayBuildWarningsNode extends ComputeNode { * Signature: AggregateStatsNode(*args) => StatMap */ class AggregateStatsNode extends ComputeNode { - constructor() { super("builder-aggregate-stats"); } + constructor() { + super("builder-aggregate-stats"); + } - compute_func(input_map) { - const output_stats = new Map(); - for (const [k, v] of input_map.entries()) { - for (const [k2, v2] of v.entries()) { - merge_stat(output_stats, k2, v2); - } - } - return output_stats; + compute_func(input_map) { + const output_stats = new Map(); + for (const [k, v] of input_map.entries()) { + for (const [k2, v2] of v.entries()) { + merge_stat(output_stats, k2, v2); + } } + return output_stats; + } } /** @@ -829,24 +1022,30 @@ class AggregateStatsNode extends ComputeNode { * Signature: AggregateEditableIDNode(build: Build, weapon: Item, *args) => StatMap */ class AggregateEditableIDNode extends ComputeNode { - constructor() { super("builder-aggregate-inputs"); } + constructor() { + super("builder-aggregate-inputs"); + } - compute_func(input_map) { - const build = input_map.get('build'); input_map.delete('build'); + compute_func(input_map) { + const build = input_map.get("build"); + input_map.delete("build"); - const output_stats = new Map(build.statMap); - for (const [k, v] of input_map.entries()) { - output_stats.set(k, v); - } - - output_stats.set('classDef', classDefenseMultipliers.get(build.weapon.statMap.get("type"))); - return output_stats; + const output_stats = new Map(build.statMap); + for (const [k, v] of input_map.entries()) { + output_stats.set(k, v); } + + output_stats.set( + "classDef", + classDefenseMultipliers.get(build.weapon.statMap.get("type")) + ); + return output_stats; + } } let edit_id_output; function resetEditableIDs() { - edit_id_output.notify(); + edit_id_output.notify(); } /** * Set the editble id fields. @@ -854,32 +1053,35 @@ function resetEditableIDs() { * Signature: EditableIDSetterNode(build: Build) => null */ class EditableIDSetterNode extends ComputeNode { - constructor(notify_nodes) { - super("builder-id-setter"); - this.notify_nodes = notify_nodes.slice(); - } + constructor(notify_nodes) { + super("builder-id-setter"); + this.notify_nodes = notify_nodes.slice(); + } - compute_func(input_map) { - if (input_map.size !== 1) { throw "EditableIDSetterNode accepts exactly one input (build)"; } - const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - for (const id of editable_item_fields) { - const val = build.statMap.get(id); - document.getElementById(id).value = val; - document.getElementById(id+'-base').textContent = 'Original Value: ' + val; - } + compute_func(input_map) { + if (input_map.size !== 1) { + throw "EditableIDSetterNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + for (const id of editable_item_fields) { + const val = build.statMap.get(id); + document.getElementById(id).value = val; + document.getElementById(id + "-base").textContent = + "Original Value: " + val; + } + } - notify() { - this.mark_dirty(); - this.update(); - // NOTE: DO NOT merge these loops for performance reasons!!! - for (const node of this.notify_nodes) { - node.mark_dirty(); - } - for (const node of this.notify_nodes) { - node.update(); - } + notify() { + this.mark_dirty(); + this.update(); + // NOTE: DO NOT merge these loops for performance reasons!!! + for (const node of this.notify_nodes) { + node.mark_dirty(); } + for (const node of this.notify_nodes) { + node.update(); + } + } } /** @@ -889,25 +1091,28 @@ class EditableIDSetterNode extends ComputeNode { * Signature: SkillPointSetterNode(build: Build) => null */ class SkillPointSetterNode extends ComputeNode { - constructor(notify_nodes) { - super("builder-skillpoint-setter"); - this.notify_nodes = notify_nodes.slice(); - } + constructor(notify_nodes) { + super("builder-skillpoint-setter"); + this.notify_nodes = notify_nodes.slice(); + } - compute_func(input_map) { - if (input_map.size !== 1) { throw "SkillPointSetterNode accepts exactly one input (build)"; } - const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - for (const [idx, elem] of skp_order.entries()) { - document.getElementById(elem+'-skp').value = build.total_skillpoints[idx]; - } - // NOTE: DO NOT merge these loops for performance reasons!!! - for (const node of this.notify_nodes) { - node.mark_dirty(); - } - for (const node of this.notify_nodes) { - node.update(); - } + compute_func(input_map) { + if (input_map.size !== 1) { + throw "SkillPointSetterNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + for (const [idx, elem] of skp_order.entries()) { + document.getElementById(elem + "-skp").value = + build.total_skillpoints[idx]; + } + // NOTE: DO NOT merge these loops for performance reasons!!! + for (const node of this.notify_nodes) { + node.mark_dirty(); + } + for (const node of this.notify_nodes) { + node.update(); + } + } } /** @@ -916,28 +1121,30 @@ class SkillPointSetterNode extends ComputeNode { * Signature: SumNumberInputNode() => int */ class SumNumberInputNode extends InputNode { - compute_func(input_map) { - let value = this.input_field.value; - if (value === "") { value = "0"; } - - let input_num = 0; - if (value.includes("+")) { - let skp = value.split("+"); - for (const s of skp) { - const val = parseInt(s,10); - if (isNaN(val)) { - return null; - } - input_num += val; - } - } else { - input_num = parseInt(value,10); - if (isNaN(input_num)) { - return null; - } - } - return input_num; + compute_func(input_map) { + let value = this.input_field.value; + if (value === "") { + value = "0"; } + + let input_num = 0; + if (value.includes("+")) { + let skp = value.split("+"); + for (const s of skp) { + const val = parseInt(s, 10); + if (isNaN(val)) { + return null; + } + input_num += val; + } + } else { + input_num = parseInt(value, 10); + if (isNaN(input_num)) { + return null; + } + } + return input_num; + } } let item_nodes = []; @@ -950,166 +1157,198 @@ let edit_agg_node; let atree_graph_creator; function builder_graph_init() { - // Phase 1/3: Set up item input, propagate updates, etc. + // Phase 1/3: Set up item input, propagate updates, etc. - // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). - for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) { - let input_field = document.getElementById(eq+"-choice"); - let item_image = document.getElementById(eq+"-img"); + // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). + for (const [eq, display_elem, none_item] of zip3( + equipment_fields, + build_fields, + none_items + )) { + let input_field = document.getElementById(eq + "-choice"); + let item_image = document.getElementById(eq + "-img"); - let item_input = new ItemInputNode(eq+'-input', input_field, none_item); - item_nodes.push(item_input); - new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); - new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input); - //new PrintNode(eq+'-debug').link_to(item_input); - //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); + let item_input = new ItemInputNode(eq + "-input", input_field, none_item); + item_nodes.push(item_input); + new ItemInputDisplayNode(eq + "-input-display", eq, item_image).link_to( + item_input + ); + new ItemDisplayNode(eq + "-item-display", display_elem).link_to(item_input); + //new PrintNode(eq+'-debug').link_to(item_input); + //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); + } + for (const [eq, none_item] of zip2(tome_fields, [ + none_tomes[0], + none_tomes[0], + none_tomes[1], + none_tomes[1], + none_tomes[1], + none_tomes[1], + none_tomes[2], + ])) { + let input_field = document.getElementById(eq + "-choice"); + let item_image = document.getElementById(eq + "-img"); + + let item_input = new ItemInputNode(eq + "-input", input_field, none_item); + item_nodes.push(item_input); + new ItemInputDisplayNode(eq + "-input-display", eq, item_image).link_to( + item_input + ); + } + + // weapon image changer node. + let weapon_image = document.getElementById("weapon-img"); + let weapon_dps = document.getElementById("weapon-dps"); + new WeaponInputDisplayNode("weapon-type", weapon_image, weapon_dps).link_to( + item_nodes[8] + ); + + // Level input node. + let level_input = new InputNode( + "level-input", + document.getElementById("level-choice") + ); + + // linking to atree verification + atree_validate.link_to(level_input, "level"); + + // "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display. + build_node = new BuildAssembleNode(); + for (const input of item_nodes) { + build_node.link_to(input); + } + build_node.link_to(level_input); + + let build_encode_node = new BuildEncodeNode(); + build_encode_node.link_to(build_node, "build"); + + let url_update_node = new URLUpdateNode(); + url_update_node.link_to(build_encode_node, "build-str"); + + for (const input of powder_inputs) { + let powder_node = new PowderInputNode( + input, + document.getElementById(input) + ); + powder_nodes.push(powder_node); + build_encode_node.link_to(powder_node, input); + } + + item_nodes[0].link_to(powder_nodes[0], "powdering"); + item_nodes[1].link_to(powder_nodes[1], "powdering"); + item_nodes[2].link_to(powder_nodes[2], "powdering"); + item_nodes[3].link_to(powder_nodes[3], "powdering"); + item_nodes[8].link_to(powder_nodes[4], "powdering"); + + // Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage + + // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) + stat_agg_node = new AggregateStatsNode(); + edit_agg_node = new AggregateEditableIDNode(); + edit_agg_node.link_to(build_node, "build"); + for (const field of editable_item_fields) { + // Create nodes that listens to each editable id input, the node name should match the "id" + const elem = document.getElementById(field); + const node = new SumNumberInputNode("builder-" + field + "-input", elem); + + edit_agg_node.link_to(node, field); + edit_input_nodes.push(node); + } + // Edit IDs setter declared up here to set ids so they will be populated by default. + edit_id_output = new EditableIDSetterNode(edit_input_nodes); // Makes shallow copy of list. + edit_id_output.link_to(build_node); + + for (const skp of skp_order) { + const elem = document.getElementById(skp + "-skp"); + const node = new SumNumberInputNode("builder-" + skp + "-input", elem); + + edit_agg_node.link_to(node, skp); + build_encode_node.link_to(node, skp); + edit_input_nodes.push(node); + skp_inputs.push(node); + } + stat_agg_node.link_to(edit_agg_node); + + // Phase 3/3: Set up atree stuff. + + let class_node = new PlayerClassNode("builder-class").link_to(build_node); + // These two are defined in `atree.js` + atree_node.link_to(class_node, "player-class"); + atree_merge.link_to(class_node, "player-class"); + atree_stats.link_to(build_node, "build"); + stat_agg_node.link_to(atree_stats, "atree-stats"); + + build_encode_node + .link_to(atree_node, "atree") + .link_to(atree_state_node, "atree-state"); + + // --------------------------------------------------------------- + // Trigger the update cascade for build! + // --------------------------------------------------------------- + for (const input_node of item_nodes.concat(powder_nodes)) { + input_node.update(); + } + armor_powder_node.update(); + level_input.update(); + + atree_graph_creator = new AbilityTreeEnsureNodesNode( + build_node, + stat_agg_node + ).link_to(atree_collect_spells, "spells"); + + // kinda janky, manually set atree and update. Some wasted compute here + if (atree_data !== null && atree_node.value !== null) { + // janky check if atree is valid + const atree_state = atree_state_node.value; + if (atree_data.length > 0) { + const active_nodes = decode_atree(atree_node.value, atree_data); + for (const node of active_nodes) { + atree_set_state(atree_state.get(node.ability.id), true); + } + atree_state_node.mark_dirty().update(); } - for (const [eq, none_item] of zip2(tome_fields, [none_tomes[0], none_tomes[0], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[2]])) { - let input_field = document.getElementById(eq+"-choice"); - let item_image = document.getElementById(eq+"-img"); + } - let item_input = new ItemInputNode(eq+'-input', input_field, none_item); - item_nodes.push(item_input); - new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); - } + // Powder specials. + let powder_special_calc = new PowderSpecialCalcNode().link_to( + powder_special_input, + "powder-specials" + ); + new PowderSpecialDisplayNode() + .link_to(powder_special_input, "powder-specials") + .link_to(stat_agg_node, "stats") + .link_to(build_node, "build"); + stat_agg_node.link_to(powder_special_calc, "powder-boost"); + stat_agg_node.link_to(armor_powder_node, "armor-powder"); + powder_special_input.update(); - // weapon image changer node. - let weapon_image = document.getElementById("weapon-img"); - let weapon_dps = document.getElementById("weapon-dps"); - new WeaponInputDisplayNode('weapon-type', weapon_image, weapon_dps).link_to(item_nodes[8]); + // Potion boost. + stat_agg_node.link_to(boosts_node, "potion-boost"); - // Level input node. - let level_input = new InputNode('level-input', document.getElementById('level-choice')); - - // linking to atree verification - atree_validate.link_to(level_input, 'level'); + // Also do something similar for skill points - // "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display. - build_node = new BuildAssembleNode(); - for (const input of item_nodes) { - build_node.link_to(input); - } - build_node.link_to(level_input); + let build_disp_node = new BuildDisplayNode(); + build_disp_node.link_to(build_node, "build"); + build_disp_node.link_to(stat_agg_node, "stats"); - let build_encode_node = new BuildEncodeNode(); - build_encode_node.link_to(build_node, 'build'); + for (const node of edit_input_nodes) { + node.update(); + } - let url_update_node = new URLUpdateNode(); - url_update_node.link_to(build_encode_node, 'build-str'); + let skp_output = new SkillPointSetterNode(edit_input_nodes); + skp_output.link_to(build_node); + let build_warnings_node = new DisplayBuildWarningsNode(); + build_warnings_node.link_to(build_node, "build"); + for (const [skp_input, skp] of zip2(skp_inputs, skp_order)) { + build_warnings_node.link_to(skp_input, skp); + } + build_warnings_node.update(); - for (const input of powder_inputs) { - let powder_node = new PowderInputNode(input, document.getElementById(input)); - powder_nodes.push(powder_node); - build_encode_node.link_to(powder_node, input); - } + // call node.update() for each skillpoint node and stat edit listener node manually + // NOTE: the text boxes for skill points are already filled out by decodeBuild() so this will fix them + // this will propagate the update to the `stat_agg_node`, and then to damage calc - item_nodes[0].link_to(powder_nodes[0], 'powdering'); - item_nodes[1].link_to(powder_nodes[1], 'powdering'); - item_nodes[2].link_to(powder_nodes[2], 'powdering'); - item_nodes[3].link_to(powder_nodes[3], 'powdering'); - item_nodes[8].link_to(powder_nodes[4], 'powdering'); - - // Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage - - // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) - stat_agg_node = new AggregateStatsNode(); - edit_agg_node = new AggregateEditableIDNode(); - edit_agg_node.link_to(build_node, 'build'); - for (const field of editable_item_fields) { - // Create nodes that listens to each editable id input, the node name should match the "id" - const elem = document.getElementById(field); - const node = new SumNumberInputNode('builder-'+field+'-input', elem); - - edit_agg_node.link_to(node, field); - edit_input_nodes.push(node); - } - // Edit IDs setter declared up here to set ids so they will be populated by default. - edit_id_output = new EditableIDSetterNode(edit_input_nodes); // Makes shallow copy of list. - edit_id_output.link_to(build_node); - - for (const skp of skp_order) { - const elem = document.getElementById(skp+'-skp'); - const node = new SumNumberInputNode('builder-'+skp+'-input', elem); - - edit_agg_node.link_to(node, skp); - build_encode_node.link_to(node, skp); - edit_input_nodes.push(node); - skp_inputs.push(node); - } - stat_agg_node.link_to(edit_agg_node); - - // Phase 3/3: Set up atree stuff. - - let class_node = new PlayerClassNode('builder-class').link_to(build_node); - // These two are defined in `atree.js` - atree_node.link_to(class_node, 'player-class'); - atree_merge.link_to(class_node, 'player-class'); - atree_stats.link_to(build_node, 'build'); - stat_agg_node.link_to(atree_stats, 'atree-stats'); - - build_encode_node.link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state'); - - // --------------------------------------------------------------- - // Trigger the update cascade for build! - // --------------------------------------------------------------- - for (const input_node of item_nodes.concat(powder_nodes)) { - input_node.update(); - } - armor_powder_node.update(); - level_input.update(); - - atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node) - .link_to(atree_collect_spells, 'spells'); - - // kinda janky, manually set atree and update. Some wasted compute here - if (atree_data !== null && atree_node.value !== null) { // janky check if atree is valid - const atree_state = atree_state_node.value; - if (atree_data.length > 0) { - const active_nodes = decode_atree(atree_node.value, atree_data); - for (const node of active_nodes) { - atree_set_state(atree_state.get(node.ability.id), true); - } - atree_state_node.mark_dirty().update(); - } - } - - // Powder specials. - let powder_special_calc = new PowderSpecialCalcNode().link_to(powder_special_input, 'powder-specials'); - new PowderSpecialDisplayNode().link_to(powder_special_input, 'powder-specials') - .link_to(stat_agg_node, 'stats').link_to(build_node, 'build'); - stat_agg_node.link_to(powder_special_calc, 'powder-boost'); - stat_agg_node.link_to(armor_powder_node, 'armor-powder'); - powder_special_input.update(); - - // Potion boost. - stat_agg_node.link_to(boosts_node, 'potion-boost'); - - // Also do something similar for skill points - - let build_disp_node = new BuildDisplayNode() - build_disp_node.link_to(build_node, 'build'); - build_disp_node.link_to(stat_agg_node, 'stats'); - - for (const node of edit_input_nodes) { - node.update(); - } - - let skp_output = new SkillPointSetterNode(edit_input_nodes); - skp_output.link_to(build_node); - - let build_warnings_node = new DisplayBuildWarningsNode(); - build_warnings_node.link_to(build_node, 'build'); - for (const [skp_input, skp] of zip2(skp_inputs, skp_order)) { - build_warnings_node.link_to(skp_input, skp); - } - build_warnings_node.update(); - - // call node.update() for each skillpoint node and stat edit listener node manually - // NOTE: the text boxes for skill points are already filled out by decodeBuild() so this will fix them - // this will propagate the update to the `stat_agg_node`, and then to damage calc - - console.log("Set up graph"); - graph_live_update = true; + console.log("Set up graph"); + graph_live_update = true; } - From 7906795f1e401964adc1b6d99407323b5cb427ad Mon Sep 17 00:00:00 2001 From: fin444 Date: Wed, 27 Jul 2022 12:15:37 -0400 Subject: [PATCH 19/19] fix typo in html --- builder/index.html | 2 +- builder/index_full.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/index.html b/builder/index.html index 3c29e78..1e51d32 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1,2 +1,2 @@ - WynnBuilder
Join the discord today to suggest new features, submit bug reports, and hangout/talk to devs!
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
background-image: url('../media/items/new.png');
0
0
Level:
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Active boosts
Earth
Thunder
Water
Fire
Air
Curse (Active)
Concentration (Passive)
Offense
Defense
Overall
Input a weapon to see abilities!

Made by hppeng, ferricles, and reschan with Atlas Inc (JavaScript required to function, nothing works without js)

Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.

\ No newline at end of file + WynnBuilder
Join the discord today to suggest new features, submit bug reports, and hangout/talk to devs!
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Level:
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Assign: 0
Original: 0
Active boosts
Earth
Thunder
Water
Fire
Air
Curse (Active)
Concentration (Passive)
Offense
Defense
Overall
Input a weapon to see abilities!

Made by hppeng, ferricles, and reschan with Atlas Inc (JavaScript required to function, nothing works without js)

Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.

\ No newline at end of file diff --git a/builder/index_full.html b/builder/index_full.html index e6e6252..d5160d5 100644 --- a/builder/index_full.html +++ b/builder/index_full.html @@ -263,8 +263,8 @@
-
-background-image: url('../media/items/new.png');
+
+