UNPKG

matrix-engine-wgpu

Version:

Networking implemented - based on kurento openvidu server. fix arcball camera,instanced draws added also effect pipeline blend with instancing option.Normalmap added, Fixed shadows casting vs camera/video texture, webGPU powered pwa application. Crazy fas

726 lines (665 loc) 29.9 kB
import {vec3} from "wgpu-matrix"; import {uploadGLBModel} from "../../../src/engine/loaders/webgpu-gltf"; import {byId, LOG_MATRIX} from "../../../src/engine/utils"; import {Hero} from "./hero"; import {Creep} from "./creep-character"; import {followPath} from "./nav-mesh"; import {creepPoints, startUpPositions} from "./static"; import {FriendlyHero} from "./friendly-character"; export class Character extends Hero { friendlyLocal = { heroes: [], creeps: [] }; creepThrust = 0.85; heroAnimationArrange = { dead: null, walk: null, salute: null, attack: null, idle: null }; friendlyCreepAnimationArrange = { dead: null, walk: null, salute: null, attack: null, idle: null }; heroFocusAttackOn = null; mouseTarget = null; // gold = 100; constructor(forestOfHollowBlood, path, name = 'MariaSword', archetypes = ["Warrior", "Mage"]) { super(name, archetypes); console.info(`%cplayer.data : ${forestOfHollowBlood.player.data}`, LOG_MATRIX); this.name = name; this.core = forestOfHollowBlood; this.heroe_bodies = []; this.loadLocalHero(path); this.loadfriendlyCreeps(); setTimeout(() => this.setupHUDForHero(name), 1100); } setupHUDForHero(name) { // console.info(`%cLOADING hero name : ${name}`, LOG_MATRIX) for(var x = 1;x < 5;x++) { byId(`magic-slot-${x - 1}`).style.background = `url("./res/textures/rpg/magics/${name.toLowerCase()}-${x}.png")`; byId(`magic-slot-${x - 1}`).style.backgroundRepeat = "round"; } byId('hudLeftBox').style.background = `url('./res/textures/rpg/hero-image/${name.toLowerCase()}.png') center center / cover no-repeat`; byId('hudDesriptionText').innerHTML = app.label.get[name.toLowerCase()]; } async loadfriendlyCreeps() { this.friendlyLocal.creeps.push(new Creep({ core: this.core, name: 'friendly_creeps0', archetypes: ["creep"], path: 'res/meshes/glb/bot.glb', position: {x: 0, y: -23, z: 0} }, ['creep'], 'friendly', app.player.data.team)); this.friendlyLocal.creeps.push(new Creep({ core: this.core, name: 'friendly_creeps1', archetypes: ["creep"], path: 'res/meshes/glb/bot.glb', position: {x: 150, y: -23, z: 0} }, ['creep'], 'friendly', app.player.data.team)); this.friendlyLocal.creeps.push(new Creep({ core: this.core, name: 'friendly_creeps2', archetypes: ["creep"], path: 'res/meshes/glb/bot.glb', position: {x: 100, y: -23, z: 0} }, ['creep'], 'friendly', app.player.data.team)); setTimeout(() => { app.localHero.setAllCreepsAtStartPos().then(() => { }).catch(() => { setTimeout(() => { app.localHero.setAllCreepsAtStartPos().then(() => { // console.log('passed in 2') }).catch(() => { setTimeout(() => { app.localHero.setAllCreepsAtStartPos().then(() => { // console.log('passed in 3') }).catch(() => { console.log('FAILD setAllCreepsAtStartPos'); }) }, 7000); }) }, 7000); }); }, 10000); } async loadLocalHero(p) { try { var glbFile01 = await fetch(p).then(res => res.arrayBuffer().then(buf => uploadGLBModel(buf, this.core.device))); this.core.addGlbObjInctance({ material: {type: 'standard', useTextureFromGlb: true}, scale: [20, 20, 20], position: { x: startUpPositions[this.core.player.data.team][0], y: startUpPositions[this.core.player.data.team][1], z: startUpPositions[this.core.player.data.team][2] }, name: this.name, texturesPaths: ['./res/meshes/glb/textures/mutant_origin.png'], raycast: {enabled: true, radius: 1.5}, pointerEffect: { enabled: true, pointer: true, energyBar: true, flameEffect: false, flameEmitter: true, circlePlane: false, circlePlaneTex: true, circlePlaneTexPath: './res/textures/star1.png', } }, null, glbFile01); // Poenter mouse click var glbFile02 = await fetch('./res/meshes/glb/ring1.glb').then(res => res.arrayBuffer().then(buf => uploadGLBModel(buf, this.core.device))); this.core.addGlbObjInctance({ material: {type: 'standard', useTextureFromGlb: false}, scale: [20, 20, 20], position: {x: 0, y: -24, z: -220}, name: 'mouseTarget', texturesPaths: ['./res/textures/default.png'], raycast: {enabled: false, radius: 1.5}, pointerEffect: { enabled: true, // circlePlane: true, circlePlaneTex: true, circlePlaneTexPath: './res/textures/star1.png', } }, null, glbFile02); // make small async - cooking glbs files mouseTarget_Circle this.setupHero().then(() => { // }).catch(() => { this.setupHero().then(() => {}).catch(() => {}) }) } catch(err) {throw err;} } setupHero() { return new Promise((resolve, reject) => { setTimeout(() => { // console.info(`%cAnimation setup...`, LOG_MATRIX) this.mouseTarget = app.getSceneObjectByName('mouseTarget_Circle'); this.mouseTarget.animationSpeed = 20000; if(typeof app.localHero.mouseTarget.instanceTargets === 'undefined') { reject(); return; } app.localHero.mouseTarget.instanceTargets[1].position[1] = 1; app.localHero.mouseTarget.instanceTargets[1].scale = [0.4, 0.4, 0.4]; this.heroe_bodies = app.mainRenderBundle.filter(obj => obj.name && obj.name.includes(this.name) ); this.core.RPG.heroe_bodies = this.heroe_bodies; this.core.RPG.heroe_bodies.forEach((subMesh, id) => { subMesh.position.thrust = this.moveSpeed; subMesh.glb.animationIndex = 0; // adapt manual if blender is not setup subMesh.glb.glbJsonData.animations.forEach((a, index) => { // console.info(`%c ANimation: ${a.name} index ${index}`, LOG_MATRIX) if(a.name == 'dead') this.heroAnimationArrange.dead = index; if(a.name == 'walk') this.heroAnimationArrange.walk = index; if(a.name == 'salute') this.heroAnimationArrange.salute = index; if(a.name == 'attack') this.heroAnimationArrange.attack = index; if(a.name == 'idle') this.heroAnimationArrange.idle = index; }) if(id == 0) subMesh.sharedState.emitAnimationEvent = true; this.core.collisionSystem.register(`local${id}`, subMesh.position, 15.0, 'local_hero'); }); if(app.localHero.heroe_bodies[0].effects) { app.localHero.heroe_bodies[0].effects.flameEmitter.recreateVertexDataRND(1); } else { console.log(`warn: ${app.localHero.heroe_bodies[0]} `); } // adapt app.localHero.heroe_bodies[0].globalAmbient = [1, 1, 1, 1]; if(app.localHero.name == 'Slayzer') { app.localHero.heroe_bodies[0].globalAmbient = [2, 2, 3, 1]; } else if(app.localHero.name == 'Steelborn') { app.localHero.heroe_bodies[0].globalAmbient = [12, 12, 12, 1] } else { app.localHero.heroe_bodies[0].globalAmbient = [2, 2, 3, 1]; } app.localHero.heroe_bodies[0].effects.circlePlaneTex.rotateEffectSpeed = 0.1; this.attachEvents(); // important! for(var x = 0;x < app.localHero.heroe_bodies.length;x++) { if(x > 0) { app.localHero.heroe_bodies[x].position = app.localHero.heroe_bodies[0].position; app.localHero.heroe_bodies[x].rotation = app.localHero.heroe_bodies[0].rotation; } } // activete net pos emit - becouse uniq name of hero body set net id by scene obj name simple // app.localHero.heroe_bodies[0].position.netObject = app.net.session.connection.connectionId; // not top solution - for now . High cost - precision good. app.localHero.heroe_bodies[0].position.netObject = app.localHero.heroe_bodies[0].name; // for now net view for rot is axis separated - cost is ok for orientaion remote pass app.localHero.heroe_bodies[0].rotation.emitY = app.localHero.heroe_bodies[0].name; dispatchEvent(new CustomEvent('local-hero-bodies-ready', { detail: `This is not sync - 99% works` })) }, 5000); // return to 2 -3 - testing on 3-4 on same computer }) } async loadFriendlyHero(p) { try { this.friendlyLocal.heroes.push(new FriendlyHero( { core: this.core, name: p.hero, archetypes: p.archetypes, path: p.path, position: {x: 0, y: -23, z: 0} } )); } catch(err) { console.error(err); } } setAllCreepsAtStartPos = () => { return new Promise((resolve, reject) => { try { this.friendlyLocal.creeps.forEach((subMesh_) => { if(typeof subMesh_.heroe_bodies === 'undefined') { reject(); return; } }) // console.info(`%c promise pass setAllCreepsAtStartPos...`, LOG_MATRIX) this.friendlyLocal.creeps.forEach((subMesh_, id) => { let subMesh = subMesh_.heroe_bodies[0]; subMesh.position.thrust = subMesh_.moveSpeed; subMesh.glb.animationIndex = 0; // adapt manual if blender is not setup subMesh.glb.glbJsonData.animations.forEach((a, index) => { // console.info(`%c ANimation: ${a.name} index ${index}`, LOG_MATRIX) if(a.name == 'dead') this.friendlyCreepAnimationArrange.dead = index; if(a.name == 'walk') this.friendlyCreepAnimationArrange.walk = index; if(a.name == 'salute') this.friendlyCreepAnimationArrange.salute = index; if(a.name == 'attack') this.friendlyCreepAnimationArrange.attack = index; if(a.name == 'idle') this.friendlyCreepAnimationArrange.idle = index; }) // if(id == 0) subMesh.sharedState.emitAnimationEvent = true; // this.core.collisionSystem.register(`local${id}`, subMesh.position, 15.0, 'local_hero'); }); app.localHero.friendlyLocal.creeps.forEach((creep, index) => { creep.heroe_bodies[0].position.setPosition( startUpPositions[this.core.player.data.team][0] + (index + 1) * 50, startUpPositions[this.core.player.data.team][1], startUpPositions[this.core.player.data.team][2] + (index + 1) * 50); }) resolve(); } catch(err) { console.info('err in: ', err); } }) } navigateCreeps() { app.localHero.friendlyLocal.creeps.forEach((creep, index) => { this.navigateCreep(creep, index); }) } distance3DArrayInput(a, b) { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; return Math.sqrt(dx * dx + dy * dy + dz * dz); } navigateCreep(creep, index) { if(creep.creepFocusAttackOn != null) { // console.log('test attacher nuuu return '); return; } creep.firstPoint = creepPoints[this.core.player.data.team].firstPoint; creep.finalPoint = creepPoints[this.core.player.data.team].finalPoint; const start = [creep.heroe_bodies[0].position.x, creep.heroe_bodies[0].position.y, creep.heroe_bodies[0].position.z]; let test = this.distance3DArrayInput(creep.firstPoint, start); if(test < 20) { creep.gotoFinal = true; } const end = [creep.firstPoint[0], creep.firstPoint[1], creep.firstPoint[2]]; const endFinal = [creep.finalPoint[0], creep.finalPoint[1], creep.finalPoint[2]]; let path; if(creep.gotoFinal) { if(creep.gotoFinal == true) { path = this.core.RPG.nav.findPath(start, endFinal); } else { path = this.core.RPG.nav.findPath(start, end); } } else { path = this.core.RPG.nav.findPath(start, end); } if(!path || path.length === 0) {console.warn('No valid path found.'); return;} this.setWalkCreep(index); followPath(creep.heroe_bodies[0], path, this.core); } setWalk() { this.core.RPG.heroe_bodies.forEach((subMesh, index) => { subMesh.glb.animationIndex = this.heroAnimationArrange.walk; // console.info(`%chero walk`, LOG_MATRIX) if(index == 0) app.net.send({ sceneName: subMesh.name, animationIndex: subMesh.glb.animationIndex }) }); } setSalute() { this.core.RPG.heroe_bodies.forEach((subMesh, index) => { subMesh.glb.animationIndex = this.heroAnimationArrange.salute; // console.info(`%chero salute`, LOG_MATRIX) if(index == 0) app.net.send({ sceneName: subMesh.name, animationIndex: subMesh.glb.animationIndex }) }); } setDead() { this.core.RPG.heroe_bodies.forEach((subMesh, index) => { subMesh.glb.animationIndex = this.heroAnimationArrange.dead; if(index == 0) app.net.send({ sceneName: subMesh.name, animationIndex: subMesh.glb.animationIndex }) console.info(`%cHero dead${subMesh.name}.`, LOG_MATRIX); }); } setIdle() { this.core.RPG.heroe_bodies.forEach((subMesh, index) => { subMesh.glb.animationIndex = this.heroAnimationArrange.idle; // console.info(`%chero idle`, LOG_MATRIX) if(index == 0) app.net.send({ sceneName: subMesh.name, animationIndex: subMesh.glb.animationIndex }) }); } setAttack(on) { this.heroFocusAttackOn = on; this.core.RPG.heroe_bodies.forEach(subMesh => { subMesh.glb.animationIndex = this.heroAnimationArrange.attack; // console.info(`%c ${subMesh.name} BEFORE SEND attack index ${subMesh.glb.animationIndex}`, LOG_MATRIX) app.net.send({ sceneName: subMesh.name, animationIndex: subMesh.glb.animationIndex }) }); app.tts.speakHero(app.player.data.hero.toLowerCase(), 'attack'); } setWalkCreep(creepIndex) { console.info(`%cfriendly setWalkCreep!`, LOG_MATRIX) // if(this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex != this.friendlyCreepAnimationArrange.walk) { // } this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex = this.friendlyCreepAnimationArrange.walk; let pos = this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].position; if(this.core.net.virtualEmiter == null) { return; } if(pos.teams.length > 0) if(pos.teams[0].length > 0) app.net.send({ toRemote: pos.teams[0], // default null remote conns sceneName: pos.netObject, // origin scene name to receive animationIndex: this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex }); if(pos.teams.length > 0) if(pos.teams[1].length > 0) app.net.send({ toRemote: pos.teams[1], // default null remote conns remoteName: pos.remoteName, // to enemy players sceneName: pos.netObject, // now not important animationIndex: this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex }); // } } setAttackCreep(creepIndex) { // console.info(`%cfriendly creep attack enemy!`, LOG_MATRIX) // if(this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex != this.friendlyCreepAnimationArrange.attack) { this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex = this.friendlyCreepAnimationArrange.attack; // } let pos = this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].position; if(this.core.net.virtualEmiter == null) { return; } if(pos.teams.length > 0) if(pos.teams[0].length > 0) app.net.send({ toRemote: pos.teams[0], // default null remote conns sceneName: pos.netObject, // origin scene name to receive animationIndex: this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex }); if(pos.teams.length > 0) if(pos.teams[1].length > 0) app.net.send({ toRemote: pos.teams[1], // default null remote conns remoteName: pos.remoteName, // to enemy players sceneName: pos.netObject, // now not important animationIndex: this.friendlyLocal.creeps[creepIndex].heroe_bodies[0].glb.animationIndex }); } attachEvents() { addEventListener('attack-magic0', (e) => { this.setSalute(); console.log(e.detail); this.core.RPG.heroe_bodies.forEach(subMesh => { // level0 have only one instance - more level more instance in visuals context console.info(`%cLOADING hero ghostPos`, LOG_MATRIX) const distance = 100.0; // how far in front of hero const lift = 0.5; // --- rotation.y in degrees → radians const yawRad = (subMesh.rotation.y || 0) * Math.PI / 180; // --- local forward vector (relative to hero) const forward = vec3.normalize([ Math.sin(yawRad), // x 0, // y Math.cos(yawRad) // z (rig faces -Z) ]); // --- compute ghost local offset const ghostOffset = vec3.mulScalar(forward, distance); ghostOffset[1] += lift; // --- apply to local instance position subMesh.instanceTargets[1].position = ghostOffset; setTimeout(() => { subMesh.instanceTargets[1].position[0] = 0; subMesh.instanceTargets[1].position[2] = 0; console.log("maybe idle? ", this.setWalk) this.setWalk() }, 1300) }); }) // Events HERO pos addEventListener('set-walk', () => { this.setWalk(); app.tts.speakHero(app.player.data.hero.toLowerCase(), 'walk'); }) addEventListener('set-idle', () => { this.setIdle(); }) addEventListener('set-attack', () => { this.setAttack(); }) addEventListener('set-dead', () => { this.setDead(); }) addEventListener('set-salute', () => { this.setSalute(); }) addEventListener('close-distance', (e) => { if((e.detail.A.id.indexOf('friendly') != -1 && e.detail.B.id.indexOf('friendly') != -1) || (e.detail.A.group == "local_hero" && e.detail.B.id.indexOf('friendly') != -1) || (e.detail.A.group == "friendly" && e.detail.B.group == "local_hero")) { // console.info('close distance BOTH friendly :', e.detail.A) return; } // core.net.virtualEmiter != null no emiter only for local hero corespondes if(e.detail.A.group == "enemy" && this.core.net.virtualEmiter != null) { if(e.detail.B.group == "friendly" && e.detail.B.id.indexOf('friendlytron') == -1) { //------------------ BLOCK let lc = app.localHero.friendlyLocal.creeps.filter((localCreep) => localCreep.name == e.detail.B.id)[0]; console.info('A = enemy vs B = friendly <close-distance> is there friendly creeps here ', lc); if(lc === undefined) {return;} lc.creepFocusAttackOn = app.enemies.enemies.filter((enemy) => enemy.name == e.detail.A.id)[0]; if(lc.creepFocusAttackOn === undefined) { lc.creepFocusAttackOn = app.enemies.creeps.filter((creep) => creep.name == e.detail.A.id)[0]; // console.info('A = enemy vs B = friendly <close-distance> is there enemy HERO here ', lc.creepFocusAttackOn); } if(lc.creepFocusAttackOn === undefined && e.detail.A.id.indexOf('enemytron') != -1) { lc.creepFocusAttackOn = app.enemytron; console.info('<generate game event here> creeps attack enemy home.', lc.creepFocusAttackOn); } if(lc.creepFocusAttackOn === undefined) {return;} app.localHero.setAttackCreep(e.detail.B.id[e.detail.B.id.length - 1]); } } else if(e.detail.A.group == "friendly" && e.detail.A.id.indexOf('friendlytron') == -1) { if(e.detail.B.group == "enemy" && this.core.net.virtualEmiter != null) { let lc = app.localHero.friendlyLocal.creeps.filter((localCreep) => localCreep.name == e.detail.A.id)[0]; if(lc === undefined) {return;} lc.creepFocusAttackOn = app.enemies.enemies.filter((enemy) => enemy.name == e.detail.B.id)[0]; if(lc.creepFocusAttackOn == undefined) { lc.creepFocusAttackOn = app.enemies.creeps.filter((creep) => creep.name == e.detail.B.id)[0]; } if(lc.creepFocusAttackOn === undefined && e.detail.B.id.indexOf('enemytron') != -1) { lc.creepFocusAttackOn = app.enemytron; // console.info('<generate game event here> creeps attack enemy home.', lc.creepFocusAttackOn); } if(lc.creepFocusAttackOn === undefined) { return; } app.localHero.setAttackCreep(e.detail.A.id[e.detail.A.id.length - 1]); } } // LOCAL if(e.detail.A.group == 'local_hero') { this.heroFocusAttackOn = app.enemies.enemies.filter((enemy) => enemy.name == e.detail.B.id)[0]; if(this.heroFocusAttackOn == undefined) { this.heroFocusAttackOn = app.enemies.creeps.filter((creep) => creep.name == e.detail.B.id)[0]; if(this.heroFocusAttackOn == undefined) { if(e.detail.B.id.indexOf('enemytron') != -1) { this.heroFocusAttackOn = app.enemytron; // console.info('<generate game event> LOCALHERO attack enemy home.', this.heroFocusAttackOn); } } } this.setAttack(this.heroFocusAttackOn); } else if(e.detail.B.group == 'local_hero') { this.heroFocusAttackOn = app.enemies.enemies.filter((enemy) => enemy.name == e.detail.A.id)[0]; if(this.heroFocusAttackOn == undefined) { this.heroFocusAttackOn = app.enemies.creeps.filter((creep) => creep.name == e.detail.A.id)[0]; if(this.heroFocusAttackOn == undefined) { if(e.detail.A.id.indexOf('enemytron') != -1) { this.heroFocusAttackOn = app.enemytron; // console.info('<generate game event here2> creeps attack enemy home.', this.heroFocusAttackOn); } } } this.setAttack(this.heroFocusAttackOn); } }) addEventListener(`animationEnd-${this.heroe_bodies[0].name}`, (e) => { if(e.detail.animationName != 'attack' || typeof this.core.enemies === 'undefined') { return; } let isEnemiesClose = false; let isEnemiesCreepClose = false; if(this.heroFocusAttackOn == null) { // console.info('animationEnd [heroFocusAttackOn == null ]', e.detail.animationName) this.core.enemies.enemies.forEach((enemy) => { if(typeof enemy.heroe_bodies === 'undefined') return; if(enemy.heroe_bodies) { let tt = this.core.RPG.distance3D( this.heroe_bodies[0].position, enemy.heroe_bodies[0].position); if(tt < this.core.RPG.distanceForAction) { console.log(`%cATTACK DAMAGE ${enemy.heroe_bodies[0].name}`, LOG_MATRIX) isEnemiesClose = true; this.calcDamage(this, enemy); } } }); this.core.enemies.creeps.forEach((creep) => { if(typeof creep.heroe_bodies === 'undefined') return; if(creep.heroe_bodies) { let tt = this.core.RPG.distance3D( this.heroe_bodies[0].position, creep.heroe_bodies[0].position); if(tt < this.core.RPG.distanceForAction) { console.log(`%cATTACK DAMAGE ${creep.heroe_bodies[0].name}`, LOG_MATRIX) isEnemiesCreepClose = true; this.calcDamage(this, creep); } } }) if(isEnemiesCreepClose == false) this.setIdle(); return; } else { if(this.core.enemies.enemies.length > 0) this.core.enemies.enemies.forEach((enemy) => { if(this.heroFocusAttackOn.name.indexOf(enemy.name) != -1) { let tt = this.core.RPG.distance3D( this.heroe_bodies[0].position, this.heroFocusAttackOn.position); if(tt < this.core.RPG.distanceForAction) { isEnemiesClose = true; console.log(`%cATTACK DAMAGE [lhero on enemy hero] ${enemy.heroe_bodies[0].name}`, LOG_MATRIX) this.calcDamage(this, enemy); return; } } }) if(this.core.enemies.creeps.length > 0) this.core.enemies.creeps.forEach((creep) => { if(this.heroFocusAttackOn.name.indexOf(creep.name) != -1) { let tt = this.core.RPG.distance3D( this.heroe_bodies[0].position, this.heroFocusAttackOn.position); if(tt < this.core.RPG.distanceForAction) { isEnemiesCreepClose = true; console.log(`%cATTACK DAMAGE [lhero on creep] ${creep.heroe_bodies[0].name}`, LOG_MATRIX) this.calcDamage(this, creep); return; } } }) let enemytron = app.RPG.distance3D(this.heroe_bodies[0].position, app.enemytron.position); if(enemytron < app.RPG.distanceForAction) { console.log(`%c HERRO ATTACK ENEMY TRON`, LOG_MATRIX); isEnemiesClose = true; this.calcDamage(this, app.enemytron); return; } } }) // This is common for all kineamtic bodies addEventListener('onTargetPositionReach', (e) => { if(e.detail.name.indexOf('friendly-creep') != -1) { let getName = e.detail.name.split('_')[0]; let t = app.localHero.friendlyLocal.creeps.filter((obj) => obj.name == getName); if(t[0].creepFocusAttackOn != null) { // console.log(`%[character base]`) return; } let testz = e.detail.body.position.z - t[0].firstPoint[2]; let testx = e.detail.body.position.x - t[0].firstPoint[0]; if(testz > 15 && testx > 15) { // got to first point t[0] for now only one sub mesh per creep... const start = [t[0].heroe_bodies[0].position.x, t[0].heroe_bodies[0].position.y, t[0].heroe_bodies[0].position.z]; const path = this.core.RPG.nav.findPath(start, t[0].firstPoint); if(!path || path.length === 0) {console.warn('No valid path found.'); return;} // getName[getName.length-1] becouse for now creekps have sum < 10 console.log('followPath creep to the FIRST POINT....') setTimeout(() => { this.setWalkCreep(getName[getName.length - 1]); followPath(t[0].heroe_bodies[0], path, app) }, 1000); } else { // goto final // console.log('SEND TO last POINT POINT to the enemy home....', t[0].finalPoint) const start = [t[0].heroe_bodies[0].position.x, t[0].heroe_bodies[0].position.y, t[0].heroe_bodies[0].position.z]; const path = this.core.RPG.nav.findPath(start, t[0].finalPoint); if(!path || path.length === 0) {console.warn('No valid path found.'); return;} // getName[getName.length-1] becouse for now creekps have sum < 10 // at the end finalPoint will be point of enemy base! setTimeout(() => { followPath(t[0].heroe_bodies[0], path, app) this.setWalkCreep(getName[getName.length - 1]); }, 1000); } return; } // for now only local hero if(this.heroFocusAttackOn == null) { let isEnemiesClose = false; this.core.enemies.enemies.forEach((enemy) => { if(typeof enemy.heroe_bodies === 'undefined') return; let tt = this.core.RPG.distance3D( this.heroe_bodies[0].position, enemy.heroe_bodies[0].position); if(tt < this.core.RPG.distanceForAction) { console.log(`%c ATTACK DAMAGE ${enemy.heroe_bodies[0].name}`, LOG_MATRIX) isEnemiesClose = true; this.calcDamage(this, enemy); } }) if(isEnemiesClose == false) this.setIdle(); } }) addEventListener('onMouseTarget', (e) => { if(this.core.RPG.selected.includes(this.heroe_bodies[0])) { // console.log("onMouseTarget POS:", e.detail.type); this.mouseTarget.position.setPosition(e.detail.x, this.mouseTarget.position.y, e.detail.z) if(e.detail.type == "attach") { this.mouseTarget.effects.circlePlaneTex.instanceTargets[0].color = [1, 0, 0, 0.9]; } else { this.mouseTarget.effects.circlePlaneTex.instanceTargets[0].color = [0.6, 0.8, 1, 0.4]; } } }) addEventListener('navigate-friendly_creeps', (e) => { if(app.net.virtualEmiter != null) { if(e.detail.localCreepNav) { console.log(`%c navigate creep ${e.detail.localCreepNav} index : ${e.detail.index}`, LOG_MATRIX) this.navigateCreep(e.detail.localCreepNav, e.detail.index); } else { this.navigateCreeps(); } } }) addEventListener('updateLocalHeroGold', (e) => { this.gold += e.detail.gold; }) } }