Advertisement
Guest User

Untitled

a guest
Oct 13th, 2021
210
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const directions = ["back", "br", "fr", "front", "right"];
  2. const anims = ["stand", "walk", "attack"];
  3. const frames = { "walk": 8, "attack": 15 };
  4.  
  5. const TEXTURE_SIZE = 2048;
  6. const DIFFERENCES = ["front", "fs_right", "right", "bs_right", "back", "bs_left", "left", "fs_left"];
  7. const ANIM_FRAME_RATE = 150;
  8.  
  9. const RESOLUTION = 'default';
  10.  
  11. /**
  12.  * Base class to represent an NPC or Character object.
  13.  */
  14. class Actor {
  15.   constructor() {
  16.  
  17.   }
  18.  
  19.   /**
  20.    * Pops some text above this Actor's head.
  21.    */
  22.   say(message) {
  23.     if (!this.text) {
  24.       this.text = document.createElement('div');
  25.       this.text.classList.add('outline');
  26.       this.text.classList.add('message');
  27.       document.body.appendChild(this.text);
  28.     }
  29.     this.text.style.display = 'block';
  30.     this.text.innerHTML = message;
  31.     this.lastMessage = message;
  32.     setTimeout(() => {
  33.       if (this.lastMessage == message) {
  34.         this.text.style.display = 'none';
  35.       }
  36.     }, 3000)
  37.   }
  38.  
  39.   /**
  40.    * Pops a 'skill bubble' above the actor's head.
  41.    */
  42.   action(anim) {
  43.     if (!this.doing) {
  44.       this.doing = document.createElement('img');
  45.       this.doing.classList.add('animation');
  46.       document.body.appendChild(this.doing);
  47.     }
  48.  
  49.     if (anim) {
  50.       this.doing.style.display = 'block';
  51.  
  52.       let ii = itemImageURL(anim);
  53.       if (ii) {
  54.         this.doing.src = ii;
  55.       } else {
  56.         this.doing.src = getStaticEndpoint('items/' + anim + '.png');
  57.       }
  58.     } else {
  59.       this.doing.style.display = 'none';
  60.     }
  61.   }
  62.  
  63.   /**
  64.    * Shows an HP bar above the actor's head.
  65.    */
  66.   health(percent) {
  67.     if (!this.hp_container) {
  68.       this.hp_container = document.createElement('div');
  69.       this.hp_container.classList.add('combat-hp-container');
  70.  
  71.       this.hp_bar = document.createElement('div');
  72.       this.hp_bar.classList.add('combat-hp-bar');
  73.       this.hp_container.appendChild(this.hp_bar);
  74.  
  75.       document.body.appendChild(this.hp_container);
  76.     }
  77.  
  78.     this.hp_container.style.display = 'block';
  79.     if (percent <= 1) {
  80.       this.hp_bar.style.width = '0px';
  81.     } else {
  82.       this.hp_bar.style.width = percent + '%';
  83.     }
  84.  
  85.     let time = new Date();
  86.     this.hp_container.spawned = time;
  87.     setTimeout(() => {
  88.       if (this.hp_container.spawned == time) {
  89.         this.hp_container.style.display = 'none';
  90.       }
  91.     }, 3500);
  92.   }
  93.  
  94.   /**
  95.    * Draws a damage splat on the player
  96.    * {
  97.    *  type: "melee_hit",
  98.    *  damage: 3
  99.    * }
  100.    */
  101.   damage(style, type, damage) {
  102.     if (!this.damage_splat) {
  103.       this.damage_splat = document.createElement('div');
  104.       this.damage_splat.classList.add('combat-splat');
  105.  
  106.       this.damage_splat_text = document.createElement('div');
  107.       this.damage_splat_text.classList.add('combat-splat-text');
  108.       this.damage_splat.appendChild(this.damage_splat_text);
  109.  
  110.       document.body.appendChild(this.damage_splat);
  111.     }
  112.  
  113.     let time = new Date();
  114.     this.damage_splat.style.display = 'flex';
  115.  
  116.     let icon = style == 'ranged' ? 'arrow' : 'sword';
  117.  
  118.     if (type == 'dodge') {
  119.       this.damage_splat.style.backgroundImage = "";
  120.     } else if (damage > 0) {
  121.       this.damage_splat.style.backgroundImage = "url(" + getStaticEndpoint('img/combat_' + icon + '_red.png') + ")";
  122.     } else {
  123.       this.damage_splat.style.backgroundImage = "url(" + getStaticEndpoint('img/combat_' + icon + '_blue.png') + ")";
  124.     }
  125.     this.damage_splat_text.innerText = type == 'dodge' ? 'evaded' : damage;
  126.     this.damage_splat.spawned = time;
  127.  
  128.     if (type == 'dodge' || damage == 0) {
  129.       AUDIO_SFX_PLAYER.play_location('combat-damage-miss', this.position());
  130.     } else if (type != 'ranged') {
  131.       AUDIO_SFX_PLAYER.play_location('combat-damage-hit-cloth', this.position());
  132.     }
  133.  
  134.     setTimeout(() => {
  135.       if (this.damage_splat.spawned == time) {
  136.         this.damage_splat.style.display = 'none';
  137.       }
  138.     }, 500);
  139.   }
  140.  
  141.   update(cam, dt) {
  142.     if (this.text && this.text.style.display != 'none') {
  143.       let p = this.position().clone();
  144.       p.y += 0.75;
  145.       this.projectWorldToCSS(this.text, p, cam);
  146.     }
  147.  
  148.     if (this.doing && this.doing.style.display != 'none') {
  149.       let p = this.position().clone();
  150.       p.y += 1.05;
  151.       this.projectWorldToCSS(this.doing, p, cam);
  152.     }
  153.  
  154.     if (this.damage_splat && this.damage_splat.style.display != 'none') {
  155.       let p = this.position().clone();
  156.       this.projectWorldToCSS(this.damage_splat, p, cam);
  157.     }
  158.  
  159.     if (this.hp_container && this.hp_container.style.display != 'none') {
  160.       let p = this.position().clone();
  161.       p.y += 0.8;
  162.       this.projectWorldToCSS(this.hp_container, p, cam);
  163.     }
  164.   }
  165.  
  166.   projectWorldToCSS(dom, p, cam) {
  167.     p.project(cam);
  168.  
  169.     let w = window.innerWidth, h = window.innerHeight;
  170.     let w2 = w / 2.0, h2 = h / 2.0;
  171.     let l = p.x * w2 + w2 - dom.offsetWidth / 2.0;
  172.     let t = -(p.y * h2) + h2 - dom.offsetHeight / 2.0;
  173.  
  174.     dom.style.left = l + "px";
  175.     dom.style.top = t + "px";
  176.   }
  177.  
  178.   onDelete() {
  179.     if (this.text) this.text.parentNode.removeChild(this.text);
  180.     if (this.doing) this.doing.parentNode.removeChild(this.doing);
  181.     if (this.damage_splat) this.damage_splat.parentNode.removeChild(this.damage_splat);
  182.     if (this.hp_container) this.hp_container.parentNode.removeChild(this.hp_container);
  183.   }
  184. }
  185.  
  186. var spineBase = getStaticEndpoint("spine/");
  187. var spineAssetManager = new spine.threejs.AssetManager(spineBase);
  188.  
  189. var meshFile = 'rig.json';
  190. var atlasFile = RESOLUTION + '.atlas';
  191. spineAssetManager.loadText(meshFile);
  192. spineAssetManager.loadText(atlasFile);
  193.  
  194. let TIL = new THREE.ImageLoader();
  195. var spineTextureCache = {};
  196.  
  197. function createAtlas(mods = {}, callback) {
  198.   let text = spineAssetManager.get(atlasFile);
  199.  
  200.   let pending = 0;
  201.   let atlas = new spine.TextureAtlas(text, (path) => {
  202.     let actual = mods[path] || path;
  203.  
  204.     if (spineTextureCache[actual]) return spineTextureCache[actual];
  205.  
  206.     pending++;
  207.     TIL.load(cacheURI(spineBase + actual), (image) => {
  208.       pending--;
  209.       spineTextureCache[actual] = new spine.threejs.ThreeJsTexture(image);
  210.       if (pending == 0) {
  211.         // recreate once all textures are ready.
  212.         createAtlas(mods, callback);
  213.       }
  214.     });
  215.  
  216.     var fake = document.createElement("img");
  217.     fake.width = 16;
  218.     fake.height = 16;
  219.     return new spine.FakeTexture(fake);
  220.   });
  221.  
  222.   if (pending == 0) callback(new spine.AtlasAttachmentLoader(atlas));
  223. }
  224.  
  225. function tintSkin(skin, r, g, b) {
  226.   for (let i in skin.attachments) {
  227.     for (let j in skin.attachments[i]) {
  228.       skin.attachments[i][j].color.r = r;
  229.       skin.attachments[i][j].color.g = g;
  230.       skin.attachments[i][j].color.b = b;
  231.     }
  232.   }
  233. }
  234.  
  235. const allSkins = JSON.parse('{"belt":true,"body":true,"cape":true,"face":true,"footwear":true,"fullbody":true,"gloves":true,"hair":true,"headgear":true,"necklace":true,"offhand":true,"pants":true,"scar":true,"shirt":true,"skirt":true,"weaponBack":true,"weaponFullSize":true,"weaponMainHand":true, "wings": true}');
  236. const slotToSkin = {
  237.   chest: "shirt",
  238.   legs: "pants",
  239.   shield: "offhand",
  240.   head: "headgear",
  241. }
  242.  
  243. function rgbToGL(color) {
  244.   color.r /= 255.0;
  245.   color.g /= 255.0;
  246.   color.b /= 255.0;
  247.   return color;
  248. }
  249.  
  250. function createMods(model) {
  251.   let skinMods = Object.assign({}, model.model);
  252.  
  253.   // TODO: This is pretty hacky.
  254.   for (let i in model.items) {
  255.     let slot = slotToSkin[i] || i;
  256.     if (model.items[i].gfxslot) slot = model.items[i].gfxslot;
  257.  
  258.     skinMods[slot] = {
  259.       override: model.items[i].style + ".png",
  260.     }
  261.     if (model.items[i].color) skinMods[slot].tint = rgbToGL(model.items[i].color);
  262.   }
  263.  
  264.   let textureMods = {};
  265.   for (let i in allSkins) {
  266.     if (skinMods[i] && skinMods[i].override) {
  267.       textureMods[RESOLUTION + '/' + i + '/' + i + '.png'] = RESOLUTION + '/' + i + '/' + skinMods[i].override;
  268.     }
  269.   }
  270.  
  271.   if (model.items && model.items.suppress_hair) {
  272.     delete skinMods['hair'];
  273.   }
  274.  
  275.   return [skinMods, textureMods];
  276. }
  277.  
  278. function createSpineMesh(def, callback) {
  279.   let [model, mods] = createMods(def);
  280.  
  281.   let scale = 0.045;
  282.  
  283.   createAtlas(mods, (atlas) => {
  284.     let skeletonJson = new spine.SkeletonJson(atlas);
  285.     skeletonJson.scale = scale;
  286.     let data = spineAssetManager.get(meshFile);
  287.     let skeletonData = skeletonJson.readSkeletonData(data);
  288.  
  289.     let skeletonMesh = new spine.threejs.SkeletonMesh(skeletonData);
  290.  
  291.     skeletonMesh.translateY(-0.9);
  292.     skeletonMesh.scale.set(scale, scale, scale);
  293.     skeletonMesh.zOffset = 0.01;
  294.  
  295.     let skin = new spine.Skin('test');
  296.  
  297.     for (let i in allSkins) {
  298.       if (!model[i]) continue;
  299.  
  300.       let s = skeletonData.findSkin(i);
  301.       if (model[i].tint) {
  302.         tintSkin(s, model[i].tint.r, model[i].tint.g, model[i].tint.b);
  303.       }
  304.       skin.addSkin(s);
  305.     }
  306.  
  307.     skeletonMesh.state.setAnimation(0, 'stand/stand_front', true);
  308.     skeletonMesh.skeleton.setSkin(skin);
  309.     skeletonMesh.skeleton.setSlotsToSetupPose();
  310.  
  311.     callback(skeletonMesh);
  312.   });
  313. }
  314.  
  315. function createShadow(height) {
  316.   let sGeo = new THREE.PlaneGeometry(1, 1, 1, 1);
  317.   let sMat = new THREE.MeshBasicMaterial({
  318.     map: TEXTUREMANAGER.get('shadow.png'),
  319.     transparent: true,
  320.     side: THREE.DoubleSide,
  321.     depthTest: false
  322.   });
  323.   let shadow = new THREE.Mesh(sGeo, sMat);
  324.   shadow.is_shadow_mesh = true;
  325.  
  326.   shadow.rotateX(THREE.Math.degToRad(-90));
  327.   shadow.position.set(0, -height + 0.01, 0);
  328.   shadow.renderOrder = -10;
  329.   return shadow;
  330. }
  331.  
  332. /**
  333.  * Adds the playerkit-specific code to the world actor
  334.  */
  335. class HumanCharacter extends Actor {
  336.   constructor(position, model, height) {
  337.     super();
  338.  
  339.     this.animation = 'stand';
  340.     this.direction = 0;
  341.     this.facing = 'front';
  342.  
  343.     let group = new THREE.Group();
  344.     group.position.set(position.x, position.y, position.z);
  345.  
  346.     group.add(createShadow(model.shadow_height || height));
  347.  
  348.     if (typeof model.scale === 'number') {
  349.       group.scale.set(model.scale, model.scale, model.scale);
  350.     } else if (model.scale && model.scale.x) {
  351.       group.scale.set(model.scale.x, model.scale.y, model.scale.z);
  352.     }
  353.  
  354.     this.threeObject = group;
  355.  
  356.     this.updateAppearance(model);
  357.   }
  358.  
  359.   updateAppearance(model) {
  360.     // TODO: better default appearance implementation
  361.     if (!model.model) {
  362.       console.log("Error: No model for character: " + JSON.stringify(model));
  363.       model.model = JSON.parse('{"body":{"override":"male.png","tint":{"r":0.55,"g":0.33,"b":0.14}},"face":{"override":"male.png"},"footwear":{"override":"black_shoes.png"},"hair":{"override":"male_cropped.png","tint":{"r":0.84,"g":0.77,"b":0.76}},"pants":{"override":"straight-grey.png"},"shirt":{"override":"male_belt-red.png","tint":{"r":0.91,"g":0.93,"b":0.1}}}');
  364.     }
  365.  
  366.     this.model = model;
  367.     createSpineMesh(model, (mesh) => {
  368.       this.replaceSpineMesh(mesh);
  369.     });
  370.   }
  371.  
  372.   replaceSpineMesh(newMesh) {
  373.     if (this.spineMesh) this.threeObject.remove(this.spineMesh);
  374.     this.threeObject.add(newMesh);
  375.  
  376.     this.spineMesh = newMesh;
  377.     this.needsUpdate = true;
  378.   }
  379.  
  380.   getThreeObject() {
  381.     return this.threeObject;
  382.   }
  383.  
  384.   position() {
  385.     if (this.combat) {
  386.       let vec = new THREE.Vector3();
  387.       this.threeObject.getWorldPosition(vec);
  388.       return vec;
  389.     } else {
  390.       return this.threeObject.position;
  391.     }
  392.   }
  393.  
  394.   setAnimation(animation, offset) {
  395.     if (this.animation == animation) { return; }
  396.     this.animation = animation;
  397.  
  398.     /*if (this.animation == 'attack') {
  399.         this.timeScale = 0.5;
  400.     }*/
  401.  
  402.     // TODO: hack
  403.     if (offset == 'half') {
  404.       this.trackTime = 0.15;
  405.     }
  406.     this.needsUpdate = true;
  407.   }
  408.  
  409.   setDirection(direction) {
  410.     this.direction = direction;
  411.     this.needsUpdate = true;
  412.   }
  413.  
  414.   updateSpineAnimation() {
  415.     if (!this.spineMesh) return;
  416.  
  417.     let prefix = this.animation + "/" + this.animation + "_";
  418.     let direction = this.facing;
  419.     if (this.animation == 'attack' && direction != 'right' && direction != 'left') {
  420.       direction = 'right';
  421.     }
  422.  
  423.     let te = this.spineMesh.state.setAnimation(0, prefix + direction, true);
  424.     if (this.trackTime) {
  425.       te.trackTime = this.trackTime;
  426.       delete this.trackTime;
  427.     }
  428.     if (this.timeScale) {
  429.       te.timeScale = this.timeScale;
  430.       delete this.timeScale;
  431.     }
  432.   }
  433.  
  434.   intersect(ray) {
  435.     if (!this.spineMesh) return;
  436.  
  437.     let i = ray.intersectObject(this.spineMesh, true);
  438.     if (i.length == 0) return;
  439.     return i[0];
  440.   }
  441.  
  442.   update(cam, dt) {
  443.     if (this.combat) {
  444.  
  445.     } else {
  446.       let camDir = 180 * Math.atan2(cam.position.z - this.threeObject.position.z, cam.position.x - this.threeObject.position.x) / Math.PI;
  447.       let playerDir = this.direction;
  448.       let diff = camDir - playerDir;
  449.       diff = Math.round(diff / 45) + 4;
  450.       diff += 4;
  451.       while (diff >= 8) diff -= 8;
  452.       while (diff < 0) diff += 8;
  453.       let cameraAdjustedDirection = DIFFERENCES[diff];
  454.  
  455.       // change sprite to the direction
  456.       if (cameraAdjustedDirection && this.facing != cameraAdjustedDirection) {
  457.         this.facing = cameraAdjustedDirection;
  458.         this.needsUpdate = true;
  459.       }
  460.       var forward = new THREE.Vector3();
  461.       cam.getWorldDirection(forward);
  462.       this.threeObject.lookAt(this.threeObject.position.x - forward.x, this.threeObject.position.y, this.threeObject.position.z - forward.z);
  463.     }
  464.  
  465.     if (this.needsUpdate) {
  466.       delete this.needsUpdate;
  467.       this.updateSpineAnimation();
  468.       if (this.spineMesh && this.animation == 'stand') {
  469.         this.spineMesh.update(0.0);
  470.       }
  471.       //this.threeObject.traverse( (e) => e.renderOrder = 25);
  472.     }
  473.  
  474.     super.update(cam, dt);
  475.  
  476.     // Skeletal animation: It's slow.
  477.     if (this.spineMesh && this.animation != 'stand') {
  478.       this.spineMesh.update(dt / 2000.0);
  479.     }
  480.   }
  481. }
  482.  
  483. const MONSTER_INDICES = {
  484.   "back_stand.png": 0, "back_walk0.png": 1, "back_walk1.png": 2, "back_walk2.png": 3,
  485.   "bs_right_stand.png": 4, "bs_right_walk0.png": 5, "bs_right_walk1.png": 6, "bs_right_walk2.png": 7,
  486.   "fs_right_stand.png": 8, "fs_right_walk0.png": 9, "fs_right_walk1.png": 10, "fs_right_walk2.png": 11,
  487.   "front_stand.png": 12, "front_walk0.png": 13, "front_walk1.png": 14, "front_walk2.png": 15,
  488.   "right_stand.png": 16, "right_walk0.png": 17, "right_walk1.png": 18, "right_walk2.png": 19, "right_attack0.png": 20, "right_attack1.png": 21, "right_attack2.png": 22, "right_attack3.png": 23, "right_hurt.png": 24
  489. };
  490. const MONSTER_COLUMNS = 5;
  491.  
  492. class MonsterCharacter extends Actor {
  493.   constructor(def, position, height) {
  494.     super();
  495.  
  496.     this.animation = 'stand';
  497.     this.facing = 'front';
  498.     this.model = def.model;
  499.     this.group = new THREE.Group();
  500.     this.group.position.set(position.x, position.y, position.z);
  501.     this.layerObjects = {};
  502.     this.direction = 0;
  503.  
  504.     let geometry = new THREE.BufferGeometry();
  505.     geometry.addAttribute('position', DEFAULT_VERTEX_BUFFER_ATTRIBUTE);
  506.     geometry.addAttribute('uv', UV_ATTRIBUTES);
  507.     geometry.setIndex(INDEX_ATTRIBUTES);
  508.  
  509.     let material = new THREE.MeshBasicMaterial(
  510.       {
  511.         transparent: true,
  512.         alphaTest: 0.1,
  513.       });
  514.  
  515.     let o = new THREE.Mesh(geometry, material);
  516.     o.matrixAutoUpdate = false;
  517.     o.layerSize = 'default';
  518.     o.visible = false;
  519.     this.group.add(o);
  520.  
  521.     this.group.add(createShadow(def.shadow_height || height));
  522.  
  523.     this.layerObjects[0] = o;
  524.  
  525.     this.dt = 0.0;
  526.     this.frameRate = 250;
  527.  
  528.     this.geometry = this.layerObjects[0].geometry;
  529.     this.material = this.layerObjects[0].material;
  530.  
  531.     if (def.scale) {
  532.       this.group.scale.set(def.scale, def.scale, def.scale);
  533.     }
  534.     if (def.exact_collision) {
  535.       this.exact_collision = true;
  536.     }
  537.  
  538.     this.layerObjects[0].visible = false;
  539.  
  540.     this.loaded = false;
  541.     TEXTUREMANAGER.getTextureAndImage(getStaticEndpoint('monsters/' + def.model + ".png"), (result) => {
  542.       this.setTextureAndImage(result[0], result[1]);
  543.     });
  544.   }
  545.  
  546.   setTextureAndImage(texture, image) {
  547.     this.texture = texture;
  548.     this.image = image;
  549.     this.material.map = this.texture;
  550.     this.material.needsUpdate = true;
  551.  
  552.     this.layerObjects[0].visible = true;
  553.  
  554.     this._set(this.facing || 'front', this.animation || 'stand', this.frame || 0);
  555.   }
  556.  
  557.   getThreeObject() {
  558.     return this.group;
  559.   }
  560.  
  561.   position() {
  562.     if (this.combat) {
  563.       let vec = new THREE.Vector3();
  564.       this.group.getWorldPosition(vec);
  565.       return vec;
  566.     } else {
  567.       return this.group.position;
  568.     }
  569.   }
  570.  
  571.   setAnimation(animation, offset) {
  572.     if (this.animation == animation) { return; }
  573.     this.frame = (offset == 'half') ? 7 : 1;
  574.     this.animation = animation;
  575.     this.needsUpdate = true;
  576.   }
  577.  
  578.   setDirection(direction) {
  579.     this.direction = direction;
  580.     this.needsUpdate = true;
  581.   }
  582.  
  583.   // boolean
  584.   intersect(ray) {
  585.     let i = ray.intersectObjects(this.group.children);
  586.     if (i.length == 0) return;
  587.  
  588.     if (this.exact_collision) {
  589.       let uv = i[0].uv;
  590.       let x = Math.floor(uv.x * this.image[0].width);
  591.       let y = Math.floor((1.0 - uv.y) * this.image[0].height);
  592.  
  593.       if (this.image[1].getImageData(x, y, 1, 1).data[3] > 0) return i[0];
  594.       return false;
  595.     } else {
  596.       return i[0];
  597.     }
  598.   }
  599.  
  600.   incrementFrame() {
  601.     if (this.animation == 'walk') {
  602.       this.frame++;
  603.       if (this.frame > 2) this.frame = 0;
  604.     } else if (this.animation == 'attack') {
  605.       this.frame++;
  606.       if (this.frame > 3) this.frame = 0;
  607.     }
  608.  
  609.     this.needsUpdate = true;
  610.   }
  611.  
  612.   _set(direction, anim, frame) {
  613.     if (!direction || !anim) return;
  614.  
  615.     // attack only exists in side-view
  616.     if (anim == 'attack' && direction != 'right' && direction != 'left') { direction = 'right'; }
  617.  
  618.     // mirroring
  619.     let invertX = false;
  620.     if (direction == 'left') {
  621.       direction = 'right';
  622.       invertX = true;
  623.     } else if (direction == 'fs_left') {
  624.       direction = 'fs_right';
  625.       invertX = true;
  626.     } else if (direction == 'bs_left') {
  627.       direction = 'bs_right';
  628.       invertX = true;
  629.     }
  630.  
  631.     let uri = direction + "_" + anim + (frames[anim] ? frame : "") + ".png";
  632.     let newIndex = MONSTER_INDICES[uri] || 0;
  633.  
  634.     let x = (newIndex % MONSTER_COLUMNS) * 1.0 / MONSTER_COLUMNS;
  635.     let y = Math.floor(newIndex / MONSTER_COLUMNS) * 1.0 / MONSTER_COLUMNS;
  636.     let w = 0.2, h = 0.2;
  637.  
  638.     let left = x;
  639.     let right = x;
  640.     if (invertX) { left += w; } else { right += w; }
  641.     let top = 1.0 - y;
  642.     let bot = 1.0 - (y + h);
  643.  
  644.     let quad_uvs = [
  645.       left, top,
  646.       right, top,
  647.       right, bot,
  648.       left, bot
  649.     ];
  650.  
  651.     var uvs = new Float32Array(quad_uvs);
  652.     this.geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2);
  653.     this.geometry.uvsNeedUpdate = true;
  654.   }
  655.  
  656.   update(cam, dt) {
  657.     if (this.combat) {
  658.       // Do nothing.
  659.     } else {
  660.       // If I were better with math there's probably a one-liner in trig here...
  661.       // TODO: Move this to shader.
  662.       let camDir = 180 * Math.atan2(cam.position.z - this.group.position.z, cam.position.x - this.group.position.x) / Math.PI;
  663.       let playerDir = this.direction;
  664.       let diff = camDir - playerDir;
  665.       diff = Math.round(diff / 45) + 4;
  666.       diff += 4;
  667.       while (diff >= 8) diff -= 8;
  668.       while (diff < 0) diff += 8;
  669.       let cameraAdjustedDirection = DIFFERENCES[diff];
  670.  
  671.       // change sprite to the direction
  672.       if (cameraAdjustedDirection && this.facing != cameraAdjustedDirection) {
  673.         this.facing = cameraAdjustedDirection;
  674.         this.needsUpdate = true;
  675.       }
  676.  
  677.       var forward = new THREE.Vector3();
  678.       cam.getWorldDirection(forward);
  679.       this.group.lookAt(this.group.position.x - forward.x, this.group.position.y, this.group.position.z - forward.z);
  680.     }
  681.  
  682.     super.update(cam, dt);
  683.  
  684.     this.dt += dt;
  685.     while (this.dt > this.frameRate) {
  686.       this.dt -= this.frameRate;
  687.       this.incrementFrame();
  688.     }
  689.  
  690.     if (this.needsUpdate) {
  691.       this._set(this.facing, this.animation, this.frame);
  692.       this.needsUpdate = false;
  693.     }
  694.   }
  695. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement