Frontend for viewing Dungeon Defender layouts
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

viewer.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. $.fn.rotate = function(rot) {
  2. return this.css('-webkit-transform', 'rotate(' + rot + 'deg)')
  3. .css('-moz-transform', 'rotate(' + rot + 'deg')
  4. .css('-o-transform', 'rotate(' + rot + 'deg')
  5. .css('-ms-transform', 'rotate(' + rot + 'deg')
  6. .css('transform', 'rotate(' + rot + 'deg');
  7. };
  8. $.fn.offsetFrom = function(el) {
  9. var offset = this.offset();
  10. var otherOffset = $(el).offset();
  11. return {top: offset.top - otherOffset.top, left: offset.left - otherOffset.left};
  12. }
  13. $.fn.offsetCentre = function() {
  14. var offset = this.offset();
  15. return {top: offset.top + this.height() / 2, left: offset.left + this.width() / 2};
  16. }
  17. function getURLParameter(name) {
  18. return decodeURIComponent((location.search.match(RegExp("[?|&]"+name+'=(.+?)(&|$)'))||[,null])[1]);
  19. }
  20. $(function() {
  21. // Instructions
  22. (function() {
  23. var cookieName = 'hideinstructions';
  24. function showInstructions() {
  25. $.cookie(cookieName, null);
  26. $('#instructions').show();
  27. $('#showinstructions').hide();
  28. }
  29. function hideInstructions() {
  30. $.cookie(cookieName, 1, { expires: 365 });
  31. $('#instructions').hide();
  32. $('#showinstructions').show();
  33. }
  34. $('#hideinstructions').click(hideInstructions);
  35. $('#showinstructions').click(showInstructions);
  36. if ($.cookie(cookieName)) {
  37. hideInstructions();
  38. }
  39. })();
  40. // Saving
  41. (function() {
  42. function saveLayout() {
  43. _gaq.push(['_trackEvent', 'General', 'Save']);
  44. layout.notes = $('#notecontent').val();
  45. $('#save_inprogress').show();
  46. $('#save_done').hide();
  47. $('#savecontainer').show();
  48. $('#save_error').hide();
  49. $.ajax({
  50. type: 'POST',
  51. url: 'res/data/layouts/new',
  52. data: {layout: JSON.stringify(layout)},
  53. success: function(res) {
  54. window.location.hash = res;
  55. var url = window.location.href;
  56. $('#link').children().remove();
  57. $('<a>').attr('href', url).text(url).appendTo($('#link'));
  58. $('#save_inprogress').hide();
  59. $('#save_done').show();
  60. },
  61. error: function(xhr, status, error) {
  62. $('#save_error').text('Save failed! Server said: ' + error).show();
  63. }
  64. });
  65. }
  66. function closeSave() {
  67. $('#savecontainer').hide();
  68. }
  69. $('#savelayout').click(saveLayout);
  70. $('#savemask').click(closeSave);
  71. $('#saveclose').click(closeSave);
  72. })();
  73. // Layout picker
  74. (function() {
  75. $.each(levels, function(key) {
  76. var name = this.name;
  77. $('<button>')
  78. .append($('<img>').attr('src', this.image))
  79. .append($('<p>').text(name))
  80. .click(function() {
  81. window.location.hash = '';
  82. _gaq.push(['_trackEvent', 'Level picker', 'Picked', name]);
  83. showBlankLayout(key + 1);
  84. closePicker();
  85. })
  86. .appendTo($('#layoutpicker .container'));
  87. });
  88. function showPicker() {
  89. _gaq.push(['_trackEvent', 'Level picker', 'Shown']);
  90. $('#layoutcontainer').show();
  91. }
  92. function closePicker() {
  93. $('#layoutcontainer').hide();
  94. }
  95. $('#createlayout').click(showPicker);
  96. $('#layoutmask').click(closePicker);
  97. $('#layoutclose').click(closePicker);
  98. })();
  99. // Address management
  100. (function() {
  101. function updateFromHash() {
  102. var id = window.location.hash;
  103. if (id === '') {
  104. showBlankLayout(1);
  105. } else if (id.substr(0,7) == '#blank:') {
  106. showBlankLayout(parseInt(id.substr(7)));
  107. } else {
  108. getLayout(id.substr(1));
  109. }
  110. }
  111. $(window).bind('hashchange', updateFromHash);
  112. updateFromHash();
  113. })();
  114. // Palette
  115. (function() {
  116. $.each(towers, function(key) {
  117. createBaseElForTower(key, this).appendTo($('#palette'));
  118. });
  119. $('.tower,.core').draggable({
  120. helper: 'clone',
  121. containment: 'document',
  122. stop: function(evt, ui) {
  123. if (!$(this).data('type')) {
  124. return;
  125. }
  126. var tower = {
  127. type: $(this).data('type'),
  128. rotation: 0,
  129. position: adjustMapOffset(ui.helper.offsetFrom('#mapcontainer'), thisLevel, 1)
  130. };
  131. layout.towers.push(tower);
  132. createElForTower(tower).appendTo($('#mapcontainer'));
  133. updateDefenseUnits();
  134. }
  135. });
  136. })();
  137. // Searching
  138. (function() {
  139. $('#search_classes img').click(function() {
  140. var el = $(this);
  141. if (el.hasClass('disabled')) {
  142. el.removeClass('disabled');
  143. } else {
  144. el.addClass('disabled');
  145. }
  146. });
  147. var sel = $('select[name=search_map]');
  148. $.each(levels, function(key) {
  149. $('<option>').val(key + 1).text(this.name).appendTo(sel);
  150. });
  151. function showSearch() {
  152. $('#searchcontainer').show();
  153. }
  154. function hideSearch() {
  155. $('#searchcontainer').hide();
  156. }
  157. function clearSearchResults() {
  158. $('#searchresults tbody tr').remove();
  159. }
  160. function buildSearchQuery() {
  161. var query = {};
  162. var level = $('select[name=search_map]').val();
  163. if (level != 'any') {
  164. query.map = level;
  165. }
  166. doSearch(query);
  167. return false;
  168. }
  169. function doSearch(data) {
  170. clearSearchResults();
  171. $.ajax({
  172. url: 'res/data/layouts/search',
  173. data: data,
  174. success: handleSearch
  175. });
  176. }
  177. function handleSearch(data) {
  178. var body = $('#searchresults tbody');
  179. $.each(data, function() {
  180. this.difficulty = this.difficulty || 'unknown';
  181. var tr = $('<tr>');
  182. tr.append($('<td>').append($('<a>').attr('href', '#' + this.id).text(this.id).click(hideSearch)));
  183. tr.append($('<td>').text(levels[this.level - 1] ? levels[this.level - 1].name : 'Unknown!'));
  184. tr.append($('<td>').addClass(this.difficulty).text(this.difficulty));
  185. tr.append($('<td>').addClass(this.type).text(this.type));
  186. body.append(tr);
  187. });
  188. }
  189. $('#search_submit').click(buildSearchQuery);
  190. $('#search').click(showSearch);
  191. $('#searchmask').click(hideSearch);
  192. $('#searchclose').click(hideSearch);
  193. })();
  194. var thisLevel;
  195. var layout;
  196. function updateDefenseUnits() {
  197. var used = 0;
  198. $.each(layout.towers, function() {
  199. used += towers[this.type].units;
  200. });
  201. $('#du_used').text(used);
  202. var hasClass = $('#du_wrapper').hasClass('over');
  203. $('#du_wrapper').removeClass('over');
  204. if (used > thisLevel.du) {
  205. $('#du_wrapper').addClass('over');
  206. if (!hasClass) {
  207. $('#du_wrapper').effect('pulsate', {times: 2}, 'fast');
  208. }
  209. }
  210. }
  211. function createElForCore() {
  212. return $('<img>')
  213. .attr('src', 'res/images/coreIcon.png')
  214. .attr('alt', 'Core')
  215. .addClass('core')
  216. .css('position', 'absolute')
  217. .css('height', (40 * thisLevel.towerscale) + 'px')
  218. .css('width', (40 * thisLevel.towerscale) + 'px');
  219. }
  220. function createBaseElForTower(key) {
  221. var type = towers[key];
  222. return $('<img>')
  223. .attr('src', type.image)
  224. .attr('alt', type.name)
  225. .data('type', key)
  226. .addClass(type.class.toLowerCase())
  227. .addClass('tower');
  228. }
  229. function createElForTower(tower) {
  230. return createBaseElForTower(tower.type)
  231. .data('tower', tower)
  232. .draggable({
  233. containment: 'document',
  234. start: function(evt) {
  235. return !evt.shiftKey;
  236. },
  237. stop: function() {
  238. var el = $(this);
  239. el.data('tower').position = adjustMapOffset({top: parseInt(el.css('top')), left: parseInt(el.css('left'))}, thisLevel, 1);
  240. }
  241. })
  242. .css('position', 'absolute')
  243. .css('height', (40 * thisLevel.towerscale * towers[tower.type].defaultscale) + 'px')
  244. .css('width', (40 * thisLevel.towerscale * towers[tower.type].defaultscale) + 'px')
  245. .offset(adjustMapOffset(tower.position, thisLevel))
  246. .rotate(tower.rotation)
  247. .dblclick(function() {
  248. layout.towers = $.grep(layout.towers, function(value) { return value != tower; });
  249. $(this).remove();
  250. updateDefenseUnits();
  251. })
  252. .mousedown(function(e) {
  253. if (!e.shiftKey) {
  254. return;
  255. }
  256. var el = $(this);
  257. var centre = el.offsetCentre();
  258. var mouseX = e.pageX - centre.left, mouseY = e.pageY - centre.top;
  259. var initialMouseAngle = Math.atan2(mouseY, mouseX);
  260. var initialRotation = tower.rotation;
  261. var moveHandler = function(evt) {
  262. var mouseX = evt.pageX - centre.left, mouseY = evt.pageY - centre.top;
  263. var newMouseAngle = Math.atan2(mouseY, mouseX);
  264. var mouseDelta = newMouseAngle - initialMouseAngle;
  265. var rotation = initialRotation + newMouseAngle * (180 / Math.PI);
  266. tower.rotation = rotation;
  267. el.rotate(rotation);
  268. };
  269. var upHandler = function() {
  270. $(document).unbind('mousemove', moveHandler);
  271. $(document).unbind('mouseup', upHandler);
  272. };
  273. $(document).mousemove(moveHandler);
  274. $(document).mouseup(upHandler);
  275. return false;
  276. });
  277. }
  278. function adjustMapOffset(towerOffset, level, reverse) {
  279. var res = $.extend({}, towerOffset);
  280. if (level.offsets && !reverse) {
  281. res.left += level.offsets.left;
  282. res.top += level.offsets.top;
  283. }
  284. if (level.scale) {
  285. if (reverse) {
  286. res.left /= level.scale.left;
  287. res.top /= level.scale.top;
  288. } else {
  289. res.left *= level.scale.left;
  290. res.top *= level.scale.top;
  291. }
  292. }
  293. if (level.offsets && reverse) {
  294. res.left -= level.offsets.left;
  295. res.top -= level.offsets.top;
  296. }
  297. return res;
  298. }
  299. function clearLayout() {
  300. $('#mapcontainer .tower').remove();
  301. if (layout) {
  302. layout.towers = [];
  303. }
  304. }
  305. function clearCores() {
  306. $('#mapcontainer .core').remove();
  307. }
  308. function updateLayout(data) {
  309. clearLayout();
  310. clearCores();
  311. layout = data;
  312. thisLevel = levels[layout.level - 1];
  313. $.each(thisLevel.cores, function() {
  314. createElForCore().offset(adjustMapOffset(this, thisLevel)).appendTo($('#mapcontainer'));
  315. });
  316. _gaq.push(['_setCustomVar', 1, 'Level', thisLevel.name, 1]);
  317. $('#mapcontainer').css('background-image', 'url("' + thisLevel.minimap + '")');
  318. $('#notecontent').val(layout.notes);
  319. $.each(layout.towers, function() {
  320. createElForTower(this).appendTo($('#mapcontainer'));
  321. });
  322. updateDefenseUnits();
  323. var difficulty = layout.difficulty ? layout.difficulty : "unknown";
  324. $('#difficulty').text(difficulty).removeClass().addClass(difficulty);
  325. var type = layout.type && layout.type != 'none' ? layout.type : "unknown";
  326. var modes = '';
  327. layout.mode && $.each(layout.mode, function() {
  328. if (this == "hardcore") {
  329. modes = '<abbr title="Hardcore">hc</abbr> ' + modes;
  330. } else if (this == "mixed") {
  331. modes = '<abbr title="Mixed mode">mm</abbr> ' + modes;
  332. } else if (this == "strategy") {
  333. modes = '<abbr title="Pure strategy">ps</abbr> ' + modes;
  334. } else if (this == "none") {
  335. modes = 'none';
  336. }
  337. });
  338. $('#type').text(type);
  339. $('#modes').html(modes == '' ? 'unknown' : modes);
  340. $('#du_total').text(thisLevel.du);
  341. }
  342. function getLayout(id) {
  343. _gaq.push(['_trackPageview', '/view/' + id]);
  344. $.getJSON('res/data/layouts/' + id + '.js', updateLayout);
  345. }
  346. function showBlankLayout(id) {
  347. window.location.hash = 'blank:' + id;
  348. _gaq.push(['_trackPageview', '/view/blank:' + id]);
  349. updateLayout({level: id, towers:[]});
  350. }
  351. });
  352. var levels = [
  353. {
  354. name: 'The Deeper Well',
  355. minimap: 'res/images/minimaps/Level1.png',
  356. image: 'res/images/levels/Level1.jpg',
  357. du: 60,
  358. offsets: {left: 130, top: 65},
  359. scale: {left: 1.19, top: 1.17},
  360. towerscape: 0.9,
  361. cores: [{left: 108, top: 576}]
  362. },
  363. {
  364. name: 'Foundries and Forges',
  365. minimap: 'res/images/minimaps/Level2.png',
  366. image: 'res/images/levels/Level2.jpg',
  367. du: 80,
  368. offsets: {left: 80, top: 35},
  369. scale: {left: 1.35, top: 1.35},
  370. towerscale: 1,
  371. cores: [{left: 286, top: 255}]
  372. },
  373. {
  374. name: 'Magus Quarters',
  375. minimap: 'res/images/minimaps/Level3.png',
  376. image: 'res/images/levels/Level3.jpg',
  377. du: 90,
  378. offsets: {left: 80, top: 45},
  379. scale: {left: 1.35, top: 1.35},
  380. towerscale: 1.2,
  381. cores: [{left: 283, top: 146}]
  382. },
  383. {
  384. name: 'Alchemical Laboratory',
  385. minimap: 'res/images/minimaps/Level4.png',
  386. image: 'res/images/levels/Level4.jpg',
  387. du: 85,
  388. offsets: {left: 280, top: 110},
  389. scale: {left: 0.92, top: 0.92},
  390. towerscale: 0.9,
  391. cores: [{left: 147, top: 422}]
  392. },
  393. {
  394. name: 'Servants Quarters',
  395. minimap: 'res/images/minimaps/Level5.png',
  396. image: 'res/images/levels/Level5.jpg',
  397. du: 85,
  398. offsets: {left: 115, top: 120},
  399. scale: {left: 1.17, top: 1.17},
  400. towerscale: 0.9,
  401. cores: [{left: 87, top: 290}, {left: 293, top: 335}]
  402. },
  403. {
  404. name: 'Castle Armory',
  405. minimap: 'res/images/minimaps/Level6.png',
  406. image: 'res/images/levels/Level6.jpg',
  407. du: 90,
  408. offsets: {left: 80, top: 45},
  409. scale: {left: 1.35, top: 1.34},
  410. towerscale: 1,
  411. cores: [{left: 234, top: 352}, {left: 341, top: 352}]
  412. },
  413. {
  414. name: 'Hall of Court',
  415. minimap: 'res/images/minimaps/Level7.png',
  416. image: 'res/images/levels/Level7.jpg',
  417. du: 100,
  418. offsets: {left: 35, top: 80},
  419. scale: {left: 1.45, top: 1.45},
  420. towerscale: 1,
  421. cores: [{left: 306, top: 264}, {left: 456, top: 264}]
  422. },
  423. {
  424. name: 'The Throne Room',
  425. minimap: 'res/images/minimaps/Level8.png',
  426. image: 'res/images/levels/Level8.jpg',
  427. du: 100,
  428. offsets: {left: 18, top: 130},
  429. scale: {left: 1.6, top: 1.5},
  430. towerscale: 1,
  431. cores: [{left: 288, top: 201}, {left: 288, top: 307}]
  432. },
  433. {
  434. name: 'Royal Gardens',
  435. minimap: 'res/images/minimaps/RoyalGardens.png',
  436. image: 'res/images/levels/Level9.jpg',
  437. du: 130,
  438. offsets: {left: 170, top: 55},
  439. scale: {left: 1.2, top: 1.2},
  440. towerscale: 0.75,
  441. cores: [{left: 175, top: 359}, {left: 322, top: 243}, {left: 322, top: 480}]
  442. },
  443. {
  444. name: 'The Ramparts',
  445. minimap: 'res/images/minimaps/Level9.png',
  446. image: 'res/images/levels/Level10.jpg',
  447. du: 110,
  448. offsets: {left: 142, top: 5},
  449. scale: {left: 1.08, top: 1.1},
  450. towerscale: 0.9,
  451. cores: [{left: 329, top: 540}, {left: 494, top: 300}, {left: 531, top: 330}]
  452. },
  453. {
  454. name: 'Endless Spires',
  455. minimap: 'res/images/minimaps/TheSpires.png',
  456. image: 'res/images/levels/Level11.jpg',
  457. du: 110,
  458. offsets: {left: 142, top: 57},
  459. scale: {left: 1.04, top: 1.04},
  460. towerscale: 0.65,
  461. cores: [{left: 420, top: 308}, {left: 424, top: 526}, {left: 262, top: 526}]
  462. },
  463. {
  464. name: 'The Summit',
  465. minimap: 'res/images/minimaps/TheSummit.png',
  466. image: 'res/images/levels/Level12.jpg',
  467. du: 150,
  468. offsets: {left: 200, top: 120},
  469. towerscale: 0.9,
  470. cores: [{left: 283, top: 362}, {left: 213, top: 548}, {left: 356, top: 547}]
  471. },
  472. {
  473. name: 'Glitterhelm Caverns',
  474. minimap: 'res/images/minimaps/caverns_minimap.png',
  475. image: 'http://placehold.it/200x100',
  476. du: 165,
  477. offsets: {left: 90, top: 35},
  478. scale: {left: 1.3, top: 1.27},
  479. towerscale: 0.8,
  480. cores: [{left: 230, top: 134}, {left: 234, top: 444}, {left: 413, top: 419}, {left: 38, top: 569}]
  481. },
  482. ];
  483. var towers = {
  484. 'spike': {name: 'Spike Blockade', image: 'res/images/towers/spikyBlockadeTower_Icon.png', class: 'Squire', units: 3, defaultscale: 1},
  485. 'bouncer': {name: 'Bouncer Blockade', image: 'res/images/towers/bouncerTower_Icon.png', class: 'Squire', units: 4, defaultscale: 1},
  486. 'harpoon': {name: 'Harpoon Turret', image: 'res/images/towers/harpoonTower_Icon.png', class: 'Squire', units: 6, defaultscale: 1},
  487. 'bowling': {name: 'Bowling Ball Turret', image: 'res/images/towers/bowlingBallTower_Icon.png', class: 'Squire', units: 7, defaultscale: 1},
  488. 'slice': {name: 'Slice N Dice Blockade', image: 'res/images/towers/slicerTower_Icon.png', class: 'Squire', units: 8},
  489. 'missile': {name: 'Magic Missile Tower', image: 'res/images/towers/missleTower_Icon.png', class: 'Apprentice', units: 3, defaultscale: 1},
  490. 'blockade': {name: 'Magic Blockade', image: 'res/images/towers/blockadeTower_Icon.png', class: 'Apprentice', units: 1, defaultscale: 1},
  491. 'fireball': {name: 'Fireball Tower', image: 'res/images/towers/fireTower_Icon.png', class: 'Apprentice', units: 5, defaultscale: 1},
  492. 'lightning': {name: 'Lightning Tower', image: 'res/images/towers/lightningTower_Icon.png', class: 'Apprentice', units: 7, defaultscale: 1},
  493. 'striker': {name: 'Deadly Striker Tower', image: 'res/images/towers/strikerTower_Icon.png', class: 'Apprentice', units: 8, defaultscale: 1},
  494. 'ensnare': {name: 'Ensnare Aura', image: 'res/images/towers/stickyGoopAura_Icon.png', class: 'Monk', units: 3, defaultscale: 2.5},
  495. 'electric': {name: 'Electric Aura', image: 'res/images/towers/deathlyHallowsAura_Icon.png', class: 'Monk', units: 5, defaultscale: 2.5},
  496. 'healing': {name: 'Healing Aura', image: 'res/images/towers/healingAura_Icon.png', class: 'Monk', units: 5, defaultscale: 2.5},
  497. 'drain': {name: 'Strength Drain Aura', image: 'res/images/towers/strengthDrainAura_Icon.png', class: 'Monk', units: 6, defaultscale: 2.5},
  498. 'enrage': {name: 'Enrage Aura', image: 'res/images/towers/enrageAura_Icon.png', class: 'Monk', units: 5, defaultscale: 2.5},
  499. 'gas': {name: 'Gas Trap', image: 'res/images/towers/gasTrap_Icon.png', class: 'Huntress', units: 3, defaultscale: 1},
  500. 'mine': {name: 'Proximity Mine Trap', image: 'res/images/towers/proxMineTrap_Icon.png', class: 'Huntress', units: 3, defaultscale: 1},
  501. 'inferno': {name: 'Inferno Trap', image: 'res/images/towers/infernoTrap_Icon.png', class: 'Huntress', units: 4, defaultscale: 1},
  502. 'etheral': {name: 'Etheral Spike Trap', image: 'res/images/towers/etherialSpikeTrap_Icon.png', class: 'Huntress', units: 3, defaultscale: 1},
  503. 'darkness': {name: 'Darkness Trap', image: 'res/images/towers/darknessTrap_Icon.png', class: 'Huntress', units: 6, defaultscale: 1},
  504. };