Best JavaScript code snippet using storybook-root
entity.js
Source:entity.js
1// Copyright (C) 2022 Radioactive642const PF = require('pathfinding');3const Cryptr = require('cryptr');4const { cloneDeep } = require('lodash');5const { lock } = require('object-property-lock');6const cryptr = new Cryptr('cachePasswordKey');7// entities8Entity = function() {9 var self = {10 entType: 'entity',11 id: null,12 x: 0,13 y: 0,14 map: 'World',15 layer: 0,16 xspeed: 0,17 yspeed: 0,18 lastx: 0,19 lasty: 0,20 gridx: 0,21 gridy: 0,22 chunkx: 0,23 chunky: 0,24 moveSpeed: 0,25 slowedDown: false,26 frozen: false,27 width: 0,28 height: 0,29 noCollision: false,30 physicsInaccuracy: 1,31 collisionBoxSize: 0,32 chunkLocation: {33 map: 'World',34 layer: 0,35 chunkx: 0,36 chunky: 037 }38 };39 self.id = Math.random();40 lock(self, ['id']);41 self.update = function update() {42 self.updatePos();43 };44 self.updatePos = function updatePos() {45 self.collide();46 };47 self.collide = function collide() {48 try {49 if (!self.frozen && (self.xspeed != 0 || self.yspeed != 0)) {50 var max = Math.max(Math.abs(self.xspeed), Math.abs(self.yspeed))/(self.physicsInaccuracy*ENV.physicsInaccuracy);51 for (let i = 0; i < max; i += max/Math.ceil(max)) {52 self.lastx = self.x;53 self.lasty = self.y;54 self.x += self.xspeed/max || 0;55 self.y += self.yspeed/max || 0;56 self.gridx = Math.floor(self.x/64);57 self.gridy = Math.floor(self.y/64);58 if (!self.noCollision && self.doPointCollision()) break;59 self.checkLayer();60 self.checkSlowdown();61 }62 self.x = Math.round(self.x);63 self.y = Math.round(self.y);64 self.gridx = Math.floor(self.x/64);65 self.gridy = Math.floor(self.y/64);66 self.chunkx = Math.floor(self.gridx/Collision.grid[self.map].chunkWidth);67 self.chunky = Math.floor(self.gridy/Collision.grid[self.map].chunkHeight);68 }69 } catch (err) {70 error(err);71 }72 };73 self.checkCollisionLine = function checkCollisionLine(x1, y1, x2, y2) {74 if (Math.floor((x2-x1)/64)) {75 var slope = (y2-y1)/(x2-x1);76 for (let x = Math.floor(Math.min(x1, x2)/64); x <= Math.floor(Math.max(x1, x2)/64); x++) {77 var y = Math.floor((slope*(x*64)+y1)/64);78 if (Collision.getColEntity(self.map, x, y, self.layer)[0]) return true;79 }80 } else {81 var x = Math.floor(x1/64);82 for (let y = Math.floor(Math.min(y1, y2)/64); y <= Math.floor(Math.max(y1, y2)/64); y++) {83 if (Collision.getColEntity(self.map, x, y, self.layer)[0]) return true;84 }85 }86 return false;87 };88 self.checkSpannedCollision = function checkSpannedCollision() {89 var x = self.x;90 var y = self.y;91 var width = self.width;92 var height = self.height;93 self.width += Math.abs(self.x-self.lastx);94 self.height += Math.abs(self.y-self.lasty);95 self.x = (self.x+self.lastx)/2;96 self.y = (self.y+self.lasty)/2;97 self.gridx = Math.floor(self.x/64);98 self.gridy = Math.floor(self.y/64);99 self.collisionBoxSize = Math.max(self.width, self.height);100 var colliding = self.checkPointCollision();101 self.x = x;102 self.y = y;103 self.width = width;104 self.height = height;105 self.collisionBoxSize = Math.max(self.width, self.height);106 self.gridx = Math.floor(self.x/64);107 self.gridy = Math.floor(self.y/64);108 return colliding;109 };110 self.checkLargeSpannedCollision = function checkLargeSpannedCollision() {111 var colliding = false;112 if (self.checkPointCollision()) colliding = true;113 if (self.checkCollisionLine(self.lastx-self.width/2, self.lasty-self.height/2, self.x-self.width/2, self.y-self.height/2)) colliding = true;114 if (self.checkCollisionLine(self.lastx-self.width/2, self.lasty+self.height/2, self.x-self.width/2, self.y+self.height/2)) colliding = true;115 if (self.checkCollisionLine(self.lastx+self.width/2, self.lasty+self.height/2, self.x+self.width/2, self.y+self.height/2)) colliding = true;116 if (self.checkCollisionLine(self.lastx+self.width/2, self.lasty-self.height/2, self.x+self.width/2, self.y-self.height/2)) colliding = true;117 if (self.checkCollisionLine(self.lastx, self.lasty, self.x, self.y)) colliding = true;118 return colliding;119 };120 self.checkPointCollision = function checkPointCollision() {121 var collisions = [];122 var range = Math.ceil(self.collisionBoxSize/128);123 for (let x = self.gridx-range; x <= self.gridx+range; x++) {124 for (let y = self.gridy-range; y <= self.gridy+range; y++) {125 collisions.push(Collision.getColEntity(self.map, x, y, self.layer));126 }127 }128 for (let i in collisions) {129 for (let j in collisions[i]) {130 if (self.collideWith(collisions[i][j])) return true;131 }132 }133 return false;134 };135 self.doPointCollision = function doPointCollision() {136 var collisions = [];137 var range = Math.ceil(self.collisionBoxSize/128);138 for (let x = self.gridx-range; x <= self.gridx+range; x++) {139 for (let y = self.gridy-range; y <= self.gridy+range; y++) {140 collisions.push(Collision.getColEntity(self.map, x, y, self.layer));141 }142 }143 var colliding = false;144 for (let i in collisions) {145 for (let j in collisions[i]) {146 if (self.collideWith(collisions[i][j])) {147 colliding = true;148 break;149 }150 }151 }152 if (colliding) {153 colliding = false;154 var x = self.x;155 self.x = self.lastx;156 for (let i in collisions) {157 for (let j in collisions[i]) {158 if (self.collideWith(collisions[i][j])) {159 colliding = true;160 break;161 }162 }163 }164 if (colliding) {165 colliding = false;166 self.x = x;167 self.y = self.lasty;168 for (let i in collisions) {169 for (let j in collisions[i]) {170 if (self.collideWith(collisions[i][j])) {171 colliding = true;172 break;173 }174 }175 }176 if (colliding) {177 colliding = false;178 self.x = self.lastx;179 self.y = self.lasty;180 for (let i in collisions) {181 for (let j in collisions[i]) {182 if (self.collideWith(collisions[i][j])) {183 colliding = true;184 break;185 }186 }187 }188 }189 }190 }191 return colliding;192 };193 self.checkLayer = function checkLayer() {194 var collisions = [];195 var range = Math.ceil(self.collisionBoxSize/128);196 for (let x = self.gridx-range; x <= self.gridx+range; x++) {197 for (let y = self.gridy-range; y <= self.gridy+range; y++) {198 collisions.push(Layer.getColEntity(self.map, x, y, self.layer));199 }200 }201 var dir = 0;202 for (let i in collisions) {203 for (let j in collisions[i]) {204 if (self.collideWith(collisions[i][j])) dir += collisions[i][j].dir;205 }206 }207 self.layer += Math.max(-1, Math.min(dir, 1));208 };209 self.checkSlowdown = function checkSlowdown() {210 var collisions = [];211 for (let y = self.gridy-1; y <= self.gridy+1; y++) {212 for (let x = self.gridx-1; x <= self.gridx+1; x++) {213 collisions.push(Slowdown.getColEntity(self.map, x, y, self.layer));214 }215 }216 var colliding = false;217 for (let i in collisions) {218 for (let j in collisions[i]) {219 if (self.collideWith(collisions[i][j])) colliding = true;220 }221 }222 self.slowedDown = colliding;223 };224 self.collideWith = function collideWith(entity) {225 if (self.getSquareDistance(entity) <= self.collisionBoxSize/2 + entity.collisionBoxSize/2) {226 var bound1left = self.x-(self.width/2);227 var bound1right = self.x+(self.width/2);228 var bound1top = self.y-(self.height/2);229 var bound1bottom = self.y+(self.height/2);230 var bound2left = entity.x-(entity.width/2);231 var bound2right = entity.x+(entity.width/2);232 var bound2top = entity.y-(entity.height/2);233 var bound2bottom = entity.y+(entity.height/2);234 if (entity.map == self.map && bound1left < bound2right && bound1right > bound2left && bound1top < bound2bottom && bound1bottom > bound2top) {235 return true;236 }237 }238 return false;239 };240 self.getDistance = function getDistance(entity) {241 return Math.sqrt(Math.pow(self.x-entity.x, 2)+Math.pow(self.y-entity.y, 2));242 };243 self.getSquareDistance = function getSquareDistance(entity) {244 return Math.max(Math.abs(self.x-entity.x), Math.abs(self.y-entity.y));245 };246 self.getGridDistance = function getGridDistance(entity) {247 if (entity.gridx != null) {248 return Math.sqrt(Math.pow(self.gridx-entity.gridx, 2)+Math.pow(self.gridy-entity.gridy, 2));249 } else {250 return Math.sqrt(Math.pow(self.gridx-entity.x, 2)+Math.pow(self.gridy-entity.y, 2));251 }252 };253 self.getSquareGridDistance = function getSquareGridDistance(entity) {254 if (entity.gridx != null) {255 return Math.max(Math.abs(self.gridx-entity.gridx), Math.abs(self.gridy-entity.gridy));256 } else {257 return Math.max(Math.abs(self.gridx-entity.x), Math.abs(self.gridy-entity.y));258 }259 };260 self.rayCast = function rayCast(x, y) {261 try {262 var ray = {263 x: self.x,264 y: self.y,265 angle: 0,266 xspeed: 0,267 yspeed: 0268 };269 ray.angle = Math.atan2(y-ray.y, x-ray.x);270 ray.xspeed = Math.cos(ray.angle)*15;271 ray.yspeed = Math.sin(ray.angle)*15;272 var distance = Math.ceil(self.getDistance({x: x, y: y})/15)273 for (let i = 0; i < distance; i++) {274 ray.x += ray.xspeed;275 ray.y += ray.yspeed;276 if (Collision.grid[self.map][self.layer][Math.floor(ray.y/64)] && Collision.grid[self.map][self.layer][Math.floor(ray.y/64)][Math.floor(ray.x/64)] != null && Collision.grid[self.map][self.layer][Math.floor(ray.y/64)][Math.floor(ray.x/64)] < 15 && Collision.grid[self.map][self.layer][Math.floor(ray.y/64)][Math.floor(ray.x/64)] != 0) {277 return true;278 }279 }280 } catch (err) {281 error(err);282 }283 return false;284 };285 self.updateChunkLocation = function updateChunkLocation() {};286 return self;287};288Entity.update = function update() {289 var pack1 = Player.update();290 var pack2 = Monster.update();291 var pack3 = Projectile.update();292 var pack4 = Npc.update();293 var pack5 = Particle.update();294 var pack6 = DroppedItem.update();295 var pack = {296 players: [],297 monsters: [],298 projectiles: [],299 particles: [],300 droppedItems: []301 };302 pack.players = pack1;303 pack.monsters = pack2;304 pack.projectiles = pack3;305 pack.players = pack.players.concat(pack4);306 pack.particles = pack5;307 pack.droppedItems = pack6;308 return pack;309};310Entity.getDebugData = function getDebugData() {311 var pack1 = Player.getDebugData();312 var pack2 = Monster.getDebugData();313 var pack3 = Projectile.getDebugData();314 var pack4 = Npc.getDebugData();315 var pack5 = DroppedItem.getDebugData();316 var pack = {317 players: [],318 monsters: [],319 projectiles: [],320 droppedItems: []321 };322 pack.players = pack1;323 pack.monsters = pack2;324 pack.projectiles = pack3;325 pack.players = pack.players.concat(pack4);326 pack.droppedItems = pack5;327 return pack;328};329// rigs330Rig = function() {331 var self = new Entity();332 self.entType = 'Rig';333 self.width = 32;334 self.height = 32;335 self.controls = {336 up: false,337 down: false,338 left: false,339 right: false,340 xaxis: 0,341 yaxis: 0,342 x: 0,343 y: 0,344 heal: false345 };346 self.xmove = 0;347 self.ymove = 0;348 self.xknockback = 0;349 self.yknockback = 0;350 self.animationStage = 0;351 self.animationLength = 0;352 self.lastFrameUpdate = 0;353 self.animationSpeed = 150;354 self.animationDirection = 'facing';355 self.facingDirection = 'down';356 self.moveSpeed = 15;357 self.stats = {358 damageType: null,359 projectileSpeed: 1,360 attack: 1,361 defense: 0,362 damageReduction: 0,363 knockbackResistance: 0,364 heal: 0,365 speed: 1,366 range: 1,367 critChance: 0,368 critPower: 1,369 knockback: 0370 };371 self.shieldStats = {372 knockbackResistance: 0,373 blockAngle: 0,374 reflectProjectileChance: 0375 };376 self.shield = false;377 self.shieldAngle = 0;378 self.invincibilityFrames = {};379 self.hp = 100;380 self.maxHP = 100;381 self.lastHeal = 0;382 self.lastAutoHeal = 0;383 self.xp = 0;384 self.maxXP = 0;385 self.xpLevel = 0;386 self.mana = 200;387 self.maxMana = 200;388 self.lastManaUse = 0;389 self.lastManaRegen = 0;390 self.effectTimers = {};391 self.alive = true;392 self.invincible = false;393 self.bcDeath = false;394 self.canMove = true;395 self.teleporting = false;396 self.name = 'empty Rig';397 self.lastAttack = 0;398 self.region = {399 name: 'The Wilderness',400 noattack: false,401 nomonster: false402 };403 self.ai = {404 entityTarget: null,405 posTarget: {406 x: null,407 y: null408 },409 charge: {410 x: 0,411 y: 0,412 time: 0413 },414 idleMove: 'none',415 idleRandom: {416 walking: false,417 waitTime: 4,418 lastPathEnd: 0419 },420 idleWaypoints: {421 walking: false,422 lastWaypoints: [],423 waypoints: [],424 pos: {425 x: null,426 y: null427 },428 waitTime: 20,429 lastPathEnd: 0430 },431 frozen: false,432 path: [],433 pathfinder: new PF.JumpPointFinder(PF.JPFMoveDiagonallyIfNoObstacles),434 grid: new PF.Grid(),435 lastPath: 0,436 maxRange: 100437 };438 self.aiControlled = true;439 self.characterStyle = {440 hair: 1,441 hairColor: '#000000',442 bodyColor: '#FFF0B4',443 shirtColor: '#FF3232',444 pantsColor: '#6464FF',445 texture: null446 };447 self.collisionBoxSize = Math.max(self.width, self.height);448 self.update = function update() {449 self.updatePos();450 self.lastAutoHeal++;451 self.lastHeal++;452 if (self.stats.heal != 0 && self.lastAutoHeal >= self.stats.heal && self.hp < self.maxHP && self.alive) {453 self.hp = Math.min(self.hp+1, self.maxHP);454 self.lastAutoHeal = 0;455 }456 if (self.controls.heal && self.hp < self.maxHP && self.lastHeal >= seconds(0.5) && self.mana >= 10) {457 var oldhp = self.hp;458 self.lastHeal = 0;459 self.hp = Math.min(self.hp+20, self.maxHP);460 self.mana -= 10;461 self.lastManaUse = 0;462 new Particle(self.map, self.x, self.y, 'heal', '+' + self.hp-oldhp);463 }464 self.lastManaRegen++;465 self.lastManaUse++;466 if (self.lastManaUse >= seconds(1.5) && self.alive) {467 self.mana = Math.min(self.mana+1, self.maxMana);468 self.lastManaRegen = 0;469 } else if (self.lastManaRegen >= seconds(0.5) && self.alive) {470 self.mana = Math.min(self.mana+1, self.maxMana);471 self.lastManaRegen = 0;472 }473 for (let i in self.invincibilityFrames) {474 self.invincibilityFrames[i]--;475 self.invincibilityFrames[i] < 1 && delete self.invincibilityFrames[i];476 }477 self.updateAnimation();478 };479 self.updatePos = function updatePos() {480 if (self.aiControlled) {481 self.ai.lastPath++;482 if (self.ai.lastPath >= seconds(0.5)) {483 self.ai.lastPath = 0;484 if (self.ai.entityTarget) self.ai.pathtoEntity();485 else if (self.ai.posTarget.x) self.ai.pathtoPos();486 else if (self.ai.idleMove != 'none') self.ai.pathIdle();487 else self.ai.path = [];488 }489 } else {490 if (self.slowedDown || self.shield) self.moveSpeed *= 0.5;491 self.controls.x = self.controls.xaxis;492 self.controls.y = self.controls.yaxis;493 if (self.controls.up) self.controls.y = Math.max(-1, Math.min(self.controls.y-1, 1));494 if (self.controls.down) self.controls.y = Math.max(-1, Math.min(self.controls.y+1, 1));495 if (self.controls.left) self.controls.x = Math.max(-1, Math.min(self.controls.x-1, 1));496 if (self.controls.right) self.controls.x = Math.max(-1, Math.min(self.controls.x+1, 1));497 self.xmove = self.controls.x*self.moveSpeed;498 self.ymove = self.controls.y*self.moveSpeed;499 if (self.slowedDown || self.shield) self.moveSpeed *= 2;500 }501 if (!self.canMove) {502 self.xmove = 0;503 self.ymove = 0;504 }505 self.xspeed = Math.round(self.xmove+self.xknockback);506 self.yspeed = Math.round(self.ymove+self.yknockback);507 self.collide();508 self.updateChunkLocation();509 self.xknockback *= 0.25;510 self.yknockback *= 0.25;511 if (Math.abs(self.xknockback) < 0.5) self.xknockback = 0;512 if (Math.abs(self.yknockback) < 0.5) self.yknockback = 0;513 if (self.ai.chargeTime != -1) {514 self.ai.charge.time -= 1;515 if (self.ai.charge.time < 0) {516 self.ai.charge.time = 0;517 self.ai.charge.x *= 0.5;518 self.ai.charge.y *= 0.5;519 if (Math.abs(self.ai.charge.x) < 0.5 || Math.abs(self.ai.charge.y) < 0.5) {520 self.ai.charge.x = 0;521 self.ai.charge.y = 0;522 self.ai.charge.time = -1;523 }524 }525 }526 self.animationDirection = 'facing';527 if (self.controls.x || self.controls.y) {528 var dir = Math.round(radians(Math.atan2(self.controls.y, self.controls.x))/45);529 if (dir <= 0) dir = 8-Math.abs(dir);530 if (dir == 8) dir = 0;531 switch (dir) {532 case 0:533 self.animationDirection = 'right';534 break;535 case 1:536 self.animationDirection = 'downright';537 break;538 case 2:539 self.animationDirection = 'down';540 break;541 case 3:542 self.animationDirection = 'downleft';543 break;544 case 4:545 self.animationDirection = 'left';546 break;547 case 5:548 self.animationDirection = 'upleft';549 break;550 case 6:551 self.animationDirection = 'up';552 break;553 case 7:554 self.animationDirection = 'upright';555 break;556 }557 }558 if (self.animationDirection != 'facing') self.facingDirection = self.animationDirection;559 var foundregion = false;560 if (Region.grid[self.map][self.gridy] && Region.grid[self.map][self.gridy][self.gridx] && Region.grid[self.map][self.gridy][self.gridx].name != self.region.name) {561 self.region = Region.grid[self.map][self.gridy][self.gridx];562 self.onRegionChange();563 }564 if (Region.grid[self.map][self.gridy] && Region.grid[self.map][self.gridy][self.gridx]) foundregion = true;565 if (!foundregion && self.region.name != 'The Wilderness') {566 self.region = {567 name: 'The Wilderness',568 noattack: false,569 nomonster: false570 };571 self.onRegionChange();572 }573 if (Teleporter.grid[self.map][self.gridy] && Teleporter.grid[self.map][self.gridy][self.gridx] && Teleporter.grid[self.map][self.gridy][self.gridx]) {574 if (self.yspeed != 00 || self.xspeed != 0) {575 var direction = Teleporter.grid[self.map][self.gridy][self.gridx].direction;576 if ((direction == 'up' && self.yspeed < 0) || (direction == 'down' && self.yspeed > 0) || (direction == 'left' && self.xspeed < 0) || (direction == 'right' && self.xspeed > 0)) {577 self.teleport(Teleporter.grid[self.map][self.gridy][self.gridx].map, Teleporter.grid[self.map][self.gridy][self.gridx].x, Teleporter.grid[self.map][self.gridy][self.gridx].y, Teleporter.grid[self.map][self.gridy][self.gridx].layer);578 }579 }580 }581 };582 self.collide = function collide() {583 try {584 if (!self.frozen && (self.xspeed != 0 || self.yspeed != 0 || self.aiControlled)) {585 self.aiControlled && self.aiControl();586 var max = Math.max(Math.abs(self.xspeed), Math.abs(self.yspeed))/(self.physicsInaccuracy*ENV.physicsInaccuracy);587 for (let i = 0; i < max; i += max/Math.ceil(max)) {588 if (self.aiControlled && self.aiControl()) max = Math.max(Math.abs(self.xspeed), Math.abs(self.yspeed))/(self.physicsInaccuracy*ENV.physicsInaccuracy);589 self.lastx = self.x;590 self.lasty = self.y;591 self.x += self.xspeed/max || 0;592 self.y += self.yspeed/max || 0;593 self.gridx = Math.floor(self.x/64);594 self.gridy = Math.floor(self.y/64);595 !self.noCollision && self.doPointCollision();596 self.checkLayer();597 self.checkSlowdown();598 }599 if (Collision.grid[self.map]) {600 if (self.x-self.width/2 < Collision.grid[self.map].offsetX*64) self.x = Collision.grid[self.map].offsetX*64+self.width/2;601 if (self.x+self.width/2 > Collision.grid[self.map].offsetX*64+Collision.grid[self.map].width*64) self.x = Collision.grid[self.map].offsetX*64+Collision.grid[self.map].width*64-self.width/2;602 if (self.y-self.height/2 < Collision.grid[self.map].offsetY*64) self.y = Collision.grid[self.map].offsetY*64+self.height/2;603 if (self.y+self.height/2 > Collision.grid[self.map].offsetY*64+Collision.grid[self.map].height*64) self.y = Collision.grid[self.map].offsetY*64+Collision.grid[self.map].height*64-self.height/2;604 }605 self.x = Math.round(self.x);606 self.y = Math.round(self.y);607 self.gridx = Math.floor(self.x/64);608 self.gridy = Math.floor(self.y/64);609 self.chunkx = Math.floor(self.gridx/Collision.grid[self.map].chunkWidth);610 self.chunky = Math.floor(self.gridy/Collision.grid[self.map].chunkHeight);611 }612 } catch (err) {613 error(err);614 }615 };616 self.aiControl = function aiControl() {617 var oldcontrols = cloneDeep(self.controls);618 self.controls = {619 up: false,620 down: false,621 left: false,622 right: false,623 xaxis: 0,624 yaxis: 0,625 x: 0,626 y: 0,627 heal: false628 };629 self.xmove = 0;630 self.ymove = 0;631 if (!self.ai.frozen && self.canMove) {632 if (self.ai.charge.time != -1) {633 self.controls.x = self.ai.charge.x;634 self.controls.y = self.ai.charge.y;635 } else if (self.ai.path[0]) {636 var angle = Math.atan2(self.ai.path[0][1]*64+32-self.y, self.ai.path[0][0]*64+32-self.x);637 self.controls.xaxis = Math.cos(angle);638 self.controls.yaxis = Math.sin(angle);639 // if (self.ai.path[0][0]*64+32 < self.x) self.controls.left = true;640 // else if (self.ai.path[0][0]*64+32 > self.x) self.controls.right = true;641 // if (self.ai.path[0][1]*64+32 < self.y) self.controls.up = true;642 // else if (self.ai.path[0][1]*64+32 > self.y) self.controls.down = true;643 if (Math.round(self.x) == self.ai.path[0][0]*64+32 && Math.round(self.y) == self.ai.path[0][1]*64+32) {644 self.ai.path.shift();645 }646 if (self.slowedDown) self.moveSpeed *= 0.5;647 self.controls.x = self.controls.xaxis;648 self.controls.y = self.controls.yaxis;649 // if (self.controls.up) self.controls.y = Math.max(-1, Math.min(self.controls.y-1, 1));650 // if (self.controls.down) self.controls.y = Math.max(-1, Math.min(self.controls.y+1, 1));651 // if (self.controls.left) self.controls.x = Math.max(-1, Math.min(self.controls.x-1, 1));652 // if (self.controls.right) self.controls.x = Math.max(-1, Math.min(self.controls.x+1, 1));653 }654 self.xmove = Math.round(self.controls.x*self.moveSpeed);655 self.ymove = Math.round(self.controls.y*self.moveSpeed);656 if (self.slowedDown) self.moveSpeed *= 2;657 self.xspeed = Math.round(self.xmove+self.xknockback);658 self.yspeed = Math.round(self.ymove+self.yknockback);659 }660 for (let i in self.controls) {661 if (self.controls[i] != oldcontrols[i]) return true;662 }663 return false;664 };665 self.updateAnimation = function updateAnimation() {666 self.lastFrameUpdate++;667 if (self.animationDirection == 'none') {668 self.animationStage = 0;669 } else if (self.animationDirection == 'loop') {670 if (self.lastFrameUpdate >= seconds(self.animationSpeed/1000)) {671 self.lastFrameUpdate = 0;672 self.animationStage++;673 if (self.animationStage > self.animationLength) self.animationStage = 0;674 }675 } else if (self.animationDirection == 'facing') {676 switch (self.facingDirection) {677 case 'up':678 self.animationStage = 24;679 break;680 case 'down':681 self.animationStage = 0;682 break;683 case 'left':684 self.animationStage = 36;685 break;686 case 'right':687 self.animationStage = 12;688 break;689 case 'upleft':690 self.animationStage = 30;691 break;692 case 'downleft':693 self.animationStage = 42;694 break;695 case 'upright':696 self.animationStage = 18;697 break;698 case 'downright':699 self.animationStage = 6;700 break;701 default:702 error('Invalid facingDirection ' + self.facingDirection);703 break;704 }705 } else {706 if (self.lastFrameUpdate >= seconds(self.animationSpeed/1000)) {707 self.lastFrameUpdate = 0;708 self.animationStage++;709 switch (self.animationDirection) {710 case 'up':711 if (self.animationStage < 24) self.animationStage = 24;712 if (self.animationStage > 29) self.animationStage = 24;713 break;714 case 'down':715 if (self.animationStage < 0) self.animationStage = 0;716 if (self.animationStage > 5) self.animationStage = 0;717 break;718 case 'left':;719 if (self.animationStage < 36) self.animationStage = 36;720 if (self.animationStage > 41) self.animationStage = 36;721 break;722 case 'right':723 if (self.animationStage < 12) self.animationStage = 12;724 if (self.animationStage > 17) self.animationStage = 12;725 break;726 case 'upleft':727 if (self.animationStage < 30) self.animationStage = 30;728 if (self.animationStage > 35) self.animationStage = 30;729 break;730 case 'downleft':731 if (self.animationStage < 42) self.animationStage = 42;732 if (self.animationStage > 47) self.animationStage = 42;733 break;734 case 'upright':735 if (self.animationStage < 18) self.animationStage = 18;736 if (self.animationStage > 23) self.animationStage = 18;737 break;738 case 'downright':739 if (self.animationStage < 6) self.animationStage = 6;740 if (self.animationStage > 11) self.animationStage = 6;741 break;742 default:743 error('Invalid animationDirection ' + self.animationDirection);744 break;745 }746 }747 }748 };749 self.ai.pathtoEntity = function pathtoEntity() {750 if (self.ai.entityTarget) {751 self.ai.path = [];752 try {753 self.ai.posTarget = {754 x: self.ai.entityTarget.gridx,755 y: self.ai.entityTarget.gridy756 };757 self.ai.pathtoPos();758 } catch (err) {759 error(err);760 }761 }762 return self.ai.path;763 };764 self.ai.pathtoPos = function pathtoPos() {765 if (self.ai.posTarget) {766 self.ai.path = [];767 try {768 if (self.getSquareGridDistance({x: self.ai.posTarget.x, y: self.ai.posTarget.y}) < ENV.maxPathfindRange) {769 self.ai.path = self.ai.pathtoTarget(self.ai.posTarget.x, self.ai.posTarget.y);770 if (self.ai.path[0] == null) {771 // const graph = cloneDeep(Layer.graph[self.map]);772 // // add node for end point773 // var openlist = [];774 // openlist.push({775 // x: self.gridx,776 // y: self.gridy,777 // layer: self.layer,778 // dir: 0,779 // parent: null,780 // connections: [781 // {782 // x: Layer.lookupTable[self.map][self.layer][self.gridy][self.gridx].x,783 // y: Layer.lookupTable[self.map][self.layer][self.gridy][self.gridx].y,784 // distance: Layer.lookupTable[self.map][self.layer][self.gridy][self.gridx].distance785 // }786 // ],787 // f: 1,788 // g: 0,789 // h: 0790 // });791 // var curr;792 // var lastcurr = null;793 // while (openlist.length) {794 // curr = openlist[0];795 // for (let i in openlist) {796 // if (openlist[i].f < curr.f) curr = openlist[i];797 // }798 // curr.closed = true;799 // openlist.splice(openlist.indexOf(curr), 1);800 801 // for (let i in curr.connections) {802 // errors for some reason803 // var neighbor = graph[curr.layer+curr.dir][curr.connections[i].y][curr.connections[i].x];804 // if (neighbor.closed) continue;805 // if (!neighbor.visited || curr.g+curr.connections[i].distance < neighbor.g) {806 // neighbor.visited = true;807 // neighbor.parent = curr;808 // neighbor.g = curr.g+curr.connections[i].distance;809 // neighbor.h = Math.abs(neighbor.x-curr.x)+Math.abs(neighbor.y-curr.y);810 // neighbor.f = neighbor.g+neighbor.h;811 // }812 // openlist.push(neighbor);813 // }814 815 // lastcurr = curr;816 // }817 }818 }819 } catch (err) {820 error(err);821 }822 }823 return self.ai.path;824 };825 self.ai.pathtoTarget = function pathtoTarget(x, y) {826 if (typeof x == 'number' && typeof y == 'number') {827 try {828 var retpath = [];829 if (self.getSquareGridDistance({x: x, y: y}) < ENV.maxPathfindRange) {830 var left = Math.min(self.gridx-ENV.pathfindBuffer, x-ENV.pathfindBuffer);831 var right = Math.max(self.gridx+ENV.pathfindBuffer, x+ENV.pathfindBuffer);832 var top = Math.min(self.gridy-ENV.pathfindBuffer, y-ENV.pathfindBuffer);833 var bottom = Math.max(self.gridy+ENV.pathfindBuffer, y+ENV.pathfindBuffer);834 self.ai.grid = new PF.Grid(right-left, bottom-top);835 if (Collision.grid[self.map] && Collision.grid[self.map][self.layer]) {836 for (let writey = 0; writey < bottom-top; writey++) {837 for (let writex = 0; writex < right-left; writex++) {838 var checkx = writex+left;839 var checky = writey+top;840 Collision.grid[self.map][self.layer][checky] && Collision.grid[self.map][self.layer][checky][checkx] && self.ai.grid.setWalkableAt(writex, writey, false);841 }842 }843 }844 var path = self.ai.pathfinder.findPath(self.gridx-left, self.gridy-top, x-left, y-top, self.ai.grid);845 path.shift();846 retpath = PF.Util.compressPath(path);847 for (let i in retpath) {848 retpath[i][0] += left;849 retpath[i][1] += top;850 }851 }852 self.ai.path = retpath;853 return retpath;854 } catch (err) {855 error(err);856 }857 }858 return [];859 };860 self.ai.pathIdle = function pathIdle() {861 // self.ai.path = [];862 if (self.ai.idleMove == 'waypoints') {863 try {864 if (self.ai.idleWaypoints.lastPathEnd >= seconds(self.ai.idleWaypoints.waitTime)*Math.random()) {865 self.ai.idleWaypoints.lastPathEnd = 0;866 var waypoints = Array.from(self.ai.idleWaypoints.waypoints);867 var lastWaypoints = self.ai.idleWaypoints.lastWaypoints;868 if (waypoints) {869 for (let i in waypoints) {870 var waypoint = waypoints[i];871 if (waypoint.map != self.map) delete waypoints[i];872 if (waypoint.x == self.gridx && waypoint.y == self.gridy && waypoint.map == self.map) {873 var alreadyExists = false;874 for (let j in lastWaypoints) {875 if (waypoint.x == lastWaypoints[j].x && waypoint.y == lastWaypoints[j].y && waypoint.map == lastWaypoints[j].map) alreadyExists = true;876 }877 if (!alreadyExists) self.ai.idleWaypoints.lastWaypoints.unshift(waypoint);878 }879 }880 var waypointCount = 0;881 for (let i in waypoints) {882 if (waypoints[i].map == self.map) waypointCount++;883 }884 if (lastWaypoints.length > Math.min(4, waypointCount-1)) lastWaypoints.pop();885 for (let i in waypoints) {886 var waypoint = waypoints[i];887 for (let j in lastWaypoints) {888 if (waypoint.x == lastWaypoints[j].x && waypoint.y == lastWaypoints[j].y && waypoint.map == lastWaypoints[j].map) delete waypoints[i];889 }890 }891 var lowest;892 for (let i in waypoints) {893 if (lowest == null) lowest = i;894 if (lowest && self.getGridDistance(waypoints[i]) < self.getGridDistance(waypoints[lowest])) {895 lowest = i;896 }897 }898 if (lowest) {899 self.ai.posTarget.x = waypoints[lowest].x;900 self.ai.posTarget.y = waypoints[lowest].y;901 self.ai.idleWaypoints.pos = waypoints[lowest];902 self.ai.idleWaypoints.lastWaypoints.unshift(self.ai.idleWaypoints.pos);903 self.ai.pathtoPos();904 self.ai.idleWaypoints.walking = true;905 self.ai.posTarget = {906 x: null,907 y: null908 };909 }910 }911 }912 if (self.x == self.ai.idleWaypoints.pos.x*64+32 && self.y == self.ai.idleWaypoints.pos.y*64+32) self.ai.idleWaypoints.walking = false;913 if (!self.ai.idleWaypoints.walking) {914 self.ai.idleWaypoints.lastPathEnd += seconds(0.1);915 self.ai.path = [];916 }917 } catch (err) {918 error(err);919 }920 } else if (self.ai.idleMove == 'random') {921 try {922 if (self.ai.idleRandom.lastPathEnd >= seconds(self.ai.idleRandom.waitTime)*Math.random()) {923 self.ai.idleRandom.lastPathEnd = 0;924 var pathAttempts = 0;925 while (true) {926 pathAttempts++;927 var attempts = 0;928 var pos = {929 x: 0,930 y: 0931 };932 while (true) {933 attempts++;934 pos.x = Math.round(self.gridx+Math.random()*2-1);935 pos.y = Math.round(self.gridy+Math.random()*2-1);936 if (Collision.grid[self.map][self.layer][pos.y] && Collision.grid[self.map][self.layer][pos.y][pos.x]) {}937 else break;938 if (attempts >= 10) break;939 }940 self.ai.posTarget.x = pos.x;941 self.ai.posTarget.y = pos.y;942 self.ai.idleWaypoints.pos = self.ai.posTarget;943 self.ai.pathtoPos();944 self.ai.idleWaypoints.walking = true;945 self.ai.posTarget = {946 x: null,947 y: null948 };949 if (self.ai.path != []) break;950 if (pathAttempts >= 10) break;951 }952 if (self.ai.path[0]) {953 self.ai.idleRandom.walking = true;954 }955 }956 if (self.gridx == self.ai.idleWaypoints.pos.x && self.gridy == self.ai.idleWaypoints.pos.y) self.ai.idleRandom.walking = false;957 if (!self.ai.idleRandom.walking) {958 self.ai.idleRandom.lastPathEnd += seconds(0.1);959 self.ai.path = [];960 }961 } catch (err) {962 error(err);963 }964 }965 return self.ai.path;966 };967 self.onHit = function onHit(entity, type) {968 let oldhp = self.hp;969 let critHp = 0;970 let parent = Monster.list[entity.parentID] ?? Player.list[entity.parentID] ?? entity;971 if (entity.critChance && Math.random() < entity.critChance) critHp = entity.damage*entity.critPower;972 let spawnParticles = true;973 let ret = false;974 if (!self.invincible) {975 switch (type) {976 case 'projectile':977 var reflected = false;978 var blocked = self.shield && Math.abs(self.shieldAngle-Math.atan2(-entity.yspeed, -entity.xspeed)) < degrees(self.shieldStats.blockAngle/2);979 if (self.invincibilityFrames[entity.type] == null && !blocked) {980 self.hp -= Math.max(Math.round(((entity.damage+critHp)*(1-self.stats.defense))-self.stats.damageReduction), 0);981 self.invincibilityFrames[entity.type] = entity.invincibilityFrame+1;982 } else {983 spawnParticles = false;984 if (entity.reflectable && Math.random() < self.shieldStats.reflectProjectileChance) {985 reflected = true;986 ret = true;987 }988 // console.log(radians(self.shieldAngle-Math.atan2(-entity.yspeed, -entity.xspeed)+self.shieldAngle));989 }990 var rand = 0.5+Math.random();991 var multiplier = entity.knockback*rand*Math.max(0, 1-self.stats.knockbackResistance-(blocked?self.shieldStats.knockbackResistance:0));992 self.xknockback += entity.xspeed*multiplier;993 self.yknockback += entity.yspeed*multiplier;994 for (let event of entity.contactEvents) {995 if (Projectile.contactEvents[event.type]) {996 Projectile.contactEvents[event.type](entity, self, event.data);997 }998 }999 if (reflected) {1000 entity.parentID = self.id;1001 entity.angle = self.shieldAngle-Math.atan2(-entity.yspeed, -entity.xspeed)+self.shieldAngle;1002 entity.travelTime = ticks(entity.maxRange)-30;1003 entity.sinAngle = Math.sin(entity.angle);1004 entity.cosAngle = Math.cos(entity.angle);1005 entity.xspeed = entity.cosAngle*entity.moveSpeed;1006 entity.yspeed = entity.sinAngle*entity.moveSpeed;1007 entity.collisionBoxSize = Math.max(Math.abs(entity.sinAngle*entity.height)+Math.abs(entity.cosAngle*entity.width), Math.abs(entity.cosAngle*entity.height)+Math.abs(entity.sinAngle*entity.width));1008 entity.deathMessage += ' by ' + parent.name + '\'s ' + entity.type + ' reflected';1009 }1010 if (self.hp < 0) self.onDeath(parent, 'killed', entity.deathMessage);1011 break;1012 case 'touch':1013 var blocked = self.shield && Math.abs(self.shieldAngle-Math.atan2(self.y-entity.lasty, self.x-entity.lastx)) < degrees(self.shieldStats.blockAngle);1014 if (self.invincibilityFrames['touch'] == null && !blocked) {1015 self.hp -= Math.max(Math.round(Math.floor((Math.random()*(entity.touchDamage/2))+(entity.touchDamage/2)+1)*(1-self.stats.defense))-self.stats.damageReduction, 0);1016 self.invincibilityFrames['touch'] = 2;1017 } else spawnParticles = false;1018 var rand = 0.5+Math.random();1019 var angle = Math.atan2(self.y-entity.y-entity.yspeed, self.x-entity.x-entity.xspeed);1020 var multiplier = Math.max(0, 1-self.stats.knockbackResistance-(blocked?self.shieldStats.knockbackResistance:0));1021 self.xknockback += (Math.cos(angle)*rand*10+entity.xspeed*rand)*multiplier;1022 self.yknockback += (Math.sin(angle)*rand*10+entity.yspeed*rand)*multiplier;1023 if (self.hp < 0) self.onDeath(entity, 'killed');1024 break;1025 case 'explosion':1026 var blocked = self.shield && Math.abs(self.shieldAngle-Math.atan2(self.y-entity.lasty, self.x-entity.lastx)) < degrees(self.shieldStats.blockAngle);1027 if (!blocked) self.hp -= Math.max(Math.round((40*entity.explosionSize*(1/(self.getDistance(entity)/64*entity.explosionSize))*(1-self.stats.defense))-self.stats.damageReduction), 0);1028 var rand = 0.5+Math.random();1029 var angle = Math.atan2(self.y-entity.y-entity.yspeed, self.x-entity.x-entity.xspeed);1030 self.xknockback += Math.cos(angle)*rand*10*entity.explosionSize*(1-self.stats.knockbackResistance);1031 self.yknockback += Math.sin(angle)*rand*10*entity.explosionSize*(1-self.stats.knockbackResistance);1032 if (self.hp < 0) self.onDeath(parent, 'explosion');1033 break;1034 default:1035 error('Invalid Entity type: ' + type);1036 break;1037 }1038 }1039 spawnParticles && critHp && new Particle(self.map, self.x, self.y, 'critdamage', self.hp-oldhp);1040 spawnParticles && !critHp && new Particle(self.map, self.x, self.y, 'damage', self.hp-oldhp);1041 if (parent && parent.entType == 'player') parent.trackedData.damageDealt += oldhp-self.hp;1042 return ret;1043 };1044 self.onDeath = function onDeath(entity, type, message) {1045 if (!self.invincible) {1046 let oldhp = self.hp;1047 self.hp = 0;1048 self.alive = false;1049 if (entity && entity.entType == 'player') {1050 entity.trackedData.kills++;1051 }1052 for (let i = 0; i < self.width*self.height/200; i++) {1053 new Particle(self.map, self.x+Math.random()*self.width-self.width/2, self.y+Math.random()*self.height-self.height/2, 'death');1054 }1055 if (self.bcDeath) switch (type) {1056 case 'killed':1057 if (entity) insertChat(self.name + ' was ' + (message ?? 'killed') + ' by ' + entity.name, 'death');1058 else insertChat(self.name + ' was ' + (message ?? 'killed'), 'death');1059 break;1060 case 'explosion':1061 if (entity) insertChat(self.name + ' was blown up by ' + entity.name, 'death');1062 else insertChat(self.name + ' blew up', 'death');1063 break;1064 case 'fire':1065 insertChat(self.name + ' caught fire', 'death');1066 break;1067 case 'debug':1068 insertChat(self.name + ' was debugged', 'death');1069 break;1070 default:1071 insertChat(self.name + ' died', 'death');1072 break;1073 }1074 if (entity && entity.entType == 'player') entity.trackedData.damageDealt += oldhp-self.hp;1075 }1076 };1077 self.onRegionChange = function onRegionChange() {};1078 self.teleport = function teleport(map, x, y, layer) {1079 if (!self.teleporting) {1080 self.teleporting = true;1081 for (let i = 0; i < 20; i++) {1082 new Particle(self.map, self.x, self.y, 'teleport');1083 }1084 self.map = map;1085 self.x = x*64+32;1086 self.y = y*64+32;1087 self.layer = layer;1088 self.ai.path = [];1089 self.ai.entityTarget = null;1090 self.ai.posTarget = {1091 x: null,1092 y: null1093 };1094 self.ai.idleRandom.walking = false;1095 self.ai.idleWaypoints.walking = false;1096 self.ai.idleWaypoints.lastWaypoints = [];1097 for (let i = 0; i < 20; i++) {1098 new Particle(self.map, self.x, self.y, 'teleport');1099 }1100 self.teleporting = false;1101 }1102 };1103 return self;1104};1105// npcs1106Npc = function(id, x, y, map) {1107 var self = new Rig();1108 self.entType = 'npc';1109 self.animationSpeed = 100;1110 self.animationDirection = 'facing';1111 self.facingDirection = 'down';1112 self.npcId = id;1113 self.invincible = true;1114 self.name = 'Npc';1115 self.stats = {1116 damageType: null,1117 projectileSpeed: 1,1118 attack: 1,1119 defense: 0,1120 damageReduction: 0,1121 knockbackResistance: 0,1122 heal: 0,1123 speed: 1,1124 range: 1,1125 critChance: 0,1126 critPower: 1,1127 knockback: 01128 };1129 self.moveSpeed = 5;1130 try {1131 var tempnpc = Npc.rawJson[id];1132 self.x = x*64+32;1133 self.y = y*64+32;1134 self.map = map;1135 self.gridx = Math.floor(self.x/64);1136 self.gridy = Math.floor(self.y/64);1137 self.chunkx = Math.floor(self.gridx/Collision.grid[self.map].chunkWidth);1138 self.chunky = Math.floor(self.gridy/Collision.grid[self.map].chunkHeight);1139 switch (tempnpc.type) {1140 case 'static':1141 self.moveSpeed = 0;1142 break;1143 case 'waypoint':1144 self.ai.idleMove = 'waypoints';1145 break;1146 case 'random':1147 self.ai.idleMove = 'random';1148 break;1149 default:1150 error('Invalid npc type ' + tempnpc.type);1151 break;1152 }1153 self.rightClickEvent = new Function('return ' + tempnpc.rightClickEvent)();1154 for (let i in tempnpc.data) {1155 self[i] = tempnpc.data[i];1156 }1157 if (tempnpc.width) self.width = tempnpc.width;1158 if (tempnpc.height) self.height = tempnpc.height;1159 tempnpc = null;1160 } catch (err) {1161 error(err);1162 return false;1163 }1164 self.conversationCount = 0;1165 self.shopCount = 0;1166 self.aiControlled = true;1167 self.collisionBoxSize = Math.max(self.width, self.height);1168 self.update = function update() {1169 self.updatePos();1170 self.animationSpeed = 15/Math.sqrt(Math.pow(self.xmove, 2)+Math.pow(self.ymove, 2))*100 || 100;1171 self.updateAnimation();1172 };1173 self.startConversation = function startConversation(player, id) {1174 if (!player.talking) {1175 self.ai.frozen = true;1176 self.conversationCount++;1177 player.prompt(id, self.id);1178 }1179 };1180 self.endConversation = function endConversation() {1181 self.conversationCount--;1182 if (self.conversationCount <= 0 && self.shopCount <= 0) self.ai.frozen = false;1183 };1184 self.openShop = function openShop(id, player) {1185 if (!player.inShop) {1186 self.ai.frozen = true;1187 self.shopCount++;1188 new Shop(id, player.socket, player.inventory, player);1189 }1190 };1191 self.closeShop = function closeShop() {1192 self.shopCount--;1193 if (self.conversationCount <= 0 && self.shopCount <= 0) self.ai.frozen = false;1194 };1195 self.onDeath = function onDeath() {};1196 Npc.list[self.id] = self;1197 return self;1198};1199Npc.update = function update() {1200 var pack = [];1201 for (let i in Npc.list) {1202 localnpc = Npc.list[i];1203 localnpc.update();1204 pack.push({1205 id: localnpc.id,1206 map: localnpc.map,1207 x: localnpc.x,1208 y: localnpc.y,1209 layer: localnpc.layer,1210 name: localnpc.name,1211 npcId: localnpc.npcId,1212 animationStage: localnpc.animationStage,1213 characterStyle: localnpc.characterStyle,1214 isNPC: true1215 });1216 }1217 1218 return pack;1219};1220Npc.getDebugData = function getDebugData() {1221 var pack = [];1222 for (let i in Npc.list) {1223 var localnpc = Npc.list[i];1224 pack.push({1225 map: localnpc.map,1226 x: localnpc.x,1227 y: localnpc.y,1228 width: localnpc.width,1229 height: localnpc.height,1230 name: localnpc.name,1231 collisionBoxSize: localnpc.collisionBoxSize,1232 path: localnpc.ai.path,1233 idleWaypoints: localnpc.ai.idleWaypoints,1234 controls: localnpc.controls,1235 });1236 }1237 return pack;1238};1239Npc.init = function init() {1240 for (let i in Npc.dialogues) {1241 for (let j in Npc.dialogues[i]) {1242 for (let k in Npc.dialogues[i][j].options) {1243 if (Npc.dialogues[i][j].options[k].action.includes('script_')) {1244 Npc.dialogues[i][j].options[k].script = new Function('return ' + Npc.dialogues[i][j].options[k].action.replace('script_', ''))();1245 } else if (Npc.dialogues[i][j].options[k].action.includes('script-end_')) {1246 Npc.dialogues[i][j].options[k].script = new Function('return ' + Npc.dialogues[i][j].options[k].action.replace('script-end_', ''))();1247 }1248 }1249 }1250 }1251};1252Npc.rawJson = require('./npc.json');1253Npc.dialogues = require('./../client/prompts.json');1254Npc.list = [];1255// players1256Player = function(socket) {1257 var self = new Rig();1258 self.entType = 'player';1259 self.socket = socket;1260 self.ip = socket.handshake.headers['x-forwarded-for'];1261 self.fingerprint = {fpjs: Math.random(), webgl: Math.random()};1262 self.map = ENV.spawnpoint.map;1263 self.x = ENV.spawnpoint.x;1264 self.y = ENV.spawnpoint.y;1265 self.layer = ENV.spawnpoint.layer;1266 self.gridx = Math.floor(self.x/64);1267 self.gridy = Math.floor(self.y/64);1268 self.chunkx = Math.floor(self.gridx/Collision.grid[self.map].chunkWidth);1269 self.chunky = Math.floor(self.gridy/Collision.grid[self.map].chunkHeight);1270 self.animationSpeed = 100;1271 self.animationDirection = 'facing';1272 self.facingDirection = 'down';1273 self.attacking = false;1274 self.disableSecond = false;1275 self.lastHeal = 0;1276 self.stats.heal = 8;1277 self.stats.maxPierce = 0;1278 self.crystalStats = {1279 damageType: null,1280 projectileSpeed: 1,1281 attack: 1,1282 speed: 1,1283 range: 1,1284 critChance: 0,1285 critPower: 1,1286 knockback: 0,1287 maxPierce: 01288 };1289 self.mouseX = 0;1290 self.mouseY = 0;1291 self.name = null;1292 self.aiControlled = false;1293 self.inventory = new Inventory(socket, self);1294 self.attack = {1295 projectile: null,1296 projectilePattern: 'single',1297 useTime: 0,1298 manaCost: 0,1299 lastUse: 0,1300 second: false1301 };1302 self.crystal = {1303 projectile: null,1304 projectilePattern: 'single',1305 useTime: 0,1306 manaCost: 0,1307 lastUse: 0,1308 second: true1309 };1310 self.heldItem = {1311 id: null,1312 shield: null,1313 usingShield: false,1314 angle: 01315 };1316 self.teleportLocation = {1317 map: 'World',1318 x: 0,1319 y: 0,1320 layer: 01321 };1322 self.invincible = true;1323 self.bcDeath = true;1324 self.canMove = false;1325 self.talking = false;1326 self.inShop = false;1327 self.currentConversation = {1328 id: null,1329 i: 01330 };1331 self.talkingWith = null;1332 self.talkedWith = null;1333 self.spectating = null;1334 self.quests = new QuestHandler(socket, self);1335 self.trackedData = {1336 monstersKilled: [],1337 kills: 0,1338 deaths: 0,1339 damageDealt: 0,1340 damageTaken: 0,1341 dps: 0,1342 maxDPS: 0,1343 bossesSlain: 0,1344 events: [],1345 last: {},1346 updateTrackers: function() {1347 var delta = {1348 monstersKilled: [],1349 playerKills: 0,1350 kills: self.trackedData.kills-self.trackedData.last.kills,1351 deaths: self.trackedData.deaths-self.trackedData.last.deaths,1352 damageDealt: self.trackedData.damageDealt-self.trackedData.last.damageDealt,1353 damageTaken: self.trackedData.damageTaken-self.trackedData.last.damageTaken,1354 dps: self.trackedData.dps,1355 maxDPS: self.trackedData.maxDPS,1356 bossesSlain: self.trackedData.bossesSlain-self.trackedData.last.bossesSlain,1357 };1358 for (let i in self.trackedData.monstersKilled) {1359 var temp = self.trackedData.monstersKilled[i];1360 var found = false;1361 for (let j in self.trackedData.last.monstersKilled) {1362 var temp2 = self.trackedData.last.monstersKilled[j];1363 if (temp.id == temp2.id) {1364 if (temp.count-temp2.count != 0) delta.monstersKilled.push({1365 id: temp.id,1366 count: temp.count-temp2.count1367 });1368 found = true;1369 }1370 }1371 if (!found) delta.monstersKilled.push({1372 id: temp.id,1373 count: temp.count1374 });1375 }1376 var data = {1377 trackedData: delta,1378 aqquiredItems: [],1379 pos: {1380 x: self.gridx,1381 y: self.gridy1382 },1383 talkedWith: self.talkedWith1384 };1385 self.quests.updateQuestRequirements(data);1386 self.talkedWith = null;1387 self.trackedData.last = {};1388 self.trackedData.last = cloneDeep(self.trackedData);1389 }1390 };1391 self.trackedData.last = cloneDeep(self.trackedData);1392 self.playTime = 0;1393 self.loginTime = null;1394 self.alive = false;1395 self.debugEnabled = false;1396 self.creds = {1397 username: null,1398 password: null1399 };1400 self.chatStyle = '';1401 self.signUpAttempts = 0;1402 const signupspamcheck = setInterval(async function() {1403 self.signUpAttempts = Math.max(self.signUpAttempts-1, 0);1404 if (!self.disconnected && self.signUpAttempts >= 1) {1405 log('User was kicked for sign up spam: IP:' + self.ip + ' FPJS Fingerprint:' + self.fingerprint.fpjs + ' WebGL Fingerprint:' + self.fingerprint.webgl);1406 await self.leave();1407 }1408 }, 10000);1409 self.signedIn = false;1410 self.collisionBoxSize = Math.max(self.width, self.height);1411 self.chunkLocation.map = 'menu';1412 self.renderDistance = 1;1413 self.toDisconnect = false;1414 self.toKick = false;1415 self.disconnected = false;1416 var maps = [];1417 for (let i in Collision.grid) {1418 maps.push(i);1419 }1420 socket.on('signIn', async function(cred) {1421 if (typeof cred == 'object' && cred != null && typeof cred.username == 'string' && typeof cred.password == 'string') {1422 if (ENV.isBetaServer && (cred.state == 'deleteAccount' || cred.state == 'signUp')) {1423 socket.emit('signInState', 'disabled');1424 return;1425 }1426 var valid = ACCOUNTS.validateCredentials(cred.username, cred.password);1427 switch (valid) {1428 case 0:1429 if (Filter.check(cred.username)) {1430 self.leave();1431 return;1432 }1433 switch (cred.state) {1434 case 'signIn':1435 if (!self.signedIn) {1436 var status = await ACCOUNTS.login(cred.username, cred.password);1437 switch (status) {1438 case 0:1439 var signedIn = false;1440 for (let i in Player.list) {1441 if (Player.list[i].creds.username == cred.username && Player.list[i].loginTime != null) {1442 signedIn = true;1443 }1444 }1445 if (!signedIn) {1446 self.creds.username = cred.username;1447 self.creds.password = cryptr.encrypt(cred.password);1448 Object.freeze(self.creds);1449 socket.emit('mapData', {maps: maps, self: self.map});1450 self.signedIn = true;1451 } else {1452 socket.emit('signInState', 'alreadySignedIn');1453 }1454 break;1455 case 1:1456 socket.emit('signInState', 'incorrectPassword');1457 break;1458 case 2:1459 socket.emit('signInState', 'noAccount');1460 break;1461 case 3:1462 socket.emit('signInState', 'banned');1463 break;1464 }1465 }1466 break;1467 case 'loaded':1468 if (cred.username == self.creds.username && cred.password == cryptr.decrypt(self.creds.password)) {1469 self.name = self.creds.username;1470 self.loginTime = Date.now();1471 await self.loadData();1472 socket.emit('signInState', 'signedIn');1473 self.updateClient();1474 insertChat(self.name + ' joined the game', 'server');1475 self.invincible = false;1476 self.canMove = true;1477 self.alive = true;1478 } else {1479 socket.emit('signInState', 'invalidSignIn');1480 }1481 break;1482 case 'signUp':1483 for (let i in Player.list) {1484 if (Player.list[i].ip == self.ip || Player.list[i].fingerprint.fpjs == self.fingerprint.fpjs || Player.list[i].fingerprint.webgl == self.fingerprint.webgl) Player.list[i].signUpAttempts++;1485 }1486 if (self.signUpAttempts > 1) {1487 log('User was kicked for sign up spam: IP:' + self.ip + ' FPJS Fingerprint:' + self.fingerprint.fpjs + ' WebGL Fingerprint:' + self.fingerprint.webgl);1488 await self.leave();1489 return;1490 }1491 var highest = 0;1492 for (let i in Player.list) {1493 if (Player.list[i].ip == self.ip) highest = Math.max(highest, Player.list[i].signUpAttempts);1494 }1495 for (let i in Player.list) {1496 if (Player.list[i].ip == self.ip) Player.list[i].signUpAttempts = highest;1497 }1498 var status = await ACCOUNTS.signup(cred.username, cred.password);1499 switch (status) {1500 case 0:1501 socket.emit('signInState', 'signedUp');1502 break;1503 case 1:1504 socket.emit('signInState', 'accountExists');1505 break;1506 case 2:1507 socket.emit('signInState', 'databaseError');1508 break;1509 case 3:1510 socket.emit('signInState', 'unavailable');1511 }1512 break;1513 case 'deleteAccount':1514 var status = await ACCOUNTS.deleteAccount(cred.username, cred.password);1515 switch (status) {1516 case 0:1517 self.name = cred.username;1518 socket.emit('signInState', 'deletedAccount');1519 break;1520 case 1:1521 socket.emit('signInState', 'incorrectPassword');1522 break;1523 case 2:1524 socket.emit('signInState', 'noAccount');1525 break;1526 case 3:1527 socket.emit('signInState', 'databaseError');1528 break;1529 }1530 break;1531 case 'changePassword':1532 if (cred.oldPassword) {1533 var status = await ACCOUNTS.changePassword(cred.username, cred.oldPassword, cred.password);1534 switch (status) {1535 case 0:1536 self.name = cred.username;1537 socket.emit('signInState', 'changedPassword');1538 break;1539 case 1:1540 socket.emit('signInState', 'incorrectPassword');1541 break;1542 case 2:1543 socket.emit('signInState', 'noAccount');1544 break;1545 case 3:1546 socket.emit('signInState', 'databaseError');1547 break;1548 }1549 } else {1550 self.kick();1551 }1552 break;1553 default:1554 error('Invalid sign in state ' + cred.state);1555 break;1556 }1557 break;1558 case 1:1559 socket.emit('signInState', 'noUsername');1560 break;1561 case 2:1562 socket.emit('signInState', 'shortUsername');1563 break;1564 case 3:1565 socket.emit('signInState', 'longUsername');1566 break;1567 case 4:1568 socket.emit('signInState', 'noPassword');1569 break;1570 case 5:1571 socket.emit('signInState', 'invalidCharacters');1572 break;1573 }1574 } else {1575 self.kick();1576 }1577 });1578 socket.on('keyPress', async function(data) {1579 if (typeof data == 'object' && data != null) {1580 if (self.alive) {1581 if (data.key == 'disableSecond') self.disableSecond = data.state;1582 else self.controls[data.key] = data.state;1583 }1584 } else {1585 self.kick();1586 }1587 });1588 socket.on('click', async function(data) {1589 if (typeof data == 'object' && data != null) {1590 if (self.alive && self.currentConversation.id == null) {1591 if (data.button == 'left') {1592 self.attacking = data.state;1593 self.mouseX = data.x;1594 self.mouseY = data.y; 1595 } else if (data.button == 'right') {1596 data.state && self.interact(data.x, data.y);1597 self.shield = data.state && !self.talking && !self.inShop;1598 self.heldItem.usingShield = data.state && self.inventory.equips.shield && !self.talking && !self.inShop;1599 self.mouseX = data.x;1600 self.mouseY = data.y; 1601 }1602 }1603 } else {1604 self.kick();1605 }1606 });1607 socket.on('mouseMove', async function(data) {1608 if (typeof data == 'object' && data != null) {1609 self.mouseX = data.x;1610 self.mouseY = data.y;1611 } else {1612 self.kick();1613 }1614 });1615 socket.on('controllerInput', async function(inputs) {1616 if (typeof inputs == 'object' && inputs != null) {1617 if (self.alive) {1618 if (Math.abs(inputs.movex) > 1 || Math.abs(inputs.movey) > 1) self.kick();1619 self.controls.xaxis = Math.round(inputs.movex*10)/10;1620 self.controls.yaxis = Math.round(inputs.movey*10)/10;1621 if (Math.abs(self.controls.xaxis) == 0.1) self.controls.xaxis = 0;1622 if (Math.abs(self.controls.yaxis) == 0.1) self.controls.yaxis = 0;1623 self.mouseX = inputs.aimx;1624 self.mouseY = inputs.aimy;1625 self.attacking = inputs.attack;1626 self.shield = inputs.second;1627 self.disableSecond = inputs.disableSecond;1628 if (inputs.interacting) self.interact(self.mouseX, self.mouseY);1629 }1630 } else {1631 self.kick();1632 }1633 });1634 socket.on('renderDistance', function(chunks) {1635 if (chunks != null) {1636 self.renderDistance = chunks;1637 } else {1638 self.kick();1639 }1640 });1641 socket.on('debug', function(state) {1642 if (state != null) {1643 self.debugEnabled = state;1644 } else {1645 self.kick();1646 }1647 });1648 var charCount = 0;1649 var msgCount = 0;1650 socket.on('chat', function(msg) {1651 if (self.signedIn) {1652 if (typeof msg == 'string') {1653 msg = msg.replace(/</g, '<');1654 msg = msg.replace(/>/g, '>');1655 try {1656 if (msg.indexOf('/') == 0) {1657 var cmd = '';1658 var arg = msg.replace('/', '');1659 while (true) {1660 cmd += arg[0];1661 arg = arg.replace(arg[0], '');1662 if (arg[0] == ' ') {1663 arg = arg.replace(arg[0], '');1664 break;1665 }1666 if (arg == '') break;1667 }1668 var args = [];1669 var i = 0;1670 while (true) {1671 if (args[i]) args[i] += arg[0];1672 else args[i] = arg[0];1673 arg = arg.replace(arg[0], '');1674 if (arg[0] == ' ') {1675 if (!(cmd == 'msg' && i == 1)) {1676 arg = arg.replace(arg[0], '');1677 i++;1678 }1679 }1680 if (arg == '') break;1681 }1682 switch (cmd) {1683 case 'help':1684 insertSingleChat('COMMAND HELP:\n/help -Returns all commands available\n/msg <username> <message> -Private message a player\n/waypoint <location> -Teleport to a waypoint\n', 'deepskyblue', self.name, false);1685 break;1686 case 'msg':1687 if (args[0] == null) {1688 insertSingleChat('No recipient!', 'error', self.name, false);1689 return;1690 }1691 if (args[0] == self.name) {1692 insertSingleChat('You can\'t message yourself!', 'error', self.name, false);1693 return;1694 }1695 var valid = false;1696 for (let i in Player.list) {1697 if (Player.list[i].name == args[0]) valid = true;1698 }1699 if (!valid) {1700 insertSingleChat('No player found with name ' + args[0], 'error', self.name, false);1701 break;1702 }1703 if (args[1] == null || args[1] == ' ') {1704 insertSingleChat('Empty message!', 'error', self.name, false);1705 return;1706 }1707 if (valid) {1708 if (!Filter.check(args[1])) {1709 insertSingleChat(self.name + '->' + args[0] + ': ' + args[1], '', self.name, true);1710 insertSingleChat(self.name + '->' + args[0] + ': ' + args[1], '', args[0], false);1711 } else insertSingleChat('Hey! Don\'t do that!', 'error', self.name, false);1712 }1713 break;1714 case 'waypoint':1715 insertSingleChat('No waypoints unlocked yet.', 'error', self.name, false);1716 break;1717 default:1718 insertSingleChat('Command not found ' + cmd, 'error', self.name, false);1719 break;1720 }1721 } else {1722 var valid = false;1723 for (let i in msg) {1724 if (msg[i] != ' ') valid = true;1725 }1726 if (valid) {1727 if (Filter.check(msg)) insertSingleChat('Hey! Don\'t do that!', 'error', self.name, false);1728 else insertChat(self.name + ': ' + msg, self.chatStyle);1729 charCount += msg.length;1730 msgCount++;1731 }1732 }1733 } catch (err) {1734 error(err);1735 }1736 } else {1737 self.kick();1738 }1739 }1740 });1741 const spamcheck = setInterval(async function() {1742 charCount = Math.max(charCount-128, 0);1743 msgCount = Math.max(msgCount-2, 0);1744 if (!self.disconnected && (charCount > 0 || msgCount > 0)) {1745 if (self.name) insertChat(self.name + ' was kicked for spamming', 'anticheat');1746 await self.leave();1747 }1748 }, 1000);1749 self.update = function update() {1750 self.updatePos();1751 self.updateUse(self.attack, self.stats);1752 self.updateUse(self.crystal, self.crystalStats);1753 self.lastAutoHeal++;1754 self.lastHeal++;1755 if (self.stats.heal != 0 && self.lastAutoHeal >= self.stats.heal && self.hp < self.maxHP && self.alive) {1756 self.hp = Math.min(self.hp+1, self.maxHP);1757 self.lastAutoHeal = 0;1758 }1759 if (self.controls.heal && self.alive && self.hp < self.maxHP && self.lastHeal >= seconds(1) && self.mana >= 20) {1760 var oldhp = self.hp;1761 self.lastHeal = 0;1762 self.hp = Math.min(self.hp+20, self.maxHP);1763 self.mana -= 20;1764 self.lastManaUse = 0;1765 new Particle(self.map, self.x, self.y, 'heal', '+' + self.hp-oldhp);1766 }1767 self.lastManaRegen++;1768 self.lastManaUse++;1769 if (self.lastManaUse >= seconds(1.5) && self.alive) {1770 self.mana = Math.min(self.mana+1, self.maxMana);1771 self.lastManaRegen = 0;1772 } else if (self.lastManaRegen >= seconds(0.5) && self.alive) {1773 self.mana = Math.min(self.mana+1, self.maxMana);1774 self.lastManaRegen = 0;1775 }1776 for (let i in self.invincibilityFrames) {1777 self.invincibilityFrames[i]--;1778 self.invincibilityFrames[i] < 1 && delete self.invincibilityFrames[i];1779 }1780 self.animationSpeed = 15/Math.sqrt(Math.pow(self.xmove, 2)+Math.pow(self.ymove, 2))*100 || 50;1781 self.updateAnimation();1782 var mouseangle = Math.atan2(self.mouseY, self.mouseX);1783 self.shieldAngle = mouseangle;1784 self.heldItem.angle = mouseangle;1785 var mouseddir = Math.round(radians(mouseangle)/45);1786 if (mouseddir <= 0) mouseddir = 8-Math.abs(mouseddir);1787 if (mouseddir == 8) mouseddir = 0;1788 var dir = 'up';1789 switch (mouseddir) {1790 case 0:1791 dir = 'right';1792 break;1793 case 1:1794 dir = 'downright';1795 break;1796 case 2:1797 dir = 'down';1798 break;1799 case 3:1800 dir = 'downleft';1801 break;1802 case 4:1803 dir = 'left';1804 break;1805 case 5:1806 dir = 'upleft';1807 break;1808 case 6:1809 dir = 'up';1810 break;1811 case 7:1812 dir = 'upright';1813 break;1814 }1815 self.facingDirection = dir;1816 self.updateClient();1817 self.trackedData.updateTrackers();1818 if (self.gridx == 3 && self.gridy == 9 && self.map == 'World' && self.alive) self.onDeath(self, 'fire');1819 };1820 self.updateUse = function updateUse(attack, stats) {1821 attack.lastUse++;1822 if (self.attacking && !self.shield && !(attack.second && self.disableSecond) && attack.lastUse > attack.useTime && !self.region.noattack && attack.projectile != null && self.mana >= attack.manaCost && self.alive) {1823 attack.lastUse = 0;1824 var angle = Math.atan2(self.mouseY, self.mouseX);1825 typeof Player.usePatterns[attack.projectilePattern] == 'function' && Player.usePatterns[attack.projectilePattern](self, attack, stats, angle);1826 typeof Player.usePatterns[attack.projectilePattern] != 'function' && error('Invalid projectilePattern ' + attack.projectilePattern);1827 self.mana -= attack.manaCost;1828 if (attack.manaCost != 0) self.lastManaUse = 0;1829 }1830 };1831 self.updateClient = function updateClient() {1832 var pack = {1833 id: self.spectating ?? self.id,1834 hp: self.hp,1835 maxHP: self.maxHP,1836 xp: self.xp,1837 maxXP: self.maxXP,1838 mana: self.mana,1839 maxMana: self.maxMana,1840 }1841 socket.emit('updateSelf', pack);1842 self.quests.updateClient();1843 };1844 self.onRegionChange = function onRegionChange() {1845 socket.emit('region', self.region.name);1846 };1847 self.teleport = function teleport(map, x, y, layer) {1848 if (!self.teleporting) {1849 self.teleporting = true;1850 self.canMove = false;1851 self.teleportLocation.map = map;1852 self.teleportLocation.x = x*64+32;1853 self.teleportLocation.y = y*64+32;1854 self.teleportLocation.layer = layer;1855 socket.emit('teleport1');1856 }1857 };1858 socket.on('teleport1', function() {1859 if (self.teleporting) {1860 for (let i = 0; i < 20; i++) {1861 new Particle(self.map, self.x, self.y, 'teleport');1862 }1863 self.map = self.teleportLocation.map;1864 self.x = self.teleportLocation.x;1865 self.y = self.teleportLocation.y;1866 self.layer = self.teleportLocation.layer;1867 self.canMove = true;1868 for (let i = 0; i < 20; i++) {1869 new Particle(self.map, self.x, self.y, 'teleport');1870 }1871 self.gridx = Math.floor(self.x/64);1872 self.gridy = Math.floor(self.y/64);1873 socket.emit('teleport2', {map: self.map, x: self.x, y: self.y});1874 }1875 });1876 socket.on('teleport2', function() {1877 self.teleporting = false;1878 });1879 const oldonHit = self.onHit;1880 self.onHit = function onHit(entity, type) {1881 let oldhp = self.hp;1882 let ret = oldonHit(entity, type);1883 self.trackedData.damageTaken += oldhp-self.hp;1884 return ret;1885 };1886 const oldonDeath = self.onDeath;1887 self.onDeath = function onDeath(entity, type, message) {1888 oldonDeath(entity, type, message);1889 for (let i = 0; i < self.width*self.height/200; i++) {1890 new Particle(self.map, self.x+Math.random()*self.width-self.width/2, self.y+Math.random()*self.height-self.height/2, 'playerdeath');1891 }1892 self.quests.failQuests('death');1893 if (self.currentConversation.id) {1894 self.canMove = true;1895 self.invincible = false;1896 self.currentConversation.id = null;1897 if (Npc.list[self.talkingWith]) Npc.list[self.talkingWith].endConversation();1898 self.talkingWith = null;1899 }1900 if (!self.invincible) {1901 socket.emit('playerDied');1902 self.controls = {1903 up: false,1904 down: false,1905 left: false,1906 right: false,1907 xaxis: 0,1908 yaxis: 0,1909 x: 0,1910 y: 0,1911 heal: false1912 };1913 self.attacking = false;1914 self.trackedData.deaths++;1915 }1916 };1917 self.respawn = function respawn() {1918 self.hp = self.maxHP;1919 self.alive = true;1920 };1921 socket.on('respawn', function() {1922 if (self.alive) {1923 self.onDeath();1924 insertChat(self.name + ' respawn cheated.', 'anticheat');1925 self.leave();1926 } else self.respawn();1927 });1928 self.updateStats = function updateStats() {1929 self.stats = {1930 damageType: null,1931 projectileSpeed: 1,1932 attack: 1,1933 defense: 0,1934 damageReduction: 0,1935 knockbackResistance: 0,1936 heal: 8,1937 speed: 1,1938 range: 1,1939 critChance: 0,1940 critPower: 1,1941 knockback: 0,1942 maxPierce: 01943 };1944 self.crystalStats = {1945 damageType: null,1946 projectileSpeed: 1,1947 attack: 1,1948 speed: 1,1949 range: 1,1950 critChance: 0,1951 critPower: 1,1952 knockback: 0,1953 maxPierce: 01954 };1955 self.shieldStats = {1956 knockbackResistance: 0,1957 blockAngle: 0,1958 reflectProjectileChance: 01959 };1960 self.attack = {1961 projectile: null,1962 projectilePattern: 'single',1963 useTime: 0,1964 manaCost: 0,1965 lastUse: 0,1966 second: false1967 };1968 self.crystal = {1969 projectile: null,1970 projectilePattern: 'single',1971 useTime: 0,1972 manaCost: 0,1973 lastUse: 0,1974 second: true1975 };1976 self.heldItem.id = null;1977 self.heldItem.shield = null;1978 self.maxHP = 100;1979 self.moveSpeed = 15;1980 if (self.inventory.equips.weapon) {1981 var item = self.inventory.equips.weapon;1982 self.stats.damageType = item.damageType;1983 self.stats.attack *= item.damage;1984 self.stats.critChance += item.critChance;1985 self.stats.critPower += item.critPower;1986 self.stats.knockback += item.knockback;1987 self.stats.maxPierce = item.maxPierce;1988 self.attack.projectile = item.projectile;1989 self.attack.projectilePattern = item.projectilePattern;1990 self.stats.projectileSpeed = item.projectileSpeed;1991 self.stats.range = item.projectileRange;1992 self.attack.useTime = item.useTime;1993 self.attack.manaCost = item.manaCost;1994 self.heldItem.id = item.id;1995 }1996 if (self.inventory.equips.crystal) {1997 var item = self.inventory.equips.crystal;1998 self.crystalStats.damageType = item.damageType;1999 self.crystalStats.attack *= item.damage;2000 self.crystalStats.critChance += item.critChance;2001 self.crystalStats.critPower += item.critPower;2002 self.crystalStats.knockback += item.knockback;2003 self.crystalStats.maxPierce = item.maxPierce;2004 self.crystal.projectile = item.projectile;2005 self.crystal.projectilePattern = item.projectilePattern;2006 self.crystalStats.projectileSpeed = item.projectileSpeed;2007 self.crystalStats.range = item.projectileRange;2008 self.crystal.useTime = item.useTime;2009 self.crystal.manaCost = item.manaCost;2010 }2011 if (self.inventory.equips.shield) {2012 var item = self.inventory.equips.shield;2013 self.shieldStats.knockbackResistance = item.knockbackResistance;2014 self.shieldStats.blockAngle = item.blockAngle;2015 self.shieldStats.reflectProjectileChance = item.projectileReflectChance;2016 self.heldItem.shield = item.id;2017 }2018 for (let i in self.inventory.equips) {2019 var localitem = self.inventory.equips[i];2020 if (i != 'weapon2' && localitem) {2021 for (let j in localitem.effects) {2022 var effect = localitem.effects[j];2023 switch (effect.id) {2024 case 'health':2025 self.maxHP = Math.round(self.maxHP*effect.value);2026 self.hp = Math.min(self.maxHP, self.hp);2027 break;2028 case 'damage':2029 self.stats.attack *= effect.value;2030 self.crystalStats.attack *= effect.value;2031 break;2032 case 'rangedDamage':2033 if (self.stats.damageType == 'ranged') self.stats.attack *= effect.value;2034 if (self.crystalStats.damageType == 'ranged') self.crystalStats.attack *= effect.value;2035 break;2036 case 'meleeDamage':2037 if (self.stats.damageType == 'melee') self.stats.attack *= effect.value;2038 if (self.crystalStats.damageType == 'melee') self.crystalStats.attack *= effect.value;2039 break;2040 case 'magicDamage':2041 if (self.stats.damageType == 'magic') self.stats.attack *= effect.value;2042 if (self.crystalStats.damageType == 'magic') self.crystalStats.attack *= effect.value;2043 break;2044 case 'range':2045 self.stats.range *= effect.value;2046 self.crystalStats.range *= effect.value;2047 break;2048 case 'critChance':2049 self.stats.critChance += effect.value;2050 self.crystalStats.critChance += effect.value;2051 break;2052 case 'critPower':2053 self.stats.critPower *= effect.value;2054 self.crystalStats.critPower *= effect.value;2055 break;2056 case 'damageReduction':2057 self.stats.damageReduction += effect.value;2058 self.crystalStats.damageReduction += effect.value;2059 break;2060 case 'defense':2061 self.stats.defense += effect.value;2062 self.stats.defense = Math.min(self.stats.defense, 1);2063 self.crystalStats.defense += effect.value;2064 self.crystalStats.defense = Math.min(self.crystalStats.defense, 1);2065 break;2066 case 'speed':2067 self.moveSpeed *= effect.value;2068 break;2069 default:2070 error('Invalid item effect ' + effect.id);2071 break;2072 }2073 }2074 }2075 }2076 self.moveSpeed = Math.round(self.moveSpeed);2077 };2078 self.interact = function interact(x, y) {2079 for (let i in DroppedItem.list) {2080 var localdroppeditem = DroppedItem.list[i];2081 if (self.map == localdroppeditem.map && self.getDistance(localdroppeditem) < 512) {2082 if (localdroppeditem.playerId == self.id || localdroppeditem.playerId == null) {2083 var cx = self.x+x;2084 var cy = self.y+y;2085 var left = localdroppeditem.x-localdroppeditem.width/2;2086 var right = localdroppeditem.x+localdroppeditem.width/2;2087 var top = localdroppeditem.y-localdroppeditem.height/2;2088 var bottom = localdroppeditem.y+localdroppeditem.height/2;2089 if (cx >= left && cx <= right && cy >= top && cy <= bottom) {2090 if (!self.inventory.full()) {2091 var res = self.inventory.addItem(localdroppeditem.itemId, localdroppeditem.stackSize, localdroppeditem.enchantments, true);2092 if (typeof res == 'object') {2093 delete DroppedItem.list[i];2094 }2095 }2096 return;2097 }2098 }2099 }2100 }2101 if (!self.talkingWith) {2102 for (let i in Npc.list) {2103 var localnpc = Npc.list[i];2104 if (self.map == localnpc.map && self.getDistance(localnpc) < 512) {2105 var cx = self.x+x;2106 var cy = self.y+y;2107 var left = localnpc.x-localnpc.width/2;2108 var right = localnpc.x+localnpc.width/2;2109 var top = localnpc.y-localnpc.height/2;2110 var bottom = localnpc.y+localnpc.height/2;2111 if (cx >= left && cx <= right && cy >= top && cy <= bottom) {2112 try {2113 localnpc.rightClickEvent(self, localnpc);2114 } catch (err) {2115 error(err);2116 }2117 return;2118 }2119 }2120 }2121 }2122 };2123 self.prompt = function prompt(id, npcId) {2124 self.attacking = false;2125 self.shield = false;2126 self.heldItem.usingShield = false;2127 self.canMove = false;2128 self.invincible = true;2129 self.controls = {2130 up: false,2131 down: false,2132 left: false,2133 right: false,2134 xaxis: 0,2135 yaxis: 0,2136 x: 0,2137 y: 0,2138 heal: false2139 };2140 self.animationDirection = 'facing';2141 self.currentConversation.id = id;2142 socket.emit('prompt', self.currentConversation);2143 self.talkingWith = npcId;2144 self.talking = true;2145 };2146 socket.on('promptChoose', function(choice) {2147 if (self.currentConversation.id) {2148 var option = Npc.dialogues[self.currentConversation.id][self.currentConversation.i].options[choice];2149 if (option) {2150 var action = option.action;2151 if (action == 'continue') {2152 self.currentConversation.i++;2153 self.prompt(self.currentConversation.id, self.talkingWith);2154 } else if (action == 'close') {2155 self.canMove = true;2156 self.invincible = false;2157 self.currentConversation.id = null;2158 self.currentConversation.i = 0;2159 if (Npc.list[self.talkingWith]) Npc.list[self.talkingWith].endConversation();2160 self.talkingWith = null;2161 self.talking = false;2162 } else if (action.startsWith('prompt_')) {2163 self.prompt(action.replace('prompt_', ''), self.talkingWith);2164 } else if (action.startsWith('quest_')) {2165 self.canMove = true;2166 self.invincible = false;2167 self.currentConversation.id = null;2168 self.currentConversation.i = 0;2169 var id = action.replace('quest_', '');2170 if (self.quests.qualifiesFor(id)) self.quests.startQuest(id);2171 if (Npc.list[self.talkingWith]) Npc.list[self.talkingWith].endConversation();2172 self.talkingWith = null;2173 self.talking = false;2174 } else if (action.startsWith('item_')) {2175 self.inventory.addItem(action.replace('item_', '').replace(action.substring(action.indexOf(':'), action.length), ''), action.substring(action.indexOf(':')+1, action.length));2176 self.currentConversation.i++;2177 self.prompt(self.currentConversation.id, self.talkingWith);2178 } else if (action.startsWith('script_')) {2179 option.script(self);2180 self.currentConversation.i++;2181 self.prompt(self.currentConversation.id, self.talkingWith);2182 } else if (action.startsWith('script-end_')) {2183 option.script(self);2184 self.canMove = true;2185 self.invincible = false;2186 self.currentConversation.id = null;2187 self.currentConversation.i = 0;2188 if (Npc.list[self.talkingWith]) Npc.list[self.talkingWith].endConversation();2189 self.talkingWith = null;2190 self.talking = false;2191 } else if (action.startsWith('talkedwith_')) {2192 self.canMove = true;2193 self.invincible = false;2194 self.currentConversation.id = null;2195 self.currentConversation.i = 0;2196 self.talkedWith = action.replace('talkedwith_', '');2197 self.talkingWith = null;2198 self.talking = false;2199 }2200 } else {2201 self.kick();2202 }2203 }2204 });2205 self.spectate = function spectate(name) {2206 self.spectating = null;2207 for (let i in Player.list) {2208 if (Player.list[i].name === name) self.spectating = i; 2209 }2210 return self.spectating;2211 };2212 self.saveData = async function saveData() {2213 self.playTime += Date.now()-self.loginTime;2214 var trackedData = JSON.parse(JSON.stringify(self.trackedData));2215 var progress = {2216 inventory: self.inventory.getSaveData(),2217 characterStyle: self.characterStyle,2218 progress: {2219 level: self.xpLevel,2220 xp: self.xp2221 },2222 quests: self.quests.getSaveData(),2223 trackedData: trackedData,2224 spawnpoint: 0,2225 lastLogin: Date.now(),2226 playTime: self.playTime,2227 saveFormat: 22228 };2229 var data = JSON.stringify(progress);2230 await ACCOUNTS.saveProgress(self.creds.username, self.creds.password, data);2231 };2232 self.loadData = async function loadData() {2233 var data = await ACCOUNTS.loadProgress(self.creds.username, self.creds.password);2234 var progress = JSON.parse(data);2235 if (progress) {2236 if (progress.saveFormat == null) { // support for accounts < v0.10.02237 self.inventory.loadSaveData(progress);2238 self.inventory.refresh();2239 } else if (progress.saveFormat == 1) {2240 try {2241 self.inventory.loadSaveData(progress.inventory);2242 self.inventory.refresh();2243 self.characterStyle = progress.characterStyle;2244 self.characterStyle.texture = null;2245 self.xpLevel = progress.progress.xpLevel;2246 self.xp = progress.progress.xp;2247 self.quests.loadSaveData(progress.quests);2248 for (let i in progress.trackedData) {2249 self.trackedData[i] = progress.trackedData[i];2250 }2251 self.trackedData.monstersKilled = Array.from(self.trackedData.monstersKilled);2252 self.trackedData.last = {};2253 self.trackedData.last = cloneDeep(self.trackedData);2254 self.trackedData.updateTrackers();2255 } catch (err) {2256 error(err);2257 }2258 } else if (progress.saveFormat == 2) {2259 try {2260 self.inventory.loadSaveData(progress.inventory);2261 self.inventory.refresh();2262 self.characterStyle = progress.characterStyle;2263 self.characterStyle.texture = null;2264 self.xpLevel = progress.progress.xpLevel;2265 self.xp = progress.progress.xp;2266 self.quests.loadSaveData(progress.quests);2267 for (let i in progress.trackedData) {2268 self.trackedData[i] = progress.trackedData[i];2269 }2270 self.trackedData.monstersKilled = Array.from(self.trackedData.monstersKilled);2271 self.trackedData.last = {};2272 self.trackedData.last = cloneDeep(self.trackedData);2273 self.trackedData.updateTrackers();2274 self.playTime = progress.playTime;2275 } catch (err) {2276 error(err);2277 }2278 } else {2279 warn('Unknown save format ' + progress.saveFormat + '! Data loss imminent!');2280 }2281 } else {2282 socket.emit('item', {2283 action: 'maxItems',2284 slots: self.inventory.maxItems2285 });2286 self.inventory.refresh();2287 }2288 for (let i in ENV.ops) {2289 if (self.name == ENV.ops[i]) self.chatStyle = 'color: #28EB57;';2290 }2291 for (let i in ENV.devs) {2292 if (self.name == ENV.devs[i]) self.chatStyle = 'color: #6BFF00;';2293 }2294 if (self.name == 'Sampleprovider(sp)') self.chatStyle = 'color: #3C70FF;';2295 if (self.name == 'sp') self.chatStyle = 'color: #FF0090;';2296 self.updateStats();2297 var noWeapon = true;2298 for (let i in self.inventory.items) {2299 if (self.inventory.items[i] && self.inventory.items[i].slotType == 'weapon') noWeapon = false;2300 }2301 if (self.inventory.equips['weapon']) noWeapon = false;2302 if (self.inventory.equips['weapon2']) noWeapon = false;2303 if (noWeapon) {2304 self.inventory.addItem('simplewoodenbow');2305 }2306 };2307 socket.on('disconnect', function() {2308 clearInterval(signupspamcheck);2309 clearInterval(spamcheck);2310 });2311 socket.on('disconnected', function() {2312 clearInterval(signupspamcheck);2313 clearInterval(spamcheck);2314 });2315 socket.on('timeout', function() {2316 clearInterval(signupspamcheck);2317 clearInterval(spamcheck);2318 });2319 socket.on('error', function() {2320 clearInterval(signupspamcheck);2321 clearInterval(spamcheck);2322 });2323 self.updateChunkLocation = function updateChunkLocation() {2324 if (self.map != self.chunkLocation.map || self.layer != self.chunkLocation.layer || self.chunkx != self.chunkLocation.chunkx || self.chunky != self.chunkLocation.chunky) {2325 if (Player.chunks[self.chunkLocation.map] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];2326 self.chunkLocation = {2327 map: self.map,2328 layer: self.layer,2329 chunkx: self.chunkx,2330 chunky: self.chunky2331 };2332 if (Player.chunks[self.chunkLocation.map] == null) Player.chunks[self.chunkLocation.map] = [];2333 if (Player.chunks[self.chunkLocation.map][self.chunkLocation.layer] == null) Player.chunks[self.chunkLocation.map][self.chunkLocation.layer] = [];2334 if (Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] == null) Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] = [];2335 if (Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx] == null) Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx] = [];2336 Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id] = self;2337 }2338 };2339 self.disconnect = async function disconnect() {2340 if (self && !self.disconnected) {2341 self.disconnected = true;2342 clearInterval(signupspamcheck);2343 clearInterval(spamcheck);2344 if (self.name) {2345 await self.saveData();2346 insertChat(self.name + ' left the game', 'server');2347 }2348 delete Player.list[self.id];2349 if (Player.chunks[self.chunkLocation.map] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];2350 else error('Could not delete player from chunks!');2351 socket.emit('disconnected');2352 socket.removeAllListeners();2353 socket.onevent = function(packet) {};2354 socket.disconnect();2355 }2356 };2357 self.socketKick = async function socketKick() {2358 if (self && !self.disconnected) {2359 self.disconnected = true;2360 clearInterval(signupspamcheck);2361 clearInterval(spamcheck);2362 if (self.name) {2363 await self.saveData();2364 insertChat(self.name + ' was kicked for socket.emit', 'anticheat');2365 }2366 delete Player.list[self.id];2367 if (Player.chunks[self.chunkLocation.map] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Player.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];2368 else error('Could not delete player from chunks!');2369 socket.emit('disconnected');2370 socket.removeAllListeners();2371 socket.onevent = function(packet) {};2372 socket.disconnect();2373 }2374 };2375 self.leave = function leave() {2376 if (self && !self.disconnected) {2377 self.toDisconnect = true;2378 socket.emit('disconnected');2379 socket.removeAllListeners();2380 socket.onevent = function(packet) {};2381 socket.disconnect();2382 }2383 };2384 self.kick = function kick() {2385 if (self && !self.disconnected) {2386 self.toKick = true;2387 socket.emit('disconnected');2388 socket.removeAllListeners();2389 socket.onevent = function(packet) {};2390 socket.disconnect();2391 }2392 };2393 Player.list[self.id] = self;2394 self.updateChunkLocation();2395 return self;2396};2397Player.update = function update() {2398 var pack = [];2399 for (let i in Player.list) {2400 var localplayer = Player.list[i];2401 if (localplayer.toDisconnect) {2402 localplayer.disconnect();2403 continue;2404 }2405 if (localplayer.toKick) {2406 localplayer.socketKick();2407 continue;2408 }2409 if (localplayer.name) {2410 localplayer.update();2411 pack.push({2412 id: localplayer.id,2413 map: localplayer.map,2414 x: localplayer.x,2415 y: localplayer.y,2416 layer: localplayer.layer,2417 name: localplayer.name,2418 animationStage: localplayer.animationStage,2419 characterStyle: localplayer.characterStyle,2420 hp: localplayer.hp,2421 maxHP: localplayer.maxHP,2422 heldItem: localplayer.heldItem,2423 isNPC: false2424 });2425 }2426 }2427 return pack;2428};2429Player.getDebugData = function getDebugData() {2430 var pack = [];2431 for (let i in Player.list) {2432 var localplayer = Player.list[i];2433 if (localplayer.name) {2434 pack.push({2435 map: localplayer.map,2436 x: localplayer.x,2437 y: localplayer.y,2438 width: localplayer.width,2439 height: localplayer.height,2440 collisionBoxSize: localplayer.collisionBoxSize,2441 controls: localplayer.controls,2442 });2443 }2444 }2445 return pack;2446};2447Player.list = [];2448Player.chunks = [];2449Player.usePatterns = {2450 single: function(self, attack, stats, angle) {2451 new Projectile(attack.projectile, angle, stats, self.id);2452 },2453 triple: function(self, attack, stats, angle) {2454 new Projectile(attack.projectile, angle-degrees(20), stats, self.id);2455 new Projectile(attack.projectile, angle, stats, self.id);2456 new Projectile(attack.projectile, angle+degrees(20), stats, self.id);2457 },2458 spray: function(self, attack, stats, angle) {2459 for (let i = 0; i < 3; i++) {2460 new Projectile(attack.projectile, angle+Math.random()*0.2-0.1, stats, self.id);2461 }2462 },2463 spray2: function(self, attack, stats, angle) {2464 for (let i = 0; i < 5; i++) {2465 new Projectile(attack.projectile, angle+Math.random()*0.3-0.15, stats, self.id);2466 }2467 },2468 line: function(self, attack, stats, angle) {2469 new Projectile(attack.projectile, angle, stats, self.id);2470 new Projectile(attack.projectile, angle-degrees(180), stats, self.id);2471 },2472 triangle: function(self, attack, stats, angle) {2473 new Projectile(attack.projectile, angle-degrees(120), stats, self.id);2474 new Projectile(attack.projectile, angle, stats, self.id);2475 new Projectile(attack.projectile, angle+degrees(120), stats, self.id);2476 },2477 ring: function(self, attack, stats, angle) {2478 new Projectile(attack.projectile, angle, stats, self.id);2479 new Projectile(attack.projectile, angle-degrees(36), stats, self.id);2480 new Projectile(attack.projectile, angle-degrees(72), stats, self.id);2481 new Projectile(attack.projectile, angle-degrees(108), stats, self.id);2482 new Projectile(attack.projectile, angle-degrees(144), stats, self.id);2483 new Projectile(attack.projectile, angle-degrees(180), stats, self.id);2484 new Projectile(attack.projectile, angle-degrees(216), stats, self.id);2485 new Projectile(attack.projectile, angle-degrees(252), stats, self.id);2486 new Projectile(attack.projectile, angle-degrees(288), stats, self.id);2487 new Projectile(attack.projectile, angle-degrees(324), stats, self.id);2488 },2489 nearest: function(self, attack, stats, angle) {2490 var lowest, target;2491 if (Monster.chunks[self.map]) {2492 for (let z in Monster.chunks[self.map]) {2493 for (let y = self.chunky-1; y <= self.chunky+1; y++) {2494 for (let x = self.chunkx-1; x <= self.chunkx+1; x++) {2495 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {2496 var monsters = Monster.chunks[self.map][z][y][x];2497 for (let i in monsters) {2498 if (lowest == null || (self.getGridDistance(monsters[i]) < 20 && self.getGridDistance(monsters[i]) < self.getGridDistance(Monster.list[lowest]) && monsters[i].alive)) lowest = i;2499 }2500 }2501 }2502 }2503 }2504 }2505 target = Monster.list[lowest];2506 target && new Projectile(attack.projectile, 0, stats, self.id, target.x, target.y);2507 }2508};2509// monsters2510Monster = function(type, x, y, map, layer) {2511 var self = new Rig();2512 self.entType = 'monster';2513 self.x = x;2514 self.y = y;2515 self.map = map;2516 if (layer != null) self.layer = layer;2517 self.gridx = Math.floor(self.x/64);2518 self.gridy = Math.floor(self.y/64);2519 self.chunkx = Math.floor(self.gridx/Collision.grid[self.map].chunkWidth);2520 self.chunky = Math.floor(self.gridy/Collision.grid[self.map].chunkHeight);2521 self.animationDirection = 'loop';2522 self.ai.attackType = 'none';2523 self.ai.lastAttack = 0;2524 self.ai.attackStage = 0;2525 self.ai.attackTime = 0;2526 self.ai.fleeing = false;2527 self.ai.fleeThreshold = 0;2528 self.ai.inNomonsterRegion = false;2529 self.ai.lastTracked = 0;2530 self.targetMonsters = false;2531 try {2532 var tempmonster = Monster.types[type];2533 self.type = type;2534 self.name = tempmonster.name;2535 self.stats = tempmonster.stats;2536 self.moveSpeed = tempmonster.moveSpeed;2537 self.width = tempmonster.width;2538 self.height = tempmonster.height;2539 self.ai.attackType = tempmonster.attackType;2540 self.ai.attackTime = 0;2541 self.ai.maxRange = tempmonster.aggroRange;2542 self.ai.targetHold = tempmonster.deaggroTime;2543 self.ai.circleTarget = tempmonster.circleTarget;2544 self.ai.circleDistance = tempmonster.circleDistance;2545 self.ai.circleDirection = -0.2;2546 self.ai.idleMove = tempmonster.idleMove;2547 self.hp = tempmonster.hp;2548 self.ai.fleeThreshold = tempmonster.fleeThreshold;2549 self.maxHP = tempmonster.hp;2550 self.touchDamage = tempmonster.touchDamage;2551 self.xpDrop = tempmonster.xpDrop;2552 self.drops = tempmonster.drops;2553 self.coinDrops = tempmonster.coinDrops;2554 self.animationLength = tempmonster.animationLength;2555 tempmonster = null2556 } catch (err) {2557 error(err);2558 return false;2559 }2560 self.characterStyle = {2561 texture: null2562 };2563 self.collisionBoxSize = Math.max(self.width, self.height);2564 self.active = false;2565 self.update = function update() {2566 self.active = false;2567 if (Player.chunks[self.map]) {2568 var range = 2;2569 search: for (let z in Player.chunks[self.map]) {2570 for (let y = self.chunky-range; y <= self.chunky+range; y++) {2571 for (let x = self.chunkx-range; x <= self.chunkx+range; x++) {2572 if (Player.chunks[self.map][z][y] && Player.chunks[self.map][z][y][x]) {2573 for (let i in Player.chunks[self.map][z][y][x]) {2574 self.active = true;2575 break search;2576 }2577 }2578 }2579 }2580 }2581 }2582 if (self.stats.heal != 0) {2583 self.lastAutoHeal++;2584 if (self.lastAutoHeal >= self.stats.heal && self.hp < self.maxHP && self.alive) {2585 self.hp = Math.min(self.hp+1, self.maxHP);2586 self.lastAutoHeal = 0;2587 }2588 }2589 if (self.active) {2590 self.updateAggro();2591 if (self.canMove) self.updatePos();2592 if (self.alive && Player.chunks[self.map] && Player.chunks[self.map][self.layer]) {2593 var range = Math.ceil(self.collisionBoxSize/(128*Math.max(Collision.grid[self.map].chunkWidth, Collision.grid[self.map].chunkHeight)));2594 for (let y = self.chunky-range; y <= self.chunky+range; y++) {2595 for (let x = self.chunkx-range; x <= self.chunkx+range; x++) {2596 if (Player.chunks[self.map][self.layer][y] && Player.chunks[self.map][self.layer][y][x]) {2597 for (let i in Player.chunks[self.map][self.layer][y][x]) {2598 Player.chunks[self.map][self.layer][y][x][i].alive && self.collideWith(Player.chunks[self.map][self.layer][y][x][i]) && Player.chunks[self.map][self.layer][y][x][i].onHit(self, 'touch');2599 }2600 }2601 }2602 }2603 }2604 self.attack();2605 self.updateAnimation();2606 }2607 for (let i in self.invincibilityFrames) {2608 self.invincibilityFrames[i]--;2609 self.invincibilityFrames[i] < 1 && delete self.invincibilityFrames[i];2610 }2611 };2612 self.updatePos = function updatePos() {2613 self.ai.lastPath++;2614 if (self.ai.lastPath >= seconds(0.1)) {2615 self.ai.lastPath = 0;2616 self.ai.path = [];2617 if (self.ai.inNomonsterRegion) {2618 var closest = {2619 x: null,2620 y: null2621 };2622 var range = self.ai.maxRange/2;2623 for (let x = self.gridx-range; x < self.gridx+range; x++) {2624 for (let y = self.gridy-range; y < self.gridy+range; y++) {2625 if (Region.grid[self.map][y] && Region.grid[self.map][y][x] && Region.grid[self.map][y][x].nomonster == false) {2626 if (closest.x == null || self.getGridDistance({x: x, y: y}) < self.getGridDistance(closest)) closest = {2627 x: x,2628 y: y2629 };2630 }2631 }2632 }2633 if (closest) {2634 self.ai.posTarget = closest;2635 self.ai.pathtoPos();2636 }2637 } else if (self.ai.entityTarget) {2638 if (self.ai.fleeing) {2639 try {2640 var offsetx = self.gridx-self.ai.maxRange-1;2641 var offsety = self.gridy-self.ai.maxRange-1;2642 var size = self.ai.maxRange*2+1;2643 var grid = [];2644 for (let y = 0; y < size; y++) {2645 grid[y] = [];2646 for (let x = 0; x < size; x++) {2647 grid[y][x] = {2648 g: 0,2649 h: 0,2650 f: 0,2651 x: x+offsetx,2652 y: y+offsety2653 };2654 }2655 }2656 for (let y = 0; y < size; y++) {2657 for (let x = 0; x < size; x++) {2658 var checkx = x+offsetx;2659 var checky = y+offsety;2660 grid[y][x].h = Math.sqrt(Math.pow(self.gridx-checkx, 2)+Math.pow(self.gridy-checky, 2));2661 grid[y][x].g = Math.sqrt(Math.pow(self.ai.entityTarget.gridx-checkx, 2)+Math.pow(self.ai.entityTarget.gridy-checky, 2));2662 if (Collision.grid[self.map][self.layer][checky] && Collision.grid[self.map][self.layer][checky][checkx]) {2663 grid[y][x].g = 999;2664 }2665 if (Region.grid[self.map] && Region.grid[self.map][Math.floor(y)] && Region.grid[self.map][Math.floor(y)][Math.floor(x)] && Region.grid[self.map][Math.floor(y)][Math.floor(x)].nomonster) {2666 grid[y][x].g = 999;2667 }2668 grid[y][x].f = grid[y][x].h-grid[y][x].g;2669 }2670 }2671 var best = null;2672 for (let y in grid) {2673 for (let x in grid) {2674 if (best == null || grid[y][x].f < best.f) best = grid[y][x];2675 }2676 }2677 if (best) {2678 self.ai.posTarget.x = best.x;2679 self.ai.posTarget.y = best.y;2680 self.ai.pathtoPos();2681 }2682 } catch (err) {2683 error(err);2684 }2685 } else if (self.ai.circleTarget && self.getGridDistance(self.ai.entityTarget) < (self.ai.circleDistance+1)*64 && !self.rayCast(self.ai.entityTarget.x, self.ai.entityTarget.y)) {2686 try {2687 var target = self.ai.entityTarget;2688 var angle = Math.atan2(target.y-self.y, target.x-self.x);2689 var x = target.gridx*64+Math.round((Math.cos(angle)*self.ai.circleDistance)*64);2690 var y = target.gridy*64+Math.round((Math.sin(angle)*self.ai.circleDistance)*64);2691 angle = Math.atan2(target.y-y, target.x-x);2692 var oldangle = angle;2693 angle += self.ai.circleDirection;2694 x = target.gridx*64+Math.round((Math.cos(angle)*self.ai.circleDistance)*64);2695 y = target.gridy*64+Math.round((Math.sin(angle)*self.ai.circleDistance)*64);2696 var invalid = false;2697 if (self.rayCast(x, y)) invalid = true; 2698 if (Math.random() <= 0.02) invalid = true;2699 if (invalid) {2700 angle = oldangle;2701 self.ai.circleDirection *= -1;2702 angle += self.ai.circleDirection;2703 x = target.gridx*64+Math.round((Math.cos(angle)*self.ai.circleDistance)*64);2704 y = target.gridy*64+Math.round((Math.sin(angle)*self.ai.circleDistance)*64);2705 }2706 self.ai.posTarget.x = target.gridx+Math.round(Math.cos(angle)*self.ai.circleDistance);2707 self.ai.posTarget.y = target.gridy+Math.round(Math.sin(angle)*self.ai.circleDistance);2708 self.ai.pathtoPos();2709 } catch (err) {2710 error(err);2711 }2712 } else {2713 self.ai.pathtoEntity();2714 }2715 } else if (self.ai.idleMove != 'none') {2716 self.ai.pathIdle();2717 } else {2718 self.ai.path = [];2719 }2720 }2721 self.xspeed = Math.round(self.xmove+self.xknockback);2722 self.yspeed = Math.round(self.ymove+self.yknockback);2723 self.collide();2724 self.xknockback *= 0.25;2725 self.yknockback *= 0.25;2726 if (0-Math.abs(self.xknockback) > -0.5) self.xknockback = 0;2727 if (0-Math.abs(self.yknockback) > -0.5) self.yknockback = 0;2728 if (self.ai.chargeTime != -1) {2729 self.ai.charge.time -= 1;2730 if (self.ai.charge.time < 0) {2731 self.ai.charge.time = 0;2732 self.ai.charge.x *= 0.5;2733 self.ai.charge.y *= 0.5;2734 if (Math.abs(self.ai.charge.x) < 0.5 || Math.abs(self.ai.charge.y) < 0.5) {2735 self.ai.charge.x = 0;2736 self.ai.charge.y = 0;2737 self.ai.charge.time = -1;2738 }2739 }2740 }2741 var foundregion = false;2742 if (Region.grid[self.map][self.gridy] && Region.grid[self.map][self.gridy][self.gridx] && Region.grid[self.map][self.gridy][self.gridx].name != self.region.name) {2743 self.region = Region.grid[self.map][self.gridy][self.gridx];2744 self.onRegionChange();2745 }2746 if (Region.grid[self.map][self.gridy] && Region.grid[self.map][self.gridy][self.gridx]) foundregion = true;2747 if (!foundregion && self.region.name != 'The Wilderness') {2748 self.region = {2749 name: 'The Wilderness',2750 noattack: false,2751 nomonster: false2752 };2753 self.onRegionChange();2754 }2755 };2756 self.updateAggro = function updateAggro() {2757 self.ai.lastTracked++;2758 if (self.targetMonsters) {2759 var lowest;2760 if (Monster.chunks[self.map]) {2761 var range = Math.ceil(self.ai.maxRange/(64*Math.max(Collision.grid[self.map].chunkWidth, Collision.grid[self.map].chunkHeight)));2762 for (let z in Monster.chunks[self.map]) {2763 for (let y = self.chunky-range; y <= self.chunky+range; y++) {2764 for (let x = self.chunkx-range; x <= self.chunkx+range; x++) {2765 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {2766 var monsters = Monster.chunks[self.map][self.layer][y][x];2767 for (let i in monsters) {2768 if (self.getGridDistance(monsters[i]) < self.ai.maxRange && (!self.rayCast(monsters[i].x, monsters[i].y) || self.getGridDistance(monsters[i]) < 4) && !monsters[i].region.nomonster && monsters[i].alive && (lowest == null || self.getGridDistance(monsters[i]) < self.getGridDistance(Monster.list[lowest]))) lowest = i;2769 }2770 }2771 }2772 }2773 }2774 }2775 if (lowest && !self.ai.fleeing) {2776 self.ai.entityTarget = Monster.list[lowest];2777 self.ai.lastTracked = 0;2778 }2779 if (self.ai.lastTracked > seconds(self.ai.targetHold)) {2780 self.ai.entityTarget = null;2781 }2782 if (self.ai.entityTarget && !self.ai.entityTarget.alive) self.ai.entityTarget = null;2783 } else {2784 var lowest;2785 if (Player.chunks[self.map]) {2786 var range = Math.ceil(self.ai.maxRange/(64*Math.max(Collision.grid[self.map].chunkWidth, Collision.grid[self.map].chunkHeight)));2787 for (let z in Player.chunks[self.map]) {2788 for (let y = self.chunky-range; y <= self.chunky+range; y++) {2789 for (let x = self.chunkx-range; x <= self.chunkx+range; x++) {2790 if (Player.chunks[self.map][z][y] && Player.chunks[self.map][z][y][x]) {2791 var players = Player.chunks[self.map][z][y][x];2792 for (let i in players) {2793 if (self.getGridDistance(players[i]) < self.ai.maxRange && (!self.rayCast(players[i].x, players[i].y) || self.getGridDistance(players[i]) < 4) && !players[i].region.nomonster && players[i].alive && (lowest == null || self.getGridDistance(players[i]) < self.getGridDistance(Player.list[lowest]))) lowest = i;2794 }2795 }2796 }2797 }2798 }2799 }2800 if (lowest && !self.ai.fleeing) {2801 self.ai.entityTarget = Player.list[lowest];2802 self.ai.lastTracked = 0;2803 }2804 if (self.ai.lastTracked > seconds(self.ai.targetHold)) {2805 self.ai.entityTarget = null;2806 }2807 if (self.ai.entityTarget && !self.ai.entityTarget.alive) self.ai.entityTarget = null;2808 }2809 };2810 self.attack = function attack() {2811 self.ai.lastAttack++;2812 typeof Monster.attacks[self.ai.attackType] == 'function' && Monster.attacks[self.ai.attackType](self);2813 typeof Monster.attacks[self.ai.attackType] != 'function' && error('Invalid monster attack type "' + self.ai.attackType + '"');2814 };2815 const oldOnHit = self.onHit;2816 self.onHit = function onHit(entity, type) {2817 let ret = oldOnHit(entity, type);2818 var parent;2819 if (entity.parentIsPlayer) parent = Player.list[entity.parentID];2820 else parent = Monster.list[entity.parentID];2821 if (parent && !self.invincible) {2822 self.ai.entityTarget = parent;2823 self.ai.lastTracked = 0;2824 }2825 if (self.hp < self.ai.fleeThreshold) self.ai.fleeing = true;2826 return ret;2827 };2828 const oldOnDeath = self.onDeath;2829 self.onDeath = function onDeath(entity, type, message) {2830 self.bcDeath = ENV.broadcastMonsterDeaths;2831 // console.log(entity.entType, message)2832 oldOnDeath(entity, type, message);2833 if (entity && entity.entType == 'player') {2834 entity.xp += self.xpDrop;2835 var found = false;2836 for (let i in entity.trackedData.monstersKilled) {2837 if (entity.trackedData.monstersKilled[i].id == self.type) {2838 entity.trackedData.monstersKilled[i].count++;2839 found = true;2840 }2841 }2842 if (!found) {2843 entity.trackedData.monstersKilled.push({2844 id: self.type,2845 count: 12846 });2847 }2848 }2849 try {2850 var multiplier = 0;2851 for (let i in self.drops) {2852 multiplier += self.drops[i];2853 }2854 var random = Math.random()*multiplier;2855 var min = 0;2856 var max = 0;2857 var items = [];2858 for (let i in self.drops) {2859 max += self.drops[i];2860 if (random >= min && random <= max) {2861 items[i] = 1;2862 break;2863 }2864 min += self.drops[i];2865 }2866 if (items['coins']) {2867 items = [];2868 for (let i in self.coinDrops) {2869 items[i] = Math.round(Math.random()*self.coinDrops[i]);2870 }2871 }2872 var id;2873 if (entity) id = entity.id;2874 for (let i in items) {2875 if (i != 'nothing' && items[i] != 0) {2876 var attempts = 0;2877 var dropx, dropy;2878 while (attempts < 100) {2879 var angle = Math.random()*2*Math.PI;2880 var distance = Math.random()*32;2881 var x = self.x+Math.cos(angle)*distance;2882 var y = self.y+Math.sin(angle)*distance;2883 var collisions = [];2884 if (Collision.grid[self.map]) {2885 for (let checkx = self.gridx-1; checkx <= self.gridx+1; checkx++) {2886 for (let checky = self.gridy-1; checky <= self.gridy+1; checky++) {2887 if (Collision.grid[self.map][checky] && Collision.grid[self.map][checky][checkx])2888 collisions.push(Collision.getColEntity(self.map, checkx, checky));2889 }2890 }2891 }2892 var colliding = false;2893 for (let i in collisions) {2894 for (let j in collisions[i]) {2895 var bound1left = x-24;2896 var bound1right = x+24;2897 var bound1top = y-24;2898 var bound1bottom = y+24;2899 var bound2left = collisions[i][j].x-(collisions[i][j].width/2);2900 var bound2right = collisions[i][j].x+(collisions[i][j].width/2);2901 var bound2top = collisions[i][j].y-(collisions[i][j].height/2);2902 var bound2bottom = collisions[i][j].y+(collisions[i][j].height/2);2903 if (bound1left < bound2right && bound1right > bound2left && bound1top < bound2bottom && bound1bottom > bound2top) {2904 colliding = true;2905 }2906 }2907 }2908 if (!colliding) {2909 dropx = x;2910 dropy = y;2911 break;2912 }2913 attempts++;2914 }2915 if (dropx) new DroppedItem(self.map, dropx, dropy, i, [], items[i], id);2916 }2917 }2918 } catch (err) {2919 error(err);2920 }2921 delete Monster.list[self.id];2922 if (Monster.chunks[self.chunkLocation.map] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];2923 else error('Could not delete monster from chunks!');2924 };2925 self.onRegionChange = function onRegionChange() {2926 if (self.region.nomonster) {2927 self.ai.inNomonsterRegion = true;2928 self.ai.entityTarget = null;2929 self.ai.posTarget = null;2930 self.ai.path = [];2931 } else {2932 self.ai.inNomonsterRegion = false;2933 }2934 };2935 self.updateChunkLocation = function updateChunkLocation() {2936 if (self.map != self.chunkLocation.map || self.layer != self.chunkLocation.layer || self.chunkx != self.chunkLocation.chunkx || self.chunky != self.chunkLocation.chunky) {2937 if (Monster.chunks[self.chunkLocation.map] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];2938 self.chunkLocation = {2939 map: self.map,2940 layer: self.layer,2941 chunkx: self.chunkx,2942 chunky: self.chunky2943 };2944 if (Monster.chunks[self.chunkLocation.map] == null) Monster.chunks[self.chunkLocation.map] = [];2945 if (Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer] == null) Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer] = [];2946 if (Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] == null) Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] = [];2947 if (Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx] == null) Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx] = [];2948 Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id] = self;2949 }2950 };2951 Monster.list[self.id] = self;2952 self.updateChunkLocation();2953 return self;2954};2955Monster.update = function update() {2956 var pack = [];2957 for (let i in Monster.list) {2958 var localmonster = Monster.list[i];2959 localmonster.update();2960 pack.push({2961 id: localmonster.id,2962 map: localmonster.map,2963 x: localmonster.x,2964 y: localmonster.y,2965 layer: localmonster.layer,2966 chunkx: localmonster.chunkx,2967 chunky: localmonster.chunky,2968 type: localmonster.type,2969 animationStage: localmonster.animationStage,2970 characterStyle: localmonster.characterStyle,2971 hp: localmonster.hp,2972 maxHP: localmonster.maxHP2973 });2974 }2975 return pack;2976};2977Monster.getDebugData = function getDebugData() {2978 var pack = [];2979 for (let i in Monster.list) {2980 var localmonster = Monster.list[i];2981 if (localmonster.active) {2982 if (localmonster.ai.entityTarget) {2983 pack.push({2984 map: localmonster.map,2985 x: localmonster.x,2986 y: localmonster.y,2987 width: localmonster.width,2988 height: localmonster.height,2989 collisionBoxSize: localmonster.collisionBoxSize,2990 animationStage: localmonster.animationStage,2991 path: localmonster.ai.path,2992 controls: localmonster.controls,2993 aggroTarget: localmonster.ai.entityTarget.id,2994 aggroRange: localmonster.ai.maxRange2995 });2996 } else {2997 pack.push({2998 map: localmonster.map,2999 x: localmonster.x,3000 y: localmonster.y,3001 width: localmonster.width,3002 height: localmonster.height,3003 collisionBoxSize: localmonster.collisionBoxSize,3004 path: localmonster.ai.path,3005 controls: localmonster.controls,3006 aggroTarget: null,3007 aggroRange: localmonster.ai.maxRange3008 });3009 }3010 }3011 }3012 return pack;3013};3014Monster.types = require('./monster.json');3015Monster.list = [];3016Monster.chunks = [];3017Monster.attacks = {3018 bird: function(self) {3019 if (self.ai.entityTarget && !self.region.noattack) {3020 if (self.ai.lastAttack > seconds(1)) {3021 if (self.ai.attackStage == 5) {3022 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3023 new Projectile('ninjastar', angle+Math.random()*0.2-0.1, self.stats, self.id);3024 self.ai.attackStage = 0;3025 self.ai.lastAttack = 0;3026 }3027 if (self.ai.attackStage == 1) {3028 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3029 new Projectile('ninjastar', angle+Math.random()*0.2-0.1, self.stats, self.id);3030 }3031 self.ai.attackStage++;3032 }3033 }3034 },3035 snowbird: function(self) {3036 if (self.ai.entityTarget && !self.region.noattack) {3037 if (self.ai.lastAttack > seconds(1)) {3038 if (self.ai.attackStage == 1) {3039 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3040 new Projectile('fastsnowball', angle+Math.random()*0.2-0.1, self.stats, self.id);3041 } else if (self.ai.attackStage == 5) {3042 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3043 new Projectile('fastsnowball', angle+Math.random()*0.2-0.1, self.stats, self.id);3044 self.ai.attackStage = 0;3045 self.ai.lastAttack = 0;3046 }3047 self.ai.attackStage++;3048 }3049 }3050 },3051 cherrybomb: function(self) {3052 if (self.ai.entityTarget && !self.region.noattack) {3053 if (self.getDistance(self.ai.entityTarget) < 64) {3054 self.ai.attackType = 'triggeredcherrybomb';3055 self.ai.attackTime = 0;3056 self.explosionSize = 10;3057 }3058 }3059 },3060 triggeredcherrybomb: function(self) {3061 if (self.ai.attackTime == 0) {3062 self.moveSpeed = 0;3063 self.invincible = true;3064 self.alive = false;3065 self.animationStage = 0;3066 self.animationLength = 10;3067 self.onDeath = function onDeath() {};3068 }3069 self.ai.attackTime++;3070 if (self.ai.attackTime >= seconds(0.3)) {3071 self.ai.attackType = 'exploding';3072 for (let i = 0; i < 100; i++) {3073 new Particle(self.map, self.x, self.y, 'explosion');3074 }3075 if (Monster.chunks[self.map]) {3076 for (let z in Monster.chunks[self.map]) {3077 for (let y = self.chunky-1; y <= self.chunky+1; y++) {3078 for (let x = self.chunkx-1; x <= self.chunkx+1; x++) {3079 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {3080 var monsters = Monster.chunks[self.map][z][y][x];3081 for (let i in monsters) {3082 if (parseFloat(i) != self.id && self.getDistance(monsters[i]) < 200 && monsters[i].alive && monsters[i].ai.attackType != 'triggeredcherrybomb') {3083 if (monsters[i].ai.attackType == 'cherrybomb') {3084 monsters[i].ai.attackType = 'triggeredcherrybomb';3085 monsters[i].ai.attackTime = 0;3086 } else {3087 monsters[i].onHit(self, 'explosion');3088 }3089 }3090 }3091 }3092 }3093 }3094 }3095 }3096 if (Player.chunks[self.map]) {3097 for (let z in Player.chunks[self.map]) {3098 for (let y = self.chunky-1; y <= self.chunky+1; y++) {3099 for (let x = self.chunkx-1; x <= self.chunkx+1; x++) {3100 if (Player.chunks[self.map][z][y] && Player.chunks[self.map][z][y][x]) {3101 var players = Player.chunks[self.map][z][y][x];3102 for (let i in players) {3103 if (self.getDistance(players[i]) < 200 && players[i].alive) {3104 players[i].onHit(self, 'explosion');3105 }3106 }3107 }3108 }3109 }3110 }3111 }3112 }3113 },3114 exploding: function(self) {3115 if (self.animationStage >= 10) {3116 delete Monster.list[self.id];3117 if (Monster.chunks[self.chunkLocation.map] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Monster.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];3118 else error('Could not delete monster from chunks!');3119 }3120 },3121 snowball: function(self) {3122 if (self.ai.lastAttack >= seconds(4)) {3123 self.ai.attackStage++;3124 if (self.ai.attackStage == 20) {3125 self.ai.attackStage = 0;3126 self.ai.lastAttack = 0;3127 self.animationLength = 0;3128 self.moveSpeed = 10;3129 }3130 }3131 if (self.ai.entityTarget && !self.region.noattack) {3132 if (self.ai.lastAttack >= seconds(4)) {3133 if (self.ai.attackStage == 1) {3134 self.animationLength = 7;3135 self.animationSpeed = 100;3136 self.moveSpeed = 16;3137 }3138 var angle = 16*self.ai.attackStage;3139 new Projectile('snowball', degrees(angle), self.stats, self.id);3140 new Projectile('snowball', degrees(angle-90), self.stats, self.id);3141 new Projectile('snowball', degrees(angle-180), self.stats, self.id);3142 new Projectile('snowball', degrees(angle-270), self.stats, self.id);3143 }3144 }3145 },3146 cavebird: function(self) {3147 if (self.ai.entityTarget && !self.region.noattack) {3148 if (self.ai.lastAttack > seconds(1.5)) {3149 if (self.ai.attackStage == 1) {3150 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3151 new Projectile('rock', angle+Math.random()*0.3-0.15, self.stats, self.id);3152 } else if (self.ai.attackStage == 3) {3153 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3154 new Projectile('rock', angle+Math.random()*0.3-0.15, self.stats, self.id);3155 } else if (self.ai.attackStage == 5) {3156 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3157 new Projectile('rock', angle+Math.random()*0.3-0.15, self.stats, self.id);3158 self.ai.attackStage = 0;3159 self.ai.lastAttack = 0;3160 }3161 self.ai.attackStage++;3162 }3163 }3164 },3165 ram: function(self) {3166 if (self.ai.entityTarget && !self.region.noattack) {3167 if ((self.ai.lastAttack > seconds(2) && self.getGridDistance(self.ai.entityTarget) < 2) || self.ai.attackStage != 0) {3168 self.ai.lastAttack = 0;3169 var angle = Math.atan2(self.ai.entityTarget.y-self.y, self.ai.entityTarget.x-self.x);3170 self.ai.charge = {3171 x: Math.cos(angle)*20,3172 y: Math.sin(angle)*20,3173 time: 23174 };3175 }3176 }3177 }3178};3179// projectiles3180Projectile = function(type, angle, stats, parentID, x, y) {3181 var self = new Entity();3182 self.entType = 'projectile';3183 self.type = type;3184 self.pierced = 0;3185 self.maxPierce = 0;3186 self.patternUpdateSpeed = 0;3187 self.lastPatternUpdate = 0;3188 try {3189 var tempprojectile = Projectile.types[type];3190 self.type = type;3191 self.width = tempprojectile.width;3192 self.height = tempprojectile.height;3193 self.moveSpeed = tempprojectile.speed;3194 self.damage = tempprojectile.damage;3195 self.noCollision = tempprojectile.noCollision;3196 self.maxRange = tempprojectile.range;3197 self.knockback = tempprojectile.knockback;3198 self.contactEvents = tempprojectile.contactEvents;3199 self.damageType = tempprojectile.damageType;3200 self.invincibilityFrame = tempprojectile.invincibilityFrame;3201 self.deathMessage = tempprojectile.deathMessage;3202 self.pattern = Projectile.patterns[tempprojectile.pattern] ?? function() {error('Invalid projectile pattern "' + tempprojectile.pattern + '"');};3203 self.piercing = tempprojectile.piercing;3204 self.patternUpdateSpeed = tempprojectile.patternUpdateSpeed;3205 self.reflectable = tempprojectile.reflectable;3206 tempprojectile = null;3207 } catch (err) {3208 error(err);3209 return false;3210 }3211 self.angle = angle;3212 self.parentID = parentID;3213 self.parentIsPlayer = true;3214 if (Monster.list[self.parentID]) self.parentIsPlayer = false;3215 var parent = Player.list[self.parentID] ?? Monster.list[self.parentID];3216 try {3217 self.x = x ?? parent.x;3218 self.y = y ?? parent.y;3219 self.map = parent.map;3220 self.layer = parent.layer;3221 self.damage *= stats.attack;3222 self.maxRange *= stats.range;3223 self.moveSpeed *= stats.projectileSpeed;3224 self.knockback *= stats.knockback;3225 self.critChance = stats.critChance;3226 self.critPower = stats.critPower;3227 self.maxPierce = stats.maxPierce;3228 } catch (err) {3229 error(err);3230 return false;3231 }3232 if (!self.piercing) self.maxPierce = 0;3233 self.traveltime = 0;3234 self.sinAngle = Math.sin(self.angle);3235 self.cosAngle = Math.cos(self.angle);3236 self.xspeed = self.cosAngle*self.moveSpeed;3237 self.yspeed = self.sinAngle*self.moveSpeed;3238 self.vertices = [3239 {x: ((self.width/2)*self.cosAngle)-((self.height/2)*self.sinAngle)+self.x, y: ((self.width/2)*self.sinAngle)+((self.height/2)*self.cosAngle)+self.y},3240 {x: ((self.width/2)*self.cosAngle)-((-self.height/2)*self.sinAngle)+self.x, y: ((self.width/2)*self.sinAngle)+((-self.height/2)*self.cosAngle)+self.y},3241 {x: ((-self.width/2)*self.cosAngle)-((-self.height/2)*self.sinAngle)+self.x, y: ((-self.width/2)*self.sinAngle)+((-self.height/2)*self.cosAngle)+self.y},3242 {x: ((-self.width/2)*self.cosAngle)-((self.height/2)*self.sinAngle)+self.x, y: ((-self.width/2)*self.sinAngle)+((self.height/2)*self.cosAngle)+self.y},3243 {x: self.x, y: self.y}3244 ];3245 self.lastvertices = self.vertices;3246 self.physicsInaccuracy = 5;3247 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3248 self.x += self.cosAngle*self.width/2;3249 self.y += self.sinAngle*self.width/2;3250 self.gridx = Math.floor(self.x/64);3251 self.gridy = Math.floor(self.y/64);3252 self.chunkx = Math.floor(self.gridx/Collision.grid[self.map].chunkWidth);3253 self.chunky = Math.floor(self.gridy/Collision.grid[self.map].chunkHeight);3254 self.toDelete = false;3255 self.update = function update() {3256 if (self.toDelete) {3257 delete Projectile.list[self.id];3258 if (Projectile.chunks[self.chunkLocation.map] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];3259 else {3260 error('Could not delete projectile from chunks!');3261 error('Location: map:' + self.chunkLocation.map + ' layer:' + self.chunkLocation.layer + ' x:' + self.chunkLocation.chunkx + ' y:' + self.chunkLocation.chunky);3262 }3263 return;3264 }3265 if (Player.list[self.parentID] == null && Monster.list[self.parentID] == null) {3266 delete Projectile.list[self.id];3267 if (Projectile.chunks[self.chunkLocation.map] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];3268 else {3269 error('Could not delete projectile from chunks!');3270 error('Location: map:' + self.chunkLocation.map + ' layer:' + self.chunkLocation.layer + ' x:' + self.chunkLocation.chunkx + ' y:' + self.chunkLocation.chunky);3271 }3272 return;3273 }3274 self.traveltime++;3275 if (self.traveltime > seconds(self.maxRange)) {3276 self.toDelete = true;3277 return;3278 }3279 if (self.updatePos()) return;3280 if ((!self.parentIsPlayer || ENV.pvp) && Player.chunks[self.map] && Player.chunks[self.map][self.layer]) {3281 var range = Math.ceil(self.collisionBoxSize/(128*Math.max(Collision.grid[self.map].chunkWidth, Collision.grid[self.map].chunkHeight)));3282 for (let y = self.chunky-range; y <= self.chunky+range; y++) {3283 for (let x = self.chunkx-range; x <= self.chunkx+range; x++) {3284 if (Player.chunks[self.map][self.layer][y] && Player.chunks[self.map][self.layer][y][x]) {3285 var players = Player.chunks[self.map][self.layer][y][x];3286 for (let i in players) {3287 if (players[i] && self.collideWith(players[i]) && players[i].alive && i != self.parentID) {3288 let noDelete = players[i].onHit(self, 'projectile');3289 self.pierced++;3290 if (self.pierced > self.maxPierce && !noDelete) self.toDelete = true;3291 return;3292 }3293 }3294 }3295 }3296 }3297 }3298 if ((self.parentIsPlayer || ENV.monsterFriendlyFire) && Monster.chunks[self.map] && Monster.chunks[self.map][self.layer]) {3299 var range = Math.ceil(self.collisionBoxSize/(128*Math.max(Collision.grid[self.map].chunkWidth, Collision.grid[self.map].chunkHeight)));3300 for (let y = self.chunky-range; y <= self.chunky+range; y++) {3301 for (let x = self.chunkx-range; x <= self.chunkx+range; x++) {3302 if (Monster.chunks[self.map][self.layer][y] && Monster.chunks[self.map][self.layer][y][x]) {3303 var monsters = Monster.chunks[self.map][self.layer][y][x];3304 for (let i in monsters) {3305 if (self.collideWith(monsters[i]) && monsters[i].alive && i != self.parentID) {3306 let noDelete = monsters[i].onHit(self, 'projectile');3307 self.pierced++;3308 if (self.pierced > self.maxPierce && !noDelete) self.toDelete = true;3309 return;3310 }3311 }3312 }3313 }3314 }3315 }3316 };3317 self.updatePos = function updatePos() {3318 self.lastPatternUpdate++;3319 self.lastPatternUpdate > self.patternUpdateSpeed && self.pattern(self);3320 self.collide();3321 self.updateChunkLocation();3322 return self.checkPointCollision() && !self.noCollision;3323 };3324 self.checkSpannedCollision = function checkSpannedCollision() {3325 var colliding = false;3326 var width = self.width;3327 var height = self.height3328 self.width += Math.abs(self.x-self.lastx);3329 self.height += Math.abs(self.y-self.lasty);3330 var x = self.x;3331 var y = self.y;3332 self.x = self.lastx;3333 self.y = self.lasty;3334 if (self.checkPointCollision()) colliding = true;3335 self.x = x;3336 self.y = y;3337 self.width = width;3338 self.height = height;3339 if (colliding) self.toDelete = true;3340 return colliding;3341 };3342 self.checkLargeSpannedCollision = function checkLargeSpannedCollision() {3343 var colliding = false;3344 if (self.checkPointCollision()) colliding = true;3345 if (self.checkCollisionLine(self.lastvertices[0].x, self.lastvertices[0].y, self.vertices[0].x, self.vertices[0].y)) colliding = true;3346 if (self.checkCollisionLine(self.lastvertices[1].x, self.lastvertices[1].y, self.vertices[1].x, self.vertices[1].y)) colliding = true;3347 if (self.checkCollisionLine(self.lastvertices[2].x, self.lastvertices[2].y, self.vertices[2].x, self.vertices[2].y)) colliding = true;3348 if (self.checkCollisionLine(self.lastvertices[3].x, self.lastvertices[3].y, self.vertices[3].x, self.vertices[3].y)) colliding = true;3349 if (self.checkCollisionLine(self.lastx, self.lasty, self.x, self.y)) colliding = true;3350 if (colliding) self.toDelete = true;3351 return colliding;3352 };3353 self.checkPointCollision = function checkPointCollision() {3354 var collisions = [];3355 var range = Math.ceil(self.collisionBoxSize/128);3356 for (let x = self.gridx-range; x <= self.gridx+range; x++) {3357 for (let y = self.gridy-range; y <= self.gridy+range; y++) {3358 collisions.push(Collision.getColEntity(self.map, x, y, self.layer));3359 }3360 }3361 self.lastvertices = self.vertices;3362 self.vertices = [3363 {x: ((self.width/2)*self.cosAngle)-((self.height/2)*self.sinAngle)+self.x, y: ((self.width/2)*self.sinAngle)+((self.height/2)*self.cosAngle)+self.y},3364 {x: ((self.width/2)*self.cosAngle)-((-self.height/2)*self.sinAngle)+self.x, y: ((self.width/2)*self.sinAngle)+((-self.height/2)*self.cosAngle)+self.y},3365 {x: ((-self.width/2)*self.cosAngle)-((-self.height/2)*self.sinAngle)+self.x, y: ((-self.width/2)*self.sinAngle)+((-self.height/2)*self.cosAngle)+self.y},3366 {x: ((-self.width/2)*self.cosAngle)-((self.height/2)*self.sinAngle)+self.x, y: ((-self.width/2)*self.sinAngle)+((self.height/2)*self.cosAngle)+self.y},3367 {x: self.x, y: self.y}3368 ];3369 for (let i in collisions) {3370 for (let j in collisions[i]) {3371 if (self.collideWith(collisions[i][j])) {3372 return true;3373 }3374 }3375 }3376 return false;3377 };3378 self.doPointCollision = function doPointCollision() {3379 var colliding = self.checkPointCollision();3380 if (colliding) {3381 self.toDelete = true;3382 for (let event of self.contactEvents) {3383 if (Projectile.contactEvents[event.type]) {3384 Projectile.contactEvents[event.type](self, null, event.data);3385 }3386 }3387 }3388 return colliding;3389 };3390 self.checkLayer = function checkLayer() {};3391 self.checkSlowdown = function checkSlowdown() {};3392 self.collideWith = function collideWith(entity) {3393 if (entity.map == self.map) {3394 if (entity.noProjectile == null || !entity.noProjectile) {3395 if (self.getSquareDistance(entity) <= self.collisionBoxSize/2 + entity.collisionBoxSize/2) {3396 var vertices2 = [3397 {x: entity.x+entity.width/2, y: entity.y+entity.height/2},3398 {x: entity.x+entity.width/2, y: entity.y-entity.height/2},3399 {x: entity.x-entity.width/2, y: entity.y-entity.height/2},3400 {x: entity.x-entity.width/2, y: entity.y+entity.height/2}3401 ];3402 for (let i = 0; i < 4; i++) {3403 if (vertices2[i].y-self.vertices[0].y < (getSlope(self.vertices[0], self.vertices[1])*(vertices2[i].x-self.vertices[0].x))) {3404 if (vertices2[i].y-self.vertices[1].y > (getSlope(self.vertices[1], self.vertices[2])*(vertices2[i].x-self.vertices[1].x))) {3405 if (vertices2[i].y-self.vertices[2].y > (getSlope(self.vertices[2], self.vertices[3])*(vertices2[i].x-self.vertices[2].x))) {3406 if (vertices2[i].y-self.vertices[3].y < (getSlope(self.vertices[3], self.vertices[0])*(vertices2[i].x-self.vertices[3].x))) {3407 return true;3408 }3409 }3410 }3411 }3412 if (self.vertices[i].x > vertices2[2].x && self.vertices[i].x < vertices2[0].x && self.vertices[i].y > vertices2[2].y && self.vertices[i].y < vertices2[0].y) {3413 return true;3414 }3415 }3416 if (self.vertices[4].x > vertices2[2].x && self.vertices[4].x < vertices2[0].x && self.vertices[4].y > vertices2[2].y && self.vertices[4].y < vertices2[0].y) {3417 return true;3418 }3419 }3420 }3421 return false;3422 }3423 };3424 self.updateChunkLocation = function updateChunkLocation() {3425 if (self.map != self.chunkLocation.map || self.layer != self.chunkLocation.layer || self.chunkx != self.chunkLocation.chunkx || self.chunky != self.chunkLocation.chunky) {3426 if (Projectile.chunks[self.chunkLocation.map] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] && Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx]) delete Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id];3427 self.chunkLocation = {3428 map: self.map,3429 layer: self.layer,3430 chunkx: self.chunkx,3431 chunky: self.chunky3432 };3433 if (Projectile.chunks[self.chunkLocation.map] == null) Projectile.chunks[self.chunkLocation.map] = [];3434 if (Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer] == null) Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer] = [];3435 if (Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] == null) Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky] = [];3436 if (Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx] == null) Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx] = [];3437 Projectile.chunks[self.chunkLocation.map][self.chunkLocation.layer][self.chunkLocation.chunky][self.chunkLocation.chunkx][self.id] = self;3438 }3439 };3440 Projectile.list[self.id] = self;3441 self.updateChunkLocation();3442 return self;3443};3444Projectile.update = function update() {3445 var pack = [];3446 for (let i in Projectile.list) {3447 localprojectile = Projectile.list[i];3448 localprojectile.update();3449 pack.push({3450 id: localprojectile.id,3451 map: localprojectile.map,3452 x: localprojectile.x,3453 y: localprojectile.y,3454 layer: localprojectile.layer,3455 chunkx: localprojectile.chunkx,3456 chunky: localprojectile.chunky,3457 angle: localprojectile.angle,3458 type: localprojectile.type3459 });3460 }3461 return pack;3462};3463Projectile.getDebugData = function getDebugData() {3464 var pack = [];3465 for (let i in Projectile.list) {3466 localprojectile = Projectile.list[i];3467 pack.push({3468 map: localprojectile.map,3469 x: localprojectile.x,3470 y: localprojectile.y,3471 width: localprojectile.width,3472 height: localprojectile.height,3473 angle: localprojectile.angle,3474 collisionBoxSize: localprojectile.collisionBoxSize,3475 parentIsPlayer: localprojectile.parentIsPlayer,3476 parent: localprojectile.parent3477 });3478 }3479 3480 return pack;3481};3482Projectile.types = require('./projectile.json');3483Projectile.list = [];3484Projectile.chunks = [];3485Projectile.patterns = {3486 none: function(self) {3487 self.lastPatternUpdate = 0;3488 },3489 spin: function(self) {3490 self.angle += degrees(25);3491 self.sinAngle = Math.sin(self.angle);3492 self.cosAngle = Math.cos(self.angle);3493 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3494 self.lastPatternUpdate = 0;3495 },3496 homing: function(self) {3497 self.angle += degrees(25);3498 self.sinAngle = Math.sin(self.angle);3499 self.cosAngle = Math.cos(self.angle);3500 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3501 var lowest, target;3502 if (self.parentIsPlayer) {3503 if (Monster.chunks[self.map]) {3504 for (let z in Monster.chunks[self.map]) {3505 for (let y = self.chunky-2; y <= self.chunky+2; y++) {3506 for (let x = self.chunkx-2; x <= self.chunkx+2; x++) {3507 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {3508 var monsters = Monster.chunks[self.map][z][y][x];3509 for (let i in monsters) {3510 if (lowest == null || (self.getGridDistance(monsters[i]) < self.getGridDistance(Monster.list[lowest]) && monsters[i].alive)) lowest = i;3511 }3512 }3513 }3514 }3515 }3516 }3517 target = Monster.list[lowest];3518 } else {3519 if (Player.chunks[self.map]) {3520 for (let z in Player.chunks[self.map]) {3521 for (let y = self.chunky-2; y <= self.chunky+2; y++) {3522 for (let x = self.chunkx-2; x <= self.chunkx+2; x++) {3523 if (Player.chunks[self.map][z][y] && Player.chunks[self.map][z][y][x]) {3524 var players = Player.chunks[self.map][z][y][x];3525 for (let i in players) {3526 if (lowest == null || (self.getGridDistance(players[i]) < self.getGridDistance(Player.list[lowest]) && players[i].alive)) lowest = i;3527 }3528 }3529 }3530 }3531 }3532 }3533 target = Player.list[lowest];3534 }3535 if (target) {3536 var angle = Math.atan2(target.y-self.y, target.x-self.x);3537 self.xspeed = Math.cos(angle)*self.moveSpeed;3538 self.yspeed = Math.sin(angle)*self.moveSpeed;3539 }3540 self.sinAngle = Math.sin(self.angle);3541 self.cosAngle = Math.cos(self.angle);3542 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3543 self.lastPatternUpdate = 0;3544 },3545 homing2: function(self) {3546 var lowest, target;3547 if (self.parentIsPlayer) {3548 if (Monster.chunks[self.map]) {3549 for (let z in Monster.chunks[self.map]) {3550 for (let y = self.chunky-2; y <= self.chunky+2; y++) {3551 for (let x = self.chunkx-2; x <= self.chunkx+2; x++) {3552 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {3553 var monsters = Monster.chunks[self.map][z][y][x];3554 for (let i in monsters) {3555 if (lowest == null || (self.getGridDistance(monsters[i]) < self.getGridDistance(Monster.list[lowest]) && monsters[i].alive)) lowest = i;3556 }3557 }3558 }3559 }3560 }3561 }3562 target = Monster.list[lowest];3563 } else {3564 if (Player.chunks[self.map]) {3565 for (let z in Player.chunks[self.map]) {3566 for (let y = self.chunky-2; y <= self.chunky+2; y++) {3567 for (let x = self.chunkx-2; x <= self.chunkx+2; x++) {3568 if (Player.chunks[self.map][z][y] && Player.chunks[self.map][z][y][x]) {3569 var players = Player.chunks[self.map][z][y][x];3570 for (let i in players) {3571 if (lowest == null || (self.getGridDistance(players[i]) < self.getGridDistance(Player.list[lowest]) && players[i].alive)) lowest = i;3572 }3573 }3574 }3575 }3576 }3577 }3578 target = Player.list[lowest];3579 }3580 if (target) {3581 var angle = Math.atan2(target.y-self.y, target.x-self.x);3582 self.angle += Math.min(0.5, Math.max(angle-self.angle, -0.5));3583 self.xspeed = Math.cos(self.angle)*self.moveSpeed;3584 self.yspeed = Math.sin(self.angle)*self.moveSpeed;3585 self.sinAngle = Math.sin(self.angle);3586 self.cosAngle = Math.cos(self.angle);3587 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3588 }3589 self.lastPatternUpdate = 0;3590 },3591 enchantHoming: function(self) {3592 var lowest, target;3593 if (Monster.chunks[self.map]) {3594 for (let z in Monster.chunks[self.map]) {3595 for (let y = self.chunky-2; y <= self.chunky+2; y++) {3596 for (let x = self.chunkx-2; x <= self.chunkx+2; x++) {3597 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {3598 var monsters = Monster.chunks[self.map][z][y][x];3599 for (let i in monsters) {3600 if (lowest == null || (self.getGridDistance(monsters[i]) < self.getGridDistance(Player.list[lowest]) && monsters[i].alive)) lowest = i;3601 }3602 }3603 }3604 }3605 }3606 }3607 target = Monster.list[lowest];3608 if (target) {3609 var angle = Math.atan2(target.y-self.y, target.x-self.x);3610 self.angle += Math.min(0.05, Math.max(angle-self.angle, -0.05));3611 self.xspeed = Math.cos(self.angle)*self.moveSpeed;3612 self.yspeed = Math.sin(self.angle)*self.moveSpeed;3613 self.sinAngle = Math.sin(self.angle);3614 self.cosAngle = Math.cos(self.angle);3615 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3616 }3617 self.lastPatternUpdate = 0;3618 },3619 follow: function(self) {3620 var parent = Player.list[self.parentID] ?? Monster.list[self.parentID];3621 if (parent != null) {3622 self.x = parent.x;3623 self.y = parent.y;3624 self.x += self.cosAngle*(self.width/2+4);3625 self.y += self.sinAngle*(self.width/2+4);3626 self.xspeed = self.cosAngle;3627 self.yspeed = self.sinAngle;3628 self.frozen = true;3629 }3630 self.lastPatternUpdate = 0;3631 },3632 followDir: function(self) {3633 var parent = Player.list[self.parentID] ?? Monster.list[self.parentID];3634 if (parent != null) {3635 self.x = parent.x;3636 self.y = parent.y;3637 if (parent.heldItem) self.angle = parent.heldItem.angle;3638 self.sinAngle = Math.sin(self.angle);3639 self.cosAngle = Math.cos(self.angle);3640 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3641 self.x += self.cosAngle*(self.width/2+4);3642 self.y += self.sinAngle*(self.width/2+4);3643 self.xspeed = self.cosAngle;3644 self.yspeed = self.sinAngle;3645 self.frozen = true;3646 }3647 self.lastPatternUpdate = 0;3648 },3649 orbit: function(self) {3650 var parent = Player.list[self.parentID] ?? Monster.list[self.parentID];3651 if (parent) {3652 self.x = parent.x;3653 self.y = parent.y;3654 self.angle += degrees(18);3655 self.sinAngle = Math.sin(self.angle);3656 self.cosAngle = Math.cos(self.angle);3657 self.collisionBoxSize = Math.max(Math.abs(self.sinAngle*self.height)+Math.abs(self.cosAngle*self.width), Math.abs(self.cosAngle*self.height)+Math.abs(self.sinAngle*self.width));3658 self.x += Math.cos(self.angle)*(self.width/2+4);3659 self.y += Math.sin(self.angle)*(self.width/2+4);3660 self.xspeed = self.cosAngle;3661 self.yspeed = self.sinAngle;3662 self.frozen = true;3663 }3664 self.lastPatternUpdate = 0;3665 }3666};3667Projectile.contactEvents = {3668 explosion: function(self, entity, data) {3669 for (let i = 0; i < data.size*15; i++) {3670 new Particle(self.map, self.x, self.y, 'explosion');3671 }3672 self.explosionSize = data.size;3673 console.log(self.chunkx, self.chunky)3674 if (Monster.chunks[self.map]) {3675 for (let z in Monster.chunks[self.map]) {3676 for (let y = self.chunky-1; y <= self.chunky+1; y++) {3677 for (let x = self.chunkx-1; x <= self.chunkx+1; x++) {3678 if (Monster.chunks[self.map][z][y] && Monster.chunks[self.map][z][y][x]) {3679 var monsters = Monster.chunks[self.map][z][y][x];3680 for (let i in monsters) {3681 if (self.getDistance(monsters[i]) <= 64*data.size && monsters[i].ai.attackType != 'triggeredcherrybomb' && monsters[i].alive) {3682 if (monsters[i].ai.attackType == 'cherrybomb') {3683 monsters[i].ai.attackType = 'triggeredcherrybomb';3684 monsters[i].ai.attackTime = 0;3685 } else {3686 monsters[i].onHit(self, 'explosion');3687 }3688 }3689 }3690 }3691 }3692 }3693 }3694 }3695 if (Player.chunks[self.map]) {3696 for (let z in Player.chunks[self.map]) {3697 for (let y = self.chunky-1; y <= self.chunky+1; y++) {3698 for (let x = self.chunkx-1; x <= self.chunkx+1; x++) {3699 if (Player.chunks[self.map][z][y] && Player.chunks[self.map][z][y][x]) {3700 var players = Player.chunks[self.map][z][y][x];3701 for (let i in players) {3702 if (self.getDistance(players[i]) <= 64*data.size && players[i].alive) {3703 players[i].onHit(self, 'explosion');3704 }3705 }3706 }3707 }3708 }3709 }3710 }3711 },3712 effect: function(self, entity, data) {3713 if (entity) {3714 // do things here3715 }3716 }3717};3718// particles3719Particle = function(map, x, y, type, value) {3720 var self = {3721 map: map,3722 x: x,3723 y: y,3724 chunkx: Math.floor(x/(Collision.grid[map].chunkWidth*64)),3725 chunky: Math.floor(y/(Collision.grid[map].chunkHeight*64)),3726 type: type,3727 value: value3728 };3729 Particle.list.push(self);3730 return self;3731};3732Particle.update = function update() {3733 var pack = [];3734 for (let i in Particle.list) {3735 localparticle = Particle.list[i];3736 pack.push(localparticle);3737 }3738 Particle.list = [];3739 return pack;3740};3741Particle.list = [];3742// dropped items3743DroppedItem = function(map, x, y, itemId, enchantments, stackSize, playerId) {3744 var self = {3745 id: null,3746 x: x,3747 y: y,3748 map: map,3749 chunkx: Math.floor(x/(Collision.grid[map].chunkWidth*64)),3750 chunky: Math.floor(y/(Collision.grid[map].chunkHeight*64)),3751 width: 48,3752 height: 48,3753 itemId: itemId,3754 enchantments: enchantments,3755 stackSize: stackSize,3756 playerId: playerId,3757 entType: 'item'3758 };3759 self.id = Math.random();3760 var valid = false;3761 for (let i in Inventory.items) {3762 if (itemId == i) {3763 valid = true;3764 break;3765 }3766 }3767 if (!valid) self.itemId = 'missing';3768 self.time = 0;3769 self.update = function update() {3770 self.time++;3771 if (self.time >= seconds(ENV.itemDespawnTime*60)) delete DroppedItem.list[self.id];3772 };3773 DroppedItem.list[self.id] = self;3774 return self;3775};3776DroppedItem.update = function update() {3777 var pack = [];3778 for (let i in DroppedItem.list) {3779 localdroppeditem = DroppedItem.list[i];3780 localdroppeditem.update();3781 pack.push({3782 id: localdroppeditem.id,3783 map: localdroppeditem.map,3784 x: localdroppeditem.x,3785 y: localdroppeditem.y,3786 layer: localdroppeditem.layer,3787 chunkx: localdroppeditem.chunkx,3788 chunky: localdroppeditem.chunky,3789 itemId: localdroppeditem.itemId,3790 stackSize: localdroppeditem.stackSize,3791 playerId: localdroppeditem.playerId3792 });3793 }3794 return pack;3795};3796DroppedItem.getDebugData = function getDebugData() {3797 var pack = [];3798 for (let i in DroppedItem.list) {3799 localdroppeditem = DroppedItem.list[i];3800 pack.push({3801 map: localdroppeditem.map,3802 x: localdroppeditem.x,3803 y: localdroppeditem.y,3804 itemId: localdroppeditem.itemId,3805 enchantments: localdroppeditem.enchantments3806 });3807 }3808 return pack;3809};3810DroppedItem.list = [];3811// utility functions3812function getSlope(pos1, pos2) {3813 return (pos2.y - pos1.y) / (pos2.x - pos1.x);3814};3815function seconds(s) {3816 return s*20;3817};3818function ticks(t) {3819 return Math.round(t/20);3820};3821function degrees(d) {3822 return d*Math.PI/180;3823};3824function radians(r) {3825 return r*180/Math.PI;...
jquery.elevatezoom.js
Source:jquery.elevatezoom.js
1/*2 * jQuery elevateZoom 3.0.83 * Demo's and documentation:4 * www.elevateweb.co.uk/image-zoom5 *6 * Copyright (c) 2012 Andrew Eades7 * www.elevateweb.co.uk8 *9 * Dual licensed under the GPL and MIT licenses.10 * http://en.wikipedia.org/wiki/MIT_License11 * http://en.wikipedia.org/wiki/GNU_General_Public_License12 *13/*14 * jQuery elevateZoom 3.0.315 * Demo's and documentation:16 * www.elevateweb.co.uk/image-zoom17 *18 * Copyright (c) 2012 Andrew Eades19 * www.elevateweb.co.uk20 *21 * Dual licensed under the GPL and MIT licenses.22 * http://en.wikipedia.org/wiki/MIT_License23 * http://en.wikipedia.org/wiki/GNU_General_Public_License24 */25if ( typeof Object.create !== 'function' ) {26 Object.create = function( obj ) {27 function F() {};28 F.prototype = obj;29 return new F();30 };31}32(function( $, window, document, undefined ) {33 var ElevateZoom = {34 init: function( options, elem ) {35 var self = this;36 self.elem = elem;37 self.$elem = $( elem );38 self.imageSrc = self.$elem.data("zoom-image") ? self.$elem.data("zoom-image") : self.$elem.attr("src");39 self.options = $.extend( {}, $.fn.elevateZoom.options, options );40 //TINT OVERRIDE SETTINGS41 if(self.options.tint) {42 self.options.lensColour = "none", //colour of the lens background43 self.options.lensOpacity = "1" //opacity of the lens44 }45 //INNER OVERRIDE SETTINGS46 if(self.options.zoomType == "inner") {self.options.showLens = false;}47 //Remove alt on hover48 self.$elem.parent().removeAttr('title').removeAttr('alt');49 self.zoomImage = self.imageSrc;50 self.refresh( 1 );51 //Create the image swap from the gallery 52 $('#'+self.options.gallery + ' a').click( function(e) { 53 //Set a class on the currently active gallery image54 if(self.options.galleryActiveClass){55 $('#'+self.options.gallery + ' a').removeClass(self.options.galleryActiveClass);56 $(this).addClass(self.options.galleryActiveClass);57 }58 //stop any link on the a tag from working59 e.preventDefault();60 //call the swap image function 61 if($(this).data("zoom-image")){self.zoomImagePre = $(this).data("zoom-image")}62 else{self.zoomImagePre = $(this).data("image");}63 self.swaptheimage($(this).data("image"), self.zoomImagePre);64 return false;65 });66 },67 refresh: function( length ) {68 var self = this;69 setTimeout(function() {70 self.fetch(self.imageSrc);71 }, length || self.options.refresh );72 },73 fetch: function(imgsrc) {74 //get the image75 var self = this;76 var newImg = new Image();77 newImg.onload = function() {78 //set the large image dimensions - used to calculte ratio's79 self.largeWidth = newImg.width;80 self.largeHeight = newImg.height;81 //once image is loaded start the calls82 self.startZoom();83 self.currentImage = self.imageSrc;84 //let caller know image has been loaded85 self.options.onZoomedImageLoaded(self.$elem);86 }87 newImg.src = imgsrc; // this must be done AFTER setting onload88 return;89 },90 startZoom: function( ) {91 var self = this;92 //get dimensions of the non zoomed image93 self.nzWidth = self.$elem.width();94 self.nzHeight = self.$elem.height();95 //activated elements96 self.isWindowActive = false;97 self.isLensActive = false;98 self.isTintActive = false;99 self.overWindow = false; 100 //CrossFade Wrappe101 if(self.options.imageCrossfade){102 self.zoomWrap = self.$elem.wrap('<div style="height:'+self.nzHeight+'px;width:'+self.nzWidth+'px;" class="zoomWrapper" />'); 103 self.$elem.css('position', 'absolute'); 104 }105 self.zoomLock = 1;106 self.scrollingLock = false;107 self.changeBgSize = false;108 self.currentZoomLevel = self.options.zoomLevel;109 //get offset of the non zoomed image110 self.nzOffset = self.$elem.offset();111 //calculate the width ratio of the large/small image112 self.widthRatio = (self.largeWidth/self.currentZoomLevel) / self.nzWidth;113 self.heightRatio = (self.largeHeight/self.currentZoomLevel) / self.nzHeight; 114 //if window zoom 115 if(self.options.zoomType == "window") {116 self.zoomWindowStyle = "overflow: hidden;"117 + "background-position: 0px 0px;text-align:center;" 118 + "background-color: " + String(self.options.zoomWindowBgColour) 119 + ";width: " + String(self.options.zoomWindowWidth) + "px;"120 + "height: " + String(self.options.zoomWindowHeight)121 + "px;float: left;"122 + "background-size: "+ self.largeWidth/self.currentZoomLevel+ "px " +self.largeHeight/self.currentZoomLevel + "px;"123 + "display: none;z-index:100;"124 + "border: " + String(self.options.borderSize) 125 + "px solid " + self.options.borderColour 126 + ";background-repeat: no-repeat;"127 + "position: absolute;";128 } 129 //if inner zoom 130 if(self.options.zoomType == "inner") {131 //has a border been put on the image? Lets cater for this132 var borderWidth = self.$elem.css("border-left-width");133 self.zoomWindowStyle = "overflow: hidden;"134 + "margin-left: " + String(borderWidth) + ";" 135 + "margin-top: " + String(borderWidth) + ";" 136 + "background-position: 0px 0px;"137 + "width: " + String(self.nzWidth) + "px;"138 + "height: " + String(self.nzHeight) + "px;"139 + "px;float: left;"140 + "display: none;"141 + "cursor:"+(self.options.cursor)+";"142 + "px solid " + self.options.borderColour 143 + ";background-repeat: no-repeat;"144 + "position: absolute;";145 } 146 //lens style for window zoom147 if(self.options.zoomType == "window") {148 // adjust images less than the window height149 if(self.nzHeight < self.options.zoomWindowWidth/self.widthRatio){150 lensHeight = self.nzHeight; 151 }152 else{153 lensHeight = String((self.options.zoomWindowHeight/self.heightRatio))154 }155 if(self.largeWidth < self.options.zoomWindowWidth){156 lensWidth = self.nzWidth;157 } 158 else{159 lensWidth = (self.options.zoomWindowWidth/self.widthRatio);160 }161 self.lensStyle = "background-position: 0px 0px;width: " + String((self.options.zoomWindowWidth)/self.widthRatio) + "px;height: " + String((self.options.zoomWindowHeight)/self.heightRatio)162 + "px;float: right;display: none;"163 + "overflow: hidden;"164 + "z-index: 999;" 165 + "-webkit-transform: translateZ(0);" 166 + "opacity:"+(self.options.lensOpacity)+";filter: alpha(opacity = "+(self.options.lensOpacity*100)+"); zoom:1;"167 + "width:"+lensWidth+"px;"168 + "height:"+lensHeight+"px;"169 + "background-color:"+(self.options.lensColour)+";" 170 + "cursor:"+(self.options.cursor)+";"171 + "border: "+(self.options.lensBorderSize)+"px" +172 " solid "+(self.options.lensBorderColour)+";background-repeat: no-repeat;position: absolute;";173 } 174 //tint style175 self.tintStyle = "display: block;"176 + "position: absolute;"177 + "background-color: "+self.options.tintColour+";" 178 + "filter:alpha(opacity=0);" 179 + "opacity: 0;" 180 + "width: " + self.nzWidth + "px;"181 + "height: " + self.nzHeight + "px;"182 ;183 //lens style for lens zoom with optional round for modern browsers184 self.lensRound = '';185 if(self.options.zoomType == "lens") {186 self.lensStyle = "background-position: 0px 0px;"187 + "float: left;display: none;"188 + "border: " + String(self.options.borderSize) + "px solid " + self.options.borderColour+";"189 + "width:"+ String(self.options.lensSize) +"px;"190 + "height:"+ String(self.options.lensSize)+"px;"191 + "background-repeat: no-repeat;position: absolute;";192 }193 //does not round in all browsers194 if(self.options.lensShape == "round") {195 self.lensRound = "border-top-left-radius: " + String(self.options.lensSize / 2 + self.options.borderSize) + "px;"196 + "border-top-right-radius: " + String(self.options.lensSize / 2 + self.options.borderSize) + "px;"197 + "border-bottom-left-radius: " + String(self.options.lensSize / 2 + self.options.borderSize) + "px;"198 + "border-bottom-right-radius: " + String(self.options.lensSize / 2 + self.options.borderSize) + "px;";199 }200 //create the div's + ""201 //self.zoomContainer = $('<div/>').addClass('zoomContainer').css({"position":"relative", "height":self.nzHeight, "width":self.nzWidth});202 self.zoomContainer = $('<div class="zoomContainer" style="-webkit-transform: translateZ(0);position:absolute;left:'+self.nzOffset.left+'px;top:'+self.nzOffset.top+'px;height:'+self.nzHeight+'px;width:'+self.nzWidth+'px;"></div>');203 $('body').append(self.zoomContainer); 204 //this will add overflow hidden and contrain the lens on lens mode 205 if(self.options.containLensZoom && self.options.zoomType == "lens"){206 self.zoomContainer.css("overflow", "hidden");207 }208 if(self.options.zoomType != "inner") {209 self.zoomLens = $("<div class='zoomLens' style='" + self.lensStyle + self.lensRound +"'> </div>")210 .appendTo(self.zoomContainer)211 .click(function () {212 self.$elem.trigger('click');213 });214 if(self.options.tint) {215 self.tintContainer = $('<div/>').addClass('tintContainer'); 216 self.zoomTint = $("<div class='zoomTint' style='"+self.tintStyle+"'></div>");217 self.zoomLens.wrap(self.tintContainer);218 self.zoomTintcss = self.zoomLens.after(self.zoomTint); 219 //if tint enabled - set an image to show over the tint220 self.zoomTintImage = $('<img style="position: absolute; left: 0px; top: 0px; max-width: none; width: '+self.nzWidth+'px; height: '+self.nzHeight+'px;" src="'+self.imageSrc+'">')221 .appendTo(self.zoomLens)222 .click(function () {223 self.$elem.trigger('click');224 });225 } 226 }227 //create zoom window 228 if(isNaN(self.options.zoomWindowPosition)){229 self.zoomWindow = $("<div style='z-index:999;left:"+(self.windowOffsetLeft)+"px;top:"+(self.windowOffsetTop)+"px;" + self.zoomWindowStyle + "' class='zoomWindow'> </div>")230 .appendTo('body')231 .click(function () {232 self.$elem.trigger('click');233 });234 }else{235 self.zoomWindow = $("<div style='z-index:999;left:"+(self.windowOffsetLeft)+"px;top:"+(self.windowOffsetTop)+"px;" + self.zoomWindowStyle + "' class='zoomWindow'> </div>")236 .appendTo(self.zoomContainer)237 .click(function () {238 self.$elem.trigger('click');239 });240 } 241 self.zoomWindowContainer = $('<div/>').addClass('zoomWindowContainer').css("width",self.options.zoomWindowWidth);242 self.zoomWindow.wrap(self.zoomWindowContainer);243 // self.captionStyle = "text-align: left;background-color: black;color: white;font-weight: bold;padding: 10px;font-family: sans-serif;font-size: 11px"; 244 // self.zoomCaption = $('<div class="elevatezoom-caption" style="'+self.captionStyle+'display: block; width: 280px;">INSERT ALT TAG</div>').appendTo(self.zoomWindow.parent());245 if(self.options.zoomType == "lens") {246 self.zoomLens.css({ backgroundImage: "url('" + self.imageSrc + "')" }); 247 }248 if(self.options.zoomType == "window") {249 self.zoomWindow.css({ backgroundImage: "url('" + self.imageSrc + "')" }); 250 }251 if(self.options.zoomType == "inner") {252 self.zoomWindow.css({ backgroundImage: "url('" + self.imageSrc + "')" }); 253 }254 /*-------------------END THE ZOOM WINDOW AND LENS----------------------------------*/255 //touch events256 self.$elem.bind('touchmove', function(e){ 257 e.preventDefault();258 var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; 259 self.setPosition(touch);260 }); 261 self.zoomContainer.bind('touchmove', function(e){ 262 if(self.options.zoomType == "inner") {263 self.showHideWindow("show");264 }265 e.preventDefault();266 var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; 267 self.setPosition(touch); 268 }); 269 self.zoomContainer.bind('touchend', function(e){ 270 self.showHideWindow("hide");271 if(self.options.showLens) {self.showHideLens("hide");}272 if(self.options.tint && self.options.zoomType != "inner") {self.showHideTint("hide");}273 }); 274 self.$elem.bind('touchend', function(e){ 275 self.showHideWindow("hide");276 if(self.options.showLens) {self.showHideLens("hide");}277 if(self.options.tint && self.options.zoomType != "inner") {self.showHideTint("hide");}278 }); 279 if(self.options.showLens) {280 self.zoomLens.bind('touchmove', function(e){ 281 e.preventDefault();282 var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; 283 self.setPosition(touch); 284 }); 285 self.zoomLens.bind('touchend', function(e){ 286 self.showHideWindow("hide");287 if(self.options.showLens) {self.showHideLens("hide");}288 if(self.options.tint && self.options.zoomType != "inner") {self.showHideTint("hide");}289 }); 290 }291 //Needed to work in IE292 self.$elem.bind('mousemove', function(e){ 293 if(self.overWindow == false){self.setElements("show");}294 //make sure on orientation change the setposition is not fired295 if(self.lastX !== e.clientX || self.lastY !== e.clientY){296 self.setPosition(e);297 self.currentLoc = e;298 } 299 self.lastX = e.clientX;300 self.lastY = e.clientY; 301 }); 302 self.zoomContainer.bind('mousemove', function(e){ 303 if(self.overWindow == false){self.setElements("show");} 304 //make sure on orientation change the setposition is not fired 305 if(self.lastX !== e.clientX || self.lastY !== e.clientY){306 self.setPosition(e);307 self.currentLoc = e;308 } 309 self.lastX = e.clientX;310 self.lastY = e.clientY; 311 }); 312 if(self.options.zoomType != "inner") {313 self.zoomLens.bind('mousemove', function(e){ 314 //make sure on orientation change the setposition is not fired315 if(self.lastX !== e.clientX || self.lastY !== e.clientY){316 self.setPosition(e);317 self.currentLoc = e;318 } 319 self.lastX = e.clientX;320 self.lastY = e.clientY; 321 });322 }323 if(self.options.tint && self.options.zoomType != "inner") {324 self.zoomTint.bind('mousemove', function(e){ 325 //make sure on orientation change the setposition is not fired326 if(self.lastX !== e.clientX || self.lastY !== e.clientY){327 self.setPosition(e);328 self.currentLoc = e;329 } 330 self.lastX = e.clientX;331 self.lastY = e.clientY; 332 });333 }334 if(self.options.zoomType == "inner") {335 self.zoomWindow.bind('mousemove', function(e) {336 //self.overWindow = true;337 //make sure on orientation change the setposition is not fired338 if(self.lastX !== e.clientX || self.lastY !== e.clientY){339 self.setPosition(e);340 self.currentLoc = e;341 } 342 self.lastX = e.clientX;343 self.lastY = e.clientY; 344 });345 }346 // lensFadeOut: 500, zoomTintFadeIn347 self.zoomContainer.add(self.$elem).mouseenter(function(){348 if(self.overWindow == false){self.setElements("show");} 349 }).mouseleave(function(){350 if(!self.scrollLock){351 self.setElements("hide");352 self.options.onDestroy(self.$elem);353 }354 });355 //end ove image356 if(self.options.zoomType != "inner") {357 self.zoomWindow.mouseenter(function(){358 self.overWindow = true; 359 self.setElements("hide"); 360 }).mouseleave(function(){361 self.overWindow = false;362 });363 }364 //end ove image365// var delta = parseInt(e.originalEvent.wheelDelta || -e.originalEvent.detail);366 // $(this).empty(); 367 // return false;368 //fix for initial zoom setting369 if (self.options.zoomLevel != 1){370 // self.changeZoomLevel(self.currentZoomLevel);371 }372 //set the min zoomlevel373 if(self.options.minZoomLevel){374 self.minZoomLevel = self.options.minZoomLevel;375 }376 else{377 self.minZoomLevel = self.options.scrollZoomIncrement * 2;378 }379 if(self.options.scrollZoom){380 self.zoomContainer.add(self.$elem).bind('mousewheel DOMMouseScroll MozMousePixelScroll', function(e){381// in IE there is issue with firing of mouseleave - So check whether still scrolling382// and on mouseleave check if scrolllock 383 self.scrollLock = true;384 clearTimeout($.data(this, 'timer'));385 $.data(this, 'timer', setTimeout(function() {386 self.scrollLock = false;387 //do something388 }, 250));389 var theEvent = e.originalEvent.wheelDelta || e.originalEvent.detail*-1390 //this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;391 // e.preventDefault();392 e.stopImmediatePropagation();393 e.stopPropagation();394 e.preventDefault();395 if(theEvent /120 > 0) {396 //scrolling up397 if(self.currentZoomLevel >= self.minZoomLevel){ 398 self.changeZoomLevel(self.currentZoomLevel-self.options.scrollZoomIncrement); 399 }400 }401 else{402 //scrolling down403 if(self.options.maxZoomLevel){404 if(self.currentZoomLevel <= self.options.maxZoomLevel){ 405 self.changeZoomLevel(parseFloat(self.currentZoomLevel)+self.options.scrollZoomIncrement);406 }407 }408 else{409 //andy 410 self.changeZoomLevel(parseFloat(self.currentZoomLevel)+self.options.scrollZoomIncrement);411 }412 }413 return false;414 });415 }416 },417 setElements: function(type) {418 var self = this;419 if(!self.options.zoomEnabled){return false;}420 if(type=="show"){421 if(self.isWindowSet){422 if(self.options.zoomType == "inner") {self.showHideWindow("show");}423 if(self.options.zoomType == "window") {self.showHideWindow("show");}424 if(self.options.showLens) {self.showHideLens("show");}425 if(self.options.tint && self.options.zoomType != "inner") {self.showHideTint("show");426 }427 }428 }429 if(type=="hide"){430 if(self.options.zoomType == "window") {self.showHideWindow("hide");}431 if(!self.options.tint) {self.showHideWindow("hide");}432 if(self.options.showLens) {self.showHideLens("hide");}433 if(self.options.tint) { self.showHideTint("hide");}434 } 435 },436 setPosition: function(e) {437 438 var self = this;439 440 if(!self.options.zoomEnabled){return false;}441 //recaclc offset each time in case the image moves442 //this can be caused by other on page elements443 self.nzHeight = self.$elem.height();444 self.nzWidth = self.$elem.width();445 self.nzOffset = self.$elem.offset();446 if(self.options.tint && self.options.zoomType != "inner") {447 self.zoomTint.css({ top: 0});448 self.zoomTint.css({ left: 0});449 }450 //set responsive 451 //will checking if the image needs changing before running this code work faster?452 if(self.options.responsive && !self.options.scrollZoom){453 if(self.options.showLens){ 454 if(self.nzHeight < self.options.zoomWindowWidth/self.widthRatio){455 lensHeight = self.nzHeight; 456 }457 else{458 lensHeight = String((self.options.zoomWindowHeight/self.heightRatio))459 }460 if(self.largeWidth < self.options.zoomWindowWidth){461 lensWidth = self.nzWidth;462 } 463 else{464 lensWidth = (self.options.zoomWindowWidth/self.widthRatio);465 }466 self.widthRatio = self.largeWidth / self.nzWidth;467 self.heightRatio = self.largeHeight / self.nzHeight; 468 if(self.options.zoomType != "lens") {469 //possibly dont need to keep recalcalculating470 //if the lens is heigher than the image, then set lens size to image size471 if(self.nzHeight < self.options.zoomWindowWidth/self.widthRatio){472 lensHeight = self.nzHeight; 473 }474 else{475 lensHeight = String((self.options.zoomWindowHeight/self.heightRatio))476 }477 if(self.nzWidth < self.options.zoomWindowHeight/self.heightRatio){478 lensWidth = self.nzWidth;479 } 480 else{481 lensWidth = String((self.options.zoomWindowWidth/self.widthRatio));482 } 483 self.zoomLens.css('width', lensWidth); 484 self.zoomLens.css('height', lensHeight); 485 if(self.options.tint){ 486 self.zoomTintImage.css('width', self.nzWidth); 487 self.zoomTintImage.css('height', self.nzHeight); 488 }489 } 490 if(self.options.zoomType == "lens") { 491 self.zoomLens.css({ width: String(self.options.lensSize) + 'px', height: String(self.options.lensSize) + 'px' }) 492 } 493 //end responsive image change494 }495 }496 //container fix497 self.zoomContainer.css({ top: self.nzOffset.top});498 self.zoomContainer.css({ left: self.nzOffset.left});499 self.mouseLeft = parseInt(e.pageX - self.nzOffset.left);500 self.mouseTop = parseInt(e.pageY - self.nzOffset.top);501 //calculate the Location of the Lens502 //calculate the bound regions - but only if zoom window503 if(self.options.zoomType == "window") {504 self.Etoppos = (self.mouseTop < (self.zoomLens.height()/2));505 self.Eboppos = (self.mouseTop > self.nzHeight - (self.zoomLens.height()/2)-(self.options.lensBorderSize*2));506 self.Eloppos = (self.mouseLeft < 0+((self.zoomLens.width()/2))); 507 self.Eroppos = (self.mouseLeft > (self.nzWidth - (self.zoomLens.width()/2)-(self.options.lensBorderSize*2))); 508 }509 //calculate the bound regions - but only for inner zoom510 if(self.options.zoomType == "inner"){ 511 self.Etoppos = (self.mouseTop < ((self.nzHeight/2)/self.heightRatio) );512 self.Eboppos = (self.mouseTop > (self.nzHeight - ((self.nzHeight/2)/self.heightRatio)));513 self.Eloppos = (self.mouseLeft < 0+(((self.nzWidth/2)/self.widthRatio)));514 self.Eroppos = (self.mouseLeft > (self.nzWidth - (self.nzWidth/2)/self.widthRatio-(self.options.lensBorderSize*2))); 515 }516 // if the mouse position of the slider is one of the outerbounds, then hide window and lens517 if (self.mouseLeft < 0 || self.mouseTop < 0 || self.mouseLeft > self.nzWidth || self.mouseTop > self.nzHeight ) { 518 self.setElements("hide");519 return;520 }521 //else continue with operations522 else {523 //lens options524 if(self.options.showLens) {525 // self.showHideLens("show");526 //set background position of lens527 self.lensLeftPos = String(Math.floor(self.mouseLeft - self.zoomLens.width() / 2));528 self.lensTopPos = String(Math.floor(self.mouseTop - self.zoomLens.height() / 2));529 }530 //adjust the background position if the mouse is in one of the outer regions 531 //Top region532 if(self.Etoppos){533 self.lensTopPos = 0;534 }535 //Left Region536 if(self.Eloppos){537 self.windowLeftPos = 0;538 self.lensLeftPos = 0;539 self.tintpos=0;540 } 541 //Set bottom and right region for window mode542 if(self.options.zoomType == "window") {543 if(self.Eboppos){544 self.lensTopPos = Math.max( (self.nzHeight)-self.zoomLens.height()-(self.options.lensBorderSize*2), 0 );545 } 546 if(self.Eroppos){547 self.lensLeftPos = (self.nzWidth-(self.zoomLens.width())-(self.options.lensBorderSize*2));548 } 549 } 550 //Set bottom and right region for inner mode551 if(self.options.zoomType == "inner") {552 if(self.Eboppos){553 self.lensTopPos = Math.max( ((self.nzHeight)-(self.options.lensBorderSize*2)), 0 );554 } 555 if(self.Eroppos){556 self.lensLeftPos = (self.nzWidth-(self.nzWidth)-(self.options.lensBorderSize*2));557 } 558 }559 //if lens zoom560 if(self.options.zoomType == "lens") { 561 self.windowLeftPos = String(((e.pageX - self.nzOffset.left) * self.widthRatio - self.zoomLens.width() / 2) * (-1)); 562 self.windowTopPos = String(((e.pageY - self.nzOffset.top) * self.heightRatio - self.zoomLens.height() / 2) * (-1));563 self.zoomLens.css({ backgroundPosition: self.windowLeftPos + 'px ' + self.windowTopPos + 'px' });564 if(self.changeBgSize){ 565 if(self.nzHeight>self.nzWidth){ 566 if(self.options.zoomType == "lens"){ 567 self.zoomLens.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });568 } 569 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });570 }571 else{ 572 if(self.options.zoomType == "lens"){ 573 self.zoomLens.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' });574 } 575 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' }); 576 }577 self.changeBgSize = false;578 } 579 self.setWindowPostition(e); 580 }581 //if tint zoom 582 if(self.options.tint && self.options.zoomType != "inner") {583 self.setTintPosition(e);584 }585 //set the css background position 586 if(self.options.zoomType == "window") {587 self.setWindowPostition(e); 588 }589 if(self.options.zoomType == "inner") {590 self.setWindowPostition(e); 591 }592 if(self.options.showLens) {593 if(self.fullwidth && self.options.zoomType != "lens"){594 self.lensLeftPos = 0;595 }596 self.zoomLens.css({ left: self.lensLeftPos + 'px', top: self.lensTopPos + 'px' }) 597 }598 } //end else599 },600 showHideWindow: function(change) {601 var self = this; 602 if(change == "show"){ 603 if(!self.isWindowActive){604 if(self.options.zoomWindowFadeIn){605 self.zoomWindow.stop(true, true, false).fadeIn(self.options.zoomWindowFadeIn);606 }607 else{self.zoomWindow.show();}608 self.isWindowActive = true;609 } 610 }611 if(change == "hide"){612 if(self.isWindowActive){613 if(self.options.zoomWindowFadeOut){614 self.zoomWindow.stop(true, true).fadeOut(self.options.zoomWindowFadeOut, function () {615 if (self.loop) {616 //stop moving the zoom window when zoom window is faded out617 clearInterval(self.loop);618 self.loop = false;619 }620 });621 }622 else{self.zoomWindow.hide();}623 self.isWindowActive = false; 624 } 625 }626 },627 showHideLens: function(change) {628 var self = this; 629 if(change == "show"){ 630 if(!self.isLensActive){631 if(self.options.lensFadeIn){632 self.zoomLens.stop(true, true, false).fadeIn(self.options.lensFadeIn);633 }634 else{self.zoomLens.show();}635 self.isLensActive = true;636 } 637 }638 if(change == "hide"){639 if(self.isLensActive){640 if(self.options.lensFadeOut){641 self.zoomLens.stop(true, true).fadeOut(self.options.lensFadeOut);642 }643 else{self.zoomLens.hide();}644 self.isLensActive = false; 645 } 646 }647 },648 showHideTint: function(change) {649 var self = this; 650 if(change == "show"){ 651 if(!self.isTintActive){652 if(self.options.zoomTintFadeIn){653 self.zoomTint.css({opacity:self.options.tintOpacity}).animate().stop(true, true).fadeIn("slow");654 }655 else{656 self.zoomTint.css({opacity:self.options.tintOpacity}).animate();657 self.zoomTint.show();658 }659 self.isTintActive = true;660 } 661 }662 if(change == "hide"){ 663 if(self.isTintActive){ 664 if(self.options.zoomTintFadeOut){665 self.zoomTint.stop(true, true).fadeOut(self.options.zoomTintFadeOut);666 }667 else{self.zoomTint.hide();}668 self.isTintActive = false; 669 } 670 }671 },672 setLensPostition: function( e ) {673 },674 setWindowPostition: function( e ) {675 //return obj.slice( 0, count );676 var self = this;677 if(!isNaN(self.options.zoomWindowPosition)){678 switch (self.options.zoomWindowPosition) { 679 case 1: //done 680 self.windowOffsetTop = (self.options.zoomWindowOffety);//DONE - 1681 self.windowOffsetLeft =(+self.nzWidth); //DONE 1, 2, 3, 4, 16682 break;683 case 2:684 if(self.options.zoomWindowHeight > self.nzHeight){ //positive margin685 self.windowOffsetTop = ((self.options.zoomWindowHeight/2)-(self.nzHeight/2))*(-1);686 self.windowOffsetLeft =(self.nzWidth); //DONE 1, 2, 3, 4, 16687 }688 else{ //negative margin689 }690 break;691 case 3: //done 692 self.windowOffsetTop = (self.nzHeight - self.zoomWindow.height() - (self.options.borderSize*2)); //DONE 3,9693 self.windowOffsetLeft =(self.nzWidth); //DONE 1, 2, 3, 4, 16694 break; 695 case 4: //done 696 self.windowOffsetTop = (self.nzHeight); //DONE - 4,5,6,7,8697 self.windowOffsetLeft =(self.nzWidth); //DONE 1, 2, 3, 4, 16698 break;699 case 5: //done 700 self.windowOffsetTop = (self.nzHeight); //DONE - 4,5,6,7,8701 self.windowOffsetLeft =(self.nzWidth-self.zoomWindow.width()-(self.options.borderSize*2)); //DONE - 5,15702 break;703 case 6: 704 if(self.options.zoomWindowHeight > self.nzHeight){ //positive margin705 self.windowOffsetTop = (self.nzHeight); //DONE - 4,5,6,7,8706 self.windowOffsetLeft =((self.options.zoomWindowWidth/2)-(self.nzWidth/2)+(self.options.borderSize*2))*(-1); 707 }708 else{ //negative margin709 }710 break;711 case 7: //done 712 self.windowOffsetTop = (self.nzHeight); //DONE - 4,5,6,7,8713 self.windowOffsetLeft = 0; //DONE 7, 13714 break;715 case 8: //done 716 self.windowOffsetTop = (self.nzHeight); //DONE - 4,5,6,7,8717 self.windowOffsetLeft =(self.zoomWindow.width()+(self.options.borderSize*2) )* (-1); //DONE 8,9,10,11,12718 break;719 case 9: //done 720 self.windowOffsetTop = (self.nzHeight - self.zoomWindow.height() - (self.options.borderSize*2)); //DONE 3,9721 self.windowOffsetLeft =(self.zoomWindow.width()+(self.options.borderSize*2) )* (-1); //DONE 8,9,10,11,12722 break;723 case 10: 724 if(self.options.zoomWindowHeight > self.nzHeight){ //positive margin725 self.windowOffsetTop = ((self.options.zoomWindowHeight/2)-(self.nzHeight/2))*(-1);726 self.windowOffsetLeft =(self.zoomWindow.width()+(self.options.borderSize*2) )* (-1); //DONE 8,9,10,11,12727 }728 else{ //negative margin729 }730 break;731 case 11: 732 self.windowOffsetTop = (self.options.zoomWindowOffety);733 self.windowOffsetLeft =(self.zoomWindow.width()+(self.options.borderSize*2) )* (-1); //DONE 8,9,10,11,12734 break;735 case 12: //done 736 self.windowOffsetTop = (self.zoomWindow.height()+(self.options.borderSize*2))*(-1); //DONE 12,13,14,15,16737 self.windowOffsetLeft =(self.zoomWindow.width()+(self.options.borderSize*2) )* (-1); //DONE 8,9,10,11,12738 break;739 case 13: //done 740 self.windowOffsetTop = (self.zoomWindow.height()+(self.options.borderSize*2))*(-1); //DONE 12,13,14,15,16741 self.windowOffsetLeft =(0); //DONE 7, 13742 break;743 case 14: 744 if(self.options.zoomWindowHeight > self.nzHeight){ //positive margin745 self.windowOffsetTop = (self.zoomWindow.height()+(self.options.borderSize*2))*(-1); //DONE 12,13,14,15,16746 self.windowOffsetLeft =((self.options.zoomWindowWidth/2)-(self.nzWidth/2)+(self.options.borderSize*2))*(-1); 747 }748 else{ //negative margin749 }750 break;751 case 15://done 752 self.windowOffsetTop = (self.zoomWindow.height()+(self.options.borderSize*2))*(-1); //DONE 12,13,14,15,16753 self.windowOffsetLeft =(self.nzWidth-self.zoomWindow.width()-(self.options.borderSize*2)); //DONE - 5,15754 break;755 case 16: //done 756 self.windowOffsetTop = (self.zoomWindow.height()+(self.options.borderSize*2))*(-1); //DONE 12,13,14,15,16757 self.windowOffsetLeft =(self.nzWidth); //DONE 1, 2, 3, 4, 16758 break; 759 default: //done 760 self.windowOffsetTop = (self.options.zoomWindowOffety);//DONE - 1761 self.windowOffsetLeft =(self.nzWidth); //DONE 1, 2, 3, 4, 16762 } 763 } //end isNAN764 else{765 //WE CAN POSITION IN A CLASS - ASSUME THAT ANY STRING PASSED IS766 self.externalContainer = $('#'+self.options.zoomWindowPosition);767 self.externalContainerWidth = self.externalContainer.width();768 self.externalContainerHeight = self.externalContainer.height();769 self.externalContainerOffset = self.externalContainer.offset();770 self.windowOffsetTop = self.externalContainerOffset.top;//DONE - 1771 self.windowOffsetLeft =self.externalContainerOffset.left; //DONE 1, 2, 3, 4, 16772 }773 self.isWindowSet = true;774 self.windowOffsetTop = self.windowOffsetTop + self.options.zoomWindowOffety;775 self.windowOffsetLeft = self.windowOffsetLeft + self.options.zoomWindowOffetx;776 self.zoomWindow.css({ top: self.windowOffsetTop});777 self.zoomWindow.css({ left: self.windowOffsetLeft});778 if(self.options.zoomType == "inner") {779 self.zoomWindow.css({ top: 0});780 self.zoomWindow.css({ left: 0});781 } 782 self.windowLeftPos = String(((e.pageX - self.nzOffset.left) * self.widthRatio - self.zoomWindow.width() / 2) * (-1)); 783 self.windowTopPos = String(((e.pageY - self.nzOffset.top) * self.heightRatio - self.zoomWindow.height() / 2) * (-1));784 if(self.Etoppos){self.windowTopPos = 0;}785 if(self.Eloppos){self.windowLeftPos = 0;} 786 if(self.Eboppos){self.windowTopPos = (self.largeHeight/self.currentZoomLevel-self.zoomWindow.height())*(-1); } 787 if(self.Eroppos){self.windowLeftPos = ((self.largeWidth/self.currentZoomLevel-self.zoomWindow.width())*(-1));} 788 //stops micro movements789 if(self.fullheight){790 self.windowTopPos = 0;791 }792 if(self.fullwidth){793 self.windowLeftPos = 0;794 }795 //set the css background position 796 if(self.options.zoomType == "window" || self.options.zoomType == "inner") {797 if(self.zoomLock == 1){798 //overrides for images not zoomable799 if(self.widthRatio <= 1){800 self.windowLeftPos = 0;801 }802 if(self.heightRatio <= 1){ 803 self.windowTopPos = 0;804 }805 }806 // adjust images less than the window height807 if (self.options.zoomType == "window") {808 if (self.largeHeight < self.options.zoomWindowHeight) {809 self.windowTopPos = 0;810 }811 if (self.largeWidth < self.options.zoomWindowWidth) {812 self.windowLeftPos = 0;813 }814 }815 //set the zoomwindow background position816 if (self.options.easing){817 // if(self.changeZoom){818 // clearInterval(self.loop);819 // self.changeZoom = false;820 // self.loop = false;821 // }822 //set the pos to 0 if not set823 if(!self.xp){self.xp = 0;}824 if(!self.yp){self.yp = 0;} 825 //if loop not already started, then run it 826 if (!self.loop){ 827 self.loop = setInterval(function(){ 828 //using zeno's paradox 829 self.xp += (self.windowLeftPos - self.xp) / self.options.easingAmount; 830 self.yp += (self.windowTopPos - self.yp) / self.options.easingAmount;831 if(self.scrollingLock){832 clearInterval(self.loop);833 self.xp = self.windowLeftPos;834 self.yp = self.windowTopPos 835 self.xp = ((e.pageX - self.nzOffset.left) * self.widthRatio - self.zoomWindow.width() / 2) * (-1);836 self.yp = (((e.pageY - self.nzOffset.top) * self.heightRatio - self.zoomWindow.height() / 2) * (-1)); 837 if(self.changeBgSize){ 838 if(self.nzHeight>self.nzWidth){ 839 if(self.options.zoomType == "lens"){ 840 self.zoomLens.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });841 } 842 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });843 }844 else{ 845 if(self.options.zoomType != "lens"){ 846 self.zoomLens.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvalueheight + 'px' });847 } 848 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' }); 849 }850 /*851 if(!self.bgxp){self.bgxp = self.largeWidth/self.newvalue;}852 if(!self.bgyp){self.bgyp = self.largeHeight/self.newvalue ;} 853 if (!self.bgloop){ 854 self.bgloop = setInterval(function(){ 855 self.bgxp += (self.largeWidth/self.newvalue - self.bgxp) / self.options.easingAmount; 856 self.bgyp += (self.largeHeight/self.newvalue - self.bgyp) / self.options.easingAmount;857 self.zoomWindow.css({ "background-size": self.bgxp + 'px ' + self.bgyp + 'px' });858 }, 16);859 }860 */861 self.changeBgSize = false;862 }863 self.zoomWindow.css({ backgroundPosition: self.windowLeftPos + 'px ' + self.windowTopPos + 'px' });864 self.scrollingLock = false;865 self.loop = false;866 }867 else if (Math.round(Math.abs(self.xp - self.windowLeftPos) + Math.abs(self.yp - self.windowTopPos)) < 1) {868 //stops micro movements869 clearInterval(self.loop);870 self.zoomWindow.css({ backgroundPosition: self.windowLeftPos + 'px ' + self.windowTopPos + 'px' });871 self.loop = false;872 }873 else{874 if(self.changeBgSize){ 875 if(self.nzHeight>self.nzWidth){ 876 if(self.options.zoomType == "lens"){ 877 self.zoomLens.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });878 } 879 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });880 }881 else{ 882 if(self.options.zoomType != "lens"){ 883 self.zoomLens.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' });884 } 885 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' }); 886 }887 self.changeBgSize = false;888 } 889 self.zoomWindow.css({ backgroundPosition: self.xp + 'px ' + self.yp + 'px' });890 } 891 }, 16);892 }893 } 894 else{ 895 if(self.changeBgSize){ 896 if(self.nzHeight>self.nzWidth){ 897 if(self.options.zoomType == "lens"){ 898 self.zoomLens.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });899 } 900 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' });901 }902 else{ 903 if(self.options.zoomType == "lens"){ 904 self.zoomLens.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' });905 } 906 if((self.largeHeight/self.newvaluewidth) < self.options.zoomWindowHeight){ 907 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvaluewidth + 'px ' + self.largeHeight/self.newvaluewidth + 'px' }); 908 }909 else{910 self.zoomWindow.css({ "background-size": self.largeWidth/self.newvalueheight + 'px ' + self.largeHeight/self.newvalueheight + 'px' }); 911 }912 }913 self.changeBgSize = false;914 } 915 self.zoomWindow.css({ backgroundPosition: self.windowLeftPos + 'px ' + self.windowTopPos + 'px' }); 916 }917 } 918 },919 setTintPosition: function(e){920 var self = this;921 self.nzOffset = self.$elem.offset();922 self.tintpos = String(((e.pageX - self.nzOffset.left)-(self.zoomLens.width() / 2)) * (-1)); 923 self.tintposy = String(((e.pageY - self.nzOffset.top) - self.zoomLens.height() / 2) * (-1)); 924 if(self.Etoppos){925 self.tintposy = 0;926 }927 if(self.Eloppos){928 self.tintpos=0;929 } 930 if(self.Eboppos){931 self.tintposy = (self.nzHeight-self.zoomLens.height()-(self.options.lensBorderSize*2))*(-1);932 } 933 if(self.Eroppos){934 self.tintpos = ((self.nzWidth-self.zoomLens.width()-(self.options.lensBorderSize*2))*(-1));935 } 936 if(self.options.tint) {937 //stops micro movements938 if(self.fullheight){939 self.tintposy = 0;940 }941 if(self.fullwidth){ 942 self.tintpos = 0;943 } 944 self.zoomTintImage.css({'left': self.tintpos+'px'});945 self.zoomTintImage.css({'top': self.tintposy+'px'});946 }947 },948 swaptheimage: function(smallimage, largeimage){949 var self = this;950 var newImg = new Image(); 951 if(self.options.loadingIcon){952 self.spinner = $('<div style="background: url(\''+self.options.loadingIcon+'\') no-repeat center;height:'+self.nzHeight+'px;width:'+self.nzWidth+'px;z-index: 2000;position: absolute; background-position: center center;"></div>');953 self.$elem.after(self.spinner);954 }955 self.options.onImageSwap(self.$elem);956 newImg.onload = function() {957 self.largeWidth = newImg.width;958 self.largeHeight = newImg.height;959 self.zoomImage = largeimage;960 self.zoomWindow.css({ "background-size": self.largeWidth + 'px ' + self.largeHeight + 'px' });961 self.swapAction(smallimage, largeimage);962 return; 963 } 964 newImg.src = largeimage; // this must be done AFTER setting onload965 },966 swapAction: function(smallimage, largeimage){967 var self = this; 968 var newImg2 = new Image(); 969 newImg2.onload = function() {970 //re-calculate values971 self.nzHeight = newImg2.height;972 self.nzWidth = newImg2.width;973 self.options.onImageSwapComplete(self.$elem);974 self.doneCallback(); 975 return; 976 } 977 newImg2.src = smallimage; 978 //reset the zoomlevel to that initially set in options979 self.currentZoomLevel = self.options.zoomLevel;980 self.options.maxZoomLevel = false;981 //swaps the main image982 //self.$elem.attr("src",smallimage);983 //swaps the zoom image 984 if(self.options.zoomType == "lens") {985 self.zoomLens.css({ backgroundImage: "url('" + largeimage + "')" }); 986 }987 if(self.options.zoomType == "window") {988 self.zoomWindow.css({ backgroundImage: "url('" + largeimage + "')" }); 989 }990 if(self.options.zoomType == "inner") {991 self.zoomWindow.css({ backgroundImage: "url('" + largeimage + "')" }); 992 } 993 self.currentImage = largeimage;994 if(self.options.imageCrossfade){995 var oldImg = self.$elem;996 var newImg = oldImg.clone(); 997 self.$elem.attr("src",smallimage)998 self.$elem.after(newImg);999 newImg.stop(true).fadeOut(self.options.imageCrossfade, function() {1000 $(this).remove(); 1001 });1002 // if(self.options.zoomType == "inner"){1003 //remove any attributes on the cloned image so we can resize later1004 self.$elem.width("auto").removeAttr("width");1005 self.$elem.height("auto").removeAttr("height");1006 // }1007 oldImg.fadeIn(self.options.imageCrossfade);1008 if(self.options.tint && self.options.zoomType != "inner") {1009 var oldImgTint = self.zoomTintImage;1010 var newImgTint = oldImgTint.clone(); 1011 self.zoomTintImage.attr("src",largeimage)1012 self.zoomTintImage.after(newImgTint);1013 newImgTint.stop(true).fadeOut(self.options.imageCrossfade, function() {1014 $(this).remove(); 1015 });1016 oldImgTint.fadeIn(self.options.imageCrossfade);1017 //self.zoomTintImage.attr("width",elem.data("image"));1018 //resize the tint window1019 self.zoomTint.css({ height: self.$elem.height()});1020 self.zoomTint.css({ width: self.$elem.width()});1021 } 1022 self.zoomContainer.css("height", self.$elem.height());1023 self.zoomContainer.css("width", self.$elem.width());1024 if(self.options.zoomType == "inner"){ 1025 if(!self.options.constrainType){1026 self.zoomWrap.parent().css("height", self.$elem.height());1027 self.zoomWrap.parent().css("width", self.$elem.width());1028 self.zoomWindow.css("height", self.$elem.height());1029 self.zoomWindow.css("width", self.$elem.width());1030 }1031 } 1032 if(self.options.imageCrossfade){ 1033 self.zoomWrap.css("height", self.$elem.height());1034 self.zoomWrap.css("width", self.$elem.width());1035 } 1036 }1037 else{1038 self.$elem.attr("src",smallimage); 1039 if(self.options.tint) {1040 self.zoomTintImage.attr("src",largeimage);1041 //self.zoomTintImage.attr("width",elem.data("image"));1042 self.zoomTintImage.attr("height",self.$elem.height());1043 //self.zoomTintImage.attr('src') = elem.data("image");1044 self.zoomTintImage.css({ height: self.$elem.height()}); 1045 self.zoomTint.css({ height: self.$elem.height()});1046 }1047 self.zoomContainer.css("height", self.$elem.height());1048 self.zoomContainer.css("width", self.$elem.width());1049 if(self.options.imageCrossfade){ 1050 self.zoomWrap.css("height", self.$elem.height());1051 self.zoomWrap.css("width", self.$elem.width());1052 } 1053 } 1054 if(self.options.constrainType){ 1055 //This will contrain the image proportions1056 if(self.options.constrainType == "height"){ 1057 self.zoomContainer.css("height", self.options.constrainSize);1058 self.zoomContainer.css("width", "auto");1059 if(self.options.imageCrossfade){ 1060 self.zoomWrap.css("height", self.options.constrainSize);1061 self.zoomWrap.css("width", "auto"); 1062 self.constwidth = self.zoomWrap.width();1063 }1064 else{ 1065 self.$elem.css("height", self.options.constrainSize);1066 self.$elem.css("width", "auto");1067 self.constwidth = self.$elem.width();1068 } 1069 if(self.options.zoomType == "inner"){1070 self.zoomWrap.parent().css("height", self.options.constrainSize);1071 self.zoomWrap.parent().css("width", self.constwidth); 1072 self.zoomWindow.css("height", self.options.constrainSize);1073 self.zoomWindow.css("width", self.constwidth); 1074 } 1075 if(self.options.tint){1076 self.tintContainer.css("height", self.options.constrainSize);1077 self.tintContainer.css("width", self.constwidth);1078 self.zoomTint.css("height", self.options.constrainSize);1079 self.zoomTint.css("width", self.constwidth);1080 self.zoomTintImage.css("height", self.options.constrainSize);1081 self.zoomTintImage.css("width", self.constwidth); 1082 } 1083 }1084 if(self.options.constrainType == "width"){ 1085 self.zoomContainer.css("height", "auto");1086 self.zoomContainer.css("width", self.options.constrainSize);1087 if(self.options.imageCrossfade){1088 self.zoomWrap.css("height", "auto");1089 self.zoomWrap.css("width", self.options.constrainSize);1090 self.constheight = self.zoomWrap.height();1091 }1092 else{ 1093 self.$elem.css("height", "auto");1094 self.$elem.css("width", self.options.constrainSize); 1095 self.constheight = self.$elem.height(); 1096 } 1097 if(self.options.zoomType == "inner"){1098 self.zoomWrap.parent().css("height", self.constheight);1099 self.zoomWrap.parent().css("width", self.options.constrainSize); 1100 self.zoomWindow.css("height", self.constheight);1101 self.zoomWindow.css("width", self.options.constrainSize); 1102 } 1103 if(self.options.tint){1104 self.tintContainer.css("height", self.constheight);1105 self.tintContainer.css("width", self.options.constrainSize);1106 self.zoomTint.css("height", self.constheight);1107 self.zoomTint.css("width", self.options.constrainSize);1108 self.zoomTintImage.css("height", self.constheight);1109 self.zoomTintImage.css("width", self.options.constrainSize); 1110 } 1111 } 1112 }1113 },1114 doneCallback: function(){1115 var self = this;1116 if(self.options.loadingIcon){1117 self.spinner.hide(); 1118 } 1119 self.nzOffset = self.$elem.offset();1120 self.nzWidth = self.$elem.width();1121 self.nzHeight = self.$elem.height();1122 // reset the zoomlevel back to default1123 self.currentZoomLevel = self.options.zoomLevel;1124 //ratio of the large to small image1125 self.widthRatio = self.largeWidth / self.nzWidth;1126 self.heightRatio = self.largeHeight / self.nzHeight; 1127 //NEED TO ADD THE LENS SIZE FOR ROUND1128 // adjust images less than the window height1129 if(self.options.zoomType == "window") {1130 if(self.nzHeight < self.options.zoomWindowWidth/self.widthRatio){1131 lensHeight = self.nzHeight; 1132 }1133 else{1134 lensHeight = String((self.options.zoomWindowHeight/self.heightRatio))1135 }1136 if(self.options.zoomWindowWidth < self.options.zoomWindowWidth){1137 lensWidth = self.nzWidth;1138 } 1139 else{1140 lensWidth = (self.options.zoomWindowWidth/self.widthRatio);1141 }1142 if(self.zoomLens){1143 self.zoomLens.css('width', lensWidth); 1144 self.zoomLens.css('height', lensHeight); 1145 }1146 }1147 },1148 getCurrentImage: function(){1149 var self = this; 1150 return self.zoomImage; 1151 }, 1152 getGalleryList: function(){1153 var self = this; 1154 //loop through the gallery options and set them in list for fancybox1155 self.gallerylist = [];1156 if (self.options.gallery){ 1157 $('#'+self.options.gallery + ' a').each(function() {1158 var img_src = '';1159 if($(this).data("zoom-image")){1160 img_src = $(this).data("zoom-image");1161 }1162 else if($(this).data("image")){1163 img_src = $(this).data("image");1164 } 1165 //put the current image at the start1166 if(img_src == self.zoomImage){1167 self.gallerylist.unshift({1168 href: ''+img_src+'',1169 title: $(this).find('img').attr("title")1170 }); 1171 }1172 else{1173 self.gallerylist.push({1174 href: ''+img_src+'',1175 title: $(this).find('img').attr("title")1176 });1177 }1178 });1179 } 1180 //if no gallery - return current image1181 else{1182 self.gallerylist.push({1183 href: ''+self.zoomImage+'',1184 title: $(this).find('img').attr("title")1185 }); 1186 }1187 return self.gallerylist;1188 },1189 changeZoomLevel: function(value){1190 var self = this; 1191 //flag a zoom, so can adjust the easing during setPosition 1192 self.scrollingLock = true; 1193 //round to two decimal places1194 self.newvalue = parseFloat(value).toFixed(2);1195 newvalue = parseFloat(value).toFixed(2);1196 //maxwidth & Maxheight of the image1197 maxheightnewvalue = self.largeHeight/((self.options.zoomWindowHeight / self.nzHeight) * self.nzHeight); 1198 maxwidthtnewvalue = self.largeWidth/((self.options.zoomWindowWidth / self.nzWidth) * self.nzWidth); 1199 //calculate new heightratio1200 if(self.options.zoomType != "inner")1201 {1202 if(maxheightnewvalue <= newvalue){1203 self.heightRatio = (self.largeHeight/maxheightnewvalue) / self.nzHeight;1204 self.newvalueheight = maxheightnewvalue;1205 self.fullheight = true;1206 }1207 else{1208 self.heightRatio = (self.largeHeight/newvalue) / self.nzHeight; 1209 self.newvalueheight = newvalue;1210 self.fullheight = false;1211 }1212// calculate new width ratio1213 if(maxwidthtnewvalue <= newvalue){1214 self.widthRatio = (self.largeWidth/maxwidthtnewvalue) / self.nzWidth;1215 self.newvaluewidth = maxwidthtnewvalue;1216 self.fullwidth = true;1217 }1218 else{1219 self.widthRatio = (self.largeWidth/newvalue) / self.nzWidth; 1220 self.newvaluewidth = newvalue;1221 self.fullwidth = false;1222 }1223 if(self.options.zoomType == "lens"){1224 if(maxheightnewvalue <= newvalue){1225 self.fullwidth = true;1226 self.newvaluewidth = maxheightnewvalue;1227 } else{1228 self.widthRatio = (self.largeWidth/newvalue) / self.nzWidth; 1229 self.newvaluewidth = newvalue;1230 self.fullwidth = false;1231 }}1232 }1233 if(self.options.zoomType == "inner")1234 {1235 maxheightnewvalue = parseFloat(self.largeHeight/self.nzHeight).toFixed(2); 1236 maxwidthtnewvalue = parseFloat(self.largeWidth/self.nzWidth).toFixed(2); 1237 if(newvalue > maxheightnewvalue){1238 newvalue = maxheightnewvalue;1239 }1240 if(newvalue > maxwidthtnewvalue){1241 newvalue = maxwidthtnewvalue;1242 } 1243 if(maxheightnewvalue <= newvalue){1244 self.heightRatio = (self.largeHeight/newvalue) / self.nzHeight; 1245 if(newvalue > maxheightnewvalue){1246 self.newvalueheight = maxheightnewvalue;1247 }else{1248 self.newvalueheight = newvalue;1249 }1250 self.fullheight = true;1251 }1252 else{1253 self.heightRatio = (self.largeHeight/newvalue) / self.nzHeight; 1254 if(newvalue > maxheightnewvalue){1255 self.newvalueheight = maxheightnewvalue;1256 }else{1257 self.newvalueheight = newvalue;1258 }1259 self.fullheight = false;1260 }1261 if(maxwidthtnewvalue <= newvalue){ 1262 self.widthRatio = (self.largeWidth/newvalue) / self.nzWidth; 1263 if(newvalue > maxwidthtnewvalue){1264 self.newvaluewidth = maxwidthtnewvalue;1265 }else{1266 self.newvaluewidth = newvalue;1267 }1268 self.fullwidth = true;1269 }1270 else{ 1271 self.widthRatio = (self.largeWidth/newvalue) / self.nzWidth; 1272 self.newvaluewidth = newvalue;1273 self.fullwidth = false;1274 } 1275 } //end inner1276 scrcontinue = false;1277 if(self.options.zoomType == "inner"){1278 if(self.nzWidth >= self.nzHeight){1279 if( self.newvaluewidth <= maxwidthtnewvalue){1280 scrcontinue = true;1281 }1282 else{1283 scrcontinue = false;1284 self.fullheight = true;1285 self.fullwidth = true;1286 }1287 }1288 if(self.nzHeight > self.nzWidth){ 1289 if( self.newvaluewidth <= maxwidthtnewvalue){1290 scrcontinue = true;1291 }1292 else{1293 scrcontinue = false; 1294 self.fullheight = true;1295 self.fullwidth = true;1296 }1297 }1298 }1299 if(self.options.zoomType != "inner"){1300 scrcontinue = true;1301 }1302 if(scrcontinue){1303 self.zoomLock = 0;1304 self.changeZoom = true;1305 //if lens height is less than image height1306 if(((self.options.zoomWindowHeight)/self.heightRatio) <= self.nzHeight){1307 self.currentZoomLevel = self.newvalueheight; 1308 if(self.options.zoomType != "lens" && self.options.zoomType != "inner") {1309 self.changeBgSize = true;1310 self.zoomLens.css({height: String((self.options.zoomWindowHeight)/self.heightRatio) + 'px' }) 1311 }1312 if(self.options.zoomType == "lens" || self.options.zoomType == "inner") { 1313 self.changeBgSize = true; 1314 } 1315 } 1316 if((self.options.zoomWindowWidth/self.widthRatio) <= self.nzWidth){1317 if(self.options.zoomType != "inner"){1318 if(self.newvaluewidth > self.newvalueheight) {1319 self.currentZoomLevel = self.newvaluewidth; 1320 }1321 }1322 if(self.options.zoomType != "lens" && self.options.zoomType != "inner") {1323 self.changeBgSize = true;1324 self.zoomLens.css({width: String((self.options.zoomWindowWidth)/self.widthRatio) + 'px' })1325 }1326 if(self.options.zoomType == "lens" || self.options.zoomType == "inner") { 1327 self.changeBgSize = true;1328 } 1329 }1330 if(self.options.zoomType == "inner"){1331 self.changeBgSize = true; 1332 if(self.nzWidth > self.nzHeight){1333 self.currentZoomLevel = self.newvaluewidth;1334 }1335 if(self.nzHeight > self.nzWidth){1336 self.currentZoomLevel = self.newvaluewidth;1337 }1338 }1339 } //under1340 //sets the boundry change, called in setWindowPos1341 self.setPosition(self.currentLoc);1342 //1343 },1344 closeAll: function(){1345 if(self.zoomWindow){self.zoomWindow.hide();}1346 if(self.zoomLens){self.zoomLens.hide();}1347 if(self.zoomTint){self.zoomTint.hide();}1348 },1349 changeState: function(value){1350 var self = this;1351 if(value == 'enable'){self.options.zoomEnabled = true;}1352 if(value == 'disable'){self.options.zoomEnabled = false;}1353 }1354 };1355 $.fn.elevateZoom = function( options ) {1356 return this.each(function() {1357 var elevate = Object.create( ElevateZoom );1358 elevate.init( options, this );1359 $.data( this, 'elevateZoom', elevate );1360 });1361 };1362 $.fn.elevateZoom.options = {1363 zoomActivation: "hover", // Can also be click (PLACEHOLDER FOR NEXT VERSION)1364 zoomEnabled: true, //false disables zoomwindow from showing1365 preloading: 1, //by default, load all the images, if 0, then only load images after activated (PLACEHOLDER FOR NEXT VERSION)1366 zoomLevel: 1, //default zoom level of image1367 scrollZoom: false, //allow zoom on mousewheel, true to activate1368 scrollZoomIncrement: 0.1, //steps of the scrollzoom1369 minZoomLevel: false,1370 maxZoomLevel: false,1371 easing: false,1372 easingAmount: 12,1373 lensSize: 200,1374 zoomWindowWidth: 400,1375 zoomWindowHeight: 400,1376 zoomWindowOffetx: 0,1377 zoomWindowOffety: 0,1378 zoomWindowPosition: 1,1379 zoomWindowBgColour: "#fff",1380 lensFadeIn: false,1381 lensFadeOut: false,1382 debug: false,1383 zoomWindowFadeIn: false,1384 zoomWindowFadeOut: false,1385 zoomWindowAlwaysShow: false,1386 zoomTintFadeIn: false,1387 zoomTintFadeOut: false,1388 borderSize: 4,1389 showLens: true,1390 borderColour: "#888",1391 lensBorderSize: 1,1392 lensBorderColour: "#000",1393 lensShape: "square", //can be "round"1394 zoomType: "window", //window is default, also "lens" available -1395 containLensZoom: false,1396 lensColour: "white", //colour of the lens background1397 lensOpacity: 0.4, //opacity of the lens1398 lenszoom: false,1399 tint: false, //enable the tinting1400 tintColour: "#333", //default tint color, can be anything, red, #ccc, rgb(0,0,0)1401 tintOpacity: 0.4, //opacity of the tint1402 gallery: false,1403 galleryActiveClass: "zoomGalleryActive",1404 imageCrossfade: false,1405 constrainType: false, //width or height1406 constrainSize: false, //in pixels the dimensions you want to constrain on1407 loadingIcon: false, //http://www.example.com/spinner.gif1408 cursor:"default", // user should set to what they want the cursor as, if they have set a click function1409 responsive:true,1410 onComplete: $.noop,1411 onDestroy: function() {},1412 onZoomedImageLoaded: function() {},1413 onImageSwap: $.noop,1414 onImageSwapComplete: $.noop1415 };...
Using AI Code Generation
1import { storiesOf } from '@storybook/react';2import { withInfo } from '@storybook/addon-info';3import { withKnobs, text, boolean, select } from '@storybook/addon-knobs';4import { action } from '@storybook/addon-actions';5import Button from './Button';6storiesOf('Button', module)7 .addDecorator(withInfo)8 .addDecorator(withKnobs)9 .add('with text', () => (10 onClick={action('clicked')}11 disabled={boolean('Disabled', false)}12 type={select('Type', ['primary', 'secondary', 'default'], 'default')}13 {text('Label', 'Hello Button')}14 ));15import React from 'react';16import { storiesOf } from '@storybook/react';17import { withInfo } from '@storybook/addon-info';18import { withKnobs, text, boolean, select } from '@storybook/addon-knobs';19import { action } from '@storybook/addon-actions';20import Button from './Button';21storiesOf('Button', module)22 .addDecorator(withInfo)23 .addDecorator(withKnobs)24 .add('with text', () => (25 onClick={action('clicked')}26 disabled={boolean('Disabled', false)}27 type={select('Type', ['primary', 'secondary', 'default'], 'default')}28 {text('Label', 'Hello Button')}29 ));30Thanks for the reply. I am using the latest version of Storybook (3.4.0). I have also tried to install the la
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!