Advertisement
Guest User

Untitled

a guest
May 25th, 2019
150
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 256.18 KB | None | 0 0
  1. /*jslint node: true */
  2. /*jshint -W061 */
  3. /*global goog, Map, let */
  4. "use strict";
  5.  
  6. // General requires
  7.  
  8. var _this = this;
  9.  
  10. require('google-closure-library');
  11. goog.require('goog.structs.PriorityQueue');
  12. goog.require('goog.structs.QuadTree');
  13.  
  14. // Import game settings.
  15. const c = require('../../config.json');
  16.  
  17. // Import utilities.
  18. const util = require('./lib/util');
  19. const ran = require('./lib/random');
  20. const hshg = require('./lib/hshg');
  21. const now = require('performance-now');
  22. const nano = require('nanotimer');
  23.  
  24. let timer = new nano();
  25.  
  26. // Let's get a cheaper array removal thing
  27. Array.prototype.remove = index => {
  28. if (index === _this.length - 1) {
  29. return _this.pop();
  30. } else {
  31. let r = _this[index];
  32. _this[index] = _this.pop();
  33. return r;
  34. }
  35. };
  36.  
  37. var placedDodecagon = false;
  38.  
  39. // Define player keys
  40.  
  41. var keys = ['ttoken1', 'ttoken2jerky', 'treejerky', 'treebacon', 'baconjerkey', 'treedirt', 'bacontreecatjerky', 'vector', 'fishy', ':b:uff:b:ate', 'panda'];
  42.  
  43. global.fps = "Unknown";
  44. var roomSpeed = c.gameSpeed;
  45.  
  46. let lastTime = now();
  47. let timestep = 1;
  48.  
  49. var previousTick = now();
  50.  
  51. const room = {
  52. lastCycle: undefined,
  53. cycleSpeed: 1000 / roomSpeed / 20,
  54. width: c.WIDTH,
  55. height: c.HEIGHT,
  56. setup: c.ROOM_SETUP,
  57. xgrid: c.X_GRID,
  58. ygrid: c.Y_GRID,
  59. gameMode: c.MODE,
  60. skillBoost: c.SKILL_BOOST,
  61. scale: {
  62. square: c.WIDTH * c.HEIGHT / 100000000,
  63. linear: Math.sqrt(c.WIDTH * c.HEIGHT / 100000000)
  64. },
  65. maxFood: c.WIDTH * c.HEIGHT / 100000 * c.FOOD_AMOUNT,
  66. isInRoom: location => {
  67. return location.x < 0 || location.x > c.WIDTH || location.y < 0 || location.y > c.HEIGHT ? false : true;
  68. },
  69. topPlayerID: -1
  70. };
  71. room.findType = type => {
  72. let output = [];
  73. let j = 0;
  74. let x = 0;
  75. const lengthrow = room.setup.length;
  76. //room.setup.forEach(row => {
  77. for (; x < lengthrow; x++) {
  78. let i = 0;
  79. let y = 0;
  80. const lengthcell = room.setup[x].length;
  81. //row.forEach(cell => {
  82. for (; y < lengthcell; y++) {
  83. if (room.setup[x][y] === type) {
  84. output.push({ x: (i + 0.5) * room.width / room.xgrid, y: (j + 0.5) * room.height / room.ygrid });
  85. }
  86. i++;
  87. }
  88. j++;
  89. }
  90. room[type] = output;
  91. };
  92. room.findType('nest');
  93. room.findType('norm');
  94. room.findType('bas1');
  95. room.findType('bas2');
  96. room.findType('bas3');
  97. room.findType('bas4');
  98. room.findType('bas5');
  99. room.findType('bas6');
  100. room.findType('roid');
  101. room.findType('rock');
  102. room.nestFoodAmount = 1.5 * Math.sqrt(room.nest.length) / room.xgrid / room.ygrid;
  103. room.random = () => {
  104. return {
  105. x: ran.irandom(room.width),
  106. y: ran.irandom(room.height)
  107. };
  108. };
  109. room.randomType = type => {
  110. let selection = room[type][ran.irandom(room[type].length - 1)];
  111. return {
  112. x: ran.irandom(0.5 * room.width / room.xgrid) * ran.choose([-1, 1]) + selection.x,
  113. y: ran.irandom(0.5 * room.height / room.ygrid) * ran.choose([-1, 1]) + selection.y
  114. };
  115. };
  116. room.gauss = clustering => {
  117. let output;
  118. do {
  119. output = {
  120. x: ran.gauss(room.width / 2, room.height / clustering),
  121. y: ran.gauss(room.width / 2, room.height / clustering)
  122. };
  123. } while (!room.isInRoom(output));
  124. };
  125. room.gaussInverse = clustering => {
  126. let output;
  127. do {
  128. output = {
  129. x: ran.gaussInverse(0, room.width, clustering),
  130. y: ran.gaussInverse(0, room.height, clustering)
  131. };
  132. } while (!room.isInRoom(output));
  133. return output;
  134. };
  135. room.gaussRing = (radius, clustering) => {
  136. let output;
  137. do {
  138. output = ran.gaussRing(room.width * radius, clustering);
  139. output = {
  140. x: output.x + room.width / 2,
  141. y: output.y + room.height / 2
  142. };
  143. } while (!room.isInRoom(output));
  144. return output;
  145. };
  146. room.isIn = (type, location) => {
  147. if (room.isInRoom(location)) {
  148. let a = Math.floor(location.y * room.ygrid / room.height);
  149. let b = Math.floor(location.x * room.xgrid / room.width);
  150. return type === room.setup[a][b];
  151. } else {
  152. return false;
  153. }
  154. };
  155. room.isInNorm = location => {
  156. if (room.isInRoom(location)) {
  157. let a = Math.floor(location.y * room.ygrid / room.height);
  158. let b = Math.floor(location.x * room.xgrid / room.width);
  159. let v = room.setup[a][b];
  160. return v !== 'nest';
  161. } else {
  162. return false;
  163. }
  164. };
  165. room.gaussType = (type, clustering) => {
  166. let selection = room[type][ran.irandom(room[type].length - 1)];
  167. let location = {};
  168. do {
  169. location = {
  170. x: ran.gauss(selection.x, room.width / room.xgrid / clustering),
  171. y: ran.gauss(selection.y, room.height / room.ygrid / clustering)
  172. };
  173. } while (!room.isIn(type, location));
  174. return location;
  175. };
  176. util.log(room.width + ' x ' + room.height + ' room initalized. Max food: ' + room.maxFood + ', max nest food: ' + room.maxFood * room.nestFoodAmount + '.');
  177.  
  178. // Define a vector
  179. class Vector {
  180. constructor(x, y) {
  181. //Vector constructor.
  182. this.x = x;
  183. this.y = y;
  184. }
  185.  
  186. update() {
  187. this.len = this.length;
  188. this.dir = this.direction;
  189. }
  190.  
  191. get length() {
  192. return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
  193. }
  194.  
  195. get direction() {
  196. return Math.atan2(this.y, this.x);
  197. }
  198. }
  199. function nullVector(v) {
  200. v.x = 0;v.y = 0; //this guy's useful
  201. }
  202.  
  203. // Get class definitions and index them
  204. var Class = (() => {
  205. let def = require('./lib/definitions'),
  206. i = 0;
  207. for (let k in def) {
  208. if (!def.hasOwnProperty(k)) continue;
  209. def[k].index = i++;
  210. }
  211. return def;
  212. })();
  213.  
  214. // Define IOs (AI)
  215. function nearest(array, location, test = () => {
  216. return true;
  217. }) {
  218. let list = new goog.structs.PriorityQueue();
  219. let d;
  220. if (!array.length) {
  221. return undefined;
  222. }
  223. array.forEach(function (instance) {
  224. d = Math.pow(instance.x - location.x, 2) + Math.pow(instance.y - location.y, 2);
  225. if (test(instance, d)) {
  226. list.enqueue(d, instance);
  227. }
  228. });
  229. return list.dequeue();
  230. }
  231. function timeOfImpact(p, v, s) {
  232. // Requires relative position and velocity to aiming point
  233. let a = s * s - (v.x * v.x + v.y * v.y);
  234. let b = p.x * v.x + p.y * v.y;
  235. let c = p.x * p.x + p.y * p.y;
  236.  
  237. let d = b * b + a * c;
  238.  
  239. let t = 0;
  240. if (d >= 0) {
  241. t = Math.max(0, (b + Math.sqrt(d)) / a);
  242. }
  243.  
  244. return t * 0.9;
  245. }
  246.  
  247. class IO {
  248. constructor(body) {
  249. this.body = body;
  250. this.acceptsFromTop = true;
  251. }
  252.  
  253. think() {
  254. return {
  255. target: null,
  256. goal: null,
  257. fire: null,
  258. main: null,
  259. alt: null,
  260. power: null
  261. };
  262. }
  263. }
  264. class io_doNothing extends IO {
  265. constructor(body) {
  266. super(body);
  267. this.acceptsFromTop = false;
  268. }
  269.  
  270. think() {
  271. return {
  272. goal: {
  273. x: this.body.x,
  274. y: this.body.y
  275. },
  276. main: false,
  277. alt: false,
  278. fire: false
  279. };
  280. }
  281. }
  282. class io_moveInCircles extends IO {
  283. constructor(body) {
  284. super(body);
  285. this.acceptsFromTop = false;
  286. this.timer = ran.irandom(10) + 3;
  287. this.goal = {
  288. x: this.body.x + 10 * Math.cos(-this.body.facing),
  289. y: this.body.y + 10 * Math.sin(-this.body.facing)
  290. };
  291. }
  292.  
  293. think() {
  294. if (!this.timer--) {
  295. this.timer = 10;
  296. this.goal = {
  297. x: this.body.x + 10 * Math.cos(-this.body.facing),
  298. y: this.body.y + 10 * Math.sin(-this.body.facing)
  299. };
  300. }
  301. return { goal: this.goal };
  302. }
  303. }
  304. class io_listenToPlayer extends IO {
  305. constructor(b, p) {
  306. super(b);
  307. this.player = p;
  308. this.acceptsFromTop = false;
  309. }
  310.  
  311. // THE PLAYER MUST HAVE A VALID COMMAND AND TARGET OBJECT
  312.  
  313. think() {
  314. let targ = {
  315. x: this.player.target.x,
  316. y: this.player.target.y
  317. };
  318. if (this.player.command.autospin) {
  319. let kk = Math.atan2(this.body.control.target.y, this.body.control.target.x) + 0.02;
  320. targ = {
  321. x: 100 * Math.cos(kk),
  322. y: 100 * Math.sin(kk)
  323. };
  324. }
  325. if (this.body.invuln) {
  326. if (this.player.command.right || this.player.command.left || this.player.command.up || this.player.command.down || this.player.command.lmb) {
  327. this.body.invuln = false;
  328. }
  329. }
  330. this.body.autoOverride = this.player.command.override;
  331. return {
  332. target: targ,
  333. goal: {
  334. x: this.body.x + this.player.command.right - this.player.command.left,
  335. y: this.body.y + this.player.command.down - this.player.command.up
  336. },
  337. fire: this.player.command.lmb || this.player.command.autofire,
  338. main: this.player.command.lmb || this.player.command.autospin || this.player.command.autofire,
  339. alt: this.player.command.rmb
  340. };
  341. }
  342. }
  343. class io_mapTargetToGoal extends IO {
  344. constructor(b) {
  345. super(b);
  346. }
  347.  
  348. think(input) {
  349. if (input.main || input.alt) {
  350. return {
  351. goal: {
  352. x: input.target.x + this.body.x,
  353. y: input.target.y + this.body.y
  354. },
  355. power: 1
  356. };
  357. }
  358. }
  359. }
  360. class io_boomerang extends IO {
  361. constructor(b) {
  362. super(b);
  363. this.r = 0;
  364. this.b = b;
  365. this.m = b.master;
  366. this.turnover = false;
  367. let len = 10 * util.getDistance({ x: 0, y: 0 }, b.master.control.target);
  368. this.myGoal = {
  369. x: 3 * b.master.control.target.x + b.master.x,
  370. y: 3 * b.master.control.target.y + b.master.y
  371. };
  372. }
  373. think(input) {
  374. if (this.b.range > this.r) this.r = this.b.range;
  375. let t = 1 - Math.sin(2 * Math.PI * this.b.range / this.r) || 1; //1 - Math.sin(2 * Math.PI * this.b.range / this.r) || 1;
  376. if (!this.turnover) {
  377. if (this.r && this.b.range < this.r * 0.5) {
  378. this.turnover = true;
  379. }
  380. return {
  381. goal: this.myGoal,
  382. power: t
  383. };
  384. } else {
  385. return {
  386. goal: {
  387. x: this.m.x,
  388. y: this.m.y
  389. },
  390. power: t
  391. };
  392. }
  393. }
  394. }
  395. class io_goToMasterTarget extends IO {
  396. constructor(body) {
  397. super(body);
  398. this.myGoal = {
  399. x: body.master.control.target.x + body.master.x,
  400. y: body.master.control.target.y + body.master.y
  401. };
  402. this.countdown = 5;
  403. }
  404.  
  405. think() {
  406. if (this.countdown) {
  407. if (util.getDistance(this.body, this.myGoal) < 1) {
  408. this.countdown--;
  409. }
  410. return {
  411. goal: {
  412. x: this.myGoal.x,
  413. y: this.myGoal.y
  414. }
  415. };
  416. }
  417. }
  418. }
  419. class io_canRepel extends IO {
  420. constructor(b) {
  421. super(b);
  422. }
  423.  
  424. think(input) {
  425. if (input.alt && input.target) {
  426. return {
  427. target: {
  428. x: -input.target.x,
  429. y: -input.target.y
  430. },
  431. main: true
  432. };
  433. }
  434. }
  435. }
  436. class io_alwaysFire extends IO {
  437. constructor(body) {
  438. super(body);
  439. }
  440.  
  441. think() {
  442. return {
  443. fire: true
  444. };
  445. }
  446. }
  447. class io_targetSelf extends IO {
  448. constructor(body) {
  449. super(body);
  450. }
  451.  
  452. think() {
  453. return {
  454. main: true,
  455. target: { x: 0, y: 0 }
  456. };
  457. }
  458. }
  459. class io_mapAltToFire extends IO {
  460. constructor(body) {
  461. super(body);
  462. }
  463.  
  464. think(input) {
  465. if (input.alt) {
  466. return {
  467. fire: true
  468. };
  469. }
  470. }
  471. }
  472. class io_onlyAcceptInArc extends IO {
  473. constructor(body) {
  474. super(body);
  475. }
  476.  
  477. think(input) {
  478. if (input.target && this.body.firingArc != null) {
  479. if (Math.abs(util.angleDifference(Math.atan2(input.target.y, input.target.x), this.body.firingArc[0])) >= this.body.firingArc[1]) {
  480. return {
  481. fire: false,
  482. alt: false,
  483. main: false
  484. };
  485. }
  486. }
  487. }
  488. }
  489. class io_nearestDifferentMaster extends IO {
  490. constructor(body) {
  491. super(body);
  492. this.targetLock = undefined;
  493. this.tick = ran.irandom(30);
  494. this.lead = 0;
  495. this.validTargets = this.buildList(body.fov);
  496. this.oldHealth = body.health.display();
  497. }
  498.  
  499. buildList(range) {
  500. // Establish whom we judge in reference to
  501. let m = { x: this.body.x, y: this.body.y },
  502. mm = { x: this.body.master.master.x, y: this.body.master.master.y },
  503. mostDangerous = 0,
  504. sqrRange = range * range,
  505. keepTarget = false;
  506. // Filter through everybody...
  507. let out = entities.map(e => {
  508. // Only look at those within our view, and our parent's view, not dead, not our kind, not a bullet/trap/block etc
  509. if (e.health.amount > 0) {
  510. if (!e.invuln) {
  511. if (e.alpha > 0.015) {
  512. if (e.master.master.team !== this.body.master.master.team) {
  513. if (e.master.master.team !== -101) {
  514. if (e.type === 'tank' || e.type === 'crasher' || !this.body.aiSettings.shapefriend && e.type === 'food') {
  515. if (Math.abs(e.x - m.x) < range && Math.abs(e.y - m.y) < range) {
  516. if (!this.body.aiSettings.blind || Math.abs(e.x - mm.x) < range && Math.abs(e.y - mm.y) < range) return e;
  517. }
  518. }
  519. }
  520. }
  521. }
  522. }
  523. }
  524. }).filter(e => {
  525. return e;
  526. });
  527.  
  528. if (!out.length) return [];
  529.  
  530. out = out.map(e => {
  531. // Only look at those within range and arc (more expensive, so we only do it on the few)
  532. let yaboi = false;
  533. if (Math.pow(this.body.x - e.x, 2) + Math.pow(this.body.y - e.y, 2) < sqrRange) {
  534. if (this.body.firingArc == null || this.body.aiSettings.view360) {
  535. yaboi = true;
  536. } else if (Math.abs(util.angleDifference(util.getDirection(this.body, e), this.body.firingArc[0])) < this.body.firingArc[1]) yaboi = true;
  537. }
  538. if (yaboi) {
  539. mostDangerous = Math.max(e.dangerValue, mostDangerous);
  540. return e;
  541. }
  542. }).filter(e => {
  543. // Only return the highest tier of danger
  544. if (e != null) {
  545. if (this.body.aiSettings.farm || e.dangerValue === mostDangerous) {
  546. if (this.targetLock) {
  547. if (e.id === this.targetLock.id) keepTarget = true;
  548. }
  549. return e;
  550. }
  551. }
  552. });
  553. // Reset target if it's not in there
  554. if (!keepTarget) this.targetLock = undefined;
  555. return out;
  556. }
  557.  
  558. think(input) {
  559. // Override target lock upon other commands
  560. if (input.main || input.alt || this.body.master.autoOverride) {
  561. this.targetLock = undefined;return {};
  562. }
  563. // Otherwise, consider how fast we can either move to ram it or shoot at a potiential target.
  564. let tracking = this.body.topSpeed,
  565. range = this.body.fov;
  566. // Use whether we have functional guns to decide
  567. for (let i = 0; i < this.body.guns.length; i++) {
  568. if (this.body.guns[i].canShoot && !this.body.aiSettings.skynet) {
  569. let v = this.body.guns[i].getTracking();
  570. tracking = v.speed;
  571. range = Math.min(range, v.speed * v.range);
  572. break;
  573. }
  574. }
  575. // Check if my target's alive
  576. if (this.targetLock) {
  577. if (this.targetLock.health.amount <= 0) {
  578. this.targetLock = undefined;
  579. this.tick = 100;
  580. }
  581. }
  582. // Think damn hard
  583. if (this.tick++ > 15 * roomSpeed) {
  584. this.tick = 0;
  585. this.validTargets = this.buildList(range);
  586. // Ditch our old target if it's invalid
  587. if (this.targetLock && this.validTargets.indexOf(this.targetLock) === -1) {
  588. this.targetLock = undefined;
  589. }
  590. // Lock new target if we still don't have one.
  591. if (this.targetLock == null && this.validTargets.length) {
  592. this.targetLock = this.validTargets.length === 1 ? this.validTargets[0] : nearest(this.validTargets, { x: this.body.x, y: this.body.y });
  593. this.tick = -90;
  594. }
  595. }
  596. // Lock onto whoever's shooting me.
  597. // let damageRef = (this.body.bond == null) ? this.body : this.body.bond;
  598. // if (damageRef.collisionArray.length && damageRef.health.display() < this.oldHealth) {
  599. // this.oldHealth = damageRef.health.display();
  600. // if (this.validTargets.indexOf(damageRef.collisionArray[0]) === -1) {
  601. // this.targetLock = (damageRef.collisionArray[0].master.id === -1) ? damageRef.collisionArray[0].source : damageRef.collisionArray[0].master;
  602. // }
  603. // }
  604. // Consider how fast it's moving and shoot at it
  605. if (this.targetLock != null) {
  606. let radial = this.targetLock.velocity;
  607. let diff = {
  608. x: this.targetLock.x - this.body.x,
  609. y: this.targetLock.y - this.body.y
  610. };
  611. /// Refresh lead time
  612. if (this.tick % 4 === 0) {
  613. this.lead = 0;
  614. // Find lead time (or don't)
  615. if (!this.body.aiSettings.chase) {
  616. let toi = timeOfImpact(diff, radial, tracking);
  617. this.lead = toi;
  618. }
  619. }
  620. // And return our aim
  621. return {
  622. target: {
  623. x: diff.x + this.lead * radial.x,
  624. y: diff.y + this.lead * radial.y
  625. },
  626. fire: true,
  627. main: true
  628. };
  629. }
  630. return {};
  631. }
  632. }
  633. class io_avoid extends IO {
  634. constructor(body) {
  635. super(body);
  636. }
  637.  
  638. think(input) {
  639. let masterId = this.body.master.id;
  640. let range = this.body.size * this.body.size * 100;
  641. this.avoid = nearest(entities, { x: this.body.x, y: this.body.y }, function (test, sqrdst) {
  642. return test.master.id !== masterId && (test.type === 'bullet' || test.type === 'drone' || test.type === 'swarm' || test.type === 'trap' || test.type === 'block') && sqrdst < range;
  643. });
  644. // Aim at that target
  645. if (this.avoid != null) {
  646. // Consider how fast it's moving.
  647. let delt = new Vector(this.body.velocity.x - this.avoid.velocity.x, this.body.velocity.y - this.avoid.velocity.y);
  648. let diff = new Vector(this.avoid.x - this.body.x, this.avoid.y - this.body.y);
  649. let comp = (delt.x * diff.x + delt.y * diff.y) / delt.length / diff.length;
  650. let goal = {};
  651. if (comp > 0) {
  652. if (input.goal) {
  653. let goalDist = Math.sqrt(range / (input.goal.x * input.goal.x + input.goal.y * input.goal.y));
  654. goal = {
  655. x: input.goal.x * goalDist - diff.x * comp,
  656. y: input.goal.y * goalDist - diff.y * comp
  657. };
  658. } else {
  659. goal = {
  660. x: -diff.x * comp,
  661. y: -diff.y * comp
  662. };
  663. }
  664. return goal;
  665. }
  666. }
  667. }
  668. }
  669. class io_minion extends IO {
  670. constructor(body) {
  671. super(body);
  672. this.turnwise = 1;
  673. }
  674.  
  675. think(input) {
  676. if (this.body.aiSettings.reverseDirection && ran.chance(0.005)) {
  677. this.turnwise = -1 * this.turnwise;
  678. }
  679. if (input.target != null && (input.alt || input.main)) {
  680. let sizeFactor = Math.sqrt(this.body.master.size / this.body.master.SIZE);
  681. let leash = 60 * sizeFactor;
  682. let orbit = 120 * sizeFactor;
  683. let repel = 135 * sizeFactor;
  684. let goal;
  685. let power = 1;
  686. let target = new Vector(input.target.x, input.target.y);
  687. if (input.alt) {
  688. // Leash
  689. if (target.length < leash) {
  690. goal = {
  691. x: this.body.x + target.x,
  692. y: this.body.y + target.y
  693. };
  694. // Spiral repel
  695. } else if (target.length < repel) {
  696. let dir = -this.turnwise * target.direction + Math.PI / 5;
  697. goal = {
  698. x: this.body.x + Math.cos(dir),
  699. y: this.body.y + Math.sin(dir)
  700. };
  701. // Free repel
  702. } else {
  703. goal = {
  704. x: this.body.x - target.x,
  705. y: this.body.y - target.y
  706. };
  707. }
  708. } else if (input.main) {
  709. // Orbit point
  710. let dir = this.turnwise * target.direction + 0.01;
  711. goal = {
  712. x: this.body.x + target.x - orbit * Math.cos(dir),
  713. y: this.body.y + target.y - orbit * Math.sin(dir)
  714. };
  715. if (Math.abs(target.length - orbit) < this.body.size * 2) {
  716. power = 0.7;
  717. }
  718. }
  719. return {
  720. goal: goal,
  721. power: power
  722. };
  723. }
  724. }
  725. }
  726. class io_hangOutNearMaster extends IO {
  727. constructor(body) {
  728. super(body);
  729. this.acceptsFromTop = false;
  730. this.orbit = 30;
  731. this.currentGoal = { x: this.body.source.x, y: this.body.source.y };
  732. this.timer = 0;
  733. }
  734. think(input) {
  735. if (this.body.source != this.body) {
  736. let bound1 = this.orbit * 0.8 + this.body.source.size + this.body.size;
  737. let bound2 = this.orbit * 1.5 + this.body.source.size + this.body.size;
  738. let dist = util.getDistance(this.body, this.body.source) + Math.PI / 8;
  739. let output = {
  740. target: {
  741. x: this.body.velocity.x,
  742. y: this.body.velocity.y
  743. },
  744. goal: this.currentGoal,
  745. power: undefined
  746. };
  747. // Set a goal
  748. if (dist > bound2 || this.timer > 30) {
  749. this.timer = 0;
  750.  
  751. let dir = util.getDirection(this.body, this.body.source) + Math.PI * ran.random(0.5);
  752. let len = ran.randomRange(bound1, bound2);
  753. let x = this.body.source.x - len * Math.cos(dir);
  754. let y = this.body.source.y - len * Math.sin(dir);
  755. this.currentGoal = {
  756. x: x,
  757. y: y
  758. };
  759. }
  760. if (dist < bound2) {
  761. output.power = 0.15;
  762. if (ran.chance(0.3)) {
  763. this.timer++;
  764. }
  765. }
  766. return output;
  767. }
  768. }
  769. }
  770. class io_spin extends IO {
  771. constructor(b) {
  772. super(b);
  773. this.a = 0;
  774. }
  775.  
  776. think(input) {
  777. this.a += 0.05;
  778. let offset = 0;
  779. if (this.body.bond != null) {
  780. offset = this.body.bound.angle;
  781. }
  782. return {
  783. target: {
  784. x: Math.cos(this.a + offset),
  785. y: Math.sin(this.a + offset)
  786. },
  787. main: true
  788. };
  789. }
  790. }
  791. class io_fastspin extends IO {
  792. constructor(b) {
  793. super(b);
  794. this.a = 0;
  795. }
  796.  
  797. think(input) {
  798. this.a += 0.072;
  799. let offset = 0;
  800. if (this.body.bond != null) {
  801. offset = this.body.bound.angle;
  802. }
  803. return {
  804. target: {
  805. x: Math.cos(this.a + offset),
  806. y: Math.sin(this.a + offset)
  807. },
  808. main: true
  809. };
  810. }
  811. }
  812. class io_testspin extends IO {
  813. constructor(b) {
  814. super(b);
  815. this.a = 0;
  816. }
  817.  
  818. think(input) {
  819. this.a += 0.04;
  820. let offset = Math.sin(this.a);
  821. if (this.body.bond != null) {
  822. offset = this.body.bound.angle;
  823. }
  824. return {
  825. target: {
  826. x: Math.cos(this.a + offset),
  827. y: Math.sin(this.a + offset)
  828. },
  829. main: true
  830. };
  831. }
  832. }
  833. class io_reversespin extends IO {
  834. constructor(b) {
  835. super(b);
  836. this.a = 0;
  837. }
  838.  
  839. think(input) {
  840. this.a -= 0.05;
  841. let offset = 0;
  842. if (this.body.bond != null) {
  843. offset = this.body.bound.angle;
  844. }
  845. return {
  846. target: {
  847. x: Math.cos(this.a + offset),
  848. y: Math.sin(this.a + offset)
  849. },
  850. main: true
  851. };
  852. }
  853. }
  854. class io_dontTurn extends IO {
  855. constructor(b) {
  856. super(b);
  857. }
  858.  
  859. think(input) {
  860. return {
  861. target: {
  862. x: 1,
  863. y: 0
  864. },
  865. main: true
  866. };
  867. }
  868. }
  869. class io_fleeAtLowHealth extends IO {
  870. constructor(b) {
  871. super(b);
  872. this.fear = util.clamp(ran.gauss(0.7, 0.15), 0.1, 0.9);
  873. }
  874.  
  875. think(input) {
  876. if (input.fire && input.target != null && this.body.health.amount < this.body.health.max * this.fear) {
  877. return {
  878. goal: {
  879. x: this.body.x - input.target.x,
  880. y: this.body.y - input.target.y
  881. }
  882. };
  883. }
  884. }
  885.  
  886. }
  887.  
  888. /***** ENTITIES *****/
  889. // Define skills
  890. const skcnv = {
  891. rld: 0,
  892. pen: 1,
  893. str: 2,
  894. dam: 3,
  895. spd: 4,
  896.  
  897. shi: 5,
  898. atk: 6,
  899. hlt: 7,
  900. rgn: 8,
  901. mob: 9
  902. };
  903. const levelers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 38, 40, 42, 44];
  904. class Skill {
  905. constructor(inital = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) {
  906. // Just skill stuff.
  907. this.changed = true;
  908. this.raw = inital;
  909. this.caps = [];
  910. this.setCaps([c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL, c.MAX_SKILL]);
  911. this.name = ['Reload', 'Bullet Penetration', 'Bullet Health', 'Bullet Damage', 'Bullet Speed', 'Shield Capacity', 'Body Damage', 'Max Health', 'Shield Regeneration', 'Movement Speed'];
  912. this.atk = 0;
  913. this.hlt = 0;
  914. this.spd = 0;
  915. this.str = 0;
  916. this.pen = 0;
  917. this.dam = 0;
  918. this.rld = 0;
  919. this.mob = 0;
  920. this.rgn = 0;
  921. this.shi = 0;
  922. this.rst = 0;
  923. this.brst = 0;
  924. this.ghost = 0;
  925. this.acl = 0;
  926.  
  927. this.reset();
  928. }
  929.  
  930. reset() {
  931. this.points = 0;
  932. this.score = 0;
  933. this.deduction = 0;
  934. this.level = 0;
  935. this.canUpgrade = false;
  936. this.update();
  937. this.maintain();
  938. }
  939.  
  940. update() {
  941. let curve = (() => {
  942. function make(x) {
  943. return Math.log(4 * x + 1) / Math.log(5);
  944. }
  945. let a = [];
  946. for (let i = 0; i < c.MAX_SKILL * 2; i++) {
  947. a.push(make(i / c.MAX_SKILL));
  948. }
  949. // The actual lookup function
  950. return x => {
  951. return a[x * c.MAX_SKILL];
  952. };
  953. })();
  954. function apply(f, x) {
  955. return x < 0 ? 1 / (1 - x * f) : f * x + 1;
  956. }
  957. for (let i = 0; i < 10; i++) {
  958. if (this.raw[i] > this.caps[i]) {
  959. this.points += this.raw[i] - this.caps[i];
  960. this.raw[i] = this.caps[i];
  961. }
  962. }
  963. let attrib = [];
  964. for (let i = 0; i < 5; i++) {
  965. for (let j = 0; j < 2; j += 1) {
  966. attrib[i + 5 * j] = curve((this.raw[i + 5 * j] + this.bleed(i, j)) / c.MAX_SKILL);
  967. }
  968. }
  969. this.rld = Math.pow(0.5, attrib[skcnv.rld]);
  970. this.pen = apply(2.5, attrib[skcnv.pen]);
  971. this.str = apply(2, attrib[skcnv.str]);
  972. this.dam = apply(3, attrib[skcnv.dam]);
  973. this.spd = (0.5 + apply(1.5, attrib[skcnv.spd])) / 3;
  974.  
  975. this.acl = apply(0.5, attrib[skcnv.rld]);
  976.  
  977. this.rst = 0.5 * attrib[skcnv.str] + 2.5 * attrib[skcnv.pen];
  978. this.ghost = attrib[skcnv.pen];
  979.  
  980. this.shi = c.GLASS_HEALTH_FACTOR * apply(3 / c.GLASS_HEALTH_FACTOR - 1, attrib[skcnv.shi]);
  981. this.atk = apply(1, attrib[skcnv.atk]);
  982. this.hlt = c.GLASS_HEALTH_FACTOR * apply(2 / c.GLASS_HEALTH_FACTOR - 1, attrib[skcnv.hlt]);
  983. this.mob = apply(0.8, attrib[skcnv.mob]);
  984. this.rgn = apply(25, attrib[skcnv.rgn]);
  985.  
  986. this.brst = 0.3 * (0.5 * attrib[skcnv.atk] + 0.5 * attrib[skcnv.hlt] + attrib[skcnv.rgn]);
  987. this.changed = true;
  988. }
  989.  
  990. set(thing) {
  991. this.raw[0] = thing[0];
  992. this.raw[1] = thing[1];
  993. this.raw[2] = thing[2];
  994. this.raw[3] = thing[3];
  995. this.raw[4] = thing[4];
  996. this.raw[5] = thing[5];
  997. this.raw[6] = thing[6];
  998. this.raw[7] = thing[7];
  999. this.raw[8] = thing[8];
  1000. this.raw[9] = thing[9];
  1001. this.update();
  1002. }
  1003.  
  1004. setCaps(thing) {
  1005. this.caps[0] = thing[0];
  1006. this.caps[1] = thing[1];
  1007. this.caps[2] = thing[2];
  1008. this.caps[3] = thing[3];
  1009. this.caps[4] = thing[4];
  1010. this.caps[5] = thing[5];
  1011. this.caps[6] = thing[6];
  1012. this.caps[7] = thing[7];
  1013. this.caps[8] = thing[8];
  1014. this.caps[9] = thing[9];
  1015. this.update();
  1016. }
  1017.  
  1018. maintain() {
  1019. if (this.level < c.SKILL_CAP) {
  1020. if (this.score - this.deduction >= this.levelScore) {
  1021. this.deduction += this.levelScore;
  1022. this.level += 1;
  1023. this.points += this.levelPoints;
  1024. if (this.level == c.TIER_1 || this.level == c.TIER_2 || this.level == c.TIER_3) {
  1025. this.canUpgrade = true;
  1026. }
  1027. this.update();
  1028. return true;
  1029. }
  1030. }
  1031. return false;
  1032. }
  1033.  
  1034. get levelScore() {
  1035. return Math.ceil(1.8 * Math.pow(this.level + 1, 1.8) - 2 * this.level + 1);
  1036. }
  1037.  
  1038. get progress() {
  1039. return this.levelScore ? (this.score - this.deduction) / this.levelScore : 0;
  1040. }
  1041.  
  1042. get levelPoints() {
  1043. if (levelers.findIndex(e => {
  1044. return e === this.level;
  1045. }) != -1) {
  1046. return 1;
  1047. }return 0;
  1048. }
  1049.  
  1050. cap(skill, real = false) {
  1051. if (!real && this.level < c.SKILL_SOFT_CAP) {
  1052. return Math.round(this.caps[skcnv[skill]] * c.SOFT_MAX_SKILL);
  1053. }
  1054. return this.caps[skcnv[skill]];
  1055. }
  1056.  
  1057. bleed(i, j) {
  1058. let a = (i + 2) % 5 + 5 * j,
  1059. b = (i + (j === 1 ? 1 : 4)) % 5 + 5 * j;
  1060. let value = 0;
  1061. let denom = Math.max(c.MAX_SKILL, this.caps[i + 5 * j]);
  1062. value += (1 - Math.pow(this.raw[a] / denom - 1, 2)) * this.raw[a] * c.SKILL_LEAK;
  1063. value -= Math.pow(this.raw[b] / denom, 2) * this.raw[b] * c.SKILL_LEAK;
  1064.  
  1065. return value;
  1066. }
  1067.  
  1068. upgrade(stat) {
  1069. if (this.points && this.amount(stat) < this.cap(stat)) {
  1070. this.change(stat, 1);
  1071. this.points -= 1;
  1072. return true;
  1073. }
  1074. return false;
  1075. }
  1076.  
  1077. title(stat) {
  1078. return this.name[skcnv[stat]];
  1079. }
  1080.  
  1081. /*
  1082. let i = skcnv[skill] % 5,
  1083. j = (skcnv[skill] - i) / 5;
  1084. let roundvalue = Math.round(this.bleed(i, j) * 10);
  1085. let string = '';
  1086. if (roundvalue > 0) { string += '+' + roundvalue + '%'; }
  1087. if (roundvalue < 0) { string += '-' + roundvalue + '%'; }
  1088. return string;
  1089. */
  1090.  
  1091. amount(skill) {
  1092. return this.raw[skcnv[skill]];
  1093. }
  1094.  
  1095. change(skill, levels) {
  1096. this.raw[skcnv[skill]] += levels;
  1097. this.update();
  1098. }
  1099. }
  1100.  
  1101. const lazyRealSizes = (() => {
  1102. let o = [1, 1, 1];
  1103. for (var i = 3; i < 16; i++) {
  1104. // We say that the real size of a 0-gon, 1-gon, 2-gon is one, then push the real sizes of triangles, squares, etc...
  1105. o.push(Math.sqrt(2 * Math.PI / i * (1 / Math.sin(2 * Math.PI / i))));
  1106. }
  1107. return o;
  1108. })();
  1109.  
  1110. // Define how guns work
  1111. class Gun {
  1112. constructor(body, info) {
  1113. this.lastShot = {
  1114. time: 0,
  1115. power: 0
  1116. };
  1117. this.body = body;
  1118. this.master = body.source;
  1119. this.label = '';
  1120. this.controllers = [];
  1121. this.children = [];
  1122. this.control = {
  1123. target: new Vector(0, 0),
  1124. goal: new Vector(0, 0),
  1125. main: false,
  1126. alt: false,
  1127. fire: false
  1128. };
  1129. this.poisoned = false;
  1130. this.poison = false;
  1131. this.poisonedBy = -1;
  1132. this.poisonLevel = 0;
  1133. this.poisonToApply = 0;
  1134. this.showpoison = false;
  1135. this.poisonTimer = 0;
  1136. this.canShoot = false;
  1137. if (info.PROPERTIES != null && info.PROPERTIES.TYPE != null) {
  1138. this.canShoot = true;
  1139. this.label = info.PROPERTIES.LABEL == null ? '' : info.PROPERTIES.LABEL;
  1140. if (Array.isArray(info.PROPERTIES.TYPE)) {
  1141. // This is to be nicer about our definitions
  1142. this.bulletTypes = info.PROPERTIES.TYPE;
  1143. this.natural = info.PROPERTIES.TYPE.BODY;
  1144. } else {
  1145. this.bulletTypes = [info.PROPERTIES.TYPE];
  1146. }
  1147. // Pre-load bullet definitions so we don't have to recalculate them every shot
  1148. let natural = {};
  1149. function setNatural(type) {
  1150. if (type.PARENT != null) {
  1151. // Make sure we load from the parents first
  1152. for (let i = 0; i < type.PARENT.length; i++) {
  1153. setNatural(type.PARENT[i]);
  1154. }
  1155. }
  1156. if (type.BODY != null) {
  1157. // Get values if they exist
  1158. for (let index in type.BODY) {
  1159. natural[index] = type.BODY[index];
  1160. }
  1161. }
  1162. }
  1163. let z = 0;
  1164. const length = this.bulletTypes.length;
  1165. for (; z < length; z++) {
  1166. setNatural(this.bulletTypes[z]);
  1167. }
  1168. this.natural = natural; // Save it
  1169. if (info.PROPERTIES.GUN_CONTROLLERS != null) {
  1170. let toAdd = [];
  1171. let self = this;
  1172. //info.PROPERTIES.GUN_CONTROLLERS.forEach(function(ioName) {
  1173. // toAdd.push(eval('new ' + ioName + '(self)'));
  1174. //});
  1175. let i = 0;
  1176. const length = info.PROPERTIES_GUN_CONTROLLERS.length;
  1177. for (; i < length; i++) {
  1178. toAdd.push(eval('new ' + info.PROPERTIES.GUN_CONTROLLERS[i] + '(self)'));
  1179. }
  1180. this.controllers = toAdd.concat(this.controllers);
  1181. }
  1182. this.autofire = info.PROPERTIES.AUTOFIRE == null ? false : info.PROPERTIES.AUTOFIRE;
  1183. this.altFire = info.PROPERTIES.ALT_FIRE == null ? false : info.PROPERTIES.ALT_FIRE;
  1184. this.settings = info.PROPERTIES.SHOOT_SETTINGS == null ? [] : info.PROPERTIES.SHOOT_SETTINGS;
  1185. this.calculator = info.PROPERTIES.STAT_CALCULATOR == null ? 'default' : info.PROPERTIES.STAT_CALCULATOR;
  1186. this.waitToCycle = info.PROPERTIES.WAIT_TO_CYCLE == null ? false : info.PROPERTIES.WAIT_TO_CYCLE;
  1187. this.bulletStats = info.PROPERTIES.BULLET_STATS == null || info.PROPERTIES.BULLET_STATS == 'master' ? 'master' : new Skill(info.PROPERTIES.BULLET_STATS);
  1188. this.settings = info.PROPERTIES.SHOOT_SETTINGS == null ? [] : info.PROPERTIES.SHOOT_SETTINGS;
  1189. this.countsOwnKids = info.PROPERTIES.MAX_CHILDREN == null ? false : info.PROPERTIES.MAX_CHILDREN;
  1190. this.syncsSkills = info.PROPERTIES.SYNCS_SKILLS == null ? false : info.PROPERTIES.SYNCS_SKILLS;
  1191. this.negRecoil = info.PROPERTIES.NEGATIVE_RECOIL == null ? false : info.PROPERTIES.NEGATIVE_RECOIL;
  1192. }
  1193. let position = info.POSITION;
  1194. this.length = position[0] / 10;
  1195. this.width = position[1] / 10;
  1196. this.aspect = position[2];
  1197. let _off = new Vector(position[3], position[4]);
  1198. this.angle = position[5] * Math.PI / 180;
  1199. this.direction = _off.direction;
  1200. this.offset = _off.length / 10;
  1201. this.delay = position[6];
  1202.  
  1203. this.position = 0;
  1204. this.motion = 0;
  1205. if (this.canShoot) {
  1206. this.cycle = !this.waitToCycle - this.delay;
  1207. this.trueRecoil = this.settings.recoil * 1.675;
  1208. }
  1209. }
  1210.  
  1211. recoil() {
  1212. if (this.motion || this.position) {
  1213. // Simulate recoil
  1214. this.motion -= 0.25 * this.position / roomSpeed;
  1215. this.position += this.motion;
  1216. if (this.position < 0) {
  1217. // Bouncing off the back
  1218. this.position = 0;
  1219. this.motion = -this.motion;
  1220. }
  1221. if (this.motion > 0) {
  1222. this.motion *= 0.75;
  1223. }
  1224. }
  1225. if (this.canShoot && !this.body.settings.hasNoRecoil) {
  1226. // Apply recoil to motion
  1227. if (this.motion > 0) {
  1228. let recoilForce = -this.position * this.trueRecoil * 0.045 / roomSpeed;
  1229. this.body.accel.x += recoilForce * Math.cos(this.body.facing + this.angle);
  1230. this.body.accel.y += recoilForce * Math.sin(this.body.facing + this.angle);
  1231. }
  1232. }
  1233. }
  1234.  
  1235. getSkillRaw() {
  1236. if (this.bulletStats === 'master') {
  1237. return [this.body.skill.raw[0], this.body.skill.raw[1], this.body.skill.raw[2], this.body.skill.raw[3], this.body.skill.raw[4], 0, 0, 0, 0, 0];
  1238. }
  1239. return this.bulletStats.raw;
  1240. }
  1241.  
  1242. getLastShot() {
  1243. return this.lastShot;
  1244. }
  1245.  
  1246. live() {
  1247. // Do
  1248. this.recoil();
  1249. // Dummies ignore this
  1250. if (this.canShoot) {
  1251. // Find the proper skillset for shooting
  1252. let sk = this.bulletStats === 'master' ? this.body.skill : this.bulletStats;
  1253. // Decides what to do based on child-counting settings
  1254. let shootPermission = this.countsOwnKids ? this.countsOwnKids > this.children.length * (this.calculator == 'necro' ? sk.rld : 1) : this.body.maxChildren ? this.body.maxChildren > this.body.children.length * (this.calculator == 'necro' ? sk.rld : 1) : true;
  1255. // Override in invuln
  1256. if (this.body.master.invuln) {
  1257. shootPermission = false;
  1258. }
  1259. // Cycle up if we should
  1260. if (shootPermission || !this.waitToCycle) {
  1261. if (this.cycle < 1) {
  1262. this.cycle += 1 / (this.settings.reload * 2) / roomSpeed / (this.calculator == 'necro' || this.calculator == 'fixed reload' ? 1 : sk.rld);
  1263. }
  1264. }
  1265. // Firing routines
  1266. if (shootPermission && (this.autofire || (this.altFire ? this.body.control.alt : this.body.control.fire))) {
  1267. if (this.cycle >= 1) {
  1268. // Find the end of the gun barrel
  1269. let gx = this.offset * Math.cos(this.direction + this.angle + this.body.facing) + (1.5 * this.length - this.width * this.settings.size / 2) * Math.cos(this.angle + this.body.facing);
  1270. let gy = this.offset * Math.sin(this.direction + this.angle + this.body.facing) + (1.5 * this.length - this.width * this.settings.size / 2) * Math.sin(this.angle + this.body.facing);
  1271. // Shoot, multiple times in a tick if needed
  1272. while (shootPermission && this.cycle >= 1) {
  1273. this.fire(gx, gy, sk);
  1274. // Figure out if we may still shoot
  1275. shootPermission = this.countsOwnKids ? this.countsOwnKids > this.children.length : this.body.maxChildren ? this.body.maxChildren > this.body.children.length : true;
  1276. // Cycle down
  1277. this.cycle -= 1;
  1278. }
  1279. } // If we're not shooting, only cycle up to where we'll have the proper firing delay
  1280. } else if (this.cycle > !this.waitToCycle - this.delay) {
  1281. this.cycle = !this.waitToCycle - this.delay;
  1282. }
  1283. }
  1284. }
  1285.  
  1286. syncChildren() {
  1287. if (this.syncsSkills) {
  1288. let self = this;
  1289. let i = 0;
  1290. const length = this.children.length;
  1291. for (; i < length; i++) {
  1292. //for (let instance of this.children) {
  1293. let instance = this.children[i];
  1294. instance.define({
  1295. BODY: self.interpret(),
  1296. SKILL: self.getSkillRaw()
  1297. });
  1298. instance.refreshBodyAttributes();
  1299. }
  1300. }
  1301. }
  1302.  
  1303. fire(gx, gy, sk) {
  1304. // Recoil
  1305. this.lastShot.time = util.time();
  1306. this.lastShot.power = 3 * Math.log(Math.sqrt(sk.spd) + this.trueRecoil + 1) + 1;
  1307. this.motion += this.lastShot.power;
  1308. // Find inaccuracy
  1309. let ss, sd;
  1310. do {
  1311. ss = ran.gauss(0, Math.sqrt(this.settings.shudder));
  1312. } while (Math.abs(ss) >= this.settings.shudder * 2);
  1313. do {
  1314. sd = ran.gauss(0, this.settings.spray * this.settings.shudder);
  1315. } while (Math.abs(sd) >= this.settings.spray / 2);
  1316. sd *= Math.PI / 180;
  1317. // Find speed
  1318. let s = new Vector((this.negRecoil ? -1 : 1) * this.settings.speed * c.runSpeed * sk.spd * (1 + ss) * Math.cos(this.angle + this.body.facing + sd), (this.negRecoil ? -1 : 1) * this.settings.speed * c.runSpeed * sk.spd * (1 + ss) * Math.sin(this.angle + this.body.facing + sd));
  1319. // Boost it if we should
  1320. if (this.body.velocity.length) {
  1321. let extraBoost = Math.max(0, s.x * this.body.velocity.x + s.y * this.body.velocity.y) / this.body.velocity.length / s.length;
  1322. if (extraBoost) {
  1323. let len = s.length;
  1324. s.x += this.body.velocity.length * extraBoost * s.x / len;
  1325. s.y += this.body.velocity.length * extraBoost * s.y / len;
  1326. }
  1327. }
  1328. // Create the bullet
  1329. /*
  1330. var o = new Entity({
  1331. x: this.body.x + this.body.size * gx - s.x,
  1332. y: this.body.y + this.body.size * gy - s.y,
  1333. }, this.master.master);
  1334. let jumpAhead = this.cycle - 1;
  1335. if (jumpAhead) {
  1336. o.x += s.x * this.cycle / jumpAhead;
  1337. o.y += s.y * this.cycle / jumpAhead;
  1338. }
  1339. o.velocity = s;
  1340. this.bulletInit(o);
  1341. o.coreSize = o.SIZE;
  1342. */
  1343. let o = new Entity({
  1344. x: this.body.x + this.body.size * gx - s.x,
  1345. y: this.body.y + this.body.size * gy - s.y
  1346. }, this.master.master);
  1347. this.bulletInit(o);
  1348. o.x = this.body.x + this.body.size * gx - s.x;
  1349. o.y = this.body.y + this.body.size * gy - s.y;
  1350. o.coreSize = o.SIZE;
  1351. o.velocity = s;
  1352. }
  1353.  
  1354. bulletInit(o) {
  1355. // Define it by its natural properties
  1356. //this.bulletTypes.forEach(type => o.define(type));
  1357. const length = this.bulletTypes.length;
  1358. let i = 0;
  1359. for (; i < length; i++) {
  1360. o.define(this.bulletTypes[i]);
  1361. }
  1362. // Pass the gun attributes
  1363. o.define({
  1364. BODY: this.interpret(),
  1365. SKILL: this.getSkillRaw(),
  1366. SIZE: this.body.size * this.width * this.settings.size / 2,
  1367. LABEL: this.master.label + (this.label ? ' ' + this.label : '') + ' ' + o.label
  1368. });
  1369. o.color = this.body.master.color;
  1370. // Keep track of it and give it the function it needs to deutil.log itself upon death
  1371. if (this.countsOwnKids) {
  1372. o.parent = this;
  1373. this.children.push(o);
  1374. } else if (this.body.maxChildren) {
  1375. o.parent = this.body;
  1376. this.body.children.push(o);
  1377. this.children.push(o);
  1378. }
  1379. o.source = this.body;
  1380. o.facing = o.velocity.direction;
  1381. // Necromancers.
  1382. if (this.calculator == 'necro') {
  1383. let oo = o;
  1384. o.necro = host => {
  1385. let shootPermission = this.countsOwnKids ? this.countsOwnKids > this.children.length * (this.bulletStats === 'master' ? this.body.skill.rld : this.bulletStats.rld) : this.body.maxChildren ? this.body.maxChildren > this.body.children.length * (this.bulletStats === 'master' ? this.body.skill.rld : this.bulletStats.rld) : true;
  1386. if (shootPermission) {
  1387. let save = {
  1388. facing: host.facing,
  1389. size: host.SIZE
  1390. };
  1391. host.define(Class.genericEntity);
  1392. this.bulletInit(host);
  1393. host.team = oo.master.master.team;
  1394. host.master = oo.master;
  1395. host.color = oo.color;
  1396. host.facing = save.facing;
  1397. host.SIZE = save.size;
  1398. host.health.amount = host.health.max;
  1399. return true;
  1400. }
  1401. return false;
  1402. };
  1403. }
  1404. // Otherwise
  1405. o.refreshBodyAttributes();
  1406. o.life();
  1407. }
  1408.  
  1409. getTracking() {
  1410. return {
  1411. speed: c.runSpeed * (this.bulletStats == 'master' ? this.body.skill.spd : this.bulletStats.spd) * this.settings.maxSpeed * this.natural.SPEED,
  1412. range: Math.sqrt(this.bulletStats == 'master' ? this.body.skill.spd : this.bulletStats.spd) * this.settings.range * this.natural.RANGE
  1413. };
  1414. }
  1415.  
  1416. interpret() {
  1417. let sizeFactor = this.master.size / this.master.SIZE;
  1418. let shoot = this.settings;
  1419. let sk = this.bulletStats == 'master' ? this.body.skill : this.bulletStats;
  1420. // Defaults
  1421. let out = {
  1422. SPEED: shoot.maxSpeed * sk.spd,
  1423. HEALTH: shoot.health * sk.str,
  1424. RESIST: shoot.resist + sk.rst,
  1425. DAMAGE: shoot.damage * sk.dam,
  1426. PENETRATION: Math.max(1, shoot.pen * sk.pen),
  1427. RANGE: shoot.range / Math.sqrt(sk.spd),
  1428. DENSITY: shoot.density * sk.pen * sk.pen / sizeFactor,
  1429. PUSHABILITY: 1 / sk.pen,
  1430. HETERO: 3 - 2.8 * sk.ghost
  1431. };
  1432. // Special cases
  1433. switch (this.calculator) {
  1434. case 'thruster':
  1435. this.trueRecoil = this.settings.recoil * Math.sqrt(sk.rld * sk.spd);
  1436. break;
  1437. case 'sustained':
  1438. out.RANGE = shoot.range;
  1439. break;
  1440. case 'swarm':
  1441. out.PENETRATION = Math.max(1, shoot.pen * (0.5 * (sk.pen - 1) + 1));
  1442. out.HEALTH /= shoot.pen * sk.pen;
  1443. break;
  1444. case 'trap':
  1445. case 'block':
  1446. out.PUSHABILITY = 1 / Math.pow(sk.pen, 0.5);
  1447. out.RANGE = shoot.range;
  1448. break;
  1449. case 'necro':
  1450. case 'drone':
  1451. out.PUSHABILITY = 1;
  1452. out.PENETRATION = Math.max(1, shoot.pen * (0.5 * (sk.pen - 1) + 1));
  1453. out.HEALTH = (shoot.health * sk.str + sizeFactor) / Math.pow(sk.pen, 0.8);
  1454. out.DAMAGE = shoot.damage * sk.dam * Math.sqrt(sizeFactor) * shoot.pen * sk.pen;
  1455. out.RANGE = shoot.range * Math.sqrt(sizeFactor);
  1456. break;
  1457. }
  1458. // Go through and make sure we respect its natural properties
  1459. for (let property in out) {
  1460. if (this.natural[property] == null || !out.hasOwnProperty(property)) continue;
  1461. out[property] *= this.natural[property];
  1462. }
  1463. return out;
  1464. }
  1465. }
  1466. // Define entities
  1467. var minimap = [];
  1468. var views = [];
  1469. var entitiesToAvoid = [];
  1470. const dirtyCheck = (p, r) => {
  1471. return entitiesToAvoid.some(e => {
  1472. return Math.abs(p.x - e.x) < r + e.size && Math.abs(p.y - e.y) < r + e.size;
  1473. });
  1474. };
  1475. const grid = new hshg.HSHG();
  1476. var entitiesIdLog = 0;
  1477. var entities = [];
  1478. const purgeEntities = () => {
  1479. entities = entities.filter(e => {
  1480. return !e.isGhost;
  1481. });
  1482. };
  1483.  
  1484. var bringToLife = (() => {
  1485. let remapTarget = (i, ref, self) => {
  1486. if (i.target == null || !i.main && !i.alt) return undefined;
  1487. return {
  1488. x: i.target.x + ref.x - self.x,
  1489. y: i.target.y + ref.y - self.y
  1490. };
  1491. };
  1492. let passer = (a, b, acceptsFromTop) => {
  1493. return index => {
  1494. if (a != null && a[index] != null && (b[index] == null || acceptsFromTop)) {
  1495. b[index] = a[index];
  1496. }
  1497. };
  1498. };
  1499. return my => {
  1500. // Size
  1501. if (my.SIZE !== my.coreSize) my.coreSize = my.SIZE;
  1502. // Think
  1503. let faucet = my.settings.independent || my.source == null || my.source === my ? {} : my.source.control;
  1504. let b = {
  1505. target: remapTarget(faucet, my.source, my),
  1506. goal: undefined,
  1507. fire: faucet.fire,
  1508. main: faucet.main,
  1509. alt: faucet.alt,
  1510. power: undefined
  1511. };
  1512. // Seek attention
  1513. if (my.settings.attentionCraver && !faucet.main && my.range) {
  1514. my.range -= 1;
  1515. }
  1516. // So we start with my master's thoughts and then we filter them down through our control stack
  1517. for (let AI of my.controllers) {
  1518. let a = AI.think(b);
  1519. let passValue = passer(a, b, AI.acceptsFromTop);
  1520. passValue('target');
  1521. passValue('goal');
  1522. passValue('fire');
  1523. passValue('main');
  1524. passValue('alt');
  1525. passValue('power');
  1526. }
  1527. my.control.target = b.target == null ? my.control.target : b.target;
  1528. my.control.goal = b.goal;
  1529. my.control.fire = b.fire;
  1530. my.control.main = b.main;
  1531. my.control.alt = b.alt;
  1532. my.control.power = b.power == null ? 1 : b.power;
  1533. // React
  1534. my.move();
  1535. my.face();
  1536. // Handle guns and turrets if we've got them
  1537. for (let gun of my.guns) {
  1538. gun.live();
  1539. }
  1540. for (let turret of my.turrets) {
  1541. turret.life();
  1542. }
  1543. if (my.skill.maintain()) my.refreshBodyAttributes();
  1544. //if (my.invisible[1]) {
  1545. // my.alpha = Math.max(0.01, my.alpha - my.invisible[1]);
  1546. // if (!(my.velocity.x * my.velocity.x + my.velocity.y * my.velocity.y < 0.15 * 0.15) || my.damageRecieved)
  1547. // my.alpha = Math.min(1, my.alpha + my.invisible[0]);
  1548. //} else my.alpha = 1;
  1549. };
  1550. })();
  1551.  
  1552. class HealthType {
  1553. constructor(health, type, resist = 0) {
  1554. this.max = health;
  1555. this.amount = health;
  1556. this.type = type;
  1557. this.resist = resist;
  1558. this.regen = 0;
  1559. }
  1560.  
  1561. set(health, regen = 0) {
  1562. this.amount = this.max ? this.amount / this.max * health : health;
  1563. this.max = health;
  1564. this.regen = regen;
  1565. }
  1566.  
  1567. display() {
  1568. return this.amount / this.max;
  1569. }
  1570.  
  1571. getDamage(amount, capped = true) {
  1572. switch (this.type) {
  1573. case 'dynamic':
  1574. return capped ? Math.min(amount * this.permeability, this.amount) : amount * this.permeability;
  1575. case 'static':
  1576. return capped ? Math.min(amount, this.amount) : amount;
  1577. }
  1578. }
  1579.  
  1580. regenerate(boost = false) {
  1581. boost /= 2;
  1582. let cons = 5;
  1583. switch (this.type) {
  1584. case 'static':
  1585. if (this.amount >= this.max || !this.amount) break;
  1586. this.amount += cons * (this.max / 10 / 60 / 2.5 + boost);
  1587. break;
  1588. case 'dynamic':
  1589. let r = util.clamp(this.amount / this.max, 0, 1);
  1590. if (!r) {
  1591. this.amount = 0.0001;
  1592. }
  1593. if (r === 1) {
  1594. this.amount = this.max;
  1595. } else {
  1596. this.amount += cons * (this.regen * Math.exp(-50 * Math.pow(Math.sqrt(0.5 * r) - 0.4, 2)) / 3 + r * this.max / 10 / 15 + boost);
  1597. }
  1598. break;
  1599. }
  1600. this.amount = util.clamp(this.amount, 0, this.max);
  1601. }
  1602.  
  1603. get permeability() {
  1604. switch (this.type) {
  1605. case 'static':
  1606. return 1;
  1607. case 'dynamic':
  1608. return this.max ? util.clamp(this.amount / this.max, 0, 1) : 0;
  1609. }
  1610. }
  1611.  
  1612. get ratio() {
  1613. return this.max ? util.clamp(1 - Math.pow(this.amount / this.max - 1, 4), 0, 1) : 0;
  1614. }
  1615. }
  1616.  
  1617. class Entity {
  1618. constructor(position, master = this) {
  1619. this.cache = [0, 0, 0, 0, 0, 0];
  1620. this.defaultset = false;
  1621. this.isGhost = false;
  1622. this.killCount = { solo: 0, assists: 0, bosses: 0, killers: [] };
  1623. this.creationTime = new Date().getTime();
  1624. // Inheritance
  1625. this.master = master;
  1626. this.source = this;
  1627. this.parent = this;
  1628. this.control = {
  1629. target: new Vector(0, 0),
  1630. goal: new Vector(0, 0),
  1631. main: false,
  1632. alt: false,
  1633. fire: false,
  1634. power: 0
  1635. };
  1636. this.isInGrid = false;
  1637. this.removeFromGrid = () => {
  1638. if (this.isInGrid) {
  1639. grid.removeObject(this);this.isInGrid = false;
  1640. }
  1641. };
  1642. this.addToGrid = () => {
  1643. if (!this.isInGrid && this.bond == null) {
  1644. grid.addObject(this);this.isInGrid = true;
  1645. }
  1646. };
  1647.  
  1648. this.activation = (() => {
  1649. let active = true;
  1650. let timer = ran.irandom(15);
  1651. return {
  1652. update: () => {
  1653. if (this.triggerFunctions.UPDATE != null) {
  1654. this.triggerFunctions.UPDATE(this);
  1655. }
  1656. if (this.isDead()) return 0;
  1657. // Check if I'm in anybody's view
  1658. if (!active) {
  1659. this.removeFromGrid();
  1660. // Remove bullets and swarm
  1661. if (this.settings.diesAtRange) this.kill();
  1662. // Still have limited update cycles but do it much more slowly.
  1663. if (!timer--) active = true;
  1664. } else {
  1665. this.addToGrid();
  1666. timer = 15;
  1667. active = views.some(v => v.check(this, 0.6));
  1668. }
  1669. },
  1670. check: () => {
  1671. return active;
  1672. }
  1673. };
  1674. })();
  1675. this.autoOverride = false;
  1676. this.controllers = [];
  1677. this.blend = {
  1678. color: '#FFFFFF',
  1679. amount: 0
  1680. };
  1681. // Objects
  1682. this.skill = new Skill();
  1683. this.health = new HealthType(1, 'static', 0);
  1684. this.shield = new HealthType(0, 'dynamic');
  1685. this.guns = [];
  1686. this.turrets = [];
  1687. this.upgrades = [];
  1688. this.settings = {};
  1689. this.aiSettings = {};
  1690. this.children = [];
  1691. // Define it
  1692. this.SIZE = 1;
  1693. this.define(Class.genericEntity);
  1694. // Initalize physics and collision
  1695. this.facing = 0;
  1696. this.vfacing = 0;
  1697. this.range = 0;
  1698. this.damageRecieved = 0;
  1699. this.stepRemaining = 1;
  1700. this.collisionArray = [];
  1701. this.invuln = false;
  1702. this.alpha = 1;
  1703. // Physics 2.0 stuff
  1704. this.x = position.x;
  1705. this.y = position.y;
  1706. this.velocity = new Vector(0, 0);
  1707. this.accel = new Vector(0, 0);
  1708. this.impulse = new Vector(0, 0);
  1709. this.maxSpeed = 0;
  1710. this.maxAccel = 0;
  1711. this.resistance = 0.13;
  1712. this.impulseZero = 0.01;
  1713. this.knockbackLimit = 125;
  1714. // Get a new unique id
  1715. this.id = entitiesIdLog++;
  1716. this.team = this.id;
  1717. this.team = master.team;
  1718. // This is for collisions
  1719. this.updateAABB = () => {};
  1720. this.getAABB = (() => {
  1721. let data = {},
  1722. savedSize = 0;
  1723. let getLongestEdge = (x1, y1, x2, y2) => {
  1724. return Math.max(Math.abs(x2 - x1), Math.abs(y2 - y1));
  1725. };
  1726. this.updateAABB = active => {
  1727. if (this.bond != null) return 0;
  1728. if (!active) {
  1729. data.active = false;return 0;
  1730. }
  1731. // Get bounds
  1732. let x1 = Math.min(this.x, this.x + this.velocity.x + this.accel.x) - this.realSize - 5;
  1733. let y1 = Math.min(this.y, this.y + this.velocity.y + this.accel.y) - this.realSize - 5;
  1734. let x2 = Math.max(this.x, this.x + this.velocity.x + this.accel.x) + this.realSize + 5;
  1735. let y2 = Math.max(this.y, this.y + this.velocity.y + this.accel.y) + this.realSize + 5;
  1736. // Size check
  1737. let size = getLongestEdge(x1, y1, x2, y1);
  1738. let sizeDiff = savedSize / size;
  1739. // Update data
  1740. data = {
  1741. min: [x1, y1],
  1742. max: [x2, y2],
  1743. active: true,
  1744. size: size
  1745. };
  1746. // Update grid if needed
  1747. if (sizeDiff > Math.SQRT2 || sizeDiff < Math.SQRT1_2) {
  1748. this.removeFromGrid();this.addToGrid();
  1749. savedSize = data.size;
  1750. }
  1751. };
  1752. return () => {
  1753. return data;
  1754. };
  1755. })();
  1756. this.updateAABB(true);
  1757. // note, these dont have to be defined but having them as null
  1758. // can make things more obvious to what trigger functions actually exist
  1759. this.triggerFunctions = {
  1760. UPDATE: myself => null, // takes in parameter (myself) which will be the entity its being called on
  1761. DEATH: myself => null // this takes in parameter (myself) which is the entity its called on, all thats information is good enough for us not needing other parameters
  1762. };
  1763. entities.push(this); // everything else
  1764. //views.forEach(v => v.add(this));
  1765. let i = 0;
  1766. const length = views.length;
  1767. for (; i < length; i++) {
  1768. views[i].add(this);
  1769. }
  1770. }
  1771.  
  1772. life() {
  1773. bringToLife(this);
  1774. }
  1775.  
  1776. addController(newIO) {
  1777. if (Array.isArray(newIO)) {
  1778. this.controllers = newIO.concat(this.controllers);
  1779. } else {
  1780. this.controllers.unshift(newIO);
  1781. }
  1782. }
  1783.  
  1784. define(set, mockupDefine = false) {
  1785. if (set.PARENT != null) {
  1786. for (let i = 0; i < set.PARENT.length; i++) {
  1787. this.define(set.PARENT[i]);
  1788. }
  1789. }
  1790. if (set.index != null) {
  1791. this.index = set.index;
  1792. }
  1793. if (set.NAME != null) {
  1794. this.name = set.NAME;
  1795. }
  1796. if (set.LABEL != null) {
  1797. this.label = set.LABEL;
  1798. }
  1799. if (set.TYPE != null) {
  1800. this.type = set.TYPE;
  1801. }
  1802. if (set.SHAPE != null) {
  1803. this.shape = typeof set.SHAPE === 'number' ? set.SHAPE : 0;
  1804. this.shapeData = set.SHAPE;
  1805. }
  1806. /*
  1807. if (set.CUSTOMSHAPE != null && set.CUSTOMSHAPE.length > 0) {
  1808. this.customshape = set.CUSTOMSHAPE;
  1809. console.log(this.customshape);
  1810. }
  1811. */
  1812. if (set.COLOR != null) {
  1813. this.color = set.COLOR;
  1814. }
  1815. if (set.CONTROLLERS != null) {
  1816. let toAdd = [];
  1817. //set.CONTROLLERS.forEach((ioName) => {
  1818. // toAdd.push(eval('new io_' + ioName + '(this)'));
  1819. //});
  1820. let i = 0;
  1821. const length = set.CONTROLLERS.length;
  1822. for (; i < length; i++) {
  1823. toAdd.push(eval('new io_' + set.CONTROLLERS[i] + '(this)'));
  1824. }
  1825. this.addController(toAdd);
  1826. }
  1827. if (set.POISON != null) {
  1828. this.poison = set.POISON;
  1829. }
  1830. if (set.POISONED != null) {
  1831. this.poisoned = set.POISONED;
  1832. }
  1833. if (set.POISON_TO_APPLY != null) {
  1834. this.poisonToApply = set.POISON_TO_APPLY;
  1835. }
  1836. if (set.SHOWPOISON != null) {
  1837. this.showpoison = set.SHOWPOISON;
  1838. }
  1839. if (set.MOTION_TYPE != null) {
  1840. this.motionType = set.MOTION_TYPE;
  1841. }
  1842. if (set.FACING_TYPE != null) {
  1843. this.facingType = set.FACING_TYPE;
  1844. }
  1845. if (set.DRAW_HEALTH != null) {
  1846. this.settings.drawHealth = set.DRAW_HEALTH;
  1847. }
  1848. if (set.DRAW_SELF != null) {
  1849. this.settings.drawShape = set.DRAW_SELF;
  1850. }
  1851. if (set.DAMAGE_EFFECTS != null) {
  1852. this.settings.damageEffects = set.DAMAGE_EFFECTS;
  1853. }
  1854. if (set.RATIO_EFFECTS != null) {
  1855. this.settings.ratioEffects = set.RATIO_EFFECTS;
  1856. }
  1857. if (set.MOTION_EFFECTS != null) {
  1858. this.settings.motionEffects = set.MOTION_EFFECTS;
  1859. }
  1860. if (set.ACCEPTS_SCORE != null) {
  1861. this.settings.acceptsScore = set.ACCEPTS_SCORE;
  1862. }
  1863. if (set.GIVE_KILL_MESSAGE != null) {
  1864. this.settings.givesKillMessage = set.GIVE_KILL_MESSAGE;
  1865. }
  1866. if (set.CAN_GO_OUTSIDE_ROOM != null) {
  1867. this.settings.canGoOutsideRoom = set.CAN_GO_OUTSIDE_ROOM;
  1868. }
  1869. if (set.HITS_OWN_TYPE != null) {
  1870. this.settings.hitsOwnType = set.HITS_OWN_TYPE;
  1871. }
  1872. if (set.DIE_AT_LOW_SPEED != null) {
  1873. this.settings.diesAtLowSpeed = set.DIE_AT_LOW_SPEED;
  1874. }
  1875. if (set.DIE_AT_RANGE != null) {
  1876. this.settings.diesAtRange = set.DIE_AT_RANGE;
  1877. }
  1878. if (set.INDEPENDENT != null) {
  1879. this.settings.independent = set.INDEPENDENT;
  1880. }
  1881. if (set.PERSISTS_AFTER_DEATH != null) {
  1882. this.settings.persistsAfterDeath = set.PERSISTS_AFTER_DEATH;
  1883. }
  1884. if (set.CLEAR_ON_MASTER_UPGRADE != null) {
  1885. this.settings.clearOnMasterUpgrade = set.CLEAR_ON_MASTER_UPGRADE;
  1886. }
  1887. if (set.HEALTH_WITH_LEVEL != null) {
  1888. this.settings.healthWithLevel = set.HEALTH_WITH_LEVEL;
  1889. }
  1890. if (set.ACCEPTS_SCORE != null) {
  1891. this.settings.acceptsScore = set.ACCEPTS_SCORE;
  1892. }
  1893. if (set.OBSTACLE != null) {
  1894. this.settings.obstacle = set.OBSTACLE;
  1895. }
  1896. if (set.NECRO != null) {
  1897. this.settings.isNecromancer = set.NECRO;
  1898. }
  1899. if (set.AUTO_UPGRADE != null) {
  1900. this.settings.upgrading = set.AUTO_UPGRADE;
  1901. }
  1902. if (set.HAS_NO_RECOIL != null) {
  1903. this.settings.hasNoRecoil = set.HAS_NO_RECOIL;
  1904. }
  1905. if (set.CRAVES_ATTENTION != null) {
  1906. this.settings.attentionCraver = set.CRAVES_ATTENTION;
  1907. }
  1908. if (set.BROADCAST_MESSAGE != null) {
  1909. this.settings.broadcastMessage = set.BROADCAST_MESSAGE === '' ? undefined : set.BROADCAST_MESSAGE;
  1910. }
  1911. if (set.DAMAGE_CLASS != null) {
  1912. this.settings.damageClass = set.DAMAGE_CLASS;
  1913. }
  1914. if (set.BUFF_VS_FOOD != null) {
  1915. this.settings.buffVsFood = set.BUFF_VS_FOOD;
  1916. }
  1917. if (set.CAN_BE_ON_LEADERBOARD != null) {
  1918. this.settings.leaderboardable = set.CAN_BE_ON_LEADERBOARD;
  1919. }
  1920. if (set.INTANGIBLE != null) {
  1921. this.intangibility = set.INTANGIBLE;
  1922. }
  1923. if (set.IS_SMASHER != null) {
  1924. this.settings.reloadToAcceleration = set.IS_SMASHER;
  1925. }
  1926. if (set.STAT_NAMES != null) {
  1927. this.settings.skillNames = set.STAT_NAMES;
  1928. }
  1929. if (set.AI != null) {
  1930. this.aiSettings = set.AI;
  1931. }
  1932. if (set.DANGER != null) {
  1933. this.dangerValue = set.DANGER;
  1934. }
  1935. if (set.VARIES_IN_SIZE != null) {
  1936. this.settings.variesInSize = set.VARIES_IN_SIZE;
  1937. this.squiggle = this.settings.variesInSize ? ran.randomRange(0.8, 1.2) : 1;
  1938. }
  1939. if (set.RESET_UPGRADES) {
  1940. this.upgrades = [];
  1941. }
  1942. if (set.ALPHA != null) this.alpha = set.ALPHA;
  1943. if (set.INVISIBLE != null) this.invisible = [set.INVISIBLE[0], set.INVISIBLE[1]];
  1944. if (set.UPGRADES_TIER_1 != null) {
  1945. //set.UPGRADES_TIER_1.forEach((e) => {
  1946. // this.upgrades.push({class: e, level: c.TIER_1, index: e.index});
  1947. //});
  1948. let i = 0;
  1949. const length = set.UPGRADES_TIER_1.length;
  1950. for (; i < length; i++) {
  1951. this.upgrades.push({ class: set.UPGRADES_TIER_1[i], level: c.TIER_1, index: set.UPGRADES_TIER_1[i].index });
  1952. }
  1953. }
  1954. if (set.UPGRADES_TIER_2 != null) {
  1955. //set.UPGRADES_TIER_2.forEach((e) => {
  1956. // this.upgrades.push({class: e, level: c.TIER_2, index: e.index});
  1957. //});
  1958. let i = 0;
  1959. const length = set.UPGRADES_TIER_2.length;
  1960. for (; i < length; i++) {
  1961. this.upgrades.push({ class: set.UPGRADES_TIER_2[i], level: c.TIER_2, index: set.UPGRADES_TIER_2[i].index });
  1962. }
  1963. }
  1964. if (set.UPGRADES_TIER_3 != null) {
  1965. //set.UPGRADES_TIER_3.forEach((e) => {
  1966. // this.upgrades.push({class: e, level: c.TIER_3, index: e.index});
  1967. //});
  1968. let i = 0;
  1969. const length = set.UPGRADES_TIER_3.length;
  1970. for (; i < length; i++) {
  1971. this.upgrades.push({ class: set.UPGRADES_TIER_3[i], level: c.TIER_3, index: set.UPGRADES_TIER_3[i].index });
  1972. }
  1973. }
  1974. if (set.SIZE != null) {
  1975. this.SIZE = set.SIZE * this.squiggle;
  1976. if (this.coreSize == null) {
  1977. this.coreSize = this.SIZE;
  1978. }
  1979. }
  1980. if (set.SKILL != null && set.SKILL != []) {
  1981. if (set.SKILL.length != 10) {
  1982. throw 'Inappropiate skill raws.';
  1983. }
  1984. this.skill.set(set.SKILL);
  1985. }
  1986. if (set.LEVEL != null) {
  1987. if (set.LEVEL === -1) {
  1988. this.skill.reset();
  1989. }
  1990. while (this.skill.level < c.SKILL_CHEAT_CAP && this.skill.level < set.LEVEL) {
  1991. this.skill.score += this.skill.levelScore;
  1992. this.skill.maintain();
  1993. }
  1994. this.refreshBodyAttributes();
  1995. }
  1996. if (set.SKILL_CAP != null && set.SKILL_CAP != []) {
  1997. if (set.SKILL_CAP.length != 10) {
  1998. throw 'Inappropiate skill caps.';
  1999. }
  2000. this.skill.setCaps(set.SKILL_CAP);
  2001. }
  2002. if (set.VALUE != null) {
  2003. this.skill.score = Math.max(this.skill.score, set.VALUE * this.squiggle);
  2004. }
  2005. if (set.ALT_ABILITIES != null) {
  2006. this.abilities = set.ALT_ABILITIES;
  2007. }
  2008. if (set.GUNS != null) {
  2009. let newGuns = [];
  2010. //set.GUNS.forEach((gundef) => {
  2011. // newGuns.push(new Gun(this, gundef));
  2012. //});
  2013. let i = 0;
  2014. const length = set.GUNS.length;
  2015. for (; i < length; i++) {
  2016. newGuns.push(new Gun(this, set.GUNS[i]));
  2017. }
  2018. this.guns = newGuns;
  2019. }
  2020. if (set.MAX_CHILDREN != null) {
  2021. this.maxChildren = set.MAX_CHILDREN;
  2022. }
  2023. if (set.FOOD != null) {
  2024. if (set.FOOD.LEVEL != null) {
  2025. this.foodLevel = set.FOOD.LEVEL;
  2026. this.foodCountup = 0;
  2027. }
  2028. }
  2029. if (set.BODY != null) {
  2030. if (set.BODY.ACCELERATION != null) {
  2031. this.ACCELERATION = set.BODY.ACCELERATION;
  2032. }
  2033. if (set.BODY.SPEED != null) {
  2034. this.SPEED = set.BODY.SPEED;
  2035. }
  2036. if (set.BODY.HEALTH != null) {
  2037. this.HEALTH = set.BODY.HEALTH;
  2038. }
  2039. if (set.BODY.RESIST != null) {
  2040. this.RESIST = set.BODY.RESIST;
  2041. }
  2042. if (set.BODY.SHIELD != null) {
  2043. this.SHIELD = set.BODY.SHIELD;
  2044. }
  2045. if (set.BODY.REGEN != null) {
  2046. this.REGEN = set.BODY.REGEN;
  2047. }
  2048. if (set.BODY.DAMAGE != null) {
  2049. this.DAMAGE = set.BODY.DAMAGE;
  2050. }
  2051. if (set.BODY.PENETRATION != null) {
  2052. this.PENETRATION = set.BODY.PENETRATION;
  2053. }
  2054. if (set.BODY.FOV != null) {
  2055. this.FOV = set.BODY.FOV;
  2056. }
  2057. if (set.BODY.RANGE != null) {
  2058. this.RANGE = set.BODY.RANGE;
  2059. }
  2060. if (set.BODY.SHOCK_ABSORB != null) {
  2061. this.SHOCK_ABSORB = set.BODY.SHOCK_ABSORB;
  2062. }
  2063. if (set.BODY.DENSITY != null) {
  2064. this.DENSITY = set.BODY.DENSITY;
  2065. }
  2066. if (set.BODY.STEALTH != null) {
  2067. this.STEALTH = set.BODY.STEALTH;
  2068. }
  2069. if (set.BODY.PUSHABILITY != null) {
  2070. this.PUSHABILITY = set.BODY.PUSHABILITY;
  2071. }
  2072. if (set.BODY.HETERO != null) {
  2073. this.heteroMultiplier = set.BODY.HETERO;
  2074. }
  2075. this.refreshBodyAttributes();
  2076. }
  2077. if (set.TURRETS != null) {
  2078. let o;
  2079. let i = 0;
  2080. let j = 0;
  2081. const lengthi = this.turrets.length;
  2082. const lengthj = set.TURRETS.length;
  2083. //this.turrets.forEach(o => o.destroy());
  2084. for (; i < lengthi; i++) {
  2085. this.turrets[i].destroy();
  2086. }
  2087. this.turrets = [];
  2088. for (; j < lengthj; j++) {
  2089. let def = set.TURRETS[j];
  2090. o = new Entity(this, this.master);
  2091. (Array.isArray(def.TYPE) ? def.TYPE : [def.TYPE]).forEach(type => o.define(type));
  2092. o.bindToMaster(def.POSITION, this);
  2093. }
  2094. }
  2095. if (set.TRIGGERS != null && mockupDefine === false) {
  2096. let entries = Object.entries(set.TRIGGERS);
  2097. for (let i = 0; i < entries.length; i++) {
  2098. let entry = entries[i];
  2099. if (typeof entry[1] === 'function') {
  2100. if (this.triggerFunctions[entry[0]] != null) {
  2101. this.triggerFunctions[entry[0]] = entry[1];
  2102. } else {
  2103. util.error('Unknown trigger function ' + entry[0] + ' being defined by definition ' + set.LABEL);
  2104. }
  2105. } else {
  2106. util.error('Trigger function ' + entry[0] + ' being defined by definition ' + set.LABEL + ' is not a function!');
  2107. }
  2108. // so if i had update or something thats setup,
  2109. // now i can call that if i have a place for it like
  2110. // inside the objects activation update the update trigger
  2111. // function is called if its actually defined,
  2112. // you could also split up update into two triggers,
  2113. // AlwaysUpdate to always update even when the object died and
  2114. // Update for when it isnt dead, though right now updates set to when
  2115. // it isnt dead. anyway lets go to exports.explosion in definitions
  2116. // to see these things in action!
  2117. }
  2118. }
  2119. if (set.mockup != null) {
  2120. this.mockup = set.mockup;
  2121. }
  2122. }
  2123.  
  2124. refreshBodyAttributes() {
  2125. if (this.cache === undefined) {
  2126. this.cache = [0, 0, 0, 0, 0, 0];
  2127. }
  2128.  
  2129. if (this.defaultset === false || this.skill.changed === true) {
  2130. let speedReduce = Math.pow(this.size / (this.coreSize || this.SIZE), 1);
  2131.  
  2132. this.acceleration = c.runSpeed * this.ACCELERATION / speedReduce;
  2133. if (this.settings.reloadToAcceleration) this.acceleration *= this.skill.acl;
  2134. this.cache[0] = this.ACCELERATION;
  2135.  
  2136. this.topSpeed = c.runSpeed * this.SPEED * this.skill.mob / speedReduce * 2.58853046592;
  2137. if (this.settings.reloadToAcceleration) this.topSpeed /= Math.sqrt(this.skill.acl);
  2138. this.cache[1] = this.SPEED;
  2139.  
  2140. this.health.set(((this.settings.healthWithLevel ? 2 * this.skill.level : 0) + this.HEALTH) * this.skill.hlt);
  2141.  
  2142. this.health.resist = 1 - 1 / Math.max(1, this.RESIST + this.skill.brst);
  2143.  
  2144. this.shield.set(((this.settings.healthWithLevel ? 0.6 * this.skill.level : 0) + this.SHIELD) * this.skill.shi, Math.max(0, ((this.settings.healthWithLevel ? 0.006 * this.skill.level : 0) + 1) * this.REGEN * this.skill.rgn));
  2145.  
  2146. this.damage = this.DAMAGE * this.skill.atk;
  2147.  
  2148. this.penetration = this.PENETRATION + 1.5 * (this.skill.brst + 0.8 * (this.skill.atk - 1));
  2149.  
  2150. if (!this.settings.dieAtRange || !this.range) {
  2151. this.range = this.RANGE;
  2152. }
  2153.  
  2154. if (this.size < 12) {
  2155. this.fov = this.FOV * 400 * Math.sqrt(1 / (0.1 * this.size)) * (0.9 + 0.0001 * (this.size / 4));
  2156. } else {
  2157. this.fov = this.FOV * 400 * Math.sqrt(1 * (0.2 * this.size)) * (0.9 + 0.0001 * (this.size / 4));
  2158. }
  2159.  
  2160. this.density = (1 + 0.08 * this.skill.level) * this.DENSITY;
  2161.  
  2162. this.stealth = this.STEALTH;
  2163.  
  2164. this.pushability = this.PUSHABILITY;
  2165.  
  2166. this.defaultset = true;
  2167. this.skill.changed = false;
  2168. }
  2169.  
  2170. /*
  2171. let speedReduce = Math.pow(this.size / (this.coreSize || this.SIZE), 1);
  2172.  
  2173. if (this.ACCELERATION != this.cache[0]) {
  2174. this.acceleration = c.runSpeed * this.ACCELERATION / speedReduce;
  2175. if (this.settings.reloadToAcceleration) this.acceleration *= this.skill.acl;
  2176. this.cache[0] = this.ACCELERATION;
  2177. }
  2178.  
  2179. if (this.SPEED != this.cache[1]) {
  2180. this.topSpeed = (c.runSpeed * this.SPEED * this.skill.mob / speedReduce) * 1.14866039425;
  2181. if (this.settings.reloadToAcceleration) this.topSpeed /= Math.sqrt(this.skill.acl);
  2182. this.cache[1] = this.SPEED;
  2183. }
  2184. */
  2185.  
  2186. /*
  2187. if (this.HEALTH != this.cache[2]) {
  2188. this.health.set(
  2189. (((this.settings.healthWithLevel) ? 2 * this.skill.level : 0) + this.HEALTH) * this.skill.hlt
  2190. );
  2191. this.health.resist = 1 - 1 / Math.max(1, this.RESIST + this.skill.brst);
  2192. this.cache[2] = this.HEALTH;
  2193. }
  2194. if (this.SHIELD != this.cache[3]) {
  2195. this.shield.set(
  2196. (((this.settings.healthWithLevel) ? 0.6 * this.skill.level : 0) + this.SHIELD) * this.skill.shi,
  2197. Math.max(0, ((((this.settings.healthWithLevel) ? 0.006 * this.skill.level : 0) + 1) * this.REGEN) * this.skill.rgn)
  2198. );
  2199. this.cache[3] = this.SHIELD;
  2200. }
  2201.  
  2202. if (this.skill.atk != this.cache[4]) {
  2203. this.damage = this.DAMAGE * this.skill.atk;
  2204. this.cache[4] = this.skill.atk;
  2205. }
  2206.  
  2207. if (this.skill.brst != this.cache[5]) {
  2208. this.penetration = this.PENETRATION + 1.5 * (this.skill.brst + 0.8 * (this.skill.atk - 1));
  2209. this.cache[5] = this.skill.brst;
  2210. }
  2211. if (!this.settings.dieAtRange || !this.range) {
  2212. this.range = this.RANGE;
  2213. }
  2214.  
  2215. this.fov = this.FOV * 250 * Math.sqrt(this.size) * (1 + 0.003 * this.skill.level);
  2216.  
  2217. this.density = (1 + 0.08 * this.skill.level) * this.DENSITY;
  2218.  
  2219. this.stealth = this.STEALTH;
  2220. this.pushability = this.PUSHABILITY;
  2221. */
  2222. }
  2223.  
  2224. bindToMaster(position, bond) {
  2225. this.bond = bond;
  2226. this.source = bond;
  2227. this.bond.turrets.push(this);
  2228. this.skill = this.bond.skill;
  2229. this.label = this.bond.label + ' ' + this.label;
  2230. // It will not be in collision calculations any more nor shall it be seen.
  2231. this.removeFromGrid();
  2232. this.settings.drawShape = false;
  2233. // Get my position.
  2234. this.bound = {};
  2235. this.bound.size = position[0] / 20;
  2236. let _off = new Vector(position[1], position[2]);
  2237. this.bound.angle = position[3] * Math.PI / 180;
  2238. this.bound.direction = _off.direction;
  2239. this.bound.offset = _off.length / 10;
  2240. this.bound.arc = position[4] * Math.PI / 180;
  2241. // Figure out how we'll be drawn.
  2242. this.bound.layer = position[5];
  2243. // Initalize.
  2244. this.facing = this.bond.facing + this.bound.angle;
  2245. this.facingType = 'bound';
  2246. this.motionType = 'bound';
  2247. this.move();
  2248. }
  2249.  
  2250. get size() {
  2251. if (this.bond == null) return (this.coreSize || this.SIZE) * (1 + this.skill.level / 45);
  2252. return this.bond.size * this.bound.size;
  2253. }
  2254.  
  2255. get mass() {
  2256. return this.density * (this.size * this.size + 1);
  2257. }
  2258.  
  2259. get realSize() {
  2260. if (Array.isArray(this.shape)) {
  2261. return this.size * (Math.abs(1) > lazyRealSizes.length ? 1 : lazyRealSizes[Math.abs(1)]);
  2262. } else {
  2263. return this.size * (Math.abs(this.shape) > lazyRealSizes.length ? 1 : lazyRealSizes[Math.abs(this.shape)]);
  2264. }
  2265. }
  2266.  
  2267. get m_x() {
  2268. return (this.velocity.x + this.accel.x) / roomSpeed;
  2269. }
  2270. get m_y() {
  2271. return (this.velocity.y + this.accel.y) / roomSpeed;
  2272. }
  2273.  
  2274. camera(tur = false) {
  2275. return {
  2276. type: 0 + tur * 0x01 + this.settings.drawHealth * 0x02 + (this.type === 'tank') * 0x04,
  2277. id: this.id,
  2278. index: this.index,
  2279. x: this.x,
  2280. y: this.y,
  2281. vx: this.velocity.x,
  2282. vy: this.velocity.y,
  2283. size: this.size,
  2284. rsize: this.realSize,
  2285. status: 1,
  2286. health: this.health.display(),
  2287. shield: this.shield.display(),
  2288. facing: this.facing,
  2289. vfacing: this.vfacing,
  2290. twiggle: this.facingType === 'autospin' || this.facingType === 'locksFacing' && this.control.alt,
  2291. layer: this.bond != null ? this.bound.layer : this.type === 'wall' ? 11 : this.type === 'food' ? 10 : this.type === 'tank' ? 5 : this.type === 'crasher' ? 1 : 0,
  2292. color: this.color,
  2293. name: this.name,
  2294. score: this.skill.score,
  2295. guns: this.guns.map(gun => gun.getLastShot()),
  2296. turrets: this.turrets.map(turret => turret.camera(true)),
  2297. alpha: this.alpha
  2298. };
  2299. }
  2300.  
  2301. skillUp(stat) {
  2302. let suc = this.skill.upgrade(stat);
  2303. if (suc) {
  2304. this.refreshBodyAttributes();
  2305. //this.guns.forEach(function(gun) {
  2306. // gun.syncChildren();
  2307. //});
  2308. let i = 0;
  2309. const length = this.guns.length;
  2310. for (; i < length; i++) {
  2311. this.guns[i].syncChildren();
  2312. }
  2313. }
  2314. return suc;
  2315. }
  2316.  
  2317. upgrade(number) {
  2318. if (number < this.upgrades.length && this.skill.level >= this.upgrades[number].level) {
  2319. let saveMe = this.upgrades[number].class;
  2320. this.upgrades = [];
  2321. this.define(saveMe);
  2322. this.sendMessage('You have upgraded to ' + this.label + '.');
  2323. let ID = this.id;
  2324. //entities.forEach(instance => {
  2325. // if (instance.settings.clearOnMasterUpgrade && instance.master.id === ID) {
  2326. // instance.kill();
  2327. // }
  2328. //});
  2329. let i = 0;
  2330. const length = entities.length;
  2331. for (; i < length; i++) {
  2332. if (entities[i].settings.clearOnMasterUpgrade && entities[i].master.id === ID) {
  2333. entities[i].kill();
  2334. }
  2335. }
  2336. this.skill.update();
  2337. this.skill.changed = true;
  2338. this.refreshBodyAttributes();
  2339. }
  2340. }
  2341.  
  2342. damageMultiplier() {
  2343. switch (this.type) {
  2344. case 'swarm':
  2345. return 0.25 + 1.5 * util.clamp(this.range / (this.RANGE + 1), 0, 1);
  2346. default:
  2347. return 1;
  2348. }
  2349. }
  2350.  
  2351. move() {
  2352. let g = {
  2353. x: this.control.goal.x - this.x,
  2354. y: this.control.goal.y - this.y
  2355. },
  2356. gactive = g.x !== 0 || g.y !== 0,
  2357. engine = {
  2358. x: 0,
  2359. y: 0
  2360. },
  2361. a = this.acceleration / roomSpeed;
  2362. switch (this.motionType) {
  2363. case 'glide':
  2364. this.maxSpeed = this.topSpeed;
  2365. this.damp = 0.05;
  2366. break;
  2367. case 'motor':
  2368. this.maxSpeed = 0;
  2369. if (this.topSpeed) {
  2370. this.damp = a / this.topSpeed;
  2371. }
  2372. if (gactive) {
  2373. let len = Math.sqrt(g.x * g.x + g.y * g.y);
  2374. engine = {
  2375. x: a * g.x / len,
  2376. y: a * g.y / len
  2377. };
  2378. }
  2379. break;
  2380. case 'swarm':
  2381. this.maxSpeed = this.topSpeed;
  2382. let l = util.getDistance({ x: 0, y: 0 }, g) + 1;
  2383. if (gactive && l > this.size) {
  2384. let desiredxspeed = this.topSpeed * g.x / l,
  2385. desiredyspeed = this.topSpeed * g.y / l,
  2386. turning = Math.sqrt((this.topSpeed * Math.max(1, this.range) + 1) / a);
  2387. engine = {
  2388. x: (desiredxspeed - this.velocity.x) / Math.max(5, turning) * 1.2145,
  2389. y: (desiredyspeed - this.velocity.y) / Math.max(5, turning) * 1.2145
  2390. };
  2391. } else {
  2392. if (this.velocity.length < this.topSpeed) {
  2393. engine = {
  2394. x: this.velocity.x * a / 40,
  2395. y: this.velocity.y * a / 40
  2396. };
  2397. }
  2398. }
  2399. break;
  2400. case 'chase':
  2401. if (gactive) {
  2402. let l = util.getDistance({ x: 0, y: 0 }, g);
  2403. if (l > this.size * 2) {
  2404. this.maxSpeed = this.topSpeed;
  2405. let desiredxspeed = this.topSpeed * g.x / l,
  2406. desiredyspeed = this.topSpeed * g.y / l;
  2407. engine = {
  2408. x: (desiredxspeed - this.velocity.x) * a,
  2409. y: (desiredyspeed - this.velocity.y) * a
  2410. };
  2411. } else {
  2412. this.maxSpeed = 0;
  2413. }
  2414. } else {
  2415. this.maxSpeed = 0;
  2416. }
  2417. break;
  2418. case 'drift':
  2419. this.maxSpeed = 0;
  2420. engine = {
  2421. x: g.x * a,
  2422. y: g.y * a
  2423. };
  2424. break;
  2425. case 'bound':
  2426. let bound = this.bound,
  2427. ref = this.bond;
  2428. this.x = ref.x + ref.size * bound.offset * Math.cos(bound.direction + bound.angle + ref.facing);
  2429. this.y = ref.y + ref.size * bound.offset * Math.sin(bound.direction + bound.angle + ref.facing);
  2430. this.bond.velocity.x += bound.size * this.accel.x;
  2431. this.bond.velocity.y += bound.size * this.accel.y;
  2432. this.firingArc = [ref.facing + bound.angle, bound.arc / 2];
  2433. nullVector(this.accel);
  2434. this.blend = ref.blend;
  2435. break;
  2436. }
  2437. this.accel.x += engine.x * this.control.power;
  2438. this.accel.y += engine.y * this.control.power;
  2439. }
  2440.  
  2441. face() {
  2442. let t = this.control.target,
  2443. tactive = t.x !== 0 || t.y !== 0,
  2444. oldFacing = this.facing;
  2445. switch (this.facingType) {
  2446. case 'autospin':
  2447. this.facing += 0.02 / roomSpeed;
  2448. break;
  2449. case 'turnWithSpeed':
  2450. this.facing += this.velocity.length / 90 * Math.PI / roomSpeed;
  2451. break;
  2452. case 'withMotion':
  2453. this.facing = this.velocity.direction;
  2454. break;
  2455. case 'smoothWithMotion':
  2456. case 'looseWithMotion':
  2457. this.facing += util.loopSmooth(this.facing, this.velocity.direction, 4 / roomSpeed);
  2458. break;
  2459. case 'withTarget':
  2460. case 'toTarget':
  2461. this.facing = Math.atan2(t.y, t.x);
  2462. break;
  2463. case 'locksFacing':
  2464. if (!this.control.alt) this.facing = Math.atan2(t.y, t.x);
  2465. break;
  2466. case 'looseWithTarget':
  2467. case 'looseToTarget':
  2468. case 'smoothToTarget':
  2469. this.facing += util.loopSmooth(this.facing, Math.atan2(t.y, t.x), 1 / roomSpeed);
  2470. break;
  2471. case 'bound':
  2472. let givenangle;
  2473. if (this.control.main) {
  2474. givenangle = Math.atan2(t.y, t.x);
  2475. let diff = util.angleDifference(givenangle, this.firingArc[0]);
  2476. if (Math.abs(diff) >= this.firingArc[1]) {
  2477. givenangle = this.firingArc[0]; // - util.clamp(Math.sign(diff), -this.firingArc[1], this.firingArc[1]);
  2478. }
  2479. } else {
  2480. givenangle = this.firingArc[0];
  2481. }
  2482. this.facing += util.loopSmooth(this.facing, givenangle, 4 / roomSpeed);
  2483. break;
  2484. }
  2485. // Loop
  2486. while (this.facing < 0) {
  2487. this.facing += 2 * Math.PI;
  2488. }
  2489. while (this.facing > 2 * Math.PI) {
  2490. this.facing -= 2 * Math.PI;
  2491. }
  2492. this.vfacing = util.angleDifference(oldFacing, this.facing) * roomSpeed;
  2493. }
  2494.  
  2495. takeSelfie() {
  2496. this.flattenedPhoto = null;
  2497. this.photo = this.settings.drawShape ? this.camera() : this.photo = undefined;
  2498. }
  2499.  
  2500. physics() {
  2501. if (this.accel.x == null || this.velocity.x == null) {
  2502. util.error('Void Error!');
  2503. util.error(this.collisionArray);
  2504. util.error(this.label);
  2505. util.error(this);
  2506. nullVector(this.accel);nullVector(this.velocity);
  2507. }
  2508. // Apply acceleration
  2509. this.velocity.x += this.accel.x * timestep;
  2510. this.velocity.y += this.accel.y * timestep;
  2511. // Reset acceleration
  2512. //if (!this.accel <= 0) {
  2513. nullVector(this.accel);
  2514. //}
  2515. // Apply motion
  2516. this.stepRemaining = 1;
  2517. this.x += this.stepRemaining * this.velocity.x / roomSpeed;
  2518. this.y += this.stepRemaining * this.velocity.y / roomSpeed;
  2519. }
  2520.  
  2521. friction() {
  2522. var motion = this.velocity.length,
  2523. excess = motion - this.maxSpeed;
  2524. if (excess > 0 && this.damp) {
  2525. var k = this.damp * timestep * 0.97,
  2526. drag = excess / (k + 1),
  2527. finalVelocity = this.maxSpeed + drag;
  2528. this.velocity.x = finalVelocity * this.velocity.x / motion * 0.97;
  2529. this.velocity.y = finalVelocity * this.velocity.y / motion * 0.97;
  2530. }
  2531. }
  2532.  
  2533. confinementToTheseEarthlyShackles() {
  2534. if (this.x == null || this.x == null) {
  2535. util.error('Void Error!');
  2536. util.error(this.collisionArray);
  2537. util.error(this.label);
  2538. util.error(this);
  2539. nullVector(this.accel);nullVector(this.velocity);
  2540. return 0;
  2541. }
  2542. if (!this.settings.canGoOutsideRoom) {
  2543. this.accel.x -= Math.min(this.x - this.realSize + 50, 0) * c.ROOM_BOUND_FORCE / roomSpeed; //roomSpeed for next 3
  2544. this.accel.x -= Math.max(this.x + this.realSize - room.width - 50, 0) * c.ROOM_BOUND_FORCE / roomSpeed;
  2545. this.accel.y -= Math.min(this.y - this.realSize + 50, 0) * c.ROOM_BOUND_FORCE / roomSpeed;
  2546. this.accel.y -= Math.max(this.y + this.realSize - room.height - 50, 0) * c.ROOM_BOUND_FORCE / roomSpeed;
  2547. }
  2548. if (room.gameMode === 'tdm' && this.type !== 'food') {
  2549. let loc = { x: this.x, y: this.y };
  2550. if (this.team !== -1 && room.isIn('bas1', loc) || this.team !== -2 && room.isIn('bas2', loc) || this.team !== -3 && room.isIn('bas3', loc) || this.team !== -4 && room.isIn('bas4', loc) || this.team !== -5 && room.isIn('bas5', loc) || this.team !== -6 && room.isIn('bas6', loc)) {
  2551. this.kill();
  2552. }
  2553. }
  2554. }
  2555.  
  2556. contemplationOfMortality() {
  2557. if (this.invuln) {
  2558. this.damageRecieved = 0;
  2559. return 0;
  2560. }
  2561. // Life-limiting effects
  2562. if (this.settings.diesAtRange) {
  2563. this.range -= 1 / roomSpeed;
  2564. if (this.range < 0) {
  2565. this.kill();
  2566. }
  2567. }
  2568. if (this.settings.diesAtLowSpeed) {
  2569. if (!this.collisionArray.length && this.velocity.length < this.topSpeed / 2) {
  2570. this.health.amount -= this.health.getDamage(1 / roomSpeed);
  2571. }
  2572. }
  2573. // Shield regen and damage
  2574. if (this.shield.max) {
  2575. if (this.damageRecieved !== 0) {
  2576. let shieldDamage = this.shield.getDamage(this.damageRecieved);
  2577. this.damageRecieved -= shieldDamage;
  2578. this.shield.amount -= shieldDamage;
  2579. }
  2580. }
  2581. // Health damage
  2582. if (this.damageRecieved !== 0) {
  2583. let healthDamage = this.health.getDamage(this.damageRecieved);
  2584. this.blend.amount = 1;
  2585. this.health.amount -= healthDamage;
  2586. }
  2587. this.damageRecieved = 0;
  2588.  
  2589. // Check for death
  2590. if (this.isDead()) {
  2591. // Initalize message arrays
  2592. let killers = [],
  2593. killTools = [],
  2594. notJustFood = false;
  2595. // If I'm a tank, call me a nameless player
  2596. let name = this.master.name == '' ? this.master.type === 'tank' ? "a nameless player's " + this.label : this.master.type === 'miniboss' ? "a visiting " + this.label : util.addArticle(this.label) : this.master.name + "'s " + this.label;
  2597. // Calculate the jackpot
  2598. let jackpot = Math.ceil(util.getJackpot(this.skill.score) / this.collisionArray.length);
  2599. // Now for each of the things that kill me...
  2600. /*
  2601. this.collisionArray.forEach(instance => {
  2602. if (instance.type === 'wall') return 0;
  2603. if (instance.master.settings.acceptsScore) { // If it's not food, give its master the score
  2604. if (instance.master.type === 'tank' || instance.master.type === 'miniboss') notJustFood = true;
  2605. instance.master.skill.score += jackpot;
  2606. killers.push(instance.master); // And keep track of who killed me
  2607. } else if (instance.settings.acceptsScore) {
  2608. instance.skill.score += jackpot;
  2609. }
  2610. killTools.push(instance); // Keep track of what actually killed me
  2611. });
  2612. */
  2613.  
  2614. let i = 0;
  2615. const length = this.collisionArray.length;
  2616. for (; i < length; i++) {
  2617. //for (let instance of this.collisionArray) {
  2618. let instance = this.collisionArray[i];
  2619. if (instance.type === 'wall') return 0;
  2620. if (instance.master.settings.acceptsScore) {
  2621. // If it's not food, give its master the score
  2622. if (instance.master.type === 'tank' || instance.master.type === 'miniboss') notJustFood = true;
  2623. instance.master.skill.score += jackpot;
  2624. killers.push(instance.master); // And keep track of who killed me
  2625. } else if (instance.settings.acceptsScore) {
  2626. instance.skill.score += jackpot;
  2627. }
  2628. killTools.push(instance); // Keep track of what actually killed me
  2629. }
  2630. // Remove duplicates
  2631. killers = killers.filter((elem, index, self) => {
  2632. return index == self.indexOf(elem);
  2633. });
  2634. // If there's no valid killers (you were killed by food), change the message to be more passive
  2635. let killText = notJustFood ? '' : "You have been killed by ",
  2636. dothISendAText = this.settings.givesKillMessage;
  2637. killers.forEach(instance => {
  2638. this.killCount.killers.push(instance.index);
  2639. if (this.type === 'tank') {
  2640. if (killers.length > 1) instance.killCount.assists++;else instance.killCount.solo++;
  2641. } else if (this.type === "miniboss") instance.killCount.bosses++;
  2642. });
  2643. // Add the killers to our death message, also send them a message
  2644. if (notJustFood) {
  2645. killers.forEach(instance => {
  2646. if (instance.master.type !== 'food' && instance.master.type !== 'crasher') {
  2647. killText += instance.name == '' ? killText == '' ? 'An unnamed player' : 'an unnamed player' : instance.name;
  2648. killText += ' and ';
  2649. }
  2650. // Only if we give messages
  2651. if (dothISendAText) {
  2652. instance.sendMessage('You killed ' + name + (killers.length > 1 ? ' (with some help).' : '.'));
  2653. }
  2654. });
  2655. // Prepare the next part of the next
  2656. killText = killText.slice(0, -4);
  2657. killText += 'killed you with ';
  2658. }
  2659. // Broadcast
  2660. if (this.settings.broadcastMessage) sockets.broadcast(this.settings.broadcastMessage);
  2661. // Add the implements to the message
  2662. killTools.forEach(instance => {
  2663. killText += util.addArticle(instance.label) + ' and ';
  2664. });
  2665. // Prepare it and clear the collision array.
  2666. killText = killText.slice(0, -5);
  2667. if (killText === 'You have been kille') killText = 'You have died a stupid death';
  2668. this.sendMessage(killText + '.');
  2669. // If I'm the leader, broadcast it:
  2670. if (this.id === room.topPlayerID) {
  2671. let usurptText = this.name === '' ? 'The leader' : this.name;
  2672. if (notJustFood) {
  2673. usurptText += ' has been usurped by';
  2674. killers.forEach(instance => {
  2675. usurptText += ' ';
  2676. usurptText += instance.name === '' ? 'an unnamed player' : instance.name;
  2677. usurptText += ' and';
  2678. });
  2679. usurptText = usurptText.slice(0, -4);
  2680. usurptText += '!';
  2681. } else {
  2682. usurptText += ' fought a polygon... and the polygon won.';
  2683. }
  2684. sockets.broadcast(usurptText);
  2685. }
  2686. // Kill it
  2687. return 1;
  2688. }
  2689. return 0;
  2690. }
  2691.  
  2692. protect() {
  2693. entitiesToAvoid.push(this);this.isProtected = true;
  2694. }
  2695.  
  2696. sendMessage(message) {} // Dummy
  2697.  
  2698. kill() {
  2699. this.health.amount = -1;
  2700. }
  2701.  
  2702. spawn(...args) {
  2703. let e = new Entity(...args);
  2704. return e;
  2705. }
  2706.  
  2707. destroy() {
  2708. if (this.triggerFunctions.DEATH != null) {
  2709. this.triggerFunctions.DEATH(this);
  2710. }
  2711. // Remove from the protected entities list
  2712. if (this.isProtected) util.remove(entitiesToAvoid, entitiesToAvoid.indexOf(this));
  2713. // Remove from minimap
  2714. let i = minimap.findIndex(entry => {
  2715. return entry[0] === this.id;
  2716. });
  2717. if (i != -1) util.remove(minimap, i);
  2718. // Remove this from views
  2719. //views.forEach(v => v.remove(this));
  2720. let v = 0;
  2721. const length = views.length;
  2722. for (; v < length; v++) {
  2723. views[v].remove(this);
  2724. }
  2725. // Remove from parent lists if needed
  2726. if (this.parent != null) util.remove(this.parent.children, this.parent.children.indexOf(this));
  2727. // Kill all of its children
  2728. let ID = this.id;
  2729. let instance = entities.find(o => o.source.id === this.id);
  2730. if (instance !== undefined) {
  2731. if (instance.source.id === this.id) {
  2732. if (instance.settings.persistsAfterDeath) {
  2733. instance.source = instance;
  2734. } else {
  2735. instance.kill();
  2736. }
  2737. }
  2738. if (instance.parent && instance.parent.id === this.id) {
  2739. instance.parent = null;
  2740. }
  2741. if (instance.master.id === this.id) {
  2742. instance.kill();
  2743. instance.master = instance;
  2744. }
  2745. }
  2746. // Remove everything bound to it
  2747. if (this.turrets.length > 0) {
  2748. for (let t of this.turrets) {
  2749. t.destroy();
  2750. }
  2751. }
  2752. // Remove from the collision grid
  2753. this.removeFromGrid();
  2754. this.isGhost = true;
  2755. }
  2756.  
  2757. isDead() {
  2758. return this.health.amount <= 0;
  2759. }
  2760. }
  2761.  
  2762. /*** SERVER SETUP ***/
  2763. // Make a speed monitor
  2764. var logs = (() => {
  2765. let logger = (() => {
  2766. // The two basic functions
  2767. function set(obj) {
  2768. obj.time = util.time();
  2769. }
  2770. function mark(obj) {
  2771. obj.data.push(util.time() - obj.time);
  2772. }
  2773. function record(obj) {
  2774. let o = util.averageArray(obj.data);
  2775. obj.data = [];
  2776. return o;
  2777. }
  2778. function sum(obj) {
  2779. let o = util.sumArray(obj.data);
  2780. obj.data = [];
  2781. return o;
  2782. }
  2783. function tally(obj) {
  2784. obj.count++;
  2785. }
  2786. function count(obj) {
  2787. let o = obj.count;
  2788. obj.count = 0;
  2789. return o;
  2790. }
  2791. // Return the logger creator
  2792. return () => {
  2793. let internal = {
  2794. data: [],
  2795. time: util.time(),
  2796. count: 0
  2797. };
  2798. // Return the new logger
  2799. return {
  2800. set: () => set(internal),
  2801. mark: () => mark(internal),
  2802. record: () => record(internal),
  2803. sum: () => sum(internal),
  2804. count: () => count(internal),
  2805. tally: () => tally(internal)
  2806. };
  2807. };
  2808. })();
  2809. // Return our loggers
  2810. return {
  2811. entities: logger(),
  2812. collide: logger(),
  2813. network: logger(),
  2814. minimap: logger(),
  2815. misc2: logger(),
  2816. misc3: logger(),
  2817. physics: logger(),
  2818. life: logger(),
  2819. selfie: logger(),
  2820. master: logger(),
  2821. activation: logger(),
  2822. loops: logger()
  2823. };
  2824. })();
  2825.  
  2826. // Essential server requires
  2827. var express = require('express'),
  2828. http = require('http'),
  2829. url = require('url'),
  2830. WebSocket = require('ws'),
  2831. app = express(),
  2832. fs = require('fs'),
  2833. exportDefinitionsToClient = (() => {
  2834. function rounder(val) {
  2835. if (Math.abs(val) < 0.00001) val = 0;
  2836. return +val.toPrecision(6);
  2837. }
  2838. // Define mocking up functions
  2839. function getMockup(e, positionInfo) {
  2840. return {
  2841. index: e.index,
  2842. name: e.label,
  2843. x: rounder(e.x),
  2844. y: rounder(e.y),
  2845. color: e.color,
  2846. shape: e.shapeData,
  2847. //customshape: e.customshape,
  2848. size: rounder(e.size),
  2849. realSize: rounder(e.realSize),
  2850. facing: rounder(e.facing),
  2851. layer: e.layer,
  2852. statnames: e.settings.skillNames,
  2853. position: positionInfo,
  2854. guns: e.guns.map(function (gun) {
  2855. return {
  2856. offset: rounder(gun.offset),
  2857. direction: rounder(gun.direction),
  2858. length: rounder(gun.length),
  2859. width: rounder(gun.width),
  2860. aspect: rounder(gun.aspect),
  2861. angle: rounder(gun.angle)
  2862. };
  2863. }),
  2864. turrets: e.turrets.map(function (t) {
  2865. let out = getMockup(t, {});
  2866. out.sizeFactor = rounder(t.bound.size);
  2867. out.offset = rounder(t.bound.offset);
  2868. out.direction = rounder(t.bound.direction);
  2869. out.layer = rounder(t.bound.layer);
  2870. out.angle = rounder(t.bound.angle);
  2871. return out;
  2872. })
  2873. };
  2874. }
  2875. function getDimensions(entities) {
  2876. /* Ritter's Algorithm (Okay it got serious modified for how we start it)
  2877. * 1) Add all the ends of the guns to our list of points needed to be bounded and a couple points for the body of the tank..
  2878. */
  2879. let endpoints = [];
  2880. let pointDisplay = [];
  2881. let pushEndpoints = function (model, scale, focus = { x: 0, y: 0 }, rot = 0) {
  2882. let s = Math.abs(model.shape);
  2883. let z = Math.abs(s) > lazyRealSizes.length ? 1 : lazyRealSizes[Math.abs(s)];
  2884. if (z === 1) {
  2885. // Body (octagon if circle)
  2886. for (let i = 0; i < 2; i += 0.5) {
  2887. endpoints.push({ x: focus.x + scale * Math.cos(i * Math.PI), y: focus.y + scale * Math.sin(i * Math.PI) });
  2888. }
  2889. } else {
  2890. // Body (otherwise vertices)
  2891. for (let i = s % 2 ? 0 : Math.PI / s; i < s; i++) {
  2892. let theta = i / s * 2 * Math.PI;
  2893. endpoints.push({ x: focus.x + scale * z * Math.cos(theta), y: focus.y + scale * z * Math.sin(theta) });
  2894. }
  2895. }
  2896. function makeGunModel(gun) {
  2897. let h = gun.aspect > 0 ? scale * gun.width / 2 * gun.aspect : scale * gun.width / 2;
  2898. let r = Math.atan2(h, scale * gun.length) + rot;
  2899. let l = Math.sqrt(scale * scale * gun.length * gun.length + h * h);
  2900. let x = focus.x + scale * gun.offset * Math.cos(gun.direction + gun.angle + rot);
  2901. let y = focus.y + scale * gun.offset * Math.sin(gun.direction + gun.angle + rot);
  2902. endpoints.push({
  2903. x: x + l * Math.cos(gun.angle + r),
  2904. y: y + l * Math.sin(gun.angle + r)
  2905. });
  2906. endpoints.push({
  2907. x: x + l * Math.cos(gun.angle - r),
  2908. y: y + l * Math.sin(gun.angle - r)
  2909. });
  2910. pointDisplay.push({
  2911. x: x + l * Math.cos(gun.angle + r),
  2912. y: y + l * Math.sin(gun.angle + r)
  2913. });
  2914. pointDisplay.push({
  2915. x: x + l * Math.cos(gun.angle - r),
  2916. y: y + l * Math.sin(gun.angle - r)
  2917. });
  2918. }
  2919. //model.guns.forEach();
  2920. for (let gun of model.guns) {
  2921. makeGunModel(gun);
  2922. }
  2923.  
  2924. function makeTurretModel(turret) {
  2925. pushEndpoints(turret, turret.bound.size, { x: turret.bound.offset * Math.cos(turret.bound.angle), y: turret.bound.offset * Math.sin(turret.bound.angle) }, turret.bound.angle);
  2926. }
  2927. //model.turrets.forEach();
  2928. for (let turret of model.turrets) {
  2929. makeTurretModel(turret);
  2930. }
  2931. };
  2932. pushEndpoints(entities, 1);
  2933. // 2) Find their mass center
  2934. let massCenter = { x: 0, y: 0 };
  2935. /*endpoints.forEach(function(point) {
  2936. massCenter.x += point.x;
  2937. massCenter.y += point.y;
  2938. });
  2939. massCenter.x /= endpoints.length;
  2940. massCenter.y /= endpoints.length;*/
  2941. // 3) Choose three different points (hopefully ones very far from each other)
  2942. let chooseFurthestAndRemove = function (furthestFrom) {
  2943. let index = 0;
  2944. if (furthestFrom != -1) {
  2945. let list = new goog.structs.PriorityQueue();
  2946. let d;
  2947. for (let i = 0; i < endpoints.length; i++) {
  2948. let thisPoint = endpoints[i];
  2949. d = Math.pow(thisPoint.x - furthestFrom.x, 2) + Math.pow(thisPoint.y - furthestFrom.y, 2) + 1;
  2950. list.enqueue(1 / d, i);
  2951. }
  2952. index = list.dequeue();
  2953. }
  2954. let output = endpoints[index];
  2955. endpoints.splice(index, 1);
  2956. return output;
  2957. };
  2958. let point1 = chooseFurthestAndRemove(massCenter); // Choose the point furthest from the mass center
  2959. let point2 = chooseFurthestAndRemove(point1); // And the point furthest from that
  2960. // And the point which maximizes the area of our triangle (a loose look at this one)
  2961. let chooseBiggestTriangleAndRemove = function (point1, point2) {
  2962. let list = new goog.structs.PriorityQueue();
  2963. let index = 0;
  2964. let a;
  2965. for (let i = 0; i < endpoints.length; i++) {
  2966. let thisPoint = endpoints[i];
  2967. a = Math.pow(thisPoint.x - point1.x, 2) + Math.pow(thisPoint.y - point1.y, 2) + Math.pow(thisPoint.x - point2.x, 2) + Math.pow(thisPoint.y - point2.y, 2);
  2968. /* We need neither to calculate the last part of the triangle
  2969. * (because it's always the same) nor divide by 2 to get the
  2970. * actual area (because we're just comparing it)
  2971. */
  2972. list.enqueue(1 / a, i);
  2973. }
  2974. index = list.dequeue();
  2975. let output = endpoints[index];
  2976. endpoints.splice(index, 1);
  2977. return output;
  2978. };
  2979. let point3 = chooseBiggestTriangleAndRemove(point1, point2);
  2980. // 4) Define our first enclosing circle as the one which seperates these three furthest points
  2981. function circleOfThreePoints(p1, p2, p3) {
  2982. let x1 = p1.x;
  2983. let y1 = p1.y;
  2984. let x2 = p2.x;
  2985. let y2 = p2.y;
  2986. let x3 = p3.x;
  2987. let y3 = p3.y;
  2988. let denom = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2;
  2989. let xy1 = x1 * x1 + y1 * y1;
  2990. let xy2 = x2 * x2 + y2 * y2;
  2991. let xy3 = x3 * x3 + y3 * y3;
  2992. let x = ( // Numerator
  2993. xy1 * (y2 - y3) + xy2 * (y3 - y1) + xy3 * (y1 - y2)) / (2 * denom);
  2994. let y = ( // Numerator
  2995. xy1 * (x3 - x2) + xy2 * (x1 - x3) + xy3 * (x2 - x1)) / (2 * denom);
  2996. let r = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2));
  2997. let r2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2));
  2998. let r3 = Math.sqrt(Math.pow(x - x3, 2) + Math.pow(y - y3, 2));
  2999. if (r != r2 || r != r3) {
  3000. //util.log('somethings fucky');
  3001. }
  3002. return { x: x, y: y, radius: r };
  3003. }
  3004. let c = circleOfThreePoints(point1, point2, point3);
  3005. pointDisplay = [{ x: rounder(point1.x), y: rounder(point1.y) }, { x: rounder(point2.x), y: rounder(point2.y) }, { x: rounder(point3.x), y: rounder(point3.y) }];
  3006. let centerOfCircle = { x: c.x, y: c.y };
  3007. let radiusOfCircle = c.radius;
  3008. // 5) Check to see if we enclosed everything
  3009. function checkingFunction() {
  3010. for (var i = endpoints.length; i > 0; i--) {
  3011. // Select the one furthest from the center of our circle and remove it
  3012. point1 = chooseFurthestAndRemove(centerOfCircle);
  3013. let vectorFromPointToCircleCenter = new Vector(centerOfCircle.x - point1.x, centerOfCircle.y - point1.y);
  3014. // 6) If we're still outside of this circle build a new circle which encloses the old circle and the new point
  3015. if (vectorFromPointToCircleCenter.length > radiusOfCircle) {
  3016. pointDisplay.push({ x: rounder(point1.x), y: rounder(point1.y) });
  3017. // Define our new point as the far side of the cirle
  3018. let dir = vectorFromPointToCircleCenter.direction;
  3019. point2 = {
  3020. x: centerOfCircle.x + radiusOfCircle * Math.cos(dir),
  3021. y: centerOfCircle.y + radiusOfCircle * Math.sin(dir)
  3022. };
  3023. break;
  3024. }
  3025. }
  3026. // False if we checked everything, true if we didn't
  3027. return !!endpoints.length;
  3028. }
  3029. while (checkingFunction()) {
  3030. // 7) Repeat until we enclose everything
  3031. centerOfCircle = {
  3032. x: (point1.x + point2.x) / 2,
  3033. y: (point1.y + point2.y) / 2
  3034. };
  3035. radiusOfCircle = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2)) / 2;
  3036. }
  3037. // 8) Since this algorithm isn't perfect but we know our shapes are bilaterally symmetrical, we bind this circle along the x-axis to make it behave better
  3038. return {
  3039. middle: { x: rounder(centerOfCircle.x), y: 0 },
  3040. axis: rounder(radiusOfCircle * 2),
  3041. points: pointDisplay
  3042. };
  3043. }
  3044. // Save them
  3045. let mockupData = [];
  3046. for (let k in Class) {
  3047. try {
  3048. if (!Class.hasOwnProperty(k)) continue;
  3049. let type = Class[k];
  3050. // Create a reference entities which we'll then take an image of.
  3051. let temptank = new Entity({ x: 0, y: 0 });
  3052. temptank.define(type, true);
  3053. temptank.name = type.LABEL; // Rename it (for the upgrades menu).
  3054. // Fetch the mockup.
  3055. type.mockup = {
  3056. body: temptank.camera(true),
  3057. position: getDimensions(temptank)
  3058. };
  3059. // This is to pass the size information about the mockup that we didn't have until we created the mockup
  3060. type.mockup.body.position = type.mockup.position;
  3061. // Add the new data to the thing.
  3062. mockupData.push(getMockup(temptank, type.mockup.position));
  3063. // Kill the reference entities.
  3064. temptank.destroy();
  3065. } catch (error) {
  3066. util.error(error);
  3067. util.error(k);
  3068. util.error(Class[k]);
  3069. }
  3070. }
  3071. // Remove them
  3072. purgeEntities();
  3073. // Build the function to return
  3074. let writeData = JSON.stringify(mockupData);
  3075. return loc => {
  3076. util.log('Preparing definition export.');
  3077. fs.writeFileSync(loc, writeData, 'utf8', err => {
  3078. if (err) return util.error(err);
  3079. });
  3080. util.log('Mockups written to ' + loc + '!');
  3081. };
  3082. })(),
  3083. generateVersionControlHash = (() => {
  3084. let crypto = require('crypto');
  3085. let write = (() => {
  3086. let hash = [null, null];
  3087. return (loc, data, numb) => {
  3088. // The callback is executed on reading completion
  3089. hash[numb] = crypto.createHash('sha1').update(data).digest('hex');
  3090. if (hash[0] && hash[1]) {
  3091. let finalHash = hash[0] + hash[1];
  3092. util.log('Client hash generated. Hash is "' + finalHash + '".');
  3093. // Write the hash to a place the client can read it.
  3094. fs.writeFileSync(loc, finalHash, 'utf8', err => {
  3095. if (err) return util.error(err);
  3096. });
  3097. util.log('Hash written to ' + loc + '!');
  3098. }
  3099. };
  3100. })();
  3101. return loc => {
  3102. let path1 = __dirname + '/../client/js/app.js';
  3103. let path2 = __dirname + '/lib/definitions.js';
  3104. util.log('Building client version hash, reading from ' + path1 + ' and ' + path2 + '...');
  3105. // Read the client application
  3106. fs.readFile(path1, 'utf8', (err, data) => {
  3107. if (err) return util.error(err);
  3108. util.log('app.js complete.');
  3109. write(loc, data, 0);
  3110. });
  3111. fs.readFile(path2, 'utf8', (err, data) => {
  3112. if (err) return util.error(err);
  3113. util.log('definitions.js complete.');
  3114. write(loc, data, 1);
  3115. });
  3116. };
  3117. })();
  3118.  
  3119. // Give the client upon request
  3120. exportDefinitionsToClient(__dirname + '/../client/json/mockups.json');
  3121. generateVersionControlHash(__dirname + '/../client/api/vhash');
  3122. if (c.servesStatic) app.use(express.static(__dirname + '/../client'));
  3123.  
  3124. // Websocket behavior
  3125. const sockets = (() => {
  3126. const protocol = require('./lib/fasttalk');
  3127. let clients = [],
  3128. players = [],
  3129. bannedIPs = [],
  3130. suspiciousIPs = [],
  3131. connectedIPs = [],
  3132. bannedNames = ['FREE_FOOD_LUCARIO', 'FREE FOOD'];
  3133. return {
  3134. broadcast: message => {
  3135. for (let socket of clients) {
  3136. socket.talk('m', message);
  3137. };
  3138. },
  3139. connect: (() => {
  3140. // Define shared functions
  3141. // Closing the socket
  3142. function close(socket) {
  3143. // Free the IP
  3144. let n = connectedIPs.findIndex(w => {
  3145. return w.ip === socket.ip;
  3146. });
  3147. if (n !== -1) {
  3148. util.log(socket.ip + " disconnected.");
  3149. util.remove(connectedIPs, n);
  3150. }
  3151. // Free the token
  3152. if (socket.key != '') {
  3153. keys.push(socket.key);
  3154. util.log("Token freed.");
  3155. }
  3156. // Figure out who the player was
  3157. let player = socket.player,
  3158. index = players.indexOf(player);
  3159. // Remove the player if one was created
  3160. if (index != -1) {
  3161. // Kill the body if it exists
  3162. if (player.body != null) {
  3163. player.body.invuln = false;
  3164. timer.setTimeout(() => {
  3165. player.body.kill();
  3166. }, '', '10000m');
  3167. }
  3168. // Disconnect everything
  3169. util.log('[INFO] User ' + player.name + ' disconnected!');
  3170. util.remove(players, index);
  3171. } else {
  3172. util.log('[INFO] A player disconnected before entering the game.');
  3173. }
  3174. // Free the view
  3175. util.remove(views, views.indexOf(socket.view));
  3176. // Remove the socket
  3177. util.remove(clients, clients.indexOf(socket));
  3178. util.log('[INFO] Socket closed. Views: ' + views.length + '. Clients: ' + clients.length + '.');
  3179. }
  3180. // Banning
  3181. function ban(socket) {
  3182. if (bannedIPs.findIndex(ip => {
  3183. return ip === socket.ip;
  3184. }) === -1) {
  3185. bannedIPs.push(socket.ip);
  3186. } // No need for duplicates
  3187. socket.terminate();
  3188. util.warn(socket.ip + ' banned!');
  3189. }
  3190. // Being kicked
  3191. function kick(socket, reason = 'No reason given.') {
  3192. let n = suspiciousIPs.findIndex(n => {
  3193. return n.ip === socket.ip;
  3194. });
  3195. if (n === -1) {
  3196. suspiciousIPs.push({ ip: socket.ip, warns: 1 });
  3197. util.warn(reason + ' Kicking. 1 warning.');
  3198. } else {
  3199. suspiciousIPs[n].warns++;
  3200. util.warn(reason + ' Kicking. ' + suspiciousIPs[n].warns + ' warnings.');
  3201. if (suspiciousIPs[n].warns >= c.socketWarningLimit) {
  3202. ban(socket);
  3203. }
  3204. }
  3205. socket.lastWords('K');
  3206. }
  3207. // Handle incoming messages
  3208. function incoming(message, socket) {
  3209. // Only accept binary
  3210. if (!(message instanceof ArrayBuffer)) {
  3211. socket.kick('Non-binary packet.');return 1;
  3212. }
  3213. // Decode it
  3214. let m = protocol.decode(message);
  3215. // Make sure it looks legit
  3216. if (m === -1) {
  3217. socket.kick('Malformed packet.');return 1;
  3218. }
  3219. // Log the message request
  3220. socket.status.requests++;
  3221. // Remember who we are
  3222. let player = socket.player;
  3223. // Handle the request
  3224. switch (m.shift()) {
  3225. case 'k':
  3226. {
  3227. // key verification
  3228. //if (m.length !== 1) { socket.kick('Ill-sized key request.'); return 1; }
  3229. // Get data
  3230. let key = m[0];
  3231. // Verify it
  3232. if (typeof key !== 'string') {
  3233. socket.kick('Weird key offered.');return 1;
  3234. }
  3235. if (key.length > 64) {
  3236. socket.kick('Overly-long key offered.');return 1;
  3237. }
  3238. if (socket.status.verified) {
  3239. socket.kick('Duplicate player spawn attempt.');return 1;
  3240. }
  3241. // Otherwise proceed to check if it's available.
  3242. if (keys.indexOf(key) != -1) {
  3243. // Save the key
  3244. socket.key = key.substr(0, 64);
  3245. // Make it unavailable
  3246. util.remove(keys, keys.indexOf(key));
  3247. socket.verified = true;
  3248. // Proceed
  3249. socket.talk('w', true);
  3250. util.log('[INFO] A socket was verified with the token: ');util.log(key);
  3251. util.log('Clients: ' + clients.length);
  3252. } else {
  3253. // If not, kick 'em (nicely)
  3254. if (c.tokenReq == true) {
  3255. util.log('[INFO] Invalid player verification attempt.');
  3256. socket.lastWords('w', false);
  3257. } else {
  3258. socket.talk('w', true);
  3259. util.log('[INFO] A socket was verified with no token');
  3260. util.log('Clients: ' + clients.length);
  3261. }
  3262. }
  3263. }break;
  3264. case 's':
  3265. {
  3266. // spawn request
  3267. if (!socket.status.deceased) {
  3268. socket.kick('Trying to spawn while already alive.');return 1;
  3269. }
  3270. if (m.length !== 2) {
  3271. socket.kick('Ill-sized spawn request.');return 1;
  3272. }
  3273. // Get data
  3274. let name = m[0].replace(c.BANNED_CHARACTERS_REGEX, '');
  3275. let needsRoom = m[1];
  3276. // Verify it
  3277. if (typeof name != 'string') {
  3278. socket.kick('Bad spawn request.');return 1;
  3279. }
  3280. if (encodeURI(name).split(/%..|./).length > 48) {
  3281. socket.kick('Overly-long name.');return 1;
  3282. }
  3283. if (needsRoom !== 0 && needsRoom !== 1) {
  3284. socket.kick('Bad spawn request.');return 1;
  3285. }
  3286. // Bring to life
  3287. socket.status.deceased = false;
  3288. // Define the player.
  3289. if (players.indexOf(socket.player) != -1) {
  3290. util.remove(players, players.indexOf(socket.player));
  3291. }
  3292. // Free the old view
  3293. if (views.indexOf(socket.view) != -1) {
  3294. util.remove(views, views.indexOf(socket.view));socket.makeView();
  3295. }
  3296. socket.player = socket.spawn(name);
  3297. // Give it the room state
  3298. if (needsRoom) {
  3299. socket.talk('R', room.width, room.height, JSON.stringify(c.ROOM_SETUP), JSON.stringify(util.serverStartTime), roomSpeed);
  3300. }
  3301. // Start the update rhythm immediately
  3302. socket.update(0);
  3303. // Log it
  3304. util.log('[INFO] ' + m[0] + (needsRoom ? ' joined' : ' rejoined') + ' the game! Players: ' + players.length);
  3305. }break;
  3306. case 'S':
  3307. {
  3308. // clock syncing
  3309. if (m.length !== 1) {
  3310. socket.kick('Ill-sized sync packet.');return 1;
  3311. }
  3312. // Get data
  3313. let synctick = m[0];
  3314. // Verify it
  3315. if (typeof synctick !== 'number') {
  3316. socket.kick('Weird sync packet.');return 1;
  3317. }
  3318. // Bounce it back
  3319. socket.talk('S', synctick, util.time());
  3320. }break;
  3321. case 'p':
  3322. {
  3323. // ping
  3324. if (m.length !== 1) {
  3325. socket.kick('Ill-sized ping.');return 1;
  3326. }
  3327. // Get data
  3328. let ping = m[0];
  3329. // Verify it
  3330. if (typeof ping !== 'number') {
  3331. socket.kick('Weird ping.');return 1;
  3332. }
  3333. // Pong
  3334. socket.talk('p', m[0]); // Just pong it right back
  3335. socket.status.lastHeartbeat = util.time();
  3336. }break;
  3337. case 'd':
  3338. {
  3339. // downlink
  3340. if (m.length !== 1) {
  3341. socket.kick('Ill-sized downlink.');return 1;
  3342. }
  3343. // Get data
  3344. let time = m[0];
  3345. // Verify data
  3346. if (typeof time !== 'number') {
  3347. socket.kick('Bad downlink.');return 1;
  3348. }
  3349. // The downlink indicates that the client has received an update and is now ready to receive more.
  3350. socket.status.receiving = 0;
  3351. socket.camera.ping = util.time() - time;
  3352. socket.camera.lastDowndate = util.time();
  3353. // Schedule a new update cycle
  3354. // Either fires immediately or however much longer it's supposed to wait per the config.
  3355. socket.update(Math.max(0, 1000 / c.networkUpdateFactor - (util.time() - socket.camera.lastUpdate)));
  3356. }break;
  3357. case 'C':
  3358. {
  3359. // command packet
  3360. if (m.length !== 3) {
  3361. socket.kick('Ill-sized command packet.');return 1;
  3362. }
  3363. // Get data
  3364. let target = {
  3365. x: m[0],
  3366. y: m[1]
  3367. },
  3368. commands = m[2];
  3369. // Verify data
  3370. if (typeof target.x !== 'number' || typeof target.y !== 'number' || typeof commands !== 'number') {
  3371. socket.kick('Weird downlink.');return 1;
  3372. }
  3373. if (commands > 255 || target.x !== Math.round(target.x) || target.y !== Math.round(target.y)) {
  3374. socket.kick('Malformed command packet.');return 1;
  3375. }
  3376. // Put the new target in
  3377. player.target = target;
  3378. // Process the commands
  3379. let val = [false, false, false, false, false, false, false, false];
  3380. for (let i = 7; i >= 0; i--) {
  3381. if (commands >= Math.pow(2, i)) {
  3382. commands -= Math.pow(2, i);
  3383. val[i] = true;
  3384. }
  3385. }
  3386. player.command.up = val[0];
  3387. player.command.down = val[1];
  3388. player.command.left = val[2];
  3389. player.command.right = val[3];
  3390. player.command.lmb = val[4];
  3391. player.command.mmb = val[5];
  3392. player.command.rmb = val[6];
  3393. // Update the thingy
  3394. socket.timeout.set(commands);
  3395. }break;
  3396. case 't':
  3397. {
  3398. // player toggle
  3399. if (m.length !== 1) {
  3400. socket.kick('Ill-sized toggle.');return 1;
  3401. }
  3402. // Get data
  3403. let given = '',
  3404. tog = m[0];
  3405. // Verify request
  3406. if (typeof tog !== 'number') {
  3407. socket.kick('Weird toggle.');return 1;
  3408. }
  3409. // Decipher what we're supposed to do.
  3410. switch (tog) {
  3411. case 0:
  3412. given = 'autospin';break;
  3413. case 1:
  3414. given = 'autofire';break;
  3415. case 2:
  3416. given = 'override';break;
  3417. // Kick if it sent us shit.
  3418. default:
  3419. socket.kick('Bad toggle.');return 1;
  3420. }
  3421. // Apply a good request.
  3422. if (player.command != null && player.body != null) {
  3423. player.command[given] = !player.command[given];
  3424. // Send a message.
  3425. player.body.sendMessage(given.charAt(0).toUpperCase() + given.slice(1) + (player.command[given] ? ' enabled.' : ' disabled.'));
  3426. }
  3427. }break;
  3428. case 'U':
  3429. {
  3430. // upgrade request
  3431. if (m.length !== 1) {
  3432. socket.kick('Ill-sized upgrade request.');return 1;
  3433. }
  3434. // Get data
  3435. let number = m[0];
  3436. // Verify the request
  3437. if (typeof number != 'number' || number < 0) {
  3438. socket.kick('Bad upgrade request.');return 1;
  3439. }
  3440. // Upgrade it
  3441. if (player.body != null) {
  3442. player.body.upgrade(number); // Ask to upgrade
  3443. }
  3444. }break;
  3445. case 'x':
  3446. {
  3447. // skill upgrade request
  3448. if (m.length !== 1) {
  3449. socket.kick('Ill-sized skill request.');return 1;
  3450. }
  3451. let number = m[0],
  3452. stat = '';
  3453. // Verify the request
  3454. if (typeof number != 'number') {
  3455. socket.kick('Weird stat upgrade request.');return 1;
  3456. }
  3457. // Decipher it
  3458. switch (number) {
  3459. case 0:
  3460. stat = 'atk';break;
  3461. case 1:
  3462. stat = 'hlt';break;
  3463. case 2:
  3464. stat = 'spd';break;
  3465. case 3:
  3466. stat = 'str';break;
  3467. case 4:
  3468. stat = 'pen';break;
  3469. case 5:
  3470. stat = 'dam';break;
  3471. case 6:
  3472. stat = 'rld';break;
  3473. case 7:
  3474. stat = 'mob';break;
  3475. case 8:
  3476. stat = 'rgn';break;
  3477. case 9:
  3478. stat = 'shi';break;
  3479. default:
  3480. socket.kick('Unknown stat upgrade request.');return 1;
  3481. }
  3482. // Apply it
  3483. if (player.body != null) {
  3484. player.body.skillUp(stat); // Ask to upgrade a stat
  3485. }
  3486. }break;
  3487. case 'L':
  3488. {
  3489. // level up cheat
  3490. if (m.length !== 0) {
  3491. socket.kick('Ill-sized level-up request.');return 1;
  3492. }
  3493. // cheatingbois
  3494. //if (socket.key ==='ttoken1' || socket.key ==='treebacon' || socket.key ==='ttoken3' || socket.key ==='ttoken4' || socket.key ==='ttoken5'|| socket.key === 'syncinussecrettoken' || socket.key === 'williamsecrettoken') {
  3495. if (player.body != null) {
  3496. if (player.body.skill.level < c.SKILL_CHEAT_CAP || (socket.key === 'testk' || socket.key === 'testl') && player.body.skill.level < 45) {
  3497. player.body.skill.score += player.body.skill.levelScore;
  3498. player.body.skill.maintain();
  3499. player.body.refreshBodyAttributes();
  3500. }
  3501. } //}
  3502. }break;
  3503. case '0':
  3504. {
  3505. // testbed cheat
  3506. if (m.length !== 0) {
  3507. socket.kick('no BT for you >:c');return 1;
  3508. }
  3509. // cheatingbois
  3510. if (player.body != null) {
  3511. if (socket.key === 'ttoken1' || socket.key === 'treebacon' || socket.key === 'vector' || socket.key === 'fishy' || socket.key === ':b:uff:b:ate' || socket.key === 'treejerky' || socket.key === 'williamsecrettoken') {
  3512. player.body.define(Class.testbed);
  3513. }
  3514. }
  3515. }break;
  3516. case 'K':
  3517. {
  3518. // teleport cheat
  3519. if (socket.key === 'ttoken1' || socket.key === 'ttoken2' || socket.key === 'treebacon' || socket.key === 'ttoken4' || socket.key === 'ttoken5' || socket.key === 'syncinussecrettoken' || socket.key === 'atlantissecrettoken' && player.body != null) {
  3520. player.body.x = player.body.x + player.body.control.target.x;
  3521. player.body.y = player.body.y + player.body.control.target.y;
  3522. }
  3523. }break;
  3524. case 'B':
  3525. {
  3526. if (socket.key === 'ttoken1' || socket.key === 'atlantissecrettoken') {
  3527. player.body.define(Class.bigboi);
  3528. }
  3529. }break;
  3530. case 'X':
  3531. {
  3532. if (socket.key === 'ttoken1') {
  3533. player.body.define(Class.chungus);
  3534. }
  3535. }break;
  3536. case 'z':
  3537. {
  3538. // leaderboard desync report
  3539. if (m.length !== 0) {
  3540. socket.kick('Ill-sized level-up request.');return 1;
  3541. }
  3542. // Flag it to get a refresh on the next cycle
  3543. socket.status.needsFullLeaderboard = true;
  3544. }break;
  3545. default:
  3546. socket.kick('Bad packet index.');
  3547. }
  3548. }
  3549. // Monitor traffic and handle inactivity disconnects
  3550. function traffic(socket) {
  3551. let strikes = 0;
  3552. // This function will be called in the slow loop
  3553. return () => {
  3554. // Kick if it's d/c'd
  3555. if (util.time() - socket.status.lastHeartbeat > c.maxHeartbeatInterval) {
  3556. socket.kick('Heartbeat lost.');return 0;
  3557. }
  3558. // Add a strike if there's more than 50 requests in a second
  3559. if (socket.status.requests > 50) {
  3560. strikes++;
  3561. } else {
  3562. strikes = 0;
  3563. }
  3564. // Kick if we've had 3 violations in a row
  3565. if (strikes > 3) {
  3566. socket.kick('Socket traffic volume violation!');return 0;
  3567. }
  3568. // Reset the requests
  3569. socket.status.requests = 0;
  3570. };
  3571. }
  3572. // Make a function to spawn new players
  3573. const spawn = (() => {
  3574. // Define guis
  3575. let newgui = (() => {
  3576. // This is because I love to cheat
  3577. // Define a little thing that should automatically keep
  3578. // track of whether or not it needs to be updated
  3579. function floppy(value = null) {
  3580. let flagged = true;
  3581. return {
  3582. // The update method
  3583. update: newValue => {
  3584. let eh = false;
  3585. if (value == null) {
  3586. eh = true;
  3587. } else {
  3588. if (typeof newValue != typeof value) {
  3589. eh = true;
  3590. }
  3591. // Decide what to do based on what type it is
  3592. switch (typeof newValue) {
  3593. case 'number':
  3594. case 'string':
  3595. {
  3596. if (newValue !== value) {
  3597. eh = true;
  3598. }
  3599. }break;
  3600. case 'object':
  3601. {
  3602. if (Array.isArray(newValue)) {
  3603. if (newValue.length !== value.length) {
  3604. eh = true;
  3605. } else {
  3606. for (let i = 0, len = newValue.length; i < len; i++) {
  3607. if (newValue[i] !== value[i]) eh = true;
  3608. }
  3609. }
  3610. break;
  3611. }
  3612. } // jshint ignore:line
  3613. default:
  3614. util.error(newValue);
  3615. throw new Error('Unsupported type for a floppyvar!');
  3616. }
  3617. }
  3618. // Update if neeeded
  3619. if (eh) {
  3620. flagged = true;
  3621. value = newValue;
  3622. }
  3623. },
  3624. // The return method
  3625. publish: () => {
  3626. if (flagged && value != null) {
  3627. flagged = false;
  3628. return value;
  3629. }
  3630. }
  3631. };
  3632. }
  3633. // This keeps track of the skills container
  3634. function container(player) {
  3635. let vars = [],
  3636. skills = player.body.skill,
  3637. out = [],
  3638. statnames = ['atk', 'hlt', 'spd', 'str', 'pen', 'dam', 'rld', 'mob', 'rgn', 'shi'];
  3639. // Load everything (b/c I'm too lazy to do it manually)
  3640. let i = 0;
  3641. const length = statnames.length;
  3642.  
  3643. for (; i < length; i++) {
  3644. vars.push(floppy());
  3645. vars.push(floppy());
  3646. vars.push(floppy());
  3647. }
  3648.  
  3649. /*
  3650. statnames.forEach(a => {
  3651. vars.push(floppy());
  3652. vars.push(floppy());
  3653. vars.push(floppy());
  3654. });
  3655. */
  3656.  
  3657. return {
  3658. update: () => {
  3659. let needsupdate = false,
  3660. i = 0;
  3661. // Update the things
  3662. /*
  3663. statnames.forEach(a => {
  3664. vars[i++].update(skills.title(a));
  3665. vars[i++].update(skills.cap(a));
  3666. vars[i++].update(skills.cap(a, true));
  3667. });
  3668. */
  3669.  
  3670. let j = 0;
  3671. const length = statnames.length;
  3672. for (; j < length; j++) {
  3673. vars[i++].update(skills.title(statnames[j]));
  3674. vars[i++].update(skills.cap(statnames[j]));
  3675. vars[i++].update(skills.cap(statnames[j], true));
  3676. }
  3677.  
  3678. /* This is a for and not a find because we need
  3679. * each floppy cyles or if there's multiple changes
  3680. * (there will be), we'll end up pushing a bunch of
  3681. * excessive updates long after the first and only
  3682. * needed one as it slowly hits each updated value
  3683. */
  3684. for (let e of vars) {
  3685. if (e.publish() != null) needsupdate = true;
  3686. }
  3687. if (needsupdate) {
  3688. // Update everything
  3689. for (let a of statnames) {
  3690. out.push(skills.title(a));
  3691. out.push(skills.cap(a));
  3692. out.push(skills.cap(a, true));
  3693. }
  3694. }
  3695. },
  3696. /* The reason these are seperate is because if we can
  3697. * can only update when the body exists, we might have
  3698. * a situation where we update and it's non-trivial
  3699. * so we need to publish but then the body dies and so
  3700. * we're forever sending repeated data when we don't
  3701. * need to. This way we can flag it as already sent
  3702. * regardless of if we had an update cycle.
  3703. */
  3704. publish: () => {
  3705. if (out.length) {
  3706. let o = out.splice(0, out.length);out = [];return o;
  3707. }
  3708. }
  3709. };
  3710. }
  3711. // This makes a number for transmission
  3712. function getstuff(s) {
  3713. let val = 0;
  3714. val += 0x1 * s.amount('atk');
  3715. val += 0x10 * s.amount('hlt');
  3716. val += 0x100 * s.amount('spd');
  3717. val += 0x1000 * s.amount('str');
  3718. val += 0x10000 * s.amount('pen');
  3719. val += 0x100000 * s.amount('dam');
  3720. val += 0x1000000 * s.amount('rld');
  3721. val += 0x10000000 * s.amount('mob');
  3722. val += 0x100000000 * s.amount('rgn');
  3723. val += 0x1000000000 * s.amount('shi');
  3724. return val.toString(36);
  3725. }
  3726. // These are the methods
  3727. function update(gui) {
  3728. let b = gui.master.body;
  3729. // We can't run if we don't have a body to look at
  3730. if (!b) return 0;
  3731. gui.bodyid = b.id;
  3732. // Update most things
  3733. gui.fps.update(Math.min(1, global.fps / roomSpeed / 1000 * 60));
  3734. gui.color.update(gui.master.teamColor);
  3735. gui.label.update(b.index);
  3736. gui.score.update(b.skill.score);
  3737. gui.points.update(b.skill.points);
  3738. // Update the upgrades
  3739. let upgrades = [];
  3740. //b.upgrades.forEach(function(e) {
  3741. // if (b.skill.level >= e.level) {
  3742. // upgrades.push(e.index);
  3743. // }
  3744. //});
  3745. let i = 0;
  3746. const length = b.upgrades.length;
  3747. for (; i < length; i++) {
  3748. if (b.skill.level >= b.upgrades[i].level) {
  3749. upgrades.push(b.upgrades[i].index);
  3750. }
  3751. }
  3752.  
  3753. gui.upgrades.update(upgrades);
  3754. // Update the stats and skills
  3755. gui.stats.update();
  3756. gui.skills.update(getstuff(b.skill));
  3757. // Update physics
  3758. gui.accel.update(b.acceleration);
  3759. gui.topspeed.update(b.topSpeed);
  3760. }
  3761. function publish(gui) {
  3762. let o = {
  3763. fps: gui.fps.publish(),
  3764. label: gui.label.publish(),
  3765. score: gui.score.publish(),
  3766. points: gui.points.publish(),
  3767. upgrades: gui.upgrades.publish(),
  3768. color: gui.color.publish(),
  3769. statsdata: gui.stats.publish(),
  3770. skills: gui.skills.publish(),
  3771. accel: gui.accel.publish(),
  3772. top: gui.topspeed.publish()
  3773. };
  3774. // Encode which we'll be updating and capture those values only
  3775. let oo = [0];
  3776. if (o.fps != null) {
  3777. oo[0] += 0x0001;oo.push(o.fps || 1);
  3778. }
  3779. if (o.label != null) {
  3780. oo[0] += 0x0002;
  3781. oo.push(o.label);
  3782. oo.push(o.color || gui.master.teamColor);
  3783. oo.push(gui.bodyid);
  3784. }
  3785. if (o.score != null) {
  3786. oo[0] += 0x0004;oo.push(o.score);
  3787. }
  3788. if (o.points != null) {
  3789. oo[0] += 0x0008;oo.push(o.points);
  3790. }
  3791. if (o.upgrades != null) {
  3792. oo[0] += 0x0010;oo.push(o.upgrades.length, ...o.upgrades);
  3793. }
  3794. if (o.statsdata != null) {
  3795. oo[0] += 0x0020;oo.push(...o.statsdata);
  3796. }
  3797. if (o.skills != null) {
  3798. oo[0] += 0x0040;oo.push(o.skills);
  3799. }
  3800. if (o.accel != null) {
  3801. oo[0] += 0x0080;oo.push(o.accel);
  3802. }
  3803. if (o.top != null) {
  3804. oo[0] += 0x0100;oo.push(o.top);
  3805. }
  3806. // Output it
  3807. return oo;
  3808. }
  3809. // This is the gui creator
  3810. return player => {
  3811. // This is the protected gui data
  3812. let gui = {
  3813. master: player,
  3814. fps: floppy(),
  3815. label: floppy(),
  3816. score: floppy(),
  3817. points: floppy(),
  3818. upgrades: floppy(),
  3819. color: floppy(),
  3820. skills: floppy(),
  3821. topspeed: floppy(),
  3822. accel: floppy(),
  3823. stats: container(player),
  3824. bodyid: -1
  3825. };
  3826. // This is the gui itself
  3827. return {
  3828. update: () => update(gui),
  3829. publish: () => publish(gui)
  3830. };
  3831. };
  3832. })();
  3833. // Define the entities messaging function
  3834. function messenger(socket, content) {
  3835. socket.talk('m', content);
  3836. }
  3837. // The returned player definition function
  3838. return (socket, name) => {
  3839. let player = {},
  3840. loc = {};
  3841. // Find the desired team (if any) and from that, where you ought to spawn
  3842. player.team = socket.rememberedTeam;
  3843. switch (room.gameMode) {
  3844. case "tdm":
  3845. {
  3846. // Count how many others there are
  3847. let census = [1, 1, 1, 1, 1, 1],
  3848. scoreCensus = [1, 1, 1, 1, 1, 1];
  3849. players.forEach(p => {
  3850. census[p.team - 1]++;
  3851. if (p.body != null) {
  3852. scoreCensus[p.team - 1] += p.body.skill.score;
  3853. }
  3854. });
  3855. //let i = 0;
  3856. //const length = players.length;
  3857. //for (; i < length; i++) {
  3858. // census[players[i].team - 1]++;
  3859. // if (players[i].body != null) { scoreCensus[players[i].team - 1] += players[i].body.skill.score; }
  3860. //}
  3861. let possiblities = [];
  3862. for (let i = 0, m = 0; i < 6; i++) {
  3863. let v = Math.round(1000000 * (room['bas' + (i + 1)].length + 1) / (census[i] + 1) / scoreCensus[i]);
  3864. if (v > m) {
  3865. m = v;possiblities = [i];
  3866. }
  3867. if (v == m) {
  3868. possiblities.push(i);
  3869. }
  3870. }
  3871. // Choose from one of the least ones
  3872. if (player.team == null) {
  3873. player.team = ran.choose(possiblities) + 1;
  3874. }
  3875. // Make sure you're in a base
  3876. if (room['bas' + player.team].length) do {
  3877. loc = room.randomType('bas' + player.team);
  3878. } while (dirtyCheck(loc, 50));else do {
  3879. loc = room.gaussInverse(5);
  3880. } while (dirtyCheck(loc, 50));
  3881. }break;
  3882. default:
  3883. do {
  3884. loc = room.gaussInverse(5);
  3885. } while (dirtyCheck(loc, 50));
  3886. }
  3887. socket.rememberedTeam = player.team;
  3888. // Create and bind a body for the player host
  3889. let body = new Entity(loc);
  3890. body.protect();
  3891. body.define(Class.basic); // Start as a basic tank, normally Class.basic
  3892. body.name = name; // Define the name
  3893. // Dev hax
  3894. if (socket.key === 'testl' || socket.key === 'testk') {
  3895. body.define({ CAN_BE_ON_LEADERBOARD: false });
  3896. }
  3897. body.addController(new io_listenToPlayer(body, player)); // Make it listen
  3898. body.sendMessage = content => messenger(socket, content); // Make it speak
  3899. body.invuln = true; // Make it safe
  3900. player.body = body;
  3901. // Decide how to color and team the body
  3902. switch (room.gameMode) {
  3903. case "tdm":
  3904. {
  3905. body.team = -player.team;
  3906. body.color = [10, 11, 12, 15, 13, 2][player.team - 1];
  3907. }break;
  3908. default:
  3909. {
  3910. body.color = c.RANDOM_COLORS ? ran.choose([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]) : 12; // red
  3911. }
  3912. }
  3913. // Decide what to do about colors when sending updates and stuff
  3914. player.teamColor = !c.RANDOM_COLORS && room.gameMode === 'ffa' ? 10 : body.color; // blue
  3915. // Set up the targeting structure
  3916. player.target = {
  3917. x: 0,
  3918. y: 0
  3919. };
  3920. // Set up the command structure
  3921. player.command = {
  3922. up: false,
  3923. down: false,
  3924. left: false,
  3925. right: false,
  3926. lmb: false,
  3927. mmb: false,
  3928. rmb: false,
  3929. autofire: false,
  3930. autospin: false,
  3931. override: false,
  3932. autoguide: false
  3933. };
  3934. // Set up the recording commands
  3935. player.records = (() => {
  3936. let begin = util.time();
  3937. return () => {
  3938. return [player.body.skill.score, Math.floor((util.time() - begin) / 1000), player.body.killCount.solo, player.body.killCount.assists, player.body.killCount.bosses, player.body.killCount.killers.length, ...player.body.killCount.killers];
  3939. };
  3940. })();
  3941. // Set up the player's gui
  3942. player.gui = newgui(player);
  3943. // Save the the player
  3944. player.socket = socket;
  3945. players.push(player);
  3946. // Focus on the new player
  3947. socket.camera.x = body.x;socket.camera.y = body.y;socket.camera.fov = 2000;
  3948. // Mark it as spawned
  3949. socket.status.hasSpawned = true;
  3950. body.sendMessage('You have spawned! Welcome to the game.');
  3951. body.sendMessage('You will be invulnerable until you move or shoot.');
  3952. // Move the client camera
  3953. socket.talk('c', socket.camera.x, socket.camera.y, socket.camera.fov);
  3954. return player;
  3955. };
  3956. })();
  3957. // Make a function that will make a function that will send out world updates
  3958. const eyes = (() => {
  3959. // Define how to prepare data for submission
  3960. function flatten(data) {
  3961. let output = [data.type]; // We will remove the first entry in the persepective method
  3962. if (data.type & 0x01) {
  3963. output.push(
  3964. // 1: facing
  3965. data.facing,
  3966. // 2: layer
  3967. data.layer);
  3968. } else {
  3969. output.push(
  3970. // 1: id
  3971. data.id,
  3972. // 2: index
  3973. data.index,
  3974. // 3: x
  3975. data.x,
  3976. // 4: y
  3977. data.y,
  3978. // 5: vx
  3979. data.vx,
  3980. // 6: vy
  3981. data.vy,
  3982. // 7: size
  3983. data.size,
  3984. // 8: facing
  3985. data.facing,
  3986. // 9: vfacing
  3987. data.vfacing,
  3988. // 10: twiggle
  3989. data.twiggle,
  3990. // 11: layer
  3991. data.layer,
  3992. // 12: color
  3993. data.color,
  3994. // 13: health
  3995. Math.ceil(255 * data.health),
  3996. // 14: shield
  3997. Math.round(255 * data.shield),
  3998. // 15: alpha
  3999. Math.round(255 * data.alpha));
  4000. if (data.type & 0x04) {
  4001. output.push(
  4002. // 15: name
  4003. data.name,
  4004. // 16: score
  4005. data.score);
  4006. }
  4007. }
  4008. // Add the gun data to the array
  4009. let gundata = [data.guns.length];
  4010. //data.guns.forEach(lastShot => {
  4011. // gundata.push(lastShot.time, lastShot.power);
  4012. //});
  4013. let i = 0;
  4014. const lengthg = data.guns.length;
  4015. for (; i < lengthg; i++) {
  4016. gundata.push(data.guns[i].time, data.guns[i].power);
  4017. }
  4018. output.push(...gundata);
  4019. // For each turret, add their own output
  4020. let turdata = [data.turrets.length];
  4021. let j = 0;
  4022. const lengtht = data.turrets.length;
  4023. for (; j < lengtht; j++) {
  4024. turdata.push(...flatten(data.turrets[j]));
  4025. }
  4026. //data.turrets.forEach(turret => { turdata.push(...flatten(turret)); });
  4027. // Push all that to the array
  4028. output.push(...turdata);
  4029. // Return it
  4030. return output;
  4031. }
  4032. function perspective(e, player, data) {
  4033. if (player.body != null) {
  4034. if (player.body.id === e.master.id) {
  4035. data = data.slice(); // So we don't mess up references to the original
  4036. // Set the proper color if it's on our team
  4037. // And make it force to our mouse if it ought to
  4038. if (player.command.autospin) {
  4039. data[10] = 1;
  4040. }
  4041. }
  4042. }
  4043. return data;
  4044. }
  4045. function check(camera, obj) {
  4046. return Math.abs(obj.x - camera.x) < camera.fov * 0.6 + 1.5 * obj.size + 100 && Math.abs(obj.y - camera.y) < camera.fov * 0.6 * 0.5625 + 1.5 * obj.size + 100;
  4047. }
  4048. // The actual update world function
  4049. return socket => {
  4050. let lastVisibleUpdate = 0;
  4051. let nearby = [];
  4052. let x = -1000;
  4053. let y = -1000;
  4054. let fov = 0;
  4055. let o = {
  4056. add: e => {
  4057. if (check(socket.camera, e)) nearby.push(e);
  4058. },
  4059. remove: e => {
  4060. let i = nearby.indexOf(e);if (i !== -1) util.remove(nearby, i);
  4061. },
  4062. check: (e, f) => {
  4063. return check(socket.camera, e);
  4064. }, //Math.abs(e.x - x) < e.size + f*fov && Math.abs(e.y - y) < e.size + f*fov; },
  4065. gazeUpon: () => {
  4066. logs.network.set();
  4067. let player = socket.player,
  4068. camera = socket.camera;
  4069. // If nothing has changed since the last update, wait (approximately) until then to update
  4070. let rightNow = room.lastCycle;
  4071. if (rightNow === camera.lastUpdate) {
  4072. socket.update(5 + room.cycleSpeed - util.time() + rightNow);
  4073. return 1;
  4074. }
  4075. // ...elseeeeee...
  4076. // Update the record.
  4077. camera.lastUpdate = rightNow;
  4078. // Get the socket status
  4079. socket.status.receiving++;
  4080. // Now prepare the data to emit
  4081. let setFov = camera.fov;
  4082. // If we are alive, update the camera
  4083. if (player.body != null) {
  4084. // But I just died...
  4085. if (player.body.isDead()) {
  4086. socket.status.deceased = true;
  4087. // Let the client know it died
  4088. socket.talk('F', ...player.records());
  4089. // Remove the body
  4090. player.body = null;
  4091. }
  4092. // I live!
  4093. else if (player.body.photo) {
  4094. // Update camera position and motion
  4095. camera.x = player.body.photo.x;
  4096. camera.y = player.body.photo.y;
  4097. camera.vx = player.body.photo.vx;
  4098. camera.vy = player.body.photo.vy;
  4099. // Get what we should be able to see
  4100. setFov = player.body.fov;
  4101. // Get our body id
  4102. player.viewId = player.body.id;
  4103. }
  4104. }
  4105. if (player.body == null) {
  4106. // u dead bro
  4107. setFov = 2000;
  4108. }
  4109. // Smoothly transition view size
  4110. camera.fov += Math.max((setFov - camera.fov) / 30, setFov - camera.fov);
  4111. // Update my stuff
  4112. x = camera.x;y = camera.y;fov = camera.fov;
  4113. // Find what the user can see.
  4114. // Update which entities are nearby
  4115. if (camera.lastUpdate - lastVisibleUpdate > c.visibleListInterval) {
  4116. // Update our timer
  4117. lastVisibleUpdate = camera.lastUpdate;
  4118. // And update the nearby list
  4119. nearby = entities.map(e => {
  4120. if (check(socket.camera, e)) return e;
  4121. }).filter(e => {
  4122. return e;
  4123. });
  4124. }
  4125. // Look at our list of nearby entities and get their updates
  4126. let visible = nearby.map(function mapthevisiblerealm(e) {
  4127. if (e.photo) {
  4128. if (Math.abs(e.x - x) < fov / 2 + 1.5 * e.size && Math.abs(e.y - y) < fov / 2 * (9 / 16) + 1.5 * e.size) {
  4129. // Grab the photo
  4130. if (!e.flattenedPhoto) e.flattenedPhoto = flatten(e.photo);
  4131. return perspective(e, player, e.flattenedPhoto);
  4132. }
  4133. }
  4134. }).filter(e => {
  4135. return e;
  4136. });
  4137. // Spread it for upload
  4138. let numberInView = visible.length,
  4139. view = [];
  4140. //visible.forEach(e => { view.push(...e); });
  4141. let i = 0;
  4142. const length = visible.length;
  4143. for (; i < length; i++) {
  4144. view.push(...visible[i]);
  4145. }
  4146. // Update the gui
  4147. player.gui.update();
  4148. // Send it to the player
  4149. socket.talk('u', rightNow, camera.x, camera.y, setFov, camera.vx, camera.vy, ...player.gui.publish(), numberInView, ...view);
  4150. // Queue up some for the front util.log if needed
  4151. if (socket.status.receiving < c.networkFrontlog) {
  4152. socket.update(Math.max(0, 1000 / c.networkUpdateFactor - (camera.lastDowndate - camera.lastUpdate), camera.ping / c.networkFrontlog));
  4153. } else {
  4154. socket.update(c.networkFallbackTime);
  4155. }
  4156. logs.network.mark();
  4157. }
  4158. };
  4159. views.push(o);
  4160. return o;
  4161. };
  4162. })();
  4163. // Make a function that will send out minimap
  4164. // and leaderboard updates. We'll also start
  4165. // the mm/lb updating loop here. It runs at 1Hz
  4166. // and also kicks inactive sockets
  4167. const broadcast = (() => {
  4168. // This is the public information we need for broadcasting
  4169. let readmap, readlb;
  4170. // Define fundamental functions
  4171. const getminimap = (() => {
  4172. // Build a map cleaner
  4173. let cleanmapreader = (() => {
  4174. function flattener() {
  4175. let internalmap = [];
  4176. // Define the flattener
  4177. function flatten(data) {
  4178. // In case it's all filtered away, we'll still have something to work with
  4179. if (data == null) data = [];
  4180. let out = [data.length];
  4181. // Push it flat
  4182. //data.forEach(d => out.push(...d));
  4183. let i = 0;
  4184. let length = data.length;
  4185. for (; i < length; i++) {
  4186. out.push(...data[i]);
  4187. }
  4188. return out;
  4189. }
  4190. // Make a test function
  4191. function challenge(value, challenger) {
  4192. return value[1] === challenger[0] && value[2] === challenger[1] && value[3] === challenger[2];
  4193. }
  4194. // Return our functions
  4195. return {
  4196. update: data => {
  4197. // Flag all old data as to be removed
  4198. //internalmap.forEach(e => e[0] = -1);
  4199. let y = 0;
  4200. const rlength = internalmap.length;
  4201. for (; y < rlength; y++) {
  4202. internalmap[y][0] = -1;
  4203. }
  4204. // Round all the old data
  4205. data = data.map(d => {
  4206. return [Math.round(255 * util.clamp(d[0] / room.width, 0, 1)), Math.round(255 * util.clamp(d[1] / room.height, 0, 1)), d[2]];
  4207. });
  4208. // Add new data and stabilze existing data, then emove old data
  4209. let j = 0;
  4210. const length = data.length;
  4211. for (; j < length; j++) {
  4212. // Find if it's already there
  4213. let i = internalmap.findIndex(e => {
  4214. return challenge(e, data[j]);
  4215. });
  4216. if (i === -1) {
  4217. // if not add it
  4218. internalmap.push([1, ...data[j]]);
  4219. } else {
  4220. // if so, flag it as stable
  4221. internalmap[i][0] = 0;
  4222. }
  4223. }
  4224. // Export all new and old data
  4225. let ex = internalmap.filter(e => e[0] !== 0);
  4226. // Remove outdated data
  4227. internalmap = internalmap.filter(e => e[0] !== -1);
  4228. // Flatten the exports
  4229. let f = flatten(ex);
  4230. return f;
  4231. },
  4232. exportall: () => {
  4233. // Returns a flattened version of the map with blanket add requests
  4234. return flatten(internalmap.map(e => {
  4235. return [1, e[1], e[2], e[3]];
  4236. }));
  4237. }
  4238. };
  4239. }
  4240. // Define the function
  4241. return room.gameMode === 'ffa' ?
  4242. // ffa function builder
  4243. (() => {
  4244. // Make flatteners
  4245. let publicmap = flattener();
  4246. // Return the function
  4247. return () => {
  4248. // Updates
  4249. let clean = publicmap.update(minimap.map(function (entry) {
  4250. return [entry[1], entry[2], entry[4] === 'miniboss' ? entry[3] : 17];
  4251. }));
  4252. let full = publicmap.exportall();
  4253. // Reader
  4254. return (team, everything = false) => {
  4255. return everything ? full : clean;
  4256. };
  4257. };
  4258. })() :
  4259. // tdm function builder
  4260. (() => {
  4261. // Make flatteners
  4262. let team1map = flattener();
  4263. let team2map = flattener();
  4264. let team3map = flattener();
  4265. let team4map = flattener();
  4266. let team5map = flattener();
  4267. let team6map = flattener();
  4268. // Return the function
  4269. return () => {
  4270. let clean = [team1map.update(minimap.map(function (entry) {
  4271. return [entry[1], entry[2], entry[4] === 'miniboss' || entry[4] === 'tank' && entry[5] === -1 ? entry[3] : 17];
  4272. })), team2map.update(minimap.map(function (entry) {
  4273. return [entry[1], entry[2], entry[4] === 'miniboss' || entry[4] === 'tank' && entry[5] === -2 ? entry[3] : 17];
  4274. })), team3map.update(minimap.map(function (entry) {
  4275. return [entry[1], entry[2], entry[4] === 'miniboss' || entry[4] === 'tank' && entry[5] === -3 ? entry[3] : 17];
  4276. })), team4map.update(minimap.map(function (entry) {
  4277. return [entry[1], entry[2], entry[4] === 'miniboss' || entry[4] === 'tank' && entry[5] === -4 ? entry[3] : 17];
  4278. })), team5map.update(minimap.map(function (entry) {
  4279. return [entry[1], entry[2], entry[4] === 'miniboss' || entry[4] === 'tank' && entry[5] === -4 ? entry[3] : 17];
  4280. })), team6map.update(minimap.map(function (entry) {
  4281. return [entry[1], entry[2], entry[4] === 'miniboss' || entry[4] === 'tank' && entry[5] === -4 ? entry[3] : 17];
  4282. }))];
  4283. let full = [team1map.exportall(), team2map.exportall(), team3map.exportall(), team4map.exportall(), team5map.exportall(), team6map.exportall()];
  4284. // The reader
  4285. return (team, everything = false) => {
  4286. return everything ? full[team - 1] : clean[team - 1];
  4287. };
  4288. };
  4289. })();
  4290. })();
  4291. // Return the builder function. This itself returns
  4292. // a reader for the map (will change based on team)
  4293. return () => {
  4294. // Update the minimap
  4295. let i = 0;
  4296. const length = entities.length;
  4297. for (; i < length; i++) {
  4298. let my = entities[i];
  4299. if (my.settings.drawShape && ran.dice(my.stealth * c.STEALTH)) {
  4300. let i = minimap.findIndex(entry => {
  4301. return entry[0] === my.id;
  4302. });
  4303. if (i != -1) {
  4304. // update position
  4305. minimap[i] = [my.id, my.x, my.y, my.color, my.type, my.team];
  4306. } else {
  4307. // add position
  4308. minimap.push([my.id, my.x, my.y, my.color, my.type, my.team]);
  4309. }
  4310. }
  4311. }
  4312. // Clean the map and return the reader
  4313. return cleanmapreader();
  4314. };
  4315. })();
  4316. const getleaderboard = (() => {
  4317. let lb = { full: [], updates: [] };
  4318. // We'll reuse these lists over and over again
  4319. let list = new goog.structs.PriorityQueue();
  4320. // This puts things in the data structure
  4321. function listify(instance) {
  4322. if (instance.settings.leaderboardable && instance.settings.drawShape && (instance.type === 'tank' || instance.killCount.solo || instance.killCount.assists)) {
  4323. list.enqueue(1 / (instance.skill.score + 1), instance);
  4324. }
  4325. }
  4326. // Build a function to prepare for export
  4327. let flatten = (() => {
  4328. let leaderboard = {};
  4329. // Define our index manager
  4330. let indices = (() => {
  4331. let data = [],
  4332. removed = [];
  4333. // Provide the index manager methods
  4334. return {
  4335. flag: () => {
  4336. //data.forEach(index => {
  4337. // index.status = -1;
  4338. //});
  4339. let i = 0;
  4340. const length = data.length;
  4341. for (; i < length; i++) {
  4342. data[i].status = -1;
  4343. }
  4344. if (data == null) {
  4345. data = [];
  4346. }
  4347. },
  4348. cull: () => {
  4349. removed = [];
  4350. data = data.filter(index => {
  4351. let doit = index.status === -1;
  4352. if (doit) removed.push(index.id);
  4353. return !doit;
  4354. });
  4355. return removed;
  4356. },
  4357. add: id => {
  4358. data.push({ id: id, status: 1 });
  4359. },
  4360. stabilize: id => {
  4361. data[data.findIndex(index => {
  4362. return index.id === id;
  4363. })].status = 0;
  4364. }
  4365. };
  4366. })();
  4367. // This processes it
  4368. let process = (() => {
  4369. // A helpful thing
  4370. function barcolor(entry) {
  4371. switch (entry.team) {
  4372. case -100:
  4373. return entry.color;
  4374. case -1:
  4375. return 10;
  4376. case -2:
  4377. return 11;
  4378. case -3:
  4379. return 12;
  4380. case -4:
  4381. return 15;
  4382. case -5:
  4383. return 13;
  4384. default:
  4385. {
  4386. if (room.gameMode === 'tdm') return entry.color;
  4387. return 11;
  4388. }
  4389. }
  4390. }
  4391. // A shared (and protected) thing
  4392. function getfull(entry) {
  4393. return [-entry.id, Math.round(entry.skill.score), entry.index, entry.name, entry.color, barcolor(entry)];
  4394. }
  4395. return {
  4396. normal: entry => {
  4397. // Check if the entry is already there
  4398. let id = entry.id,
  4399. score = Math.round(entry.skill.score);
  4400. let lb = leaderboard['_' + id];
  4401. if (lb != null) {
  4402. // Unflag it for removal
  4403. indices.stabilize(id);
  4404. // Figure out if we need to update anything
  4405. if (lb.score !== score || lb.index !== entry.index) {
  4406. // If so, update our record first
  4407. lb.score = score;
  4408. lb.index = entry.index;
  4409. // Return it for broadcasting
  4410. return [id, score, entry.index];
  4411. }
  4412. } else {
  4413. // Record it
  4414. indices.add(id);
  4415. leaderboard['_' + id] = {
  4416. score: score,
  4417. name: entry.name,
  4418. index: entry.index,
  4419. color: entry.color,
  4420. bar: barcolor(entry)
  4421. };
  4422. // Return it for broadcasting
  4423. return getfull(entry);
  4424. }
  4425. },
  4426. full: entry => {
  4427. return getfull(entry);
  4428. }
  4429. };
  4430. })();
  4431. // The flattening functions
  4432. return data => {
  4433. // Start
  4434. indices.flag();
  4435. // Flatten the orders
  4436. let orders = data.map(process.normal).filter(e => {
  4437. return e;
  4438. }),
  4439. refresh = data.map(process.full).filter(e => {
  4440. return e;
  4441. }),
  4442. flatorders = [],
  4443. flatrefresh = [];
  4444. let lengtho = orders.length;
  4445. let lengthr = refresh.length;
  4446. let o = 0;
  4447. let r = 0;
  4448. for (; o < lengtho; o++) {
  4449. flatorders.push(...orders[o]);
  4450. }
  4451. for (; r < lengthr; r++) {
  4452. flatrefresh.push(...refresh[r]);
  4453. }
  4454. //rders.forEach(e => flatorders.push(...e));
  4455. //refresh.forEach(e => flatrefresh.push(...e));
  4456. // Find the stuff to remove
  4457. let removed = indices.cull();
  4458. // Make sure we sync the leaderboard
  4459. let i = 0;
  4460. const length = removed.length;
  4461. //for (; i < length; i++) {
  4462. // delete leaderboard['_' + removed[i]];
  4463. //}
  4464. removed.forEach(id => {
  4465. delete leaderboard['_' + id];
  4466. });
  4467. return {
  4468. updates: [removed.length, ...removed, orders.length, ...flatorders],
  4469. full: [-1, refresh.length, ...flatrefresh] // The -1 tells the client it'll be a full refresh
  4470. };
  4471. };
  4472. })();
  4473. // The update function (returns a reader)
  4474. return () => {
  4475. list.clear();
  4476. // Sort everything
  4477. let i = 0;
  4478. const length = entities.length;
  4479. for (; i < length; i++) {
  4480. listify(entities[i]);
  4481. }
  4482. //entities.forEach(listify);
  4483. // Get the top ten
  4484. let topTen = [];
  4485. for (let i = 0; i < 10; i++) {
  4486. // Only if there's anything in the list of course
  4487. if (list.getCount()) {
  4488. topTen.push(list.dequeue());
  4489. } else {
  4490. break;
  4491. }
  4492. }
  4493. topTen = topTen.filter(e => {
  4494. return e;
  4495. });
  4496. room.topPlayerID = topTen.length ? topTen[0].id : -1;
  4497. // Remove empty values and process it
  4498. lb = flatten(topTen);
  4499. // Return the reader
  4500. return (full = false) => {
  4501. return full ? lb.full : lb.updates;
  4502. };
  4503. };
  4504. })();
  4505. // Define a 1 Hz update loop
  4506. function slowloop() {
  4507. // Build the minimap
  4508. logs.minimap.set();
  4509. readmap = getminimap();
  4510. // Build the leaderboard
  4511. readlb = getleaderboard();
  4512. logs.minimap.mark();
  4513. // Check sockets
  4514. let time = util.time();
  4515. for (let socket of clients) {
  4516. if (socket.timeout.check(time)) socket.kick('Kicked for inactivity.');
  4517. if (time - socket.statuslastHeartbeat > c.maxHeartbeatInterval) socket.kick('Lost heartbeat.');
  4518. }
  4519. }
  4520. // Start it
  4521. slowloop();
  4522. setInterval(slowloop, 1000);
  4523. // Give the broadcast method
  4524. return socket => {
  4525. // Make sure it's spawned first
  4526. if (socket.status.hasSpawned) {
  4527. let m = [0],
  4528. lb = [0, 0];
  4529. m = readmap(socket.player.team, socket.status.needsFullMap);
  4530. socket.status.needsFullMap = false;
  4531. lb = readlb(socket.status.needsFullLeaderboard);
  4532. socket.status.needsFullLeaderboard = false;
  4533. // Don't broadcast if you don't need to
  4534. if (m !== [0] || lb !== [0, 0]) {
  4535. socket.talk('b', ...m, ...lb);
  4536. }
  4537. }
  4538. };
  4539. })();
  4540. // Build the returned function
  4541. // This function initalizes the socket upon connection
  4542. return (socket, req) => {
  4543. // Get information about the new connection and verify it
  4544. if (c.servesStatic || req.connection.remoteAddress === '::ffff:127.0.0.1' || req.connection.remoteAddress === '::1') {
  4545. socket.ip = req.headers['x-forwarded-for'];
  4546. // Make sure we're not banned...
  4547. if (bannedIPs.findIndex(ip => {
  4548. return ip === socket.ip;
  4549. }) !== -1) {
  4550. socket.terminate();
  4551. return 1;
  4552. }
  4553. // Make sure we're not already connected...
  4554. if (!c.servesStatic) {
  4555. let n = connectedIPs.findIndex(w => {
  4556. return w.ip === socket.ip;
  4557. });
  4558. if (n !== -1) {
  4559. // Don't allow more than 2
  4560. if (connectedIPs[n].number > 1) {
  4561. util.warn('Too many connections from the same IP. [' + socket.ip + ']');
  4562. socket.terminate();
  4563. return 1;
  4564. } else connectedIPs[n].number++;
  4565. } else connectedIPs.push({ ip: socket.ip, number: 1 });
  4566. }
  4567. } else {
  4568. // Don't let banned IPs connect.
  4569. util.warn(req.connection.remoteAddress);
  4570. util.warn(req.headers['x-forwarded-for']);
  4571. socket.terminate();
  4572. util.warn('Inappropiate connection request: header spoofing. Socket terminated.');
  4573. return 1;
  4574. }
  4575. util.log(socket.ip + ' is trying to connect...');
  4576. // Set it up
  4577. socket.binaryType = 'arraybuffer';
  4578. socket.key = '';
  4579. socket.player = { camera: {} };
  4580. socket.timeout = (() => {
  4581. let mem = 0;
  4582. let timer = 0;
  4583. return {
  4584. set: val => {
  4585. if (mem !== val) {
  4586. mem = val;timer = util.time();
  4587. }
  4588. },
  4589. check: time => {
  4590. return timer && time - timer > c.maxHeartbeatInterval;
  4591. }
  4592. };
  4593. })();
  4594. // Set up the status container
  4595. socket.status = {
  4596. verified: false,
  4597. receiving: 0,
  4598. deceased: true,
  4599. requests: 0,
  4600. hasSpawned: false,
  4601. needsFullMap: true,
  4602. needsFullLeaderboard: true,
  4603. lastHeartbeat: util.time()
  4604. };
  4605. // Set up loops
  4606. socket.loops = (() => {
  4607. let nextUpdateCall = null; // has to be started manually
  4608. let trafficMonitoring = setInterval(() => traffic(socket), 1000);
  4609. let broadcastingGuiStuff = setInterval(() => broadcast(socket), 1500);
  4610. // Return the loop methods
  4611. return {
  4612. setUpdate: timeout => {
  4613. nextUpdateCall = timeout;
  4614. },
  4615. cancelUpdate: () => {
  4616. clearTimeout(nextUpdateCall);
  4617. },
  4618. terminate: () => {
  4619. clearTimeout(nextUpdateCall);
  4620. clearTimeout(trafficMonitoring);
  4621. clearTimeout(broadcastingGuiStuff);
  4622. }
  4623. };
  4624. })();
  4625. // Set up the camera
  4626. socket.camera = {
  4627. x: undefined,
  4628. y: undefined,
  4629. vx: 0,
  4630. vy: 0,
  4631. lastUpdate: util.time(),
  4632. lastDowndate: undefined,
  4633. fov: 2000
  4634. };
  4635. // Set up the viewer
  4636. socket.makeView = () => {
  4637. socket.view = eyes(socket);
  4638. };
  4639. socket.makeView();
  4640. // Put the fundamental functions in the socket
  4641. socket.ban = () => ban(socket);
  4642. socket.kick = reason => kick(socket, reason);
  4643. socket.talk = (...message) => {
  4644. if (socket.readyState === socket.OPEN) {
  4645. socket.send(protocol.encode(message), { binary: true });
  4646. }
  4647. };
  4648. socket.lastWords = (...message) => {
  4649. if (socket.readyState === socket.OPEN) {
  4650. socket.send(protocol.encode(message), { binary: true }, () => timer.setTimeout(() => socket.terminate(), '', '1000m'));
  4651. }
  4652. };
  4653. socket.on('message', message => incoming(message, socket));
  4654. socket.on('close', () => {
  4655. socket.loops.terminate();close(socket);
  4656. });
  4657. socket.on('error', e => {
  4658. util.log('[ERROR]:');util.error(e);
  4659. });
  4660. // Put the player functions in the socket
  4661. socket.spawn = name => {
  4662. return spawn(socket, name);
  4663. };
  4664. // And make an update
  4665. socket.update = time => {
  4666. socket.loops.cancelUpdate();
  4667. socket.loops.setUpdate(setTimeout(() => {
  4668. socket.view.gazeUpon();
  4669. }, time.toString()));
  4670. };
  4671. // Log it
  4672. clients.push(socket);
  4673. util.log('[INFO] New socket opened with ', socket.ip);
  4674. };
  4675. })()
  4676. };
  4677. })();
  4678.  
  4679. /**** GAME SETUP ****/
  4680. // Define how the game lives
  4681. // The most important loop. Fast looping.
  4682.  
  4683. var gameloop = (() => {
  4684. // Collision stuff
  4685. let collide = (() => {
  4686. function simplecollide(my, n) {
  4687. let diff = (1 + util.getDistance(my, n) / 2) * roomSpeed;
  4688. let a = my.intangibility ? 1 : my.pushability,
  4689. b = n.intangibility ? 1 : n.pushability,
  4690. c = 0.05 * (my.x - n.x) / diff,
  4691. d = 0.05 * (my.y - n.y) / diff;
  4692. my.accel.x += a / (b + 0.3) * c;
  4693. my.accel.y += a / (b + 0.3) * d;
  4694. n.accel.x -= b / (a + 0.3) * c;
  4695. n.accel.y -= b / (a + 0.3) * d;
  4696. }
  4697. function firmcollide(my, n, buffer = 0) {
  4698. let item1 = { x: my.x + my.m_x, y: my.y + my.m_y };
  4699. let item2 = { x: n.x + n.m_x, y: n.y + n.m_y };
  4700. let dist = util.getDistance(item1, item2);
  4701. let s1 = Math.max(my.velocity.length, my.topSpeed);
  4702. let s2 = Math.max(n.velocity.length, n.topSpeed);
  4703. let strike1, strike2;
  4704. if (buffer > 0 && dist <= my.realSize + n.realSize + buffer) {
  4705. let repel = (my.acceleration + n.acceleration) * (my.realSize + n.realSize + buffer - dist) / buffer / roomSpeed;
  4706. my.accel.x += repel * (item1.x - item2.x) / dist * 1.525;
  4707. my.accel.y += repel * (item1.y - item2.y) / dist * 1.525;
  4708. n.accel.x -= repel * (item1.x - item2.x) / dist * 1.525;
  4709. n.accel.y -= repel * (item1.y - item2.y) / dist * 1.525;
  4710. }
  4711. while (dist <= my.realSize + n.realSize && !(strike1 && strike2)) {
  4712. strike1 = false;strike2 = false;
  4713. if (my.velocity.length <= s1) {
  4714. my.velocity.x -= 0.05 * (item2.x - item1.x) / dist / roomSpeed;
  4715. my.velocity.y -= 0.05 * (item2.y - item1.y) / dist / roomSpeed;
  4716. } else {
  4717. strike1 = true;
  4718. }
  4719. if (n.velocity.length <= s2) {
  4720. n.velocity.x += 0.05 * (item2.x - item1.x) / dist / roomSpeed;
  4721. n.velocity.y += 0.05 * (item2.y - item1.y) / dist / roomSpeed;
  4722. } else {
  4723. strike2 = true;
  4724. }
  4725. item1 = { x: my.x + my.m_x, y: my.y + my.m_y };
  4726. item2 = { x: n.x + n.m_x, y: n.y + n.m_y };
  4727. dist = util.getDistance(item1, item2);
  4728. }
  4729. }
  4730. function reflectcollide(wall, bounce) {
  4731. let delt = new Vector(wall.x - bounce.x, wall.y - bounce.y);
  4732. let dist = delt.length;
  4733. let diff = wall.size + bounce.size - dist;
  4734. if (diff > 0) {
  4735. bounce.accel.x -= diff * delt.x / dist * 1.525;
  4736. bounce.accel.y -= diff * delt.y / dist * 1.525;
  4737. return 1;
  4738. }
  4739. return 0;
  4740. }
  4741. function advancedcollide(my, n, doDamage, doInelastic, nIsFirmCollide = false) {
  4742. // Prepare to check
  4743. let tock = Math.min(my.stepRemaining, n.stepRemaining),
  4744. combinedRadius = n.size + my.size,
  4745. motion = {
  4746. _me: new Vector(my.m_x, my.m_y),
  4747. _n: new Vector(n.m_x, n.m_y)
  4748. },
  4749. delt = new Vector(tock * (motion._me.x - motion._n.x), tock * (motion._me.y - motion._n.y)),
  4750. diff = new Vector(my.x - n.x, my.y - n.y),
  4751. dir = new Vector((n.x - my.x) / diff.length, (n.y - my.y) / diff.length),
  4752. component = Math.max(0, dir.x * delt.x + dir.y * delt.y);
  4753.  
  4754. if (n.customshapes !== undefined) {
  4755. if (n.customshapes.length > 0) {
  4756. let centerX = n.x;
  4757. let centerY = n.y;
  4758. let points = [];
  4759. let edgevaluesx = [];
  4760. let edgevaluesy = [];
  4761. for (let point in n.customshapes) {
  4762. points.push(point);
  4763. }
  4764. for (let i = 0; i < points.length; i++) {
  4765. let xDifference;
  4766. let yDifference;
  4767. if (i + 1 <= points.length) {
  4768. xDifference = Math.abs(points[i][0] - points[i + 1][0]);
  4769. yDifference = Math.abs(points[i][1] - points[i + 1][1]);
  4770. let changeX = 1;
  4771. let changeY = 1;
  4772. if (points[i + 1][0] < points[i][0]) {
  4773. changeX = -1;
  4774. }
  4775. if (points[i + 1][1] < points[i][1]) {
  4776. changeY = -1;
  4777. }
  4778. for (let i = 0; i < xDifference; i++) {
  4779. edgevaluesx.push(points[i][0] + i * changeX);
  4780. }
  4781. for (let j = 0; i < yDifference; j++) {
  4782. edgevaluesy.push(points[j][0] + j * changeY);
  4783. }
  4784. }
  4785. }
  4786. }
  4787. }
  4788.  
  4789. if (component >= diff.length - combinedRadius) {
  4790. // A simple check
  4791. // A more complex check
  4792. let goahead = false,
  4793. tmin = 1 - tock,
  4794. tmax = 1,
  4795. A = Math.pow(delt.x, 2) + Math.pow(delt.y, 2),
  4796. B = 2 * delt.x * diff.x + 2 * delt.y * diff.y,
  4797. C = Math.pow(diff.x, 2) + Math.pow(diff.y, 2) - Math.pow(combinedRadius, 2),
  4798. det = B * B - 4 * A * C,
  4799. t;
  4800.  
  4801. if (!A || det < 0 || C < 0) {
  4802. // This shall catch mathematical errors
  4803. t = 0;
  4804. if (C < 0) {
  4805. // We have already hit without moving
  4806. goahead = true;
  4807. }
  4808. } else {
  4809. let t1 = (-B - Math.sqrt(det)) / (2 * A),
  4810. t2 = (-B + Math.sqrt(det)) / (2 * A);
  4811. if (t1 < tmin || t1 > tmax) {
  4812. // 1 is out of range
  4813. if (t2 < tmin || t2 > tmax) {
  4814. // 2 is out of range;
  4815. t = false;
  4816. } else {
  4817. // 1 is out of range but 2 isn't
  4818. t = t2;goahead = true;
  4819. }
  4820. } else {
  4821. // 1 is in range
  4822. if (t2 >= tmin && t2 <= tmax) {
  4823. // They're both in range!
  4824. t = Math.min(t1, t2);goahead = true; // That means it passed in and then out again. Let's use when it's going in
  4825. } else {
  4826. // Only 1 is in range
  4827. t = t1;goahead = true;
  4828. }
  4829. }
  4830. }
  4831. if (goahead) {
  4832. // Add to record
  4833. my.collisionArray.push(n);
  4834. n.collisionArray.push(my);
  4835. if (t) {
  4836. // Only if we still need to find the collision
  4837. // Step to where the collision occured
  4838. my.x += motion._me.x * t;
  4839. my.y += motion._me.y * t;
  4840. n.x += motion._n.x * t;
  4841. n.y += motion._n.y * t;
  4842.  
  4843. my.stepRemaining -= t;
  4844. n.stepRemaining -= t;
  4845.  
  4846. // Update things
  4847. diff = new Vector(my.x - n.x, my.y - n.y);
  4848. dir = new Vector((n.x - my.x) / diff.length, (n.y - my.y) / diff.length);
  4849. component = Math.max(0, dir.x * delt.x + dir.y * delt.y);
  4850. }
  4851. let componentNorm = component / delt.length;
  4852. // Prepare some things
  4853. let reductionFactor = 1,
  4854. deathFactor = {
  4855. _me: 1,
  4856. _n: 1
  4857. },
  4858. accelerationFactor = delt.length ? combinedRadius / 4 / (Math.floor(combinedRadius / delt.length) + 1) : 0.001,
  4859. depth = {
  4860. _me: util.clamp((combinedRadius - diff.length) / (2 * my.size), 0, 1), //1: I am totally within it
  4861. _n: util.clamp((combinedRadius - diff.length) / (2 * n.size), 0, 1) //1: It is totally within me
  4862. },
  4863. combinedDepth = {
  4864. up: depth._me * depth._n,
  4865. down: (1 - depth._me) * (1 - depth._n)
  4866. },
  4867. pen = {
  4868. _me: {
  4869. sqr: Math.pow(my.penetration, 2),
  4870. sqrt: Math.sqrt(my.penetration)
  4871. },
  4872. _n: {
  4873. sqr: Math.pow(n.penetration, 2),
  4874. sqrt: Math.sqrt(n.penetration)
  4875. }
  4876. },
  4877. savedHealthRatio = {
  4878. _me: my.health.ratio,
  4879. _n: n.health.ratio
  4880. };
  4881. if (doDamage) {
  4882. let speedFactor = { // Avoid NaNs and infinities
  4883. _me: my.maxSpeed ? Math.pow(motion._me.length / my.maxSpeed, 0.25) : 1,
  4884. _n: n.maxSpeed ? Math.pow(motion._n.length / n.maxSpeed, 0.25) : 1
  4885. };
  4886.  
  4887. let bail = false;
  4888. if (my.shape === n.shape && my.settings.isNecromancer && n.type === 'food') {
  4889. //bail = my.necro(n);
  4890. } else if (my.shape === n.shape && n.settings.isNecromancer && my.type === 'food') {
  4891. //bail = n.necro(my);
  4892. }
  4893. if (!bail) {
  4894. // Calculate base damage
  4895. let resistDiff = my.health.resist - n.health.resist,
  4896. damage = {
  4897. _me: c.DAMAGE_CONSTANT * my.damage * (1 + resistDiff) * (1 + n.heteroMultiplier * (my.settings.damageClass === n.settings.damageClass)) * (my.settings.buffVsFood && n.settings.damageType === 1 ? 3 : 1) * my.damageMultiplier() * Math.min(2, Math.max(speedFactor._me, 1) * speedFactor._me),
  4898. _n: c.DAMAGE_CONSTANT * n.damage * (1 - resistDiff) * (1 + my.heteroMultiplier * (my.settings.damageClass === n.settings.damageClass)) * (n.settings.buffVsFood && my.settings.damageType === 1 ? 3 : 1) * n.damageMultiplier() * Math.min(2, Math.max(speedFactor._n, 1) * speedFactor._n)
  4899. };
  4900. // Advanced damage calculations
  4901. if (my.settings.ratioEffects) {
  4902. damage._me *= Math.min(1, Math.pow(Math.max(my.health.ratio, my.shield.ratio), 1 / my.penetration));
  4903. }
  4904. if (n.settings.ratioEffects) {
  4905. damage._n *= Math.min(1, Math.pow(Math.max(n.health.ratio, n.shield.ratio), 1 / n.penetration));
  4906. }
  4907. if (my.settings.damageEffects) {
  4908. damage._me *= accelerationFactor * (1 + (componentNorm - 1) * (1 - depth._n) / my.penetration) * (1 + pen._n.sqrt * depth._n - depth._n) / pen._n.sqrt;
  4909. }
  4910. if (n.settings.damageEffects) {
  4911. damage._n *= accelerationFactor * (1 + (componentNorm - 1) * (1 - depth._me) / n.penetration) * (1 + pen._me.sqrt * depth._me - depth._me) / pen._me.sqrt;
  4912. }
  4913. // Find out if you'll die in this cycle, and if so how much damage you are able to do to the other target
  4914. let damageToApply = {
  4915. _me: damage._me,
  4916. _n: damage._n
  4917. };
  4918. if (n.shield.max) {
  4919. damageToApply._me -= n.shield.getDamage(damageToApply._me);
  4920. }
  4921. if (my.shield.max) {
  4922. damageToApply._n -= my.shield.getDamage(damageToApply._n);
  4923. }
  4924. let stuff = my.health.getDamage(damageToApply._n, false);
  4925. deathFactor._me = stuff > my.health.amount ? my.health.amount / stuff : 1;
  4926. stuff = n.health.getDamage(damageToApply._me, false);
  4927. deathFactor._n = stuff > n.health.amount ? n.health.amount / stuff : 1;
  4928.  
  4929. reductionFactor = Math.min(deathFactor._me, deathFactor._n);
  4930.  
  4931. // Now apply it
  4932. my.damageRecieved += damage._n * deathFactor._n;
  4933. n.damageRecieved += damage._me * deathFactor._me;
  4934. }
  4935. }
  4936. /************* POISON ***********/
  4937. if (n.poison) {
  4938. my.poisoned = true;
  4939. my.poisonedLevel = n.poisionToApply;
  4940. my.poisonTime = 20;
  4941. my.poisonedBy = n.master;
  4942. }
  4943. if (my.poison) {
  4944. n.poisoned = true;
  4945. n.poisonedLevel = my.poisionToApply;
  4946. n.poisonTime = 20;
  4947. n.poisonedBy = my.master;
  4948. }
  4949. if (n.breakarmor) {
  4950. my.armorbroken = true;
  4951. }
  4952. if (my.breakarmor) {
  4953. n.armorbroken = true;
  4954. }
  4955. /************* DO MOTION ***********/
  4956. if (nIsFirmCollide < 0) {
  4957. nIsFirmCollide *= -0.5;
  4958. my.accel.x -= nIsFirmCollide * component * dir.x;
  4959. my.accel.y -= nIsFirmCollide * component * dir.y;
  4960. n.accel.x += nIsFirmCollide * component * dir.x;
  4961. n.accel.y += nIsFirmCollide * component * dir.y;
  4962. } else if (nIsFirmCollide > 0) {
  4963. n.accel.x += nIsFirmCollide * (component * dir.x + combinedDepth.up);
  4964. n.accel.y += nIsFirmCollide * (component * dir.y + combinedDepth.up);
  4965. } else {
  4966. // Calculate the impulse of the collision
  4967. let elasticity = 2 - 4 * Math.atan(my.penetration * n.penetration) / Math.PI;
  4968. if (doInelastic && my.settings.motionEffects && n.settings.motionEffects) {
  4969. elasticity *= savedHealthRatio._me / pen._me.sqrt + savedHealthRatio._n / pen._n.sqrt;
  4970. } else {
  4971. elasticity *= 2;
  4972. }
  4973. let spring = 2 * Math.sqrt(savedHealthRatio._me * savedHealthRatio._n) / roomSpeed,
  4974. elasticImpulse = Math.pow(combinedDepth.down, 2) * elasticity * component * my.mass * n.mass / (my.mass + n.mass),
  4975. springImpulse = c.KNOCKBACK_CONSTANT * spring * combinedDepth.up,
  4976. impulse = -(elasticImpulse + springImpulse) * (1 - my.intangibility) * (1 - n.intangibility),
  4977. force = {
  4978. x: impulse * dir.x,
  4979. y: impulse * dir.y
  4980. },
  4981. modifiers = {
  4982. _me: c.KNOCKBACK_CONSTANT * my.pushability / my.mass * deathFactor._n,
  4983. _n: c.KNOCKBACK_CONSTANT * n.pushability / n.mass * deathFactor._me
  4984. };
  4985. // Apply impulse as force
  4986. my.accel.x += modifiers._me * force.x * 1.6;
  4987. my.accel.y += modifiers._me * force.y * 1.6;
  4988. n.accel.x -= modifiers._n * force.x * 1.6;
  4989. n.accel.y -= modifiers._n * force.y * 1.6;
  4990. }
  4991. }
  4992. }
  4993. }
  4994. // The actual collision resolution function
  4995. return collision => {
  4996. // Pull the two objects from the collision grid
  4997. let instance = collision[0],
  4998. other = collision[1];
  4999. // Check for ghosts...
  5000. if (other.isGhost) {
  5001. util.error('GHOST FOUND');
  5002. util.error(other.label);
  5003. util.error('x: ' + other.x + ' y: ' + other.y);
  5004. util.error(other.collisionArray);
  5005. util.error('health: ' + other.health.amount);
  5006. util.warn('Ghost removed.');
  5007. if (grid.checkIfInHSHG(other)) {
  5008. util.warn('Ghost removed.');grid.removeObject(other);
  5009. }
  5010. return 0;
  5011. }
  5012. if (instance.isGhost) {
  5013. util.error('GHOST FOUND');
  5014. util.error(instance.label);
  5015. util.error('x: ' + instance.x + ' y: ' + instance.y);
  5016. util.error(instance.collisionArray);
  5017. util.error('health: ' + instance.health.amount);
  5018. if (grid.checkIfInHSHG(instance)) {
  5019. util.warn('Ghost removed.');grid.removeObject(instance);
  5020. }
  5021. return 0;
  5022. }
  5023. let delt = new Vector(instance.x - other.x, instance.y - other.y);
  5024. let dist = delt.length;
  5025. let diff = instance.size + other.size - dist;
  5026.  
  5027. if (diff > 0) {
  5028. if (!instance.activation.check() && !other.activation.check()) {
  5029. util.warn('Tried to collide with an inactive instance.');return 0;
  5030. }
  5031. // Handle walls
  5032. if (instance.type === 'wall' || other.type === 'wall') {
  5033. let a = instance.type === 'bullet' || other.type === 'bullet' ? 1 + 10 / (Math.max(instance.velocity.length, other.velocity.length) + 10) : 1;
  5034. if (instance.type === 'wall') advancedcollide(instance, other, false, false, a);else advancedcollide(other, instance, false, false, a);
  5035. } else
  5036. // If they can firm collide, do that
  5037. if (instance.type === 'crasher' && other.type === 'food' || other.type === 'crasher' && instance.type === 'food') {
  5038. firmcollide(instance, other);
  5039. } else
  5040. // Otherwise, collide normally if they're from different teams
  5041. if (instance.team !== other.team) {
  5042. advancedcollide(instance, other, true, true);
  5043. } else
  5044. // Ignore them if either has asked to be
  5045. if (instance.settings.hitsOwnType == 'never' || other.settings.hitsOwnType == 'never') {
  5046. // Do jack
  5047. } else
  5048. // Standard collision resolution
  5049. if (instance.settings.hitsOwnType === other.settings.hitsOwnType) {
  5050. switch (instance.settings.hitsOwnType) {
  5051. case 'push':
  5052. advancedcollide(instance, other, false, false);break;
  5053. case 'hard':
  5054. firmcollide(instance, other);break;
  5055. case 'hardWithBuffer':
  5056. firmcollide(instance, other, 30);break;
  5057. case 'repel':
  5058. simplecollide(instance, other);break;
  5059. }
  5060. }
  5061. }
  5062. };
  5063. })();
  5064. // Living stuff
  5065. function entitiesactivationloop(my) {
  5066. // Update collisions.
  5067. my.collisionArray = [];
  5068. // Activation
  5069. my.activation.update();
  5070. my.updateAABB(my.activation.check());
  5071. }
  5072. function entitiesliveloop(my) {
  5073. // Consider death.
  5074. if (my.contemplationOfMortality()) my.destroy();else {
  5075. if (my.bond == null) {
  5076. // Resolve the physical behavior from the last collision cycle.
  5077. logs.physics.set();
  5078. my.physics();
  5079. logs.physics.mark();
  5080. }
  5081. if (my.activation.check()) {
  5082. logs.entities.tally();
  5083. // Think about my actions.
  5084. logs.life.set();
  5085. my.life();
  5086. logs.life.mark();
  5087. // Apply friction.
  5088. my.friction();
  5089. my.confinementToTheseEarthlyShackles();
  5090. logs.selfie.set();
  5091. my.takeSelfie();
  5092. logs.selfie.mark();
  5093. }
  5094. }
  5095. // Update collisions.
  5096. my.collisionArray = [];
  5097. }
  5098. let time;
  5099. // Return the loop function
  5100. return () => {
  5101. //let curTime = now();
  5102. //timestep = 0.007875 * (curTime - lastTime);
  5103. //if (timestep <= 0 || timestep > 7.875) {
  5104. // timestep = 0.007875;
  5105. //}
  5106. logs.loops.tally();
  5107. logs.master.set();
  5108. logs.activation.set();
  5109. for (var e of entities) {
  5110. entitiesactivationloop(e);
  5111. }
  5112. //entities.forEach(e => entitiesactivationloop(e));
  5113. logs.activation.mark();
  5114. // Do collisions
  5115. logs.collide.set();
  5116.  
  5117. if (entities.length > 1) {
  5118. // Load the grid
  5119. grid.update();
  5120. for (let collision of grid.queryForCollisionPairs()) {
  5121. collide(collision);
  5122. }
  5123. //grid.queryForCollisionPairs().forEach(collision => collide(collision));
  5124. }
  5125. logs.collide.mark();
  5126. // Do entities life
  5127. logs.entities.set();
  5128. for (var e of entities) {
  5129. entitiesliveloop(e);
  5130. }
  5131. logs.entities.mark();
  5132. logs.master.mark();
  5133. // Remove dead entities
  5134. purgeEntities();
  5135. room.lastCycle = util.time();
  5136. //lastTime = curTime;
  5137. };
  5138. //let expected = 1000 / c.gameSpeed / 30;
  5139. //let alphaFactor = (delta > expected) ? expected / delta : 1;
  5140. //roomSpeed = c.gameSpeed * alphaFactor;
  5141. //setTimeout(moveloop, 1000 / roomSpeed / 30 - delta);
  5142. })();
  5143. var funloop = (() => {
  5144. // Fun stuff, like RAINBOWS :D
  5145. function rainbow(my) {
  5146. let rainbow = [12, 2, 3, 11, 10, 14];
  5147. entities.forEach(function (element) {
  5148. if (element.rainbow) {
  5149. if (rainbow.indexOf(element.color) == -1 || element.color == undefined) {
  5150. element.color = 12;
  5151. } else {
  5152. if (element.rainbow_back == false) {
  5153. element.color = rainbow[rainbow.indexOf(element.color) + 1];
  5154. } else {
  5155. element.color = rainbow[rainbow.indexOf(element.color) - 1];
  5156. }
  5157. }
  5158. if (element.color == 14) {
  5159. element.rainbow_back = true;
  5160. }
  5161. if (element.color == 12) {
  5162. element.rainbow_back = false;
  5163. }
  5164. }
  5165. });
  5166. }
  5167. return () => {
  5168. // run the fun stuff :P
  5169. rainbow();
  5170. };
  5171. })();
  5172. // A less important loop. Runs at an actual 5Hz regardless of game speed.
  5173. var poisonLoop = (() => {
  5174. // Fun stuff, like RAINBOWS :D
  5175. function poison(my) {
  5176. entities.forEach(function (element) {
  5177. if (element.showpoison) {
  5178. let x = element.size + 10;
  5179. let y = element.size + 10;
  5180. Math.random() < 0.5 ? x *= -1 : x;
  5181. Math.random() < 0.5 ? y *= -1 : y;
  5182. Math.random() < 0.5 ? x *= Math.random() + 1 : x;
  5183. Math.random() < 0.5 ? y *= Math.random() + 1 : y;
  5184. var o = new Entity({
  5185. x: element.x + x,
  5186. y: element.y + y
  5187. });
  5188. o.define(Class['poisonEffect']);
  5189. }
  5190. if (element.poisoned && (element.type == 'tank' || element.type == 'crasher' || element.type == 'food' || element.type == 'minion' || element.type == 'drone' || element.type == 'swarm')) {
  5191. let x = element.size + 10;
  5192. let y = element.size + 10;
  5193. Math.random() < 0.5 ? x *= -1 : x;
  5194. Math.random() < 0.5 ? y *= -1 : y;
  5195. Math.random() < 0.5 ? x *= Math.random() + 1 : x;
  5196. Math.random() < 0.5 ? y *= Math.random() + 1 : y;
  5197. var o = new Entity({
  5198. x: element.x + x,
  5199. y: element.y + y
  5200. });
  5201. o.define(Class['poisonEffect']);
  5202.  
  5203. if (!element.invuln) {
  5204. element.health.amount -= element.health.max / (45 - element.poisonLevel);
  5205. element.shield.amount -= element.shield.max / (10 - element.poisonLevel);
  5206. }
  5207.  
  5208. element.poisonTime -= 1;
  5209. if (element.poisonTime <= 0) element.poisoned = false;
  5210.  
  5211. if (element.health.amount <= 0 && element.poisonedBy != undefined && element.poisonedBy.skill != undefined) {
  5212. element.poisonedBy.skill.score += Math.ceil(util.getJackpot(element.poisonedBy.skill.score));
  5213. element.poisonedBy.sendMessage('You killed ' + element.name + ' with poison.');
  5214. if (element.poisonedBy.name != null) {
  5215. element.sendMessage('You have been killed by ' + element.poisonedBy.name + ' with poison.');
  5216. } else if (element.poisonedBy.name == null) {
  5217. element.sendMessage('You have been killed with poison.');
  5218. }
  5219. }
  5220. }
  5221. });
  5222. }
  5223. return () => {
  5224. // run the poison
  5225. poison();
  5226. };
  5227. })();
  5228. var maintainloop = (() => {
  5229. // Place obstacles
  5230. function placeRoids() {
  5231. function placeRoid(type, entityClass) {
  5232. let x = 0;
  5233. let position;
  5234. do {
  5235. position = room.randomType(type);
  5236. x++;
  5237. if (x > 200) {
  5238. util.warn("Could not place some roids.");return 0;
  5239. }
  5240. } while (dirtyCheck(position, 10 + entityClass.SIZE));
  5241. let o = new Entity(position);
  5242. o.define(entityClass);
  5243. o.team = -101;
  5244. o.facing = ran.randomAngle();
  5245. o.protect();
  5246. o.life();
  5247. }
  5248. // Start placing them
  5249. let roidcount = room.roid.length * room.width * room.height / room.xgrid / room.ygrid / 50000 / 1.5;
  5250. let rockcount = room.rock.length * room.width * room.height / room.xgrid / room.ygrid / 250000 / 1.5;
  5251. let count = 0;
  5252. for (let i = Math.ceil(roidcount); i; i--) {
  5253. count++;placeRoid('roid', ran.choose([Class.obstacle, Class.obstacle, Class.obstacle, Class.obstacle, Class.autoroid]));
  5254. }
  5255. for (let i = Math.ceil(roidcount * 0.3); i; i--) {
  5256. count++;placeRoid('roid', ran.choose([Class.babyObstacle, Class.babyObstacle, Class.babyObstacle, Class.babyObstacle, Class.autorock]));
  5257. }
  5258. for (let i = Math.ceil(rockcount); i; i--) {
  5259. count++;placeRoid('rock', ran.choose([Class.obstacle, Class.obstacle, Class.obstacle, Class.obstacle, Class.autoroid]));
  5260. }
  5261. for (let i = Math.ceil(rockcount * 0.3); i; i--) {
  5262. count++;placeRoid('rock', ran.choose([Class.babyObstacle, Class.babyObstacle, Class.babyObstacle, Class.babyObstacle, Class.autorock, Class.autosquare]));
  5263. }
  5264. util.log('Placing ' + count + ' obstacles!');
  5265. }
  5266. placeRoids();
  5267. // Spawning functions
  5268. let spawnBosses = (() => {
  5269. let timer = 0;
  5270. let boss = (() => {
  5271. let i = 0,
  5272. names = [],
  5273. bois = [Class.egg],
  5274. n = 0,
  5275. begin = 'yo some shit is about to move to a lower position',
  5276. arrival = 'Something happened lol u should probably let Neph know this broke',
  5277. loc = 'norm';
  5278. let spawn = () => {
  5279. let spot,
  5280. m = 0;
  5281. do {
  5282. spot = room.randomType(loc);m++;
  5283. } while (dirtyCheck(spot, 500) && m < 30);
  5284. let o = new Entity(spot);
  5285. o.define(ran.choose(bois));
  5286. o.team = -100;
  5287. o.name = names[i++];
  5288. };
  5289. return {
  5290. prepareToSpawn: (classArray, number, nameClass, typeOfLocation = 'norm') => {
  5291. n = number;
  5292. bois = classArray;
  5293. loc = typeOfLocation;
  5294. names = ran.chooseBossName(nameClass, number);
  5295. i = 0;
  5296. if (n === 1) {
  5297. begin = 'A visitor is coming.';
  5298. arrival = names[0] + ' has arrived.';
  5299. } else {
  5300. begin = 'Visitors are coming.';
  5301. arrival = '';
  5302. for (let i = 0; i < n - 2; i++) arrival += names[i] + ', ';
  5303. arrival += names[n - 2] + ' and ' + names[n - 1] + ' have arrived.';
  5304. }
  5305. },
  5306. spawn: () => {
  5307. sockets.broadcast(begin);
  5308. for (let i = 0; i < n; i++) {
  5309. setTimeout(spawn, ran.randomRange(3500, 5000));
  5310. }
  5311. // Wrap things up.
  5312. setTimeout(() => sockets.broadcast(arrival), 5000);
  5313. util.log('[SPAWN] ' + arrival);
  5314. }
  5315. };
  5316. })();
  5317. return census => {
  5318. if (timer > 500 && ran.dice(510 - timer)) {
  5319. util.log('[SPAWN] Preparing to spawn...');
  5320. timer = 0;
  5321. let choice = [];
  5322. switch (ran.chooseChance(1, 1, 1, 1)) {
  5323. case 0:
  5324. choice = [[Class.palisade], 1, 'castle', 'norm'];
  5325. sockets.broadcast('A strange trembling...');
  5326. break;
  5327. case 1:
  5328. sockets.broadcast('Rifles Unite...');
  5329. choice = [[Class.riflespin], 2, 'castle', 'norm'];
  5330. break;
  5331. case 2:
  5332. choice = [[Class.extradice], 3, 'castle', 'norm'];
  5333. sockets.broadcast('Theres a Disturbance in the air...');
  5334. break;
  5335.  
  5336. case 3:
  5337. choice = [[Class.elitesquare], 1, 'castle', 'norm'];
  5338. sockets.broadcast('The Squares are fed up...');
  5339. break;
  5340.  
  5341. }
  5342. boss.prepareToSpawn(...choice);
  5343. setTimeout(boss.spawn, 300);
  5344. // Set the timeout for the spawn functions
  5345. } else if (!census.miniboss) timer++;
  5346. };
  5347. })();
  5348.  
  5349. let spawnCrasher = census => {
  5350. if (ran.chance(1 - 0.8 * census.crasher / room.maxFood / room.nestFoodAmount)) {
  5351. let spot,
  5352. i = 30;
  5353. do {
  5354. spot = room.randomType('nest');i--;if (!i) return 0;
  5355. } while (dirtyCheck(spot, 100));
  5356. let type = ran.dice(80) ? ran.choose([Class.sentryGun, Class.sentrySwarm, Class.sentryTrap]) : Class.crasher;
  5357. let o = new Entity(spot);
  5358. o.define(type);
  5359. o.team = -100;
  5360. }
  5361. };
  5362.  
  5363. // The NPC function
  5364. let makenpcs = (() => {
  5365. // Make base protectors if needed.
  5366. /*let f = (loc, team) => {
  5367. let o = new Entity(loc);
  5368. o.define(Class.baseProtector);
  5369. o.team = -team;
  5370. o.color = [10, 11, 12, 15][team-1];
  5371. };
  5372. for (let i=1; i<5; i++) {
  5373. room['bas' + i].forEach((loc) => { f(loc, i); });
  5374. }*/
  5375. // Return the spawning function
  5376. let bots = [];
  5377. return () => {
  5378. let census = {
  5379. crasher: 0,
  5380. miniboss: 0,
  5381. tank: 0,
  5382. dodecagon: 0
  5383. };
  5384. let npcs = entities.map(function npcCensus(instance) {
  5385. if (census[instance.type] != null) {
  5386. census[instance.type]++;
  5387. return instance;
  5388. }
  5389. }).filter(e => {
  5390. return e;
  5391. });
  5392. // Spawning
  5393. //spawnCrasher(census);
  5394. spawnBosses(census);
  5395. // Bots
  5396. if (bots.length < c.BOTS) {
  5397. let o = new Entity(room.random());
  5398. o.color = 17;
  5399. o.define(Class.bot);
  5400. o.define(Class.basic);
  5401. o.name += ran.chooseBotName();
  5402. o.refreshBodyAttributes();
  5403. o.color = 17;
  5404. bots.push(o);
  5405. }
  5406. // Remove dead ones
  5407. bots = bots.filter(e => {
  5408. return !e.isDead();
  5409. });
  5410. };
  5411. })();
  5412. // The big food function
  5413. let makefood = (() => {
  5414. let food = [],
  5415. foodSpawners = [];
  5416. // The two essential functions
  5417. function getFoodClass(level) {
  5418. let a = {};
  5419. switch (level) {
  5420. case 0:
  5421. a = Class.egg;break;
  5422. case 1:
  5423. a = Class.square;break;
  5424. case "e":
  5425. a = Class.wiffle;break;
  5426. case 2:
  5427. a = Class.triangle;break;
  5428. case 3:
  5429. a = Class.pentagon;break;
  5430. case 4:
  5431. a = Class.bigPentagon;break;
  5432. case 5:
  5433. a = Class.hugePentagon;break;
  5434. case 6:
  5435. a = Class.megaPentagon;break;
  5436. case "h":
  5437. a = Class.hexagon;break;
  5438. case "d":
  5439. a = Class.dodecagon;break;
  5440. default:
  5441. throw 'bad food level';
  5442. }
  5443. if (a !== {}) {
  5444. a.BODY.ACCELERATION = 0.015 / (a.FOOD.LEVEL + 1);
  5445. }
  5446. return a;
  5447. }
  5448. let placeNewFood = (position, scatter, level, allowInNest = false) => {
  5449. let o = nearest(food, position);
  5450. let mitosis = false;
  5451. let seed = false;
  5452. // Find the nearest food and determine if we can do anything with it
  5453. if (o != null) {
  5454. for (let i = 50; i > 0; i--) {
  5455. if (scatter == -1 || util.getDistance(position, o) < scatter) {
  5456. if (ran.dice((o.foodLevel + 1) * (o.foodLevel + 1))) {
  5457. mitosis = true;break;
  5458. } else {
  5459. seed = true;break;
  5460. }
  5461. }
  5462. }
  5463. }
  5464. // Decide what to do
  5465. if (scatter != -1 || mitosis || seed) {
  5466. // Splitting
  5467. if (o != null && (mitosis || seed) && room.isIn('nest', o) === allowInNest) {
  5468. let levelToMake = mitosis ? o.foodLevel : level,
  5469. place = {
  5470. x: o.x + o.size * Math.cos(o.facing),
  5471. y: o.y + o.size * Math.sin(o.facing)
  5472. };
  5473. let new_o = new Entity(place);
  5474. new_o.define(getFoodClass(levelToMake));
  5475. new_o.team = -100;
  5476. new_o.facing = o.facing + ran.randomRange(Math.PI / 2, Math.PI);
  5477. food.push(new_o);
  5478. return new_o;
  5479. }
  5480. // Brand new
  5481. else if (room.isIn('nest', position) === allowInNest) {
  5482. if (!dirtyCheck(position, 20)) {
  5483. o = new Entity(position);
  5484. o.define(getFoodClass(level));
  5485. o.team = -100;
  5486. o.facing = ran.randomAngle();
  5487. food.push(o);
  5488. return o;
  5489. }
  5490. }
  5491. }
  5492. };
  5493. // Define foodspawners
  5494. class FoodSpawner {
  5495. constructor() {
  5496. this.foodToMake = Math.ceil(Math.abs(ran.gauss(0, room.scale.linear * 80)));
  5497. this.size = Math.sqrt(this.foodToMake) * 25;
  5498.  
  5499. // Determine where we ought to go
  5500. let position = {};let o;
  5501. do {
  5502. position = room.gaussRing(1 / 3, 20);
  5503. o = placeNewFood(position, this.size, 0);
  5504. } while (o == null);
  5505.  
  5506. // Produce a few more
  5507. for (let i = Math.ceil(Math.abs(ran.gauss(0, 4))); i <= 0; i--) {
  5508. placeNewFood(o, this.size, 0);
  5509. }
  5510. // Set location
  5511. this.x = o.x;
  5512. this.y = o.y;
  5513. //util.debug('FoodSpawner placed at ('+this.x+', '+this.y+'). Set to produce '+this.foodToMake+' food.');
  5514. }
  5515. rot() {
  5516. if (--this.foodToMake < 0) {
  5517. //util.debug('FoodSpawner rotted, respawning.');
  5518. util.remove(foodSpawners, foodSpawners.indexOf(this));
  5519. foodSpawners.push(new FoodSpawner());
  5520. }
  5521. }
  5522. }
  5523. // Add them
  5524. foodSpawners.push(new FoodSpawner());
  5525. foodSpawners.push(new FoodSpawner());
  5526. foodSpawners.push(new FoodSpawner());
  5527. foodSpawners.push(new FoodSpawner());
  5528. // Food making functions
  5529. let makeGroupedFood = () => {
  5530. // Create grouped food
  5531. // Choose a location around a spawner
  5532. let spawner = foodSpawners[ran.irandom(foodSpawners.length - 1)],
  5533. bubble = ran.gaussRing(spawner.size, 1 / 4);
  5534. placeNewFood({ x: spawner.x + bubble.x, y: spawner.y + bubble.y }, -1, 0);
  5535. spawner.rot();
  5536. };
  5537. let makeDistributedFood = () => {
  5538. // Distribute food everywhere
  5539. //util.debug('Creating new distributed food.');
  5540. let spot = {};
  5541. do {
  5542. spot = room.gaussRing(1 / 2, 2);
  5543. } while (room.isInNorm(spot));
  5544. placeNewFood(spot, 0.01 * room.width, 0);
  5545. };
  5546. let makeCornerFood = () => {
  5547. // Distribute food in the corners
  5548. let spot = {};
  5549. do {
  5550. spot = room.gaussInverse(5);
  5551. } while (room.isInNorm(spot));
  5552. placeNewFood(spot, 0.05 * room.width, 0);
  5553. };
  5554. let makeNestFood = () => {
  5555. // Make nest pentagons
  5556. let spot = room.randomType('nest');
  5557. placeNewFood(spot, 0.01 * room.width, 3, true);
  5558. };
  5559. // Return the full function
  5560. return () => {
  5561. // Find and understand all food
  5562. let census = {
  5563. [0]: 0, // Egg
  5564. [1]: 0, // Square
  5565. [2]: 0, // Triangle
  5566. [3]: 0, // Penta
  5567. [4]: 0, // Beta
  5568. [5]: 0, // Alpha
  5569. [6]: 0, // Gamma
  5570. [7]: 0,
  5571. tank: 0,
  5572. sum: 0
  5573. };
  5574. let censusNest = {
  5575. [0]: 0, // Egg
  5576. [1]: 0, // Square
  5577. [2]: 0, // Triangle
  5578. [3]: 0, // Penta
  5579. [4]: 0, // Beta
  5580. [5]: 0, // Alpha
  5581. [6]: 0, // Gamma
  5582. [7]: 0,
  5583. sum: 0
  5584. };
  5585. // Do the censusNest
  5586. food = entities.map(instance => {
  5587. try {
  5588. if (instance.type === 'tank') {
  5589. census.tank++;
  5590. } else if (instance.foodLevel > -1) {
  5591. if (room.isIn('nest', { x: instance.x, y: instance.y })) {
  5592. censusNest.sum++;censusNest[instance.foodLevel]++;
  5593. } else {
  5594. census.sum++;census[instance.foodLevel]++;
  5595. }
  5596. return instance;
  5597. }
  5598. } catch (err) {
  5599. util.error(instance.label);util.error(err);instance.kill();
  5600. }
  5601. }).filter(e => {
  5602. return e;
  5603. });
  5604. // Sum it up
  5605. let maxFood = 1 + room.maxFood + 15 * census.tank;
  5606. let maxNestFood = 1 + room.maxFood * room.nestFoodAmount;
  5607. let foodAmount = census.sum;
  5608. let nestFoodAmount = censusNest.sum;
  5609. /*********** ROT OLD SPAWNERS **********/
  5610. //foodSpawners.forEach(spawner => { if (ran.chance(1 - foodAmount/maxFood)) spawner.rot(); });
  5611. let i = 0;
  5612. const length = foodSpawners.length;
  5613. for (; i < length; i++) {
  5614. if (ran.chance(1 - foodAmount / maxFood)) {
  5615. foodSpawners[i].rot();
  5616. }
  5617. }
  5618. /************** MAKE FOOD **************/
  5619. while (ran.chance(0.8 * (1 - foodAmount * foodAmount / maxFood / maxFood))) {
  5620. switch (ran.chooseChance(10, 2, 1)) {
  5621. case 0:
  5622. makeGroupedFood();break;
  5623. case 1:
  5624. makeDistributedFood();break;
  5625. case 2:
  5626. makeCornerFood();break;
  5627. }
  5628. }
  5629. while (ran.chance(0.5 * (1 - nestFoodAmount * nestFoodAmount / maxNestFood / maxNestFood))) makeNestFood();
  5630. /************* UPGRADE FOOD ************/
  5631. if (!food.length) return 0;
  5632. for (let i = Math.ceil(food.length / 100); i > 0; i--) {
  5633. let o = food[ran.irandom(food.length - 1)],
  5634. // A random food instance
  5635. oldId = -1000,
  5636. overflow,
  5637. location;
  5638. // Bounce 6 times
  5639. for (let j = 0; j < 6; j++) {
  5640. overflow = 10;
  5641. // Find the nearest one that's not the last one
  5642. do {
  5643. o = nearest(food, { x: ran.gauss(o.x, 30), y: ran.gauss(o.y, 30) });
  5644. } while (o.id === oldId && --overflow);
  5645. if (!overflow) continue;
  5646. // Configure for the nest if needed
  5647. let proportions = c.FOOD,
  5648. cens = census,
  5649. amount = foodAmount;
  5650. if (room.isIn('nest', o)) {
  5651. proportions = c.FOOD_NEST;
  5652. cens = censusNest;
  5653. amount = nestFoodAmount;
  5654. }
  5655. // Upgrade stuff
  5656. o.foodCountup += Math.ceil(Math.abs(ran.gauss(0, 10)));
  5657. while (o.foodCountup >= (o.foodLevel + 1) * 100) {
  5658. o.foodCountup -= (o.foodLevel + 1) * 100;
  5659. if (ran.chance(1 - cens[o.foodLevel + 1] / amount / proportions[o.foodLevel + 1])) {
  5660. o.define(getFoodClass(o.foodLevel + 1));
  5661. }
  5662. }
  5663. }
  5664. }
  5665. };
  5666. })();
  5667. // Define food and food spawning
  5668. return () => {
  5669. // Do stuff
  5670. makenpcs();
  5671. makefood();
  5672. // Regen health and update the grid
  5673. let i = 0;
  5674. const length = entities.length;
  5675. //for (var instance of entities) {
  5676. for (; i < length; i++) {
  5677. if (entities[i].shield.max) {
  5678. entities[i].shield.regenerate();
  5679. }
  5680. if (entities[i].health.amount) {
  5681. entities[i].health.regenerate(entities[i].shield.max && entities[i].shield.max === entities[i].shield.amount);
  5682. }
  5683. };
  5684. };
  5685. })();
  5686.  
  5687. // This is the checking loop. Runs at 1Hz.
  5688. var speedcheckloop = (() => {
  5689. let fails = 0;
  5690. // Return the function
  5691. return () => {
  5692. let activationtime = logs.activation.sum(),
  5693. collidetime = logs.collide.sum(),
  5694. movetime = logs.entities.sum(),
  5695. playertime = logs.network.sum(),
  5696. maptime = logs.minimap.sum(),
  5697. physicstime = logs.physics.sum(),
  5698. lifetime = logs.life.sum(),
  5699. selfietime = logs.selfie.sum();
  5700. let sum = logs.master.record();
  5701. let loops = logs.loops.count(),
  5702. active = logs.entities.count();
  5703. global.fps = 1000 / sum;
  5704. if (sum > 1000 / roomSpeed / global.fps) {
  5705. //fails++;
  5706. util.warn('~~ LOOPS: ' + loops + '. ENTITY #: ' + entities.length + '//' + Math.round(active / loops) + '. VIEW #: ' + views.length + '. BACKLOGGED :: ' + (sum * roomSpeed * 3).toFixed(3) + '%! ~~');
  5707. util.warn('Total activation time: ' + activationtime);
  5708. util.warn('Total collision time: ' + collidetime);
  5709. util.warn('Total cycle time: ' + movetime);
  5710. util.warn('Total player update time: ' + playertime);
  5711. util.warn('Total lb+minimap processing time: ' + maptime);
  5712. util.warn('Total entity physics calculation time: ' + physicstime);
  5713. util.warn('Total entity life+thought cycle time: ' + lifetime);
  5714. util.warn('Total entity selfie-taking time: ' + selfietime);
  5715. util.warn('Total time: ' + (activationtime + collidetime + movetime + playertime + maptime + physicstime + lifetime + selfietime));
  5716. if (fails > 60) {
  5717. util.error("FAILURE!");
  5718. //process.exit(1);
  5719. }
  5720. } else {
  5721. fails = 0;
  5722. }
  5723. };
  5724. })();
  5725.  
  5726. /** BUILD THE SERVERS **/
  5727. // Turn the server on
  5728. var server = http.createServer(app);
  5729. var websockets = (() => {
  5730. // Configure the websocketserver
  5731. let config = { server: server };
  5732. if (c.servesStatic) {
  5733. server.listen(c.port, function httpListening() {
  5734. util.log(new Date() + ". Joint HTTP+Websocket server turned on, listening on port " + server.address().port + ".");
  5735. });
  5736. } else {
  5737. config.port = c.port;
  5738. util.log(new Date() + 'Websocket server turned on, listening on port ' + c.port + '.');
  5739. }
  5740. // Build it
  5741. return new WebSocket.Server(config);
  5742. })().on('connection', sockets.connect);
  5743.  
  5744. var tickLengthMs = 1000 / 20;
  5745. var previousTick = now();
  5746. var actualTicks = 0;
  5747. var gameexecution = function () {
  5748. var current = now();
  5749.  
  5750. actualTicks++;
  5751. if (previousTick + tickLengthMs <= current) {
  5752. //var delta = (current - previousTick) / 1000;
  5753. var curTime = now();
  5754. timestep = 0.01575 * (curTime - lastTime);
  5755. if (timestep <= 0 || timestep > 7.875) {
  5756. timestep = 0.01575;
  5757. }
  5758.  
  5759. previousTick = current;
  5760.  
  5761. gameloop();
  5762.  
  5763. actualTicks = 0;
  5764. lastTime = curTime;
  5765. }
  5766.  
  5767. if (now() - previousTick < tickLengthMs - 50) {
  5768. timer.setTimeout(gameexecution, '', '50m');
  5769. } else {
  5770. //timer.setTimeout(gameexecution, '', '1m');
  5771. setImmediate(gameexecution);
  5772. }
  5773. };
  5774.  
  5775. // Bring it to life
  5776. gameexecution();
  5777. //setInterval(gameloop, room.cycleSpeed);
  5778. setInterval(maintainloop, 100);
  5779. setInterval(speedcheckloop, 1000);
  5780. setInterval(poisonLoop, room.cycleSpeed * 20);
  5781.  
  5782. // Graceful shutdown
  5783. let shutdownWarning = false;
  5784. if (process.platform === "win32") {
  5785. var rl = require("readline").createInterface({
  5786. input: process.stdin,
  5787. output: process.stdout
  5788. });
  5789. rl.on("SIGINT", () => {
  5790. process.emit("SIGINT");
  5791. });
  5792. }
  5793. process.on("SIGINT", () => {
  5794. if (!shutdownWarning) {
  5795. shutdownWarning = true;
  5796. sockets.broadcast("The server is shutting down.");
  5797. util.log('Server going down! Warning broadcasted.');
  5798. setTimeout(() => {
  5799. sockets.broadcast("Arena closed.");
  5800. util.log('Final warning broadcasted.');
  5801. setTimeout(() => {
  5802. util.warn('Process ended.');
  5803. process.exit();
  5804. }, 3000);
  5805. }, 17000);
  5806. }
  5807. });
  5808.  
  5809. const Eris = require('eris');
  5810. const bot = new Eris(process.env.bot_token);
  5811.  
  5812. bot.on('ready', () => {
  5813. console.log('\nBot ready!\n');
  5814. var canLogToDiscord = true;
  5815. });
  5816.  
  5817. var unauth = '```patch\n- ERROR: UNATHORIZED USER```';
  5818. var mliststring = '';
  5819. var mliststring2 = '';
  5820. /*mlist.forEach(function(element) {
  5821. if (mliststring.length < 1970) {
  5822. mliststring += element += ', ';
  5823. } else {
  5824. mliststring2 += element += ', '
  5825. }
  5826. });*/
  5827.  
  5828. function parse(input) {
  5829. let out = input.split(" ");
  5830. return out;
  5831. }
  5832.  
  5833. bot.on('messageCreate', msg => {
  5834. try {
  5835. if (msg.author.id != 452793079238361089) {
  5836. if (msg.content.startsWith("k!select ")) {
  5837. if (process.env.ISONGLITCH == undefined) {
  5838. let sendError = true;
  5839. let lookfor = msg.content.split("k!select ").pop();
  5840. function thing(element) {
  5841. if (typeof element.sendMessage == "function" && element.name == lookfor) {
  5842. sendError = false;
  5843. bot.createMessage(msg.channel.id, String(element.name + '\nTank: ' + element.label + '\nId: ' + element.id + '\nAlpha: ' + element.alpha + '\nColor: ' + element.blend.color + '\nMax Health: ' + element.health.max + '\nCurrent Health: ' + element.health.amount + '\nIs Invulnerable: ' + element.invuln + '\nScore: ' + element.photo.score + '\nLevel: ' + element.skill.level));
  5844. }
  5845. }
  5846. for (var element of entities) {
  5847. thing(element);
  5848. }
  5849. if (sendError) {
  5850. bot.createMessage(msg.channel.id, "Was unable to find an entity by that name");
  5851. }
  5852. }
  5853. }
  5854. if (msg.content == 'k!ping') {
  5855. bot.createMessage(msg.channel.id, 'Pong!\n' + "\nConnections amount: " + global.extPlayers + "\nRunning on glitch: " + process.env.ISONGLITCH + "\nDirectory: " + __dirname + "\nFile name: " + __filename);
  5856. }
  5857. if (msg.content == 'k!list') {
  5858. //if (process.env.ISONGLITCH == undefined) {
  5859. bot.createMessage(msg.channel.id, mliststring);
  5860. bot.createMessage(msg.channel.id, mliststring2);
  5861. } //}
  5862. if (msg.content.includes('k!help')) {
  5863. if (process.env.ISONGLITCH == undefined) {
  5864. bot.createMessage(msg.channel.id, '***COMMANDS*** \nPrefix: k! \n(No space after k! when running command) \n \n**ping** - tells u if the server is running\n**kill** - kills the server (Authorization required)\n**broadcast** *<message>* - broadcasts a message (Authorization required)\n**list** - Lists all tanks as their internal names\n**query** *<internalname>* - returns some data about a tank (use list first to get tank names that this will accept)\n**select** *<name>* - returns some data about in-game users\n**summon** *<type>* *<class>* - summons a thing\n**banish** *<id>* - Banishes a player (Authorization required)\n**players** - list in-game players\n**stat** *<id> <path to stat> <new value>* - modifies a stat (Authorization required)');
  5865. }
  5866. }
  5867. if (msg.content == 'k!kill') {
  5868. if (msg.author.id == 181829457852628993) {
  5869. console.log("\n SERVER TERMINATED BY AN AUTHORIZED USER \n");
  5870. bot.createMessage(msg.channel.id, 'Terminating.....');
  5871. process.emit("SIGINT");
  5872. } else {
  5873. console.log("Unauthorized user", msg.author.username, "tried to end server");
  5874. bot.createMessage(msg.channel.id, unauth);
  5875. }
  5876. }
  5877. if (msg.content.startsWith('k!broadcast')) {
  5878. if (msg.author.id == 181829457852628993 || 340270483838599168) {
  5879. if (process.env.ISONGLITCH == undefined) {
  5880. sockets.broadcast(msg.content.split("k!broadcast").pop() + " - " + msg.author.username);
  5881. bot.createMessage(msg.channel.id, 'Message Broadcasted and rendered.');
  5882. }
  5883. } else {
  5884. console.log("Unauthorized user", msg.author.username, "tried to broadcast a message");
  5885. bot.createMessage(msg.channel.id, unauth);
  5886. }
  5887. }
  5888. if (msg.content.startsWith('k!query')) {
  5889. if (process.env.ISONGLITCH == undefined) {
  5890. let output = '';
  5891. var query = msg.content.split(">query ").pop();
  5892. try {
  5893. var botreturn = eval('Class.' + query);
  5894. for (var key in botreturn) {
  5895. if (output.length > 500) {
  5896. console.log(output.length);bot.createMessage(msg.channel.id, output);output = '';
  5897. }
  5898. output += String(key) + ': ' + eval('Class.' + query + '.' + String(key)) + '\n';
  5899. var returned = typeof eval('Class.' + query + '.' + String(key));
  5900. if (returned == 'object') {
  5901. for (var key2 in eval('Class.' + query + '.' + String(key))) {
  5902. if (key2 != 'remove') {
  5903. try {
  5904. output += "^ " + String(key2) + ': ' + eval('Class.' + query + '.' + String(key) + '[' + String(key2) + ']') + '\n';
  5905. var returned = typeof eval('Class.' + query + '.' + String(key) + '[' + String(key2) + ']');
  5906. var returnedobj = eval('Class.' + query + '.' + String(key) + '[' + String(key2) + ']');
  5907. } catch (err) {
  5908. output += "^ " + String(key2) + ': ' + eval('Class.' + query + '.' + String(key) + '.' + String(key2)) + '\n';
  5909. var returned = typeof eval('Class.' + query + '.' + String(key) + '.' + String(key2));
  5910. var returnedobj = eval('Class.' + query + '.' + String(key) + '.' + String(key2));
  5911. }
  5912. if (returned == 'object') {
  5913. for (var key3 in returnedobj) {
  5914. if (key3 != 'remove') {
  5915. try {
  5916. output += "^ ^ " + String(key3) + ': ' + eval('Class.' + query + '.' + String(key) + '[' + String(key2) + ']' + '[' + String(key3) + ']') + '\n';
  5917. } catch (err) {
  5918. try {
  5919. output += "^ ^ " + String(key3) + ': ' + eval('Class.' + query + '.' + String(key) + '[' + String(key2) + ']' + '.' + String(key3)) + '\n';
  5920. } catch (err) {
  5921. try {
  5922. output += "^ ^ " + String(key3) + ': ' + eval('Class.' + query + '.' + String(key) + '.' + String(key2) + '[' + String(key3) + ']') + '\n';
  5923. } catch (err) {
  5924. output += "^ ^ " + String(key3) + ': ' + eval('Class.' + query + '.' + String(key) + '.' + String(key2) + '.' + String(key3)) + '\n';
  5925. }
  5926. }
  5927. }
  5928. }
  5929. }
  5930. }
  5931. }
  5932. }
  5933. }
  5934. }
  5935. } catch (err) {
  5936. bot.createMessage(msg.channel.id, String(err));
  5937. }
  5938. bot.createMessage(msg.channel.id, output);
  5939. }
  5940. }
  5941. /*if (msg.content.startsWith('k!summon ')) {
  5942. if (msg.author.id == 181829457852628993) {
  5943. var spawnClass = msg.content.split("k!summon ").pop().substr(0, 3)
  5944. console.log(msg.content.split("k!summon ").pop().substr(0, 3))
  5945. var type = msg.content.split(" ").pop()
  5946. if (spawnClass == 'bot') {
  5947. botSpawn = type
  5948. bot.createMessage(msg.channel.id, "Next bot will be a " + type);
  5949. } else if (spawnClass == 'food') {
  5950.  
  5951. } else {
  5952. bot.createMessage(msg.channel.id, "Was unable to complete request, unknown summon type: " + spawnClass);
  5953. }
  5954. } else {
  5955. console.log("Unauthorized user", msg.author.username, "tried to broadcast")
  5956. bot.createMessage(msg.channel.id, unauth);
  5957. }
  5958. }*/
  5959. if (msg.content == 'k!players') {
  5960. let output = '';
  5961. let outWillFail = true;
  5962. //entities.forEach(function(element) {
  5963. //if (typeof element.sendMessage == "function" && element.name != '') {
  5964. // output += String(element.name + ' - ' + element.id + '\n')
  5965. // outWillFail = false
  5966. //}
  5967. //})
  5968.  
  5969. let i = 0;
  5970. const length = entities.length;
  5971. for (; i < length; i++) {
  5972. //for (let instance of entities) {
  5973. let instance = entities[i];
  5974. if (typeof instance.sendMessage == "function" && instance.name != '') {
  5975. output += String(instance.name + ' - ' + instance.id + '\n');
  5976. outWillFail = false;
  5977. }
  5978. }
  5979. if (!outWillFail) {
  5980. bot.createMessage(msg.channel.id, output);
  5981. } else {
  5982. bot.createMessage(msg.channel.id, "There are currently no players on the server");
  5983. }
  5984. }
  5985. if (msg.content.startsWith('k!stat ')) {
  5986. if (msg.author.id == 181829457852628993 || 340270483838599168 || 350336602956103683) {
  5987. let s_command = parse(msg.content);
  5988. let s_lookForId = s_command[1];
  5989. let s_statpath = s_command[2];
  5990. let s_newvalue = s_command[3];
  5991. entities.forEach(function (element) {
  5992. if (element.id == s_lookForId) {
  5993. try {
  5994. eval('element' + s_statpath + ' = ' + s_newvalue);
  5995. } catch (err) {
  5996. eval('element' + s_statpath + ' = "' + s_newvalue + '"');
  5997. }
  5998. bot.createMessage(msg.channel.id, "Value set to " + String(eval('element' + s_statpath)));
  5999. }
  6000. });
  6001. } else {
  6002. bot.createMessage(msg.channel.id, unauth);
  6003. }
  6004. }
  6005. if (msg.content.startsWith('k!define ')) {
  6006. let printerror = true;
  6007. let command = parse(msg.content);
  6008. let inputid = command[1];
  6009. let inputclass = command[2];
  6010. if (msg.author.id == 181829457852628993 || 340270483838599168) {
  6011. if (eval(Class[inputclass]) != undefined) {
  6012. entities.filter(r => r.id == inputid)[0].define(Class[inputclass]);
  6013. printerror = false;
  6014. bot.createMessage(msg.channel.id, 'Defined user as Class.' + inputclass);
  6015. } else {
  6016. bot.createMessage(msg.channel.id, inputclass + ' is not a valid tank');
  6017. }
  6018. if (printerror) {
  6019. bot.createMessage(msg.channel.id, "Count find any users by the id: " + inputid);
  6020. }
  6021. } else {
  6022. bot.createMessage(msg.channel.id, unauth);
  6023. }
  6024. }
  6025. }
  6026. } catch (err) {
  6027. // log the error in chat
  6028. bot.createMessage(msg.channel.id, String(err));
  6029. }
  6030. });
  6031.  
  6032. bot.editStatus('online', {
  6033. name: 'Type k!help for commands!',
  6034. type: 0
  6035. });
  6036.  
  6037. bot.connect();exports.circleArea = function (radius) {
  6038. return Math.pow(radius, 2) * Math.PI;
  6039. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement