var Ennemy = function(game, options) { var self=this; self.id=game.getNewId(); self.is_hoverable=true; self.options = options; self.game=game; self.move_speed= 0.4; self.run_speed= 1.0; self.check_vision_every= 80; self.attack_range = 10; self.hovered=false; self.is_dead=false; self.weapon_speed= 0.4; self.weapon_range= 15; self.weapon_attack_damage = 5; self.life=30; self.max_life=30; self.is_running= false; self.running_timer= null; self.move_action_weight=0; self.is_moving=false; self.in_cells=[]; self.vision_angle = 55; self.vision_distance=game.opt.door_size*1.0; self.ennemy_detection_distance = game.opt.door_size*2.0; this.build = function() { this.patrol_id = 0; this.patrol_inc = 1; this.patrol_waiting_timer = null; this.patrol_right_left_inc = 4; this.patrol_right_left_deg = 180; this.next_pos = this.get_next_patrol_point(); var path = this.options.patrol_positions; // Debug draw path for(var i=1; i1, opacity:game.opt.debug_level>1 ? 1 : 0, container: game.scene, color: Math.random()*0xffffff, origin: path[i-1], destination: path[i] }); } this.create(); var v = new THREE.Vector3(this.next_pos.x, this.next_pos.y, this.next_pos.z); this.view_vector = v.sub(this.container.position); }; }; Ennemy.prototype.create =function() { var self=this; this.container = new THREE.Object3D(); this.game.scene.add(this.container); // Cube simulating ennemy, for collision detection var cube_material = new THREE.MeshPhongMaterial( { visible:false } ); if(game.opt.debug_level>1) { cube_material = new THREE.MeshPhongMaterial( { color: 0xbbbbff, wireframe: true, transparent:true, opacity: 1 } ); } var cube_geo = new THREE.BoxGeometry(10 , 10, 10); this.container_mesh = new THREE.Mesh(cube_geo, cube_material); this.container_mesh.name='ennemy'; this.container_mesh.position.y=1; this.container_mesh.id= game.getNewId(); this.container_mesh.object = this; this.container.add(this.container_mesh); this.life_container = new THREE.Object3D(); this.life_container.name='ennemy'; this.life_container.position.y=2; this.life_container.rotation.x = Math.radians(90); var life_geo = new THREE.RingGeometry(5, 6, 6, 1, 3, Math.PI*2); this.life_material = new THREE.MeshPhongMaterial( { color: 0xaa3333, side: THREE.DoubleSide, visible: false } ); this.life_mesh = new THREE.Mesh(life_geo, this.life_material); this.life_container.add(this.life_mesh); this.container.add(this.life_container); this.container.position.x = this.options.x; this.container.position.y = 0; this.container.position.z = this.options.z; // Vision geometry this.vision_geom = new THREE.Geometry(); var current_deg_angle = -this.vision_angle; var v1 = new THREE.Vector3(0, 1, 0); var v2 = new THREE.Vector3(-Math.cos(angle)*this.vision_distance,1,Math.sin(angle)*this.vision_distance); var v3 = new THREE.Vector3(Math.cos(angle)*this.vision_distance,1,Math.sin(angle)*this.vision_distance); this.vision_orig_vertices=[v1]; this.vision_geom.vertices.push(v1); var vIndex=2; var vision_step = this.vision_angle/10; for(true; current_deg_angle<=this.vision_angle; current_deg_angle+=vision_step) { var angle = Math.radians(90+current_deg_angle); vLast = new THREE.Vector3(Math.cos(angle)*this.vision_distance,1,Math.sin(angle)*this.vision_distance); this.vision_geom.vertices.push(vLast); this.vision_orig_vertices.push(vLast.clone()); if(vIndex>3) { this.vision_geom.faces.push(new THREE.Face3(0,vIndex-1,vIndex-2)); } vIndex+=1; } this.vision_geom.computeFaceNormals(); this.vision_geom.dynamic=true; this.vision_geom.verticesNeedUpdate=true; var vision_material = new THREE.MeshPhongMaterial( { color: 0xaef8a8, wireframe:false, transparent:true, opacity: 0.3, visible:false } ); if(game.opt.debug_level>1) { vision_material = new THREE.MeshPhongMaterial( { color: 0xaaffaa, wireframe:true, transparent:true, opacity: 1, visible:false } ); } this.vision = new THREE.Mesh( this.vision_geom, vision_material); this.vision.rotation.y=Math.radians(0); this.vision.position.x = this.options.x; this.vision.position.y = 0; this.vision.position.z = this.options.z; game.scene.add(this.vision); var materials = game.assets.ennemy_mat; for ( var i = 0; i < materials.length; i ++ ) { var m = materials[ i ]; m.skinning = true; m.morphTargets = true; } this.mesh = new THREE.SkinnedMesh( game.assets.ennemy_geo, new THREE.MultiMaterial(materials)); this.mesh.scale.x=15; this.mesh.scale.y=15; this.mesh.scale.z=15; this.container.add(this.mesh); this.mesh.receiveShadow = true; //this.mesh.castShadow = true; this.mesh.position.x = 0; this.mesh.position.y = 1; this.mesh.position.z = 0; this.mixer = new THREE.AnimationMixer( this.mesh ); this.walkingClip = game.assets.ennemy_geo.animations[2]; this.iddlingClip = game.assets.ennemy_geo.animations[1]; this.attackingClip = game.assets.perso_geo.animations[3]; this.dyingClip = game.assets.perso_geo.animations[4]; this.move_action = this.mixer.clipAction(this.walkingClip, null ).setDuration(0.45); this.move_action.name='move'; this.idle_action = this.mixer.clipAction(this.iddlingClip, null ).setDuration(5); this.idle_action.name='idle'; this.attack_action = this.mixer.clipAction(this.attackingClip, null ).setDuration(this.weapon_speed); this.attack_action.name='attack'; this.attack_action.setLoop(THREE.LoopOnce, 0); this.attack_action.clampWhenFinished = true; this.mixer.addEventListener('finished', this.end_attack.bind(this)); this.dying_action = this.mixer.clipAction(this.dyingClip, null ).setDuration(0.7); this.dying_action.setLoop(THREE.LoopOnce, 0); this.dying_action.clampWhenFinished = true; this.move_action.play(); this.idle_action.play(); this.attack_action.setEffectiveWeight(0); this.dying_action.setEffectiveWeight(0); this.move_action.setEffectiveWeight(0); this.idle_action.setEffectiveWeight(1); this.walk(); }; Ennemy.prototype.hover=function() { this.hovered=true; this.life_material.visible=true; //console.log('hover in me!',this); }; Ennemy.prototype.unhover=function() { this.hovered=false; this.life_material.visible=false; //console.log('unhover in me!',this); }; Ennemy.prototype.targeted=function(from) { if(!this.is_dying) { var distance = from.container.position.distanceTo(this.container.position); if(distancethis.attack_range || !this.is_running) { // Moving X restrictions if(Math.abs(this.container.position.x - this.move_destination.x) > 1) { this.container.position.add(this.move_step_vector_x); this.vision.position.add(this.move_step_vector_x); } else { this.container.position.x = this.move_destination.x; this.vision.position.x = this.move_destination.x; } if(Math.abs(this.container.position.z - this.move_destination.z) > 1) { this.container.position.add(this.move_step_vector_z); this.vision.position.add(this.move_step_vector_z); } else { this.container.position.z = this.move_destination.z; this.vision.position.z = this.move_destination.z; } } else if(!game.focus_perso.is_dying) { this.attack(game.focus_perso); } if(!moving) { game.assets.step_sound.pause(); this.move_weight_destination = 0; this.is_moving=false; this.move_destination=null; } } else { this.patrol_right_left_deg+=this.patrol_right_left_inc; this.patrol_right_left_deg = this.patrol_right_left_deg%360; var rad_ang = Math.radians(this.patrol_right_left_deg); var distance = game.opt.door_size; /* this.lookAt(this.move_destination,{ x: this.container.position.x + this.view_vector.x*distance/4 + Math.cos(rad_ang) * distance, y: this.container.position.y + this.view_vector.y*distance/4, z: this.container.position.z + this.view_vector.z*distance/4 + Math.sin(rad_ang) * distance }); */ } }; Ennemy.prototype.attack = function(target, reload) { if(!this.attacking || reload) { if(reload) { this.attack_action.reset(); } else { this.attack_action.stop(); this.attack_action.play(); } console.log('attack!', reload); play_multiple(game.assets.ennemy_attack_sound); this.attacking=true; this.attack_target = target; target.attacked(this); this.move_action.setEffectiveWeight(0); this.idle_action.setEffectiveWeight(0); this.attack_action.setEffectiveWeight(1); } }; Ennemy.prototype.end_attack = function(x) { if(!this.is_dying) { if(this.attack_target.is_dying) { this.attacking=false; this.move_weight_destination=1; this.attack_action.setEffectiveWeight(0); this.idle_action.setEffectiveWeight(1); } else { var distance = this.container.position.distanceTo(this.attack_target.container.position); if(distance 0) { is_near=collisionResults.filter(function(x) { return x.object.name=='p';}).length>0; // It is visible to the user. Now let's check if the user is looking at it if(collisionResults[0].object.name=='p') { if(collisionResults[0].distance < this.vision_distance && this.vision_destination) { var angle = find_angle(game.focus_perso.container.position,this.container.position, this.vision_destination); angle = angle*180/Math.PI; if(angle0) { this.run(game.focus_perso.container.position.clone()); } this.vision.material.visible=is_near; // Update vision mesh if needed if(is_near) { for (var vertexIndex = 1; vertexIndex < this.vision.geometry.vertices.length; vertexIndex++) { var localVertex = this.vision.geometry.vertices[vertexIndex].clone(); var globalVertex = localVertex.applyMatrix4( this.vision.matrix ); var directionVector = globalVertex.sub( this.vision.position ); var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize(),0, this.vision_distance); var collisionResults = ray.intersectObjects(static_obstacles); this.vision.geometry.vertices[vertexIndex].x = this.vision_orig_vertices[vertexIndex].x; this.vision.geometry.vertices[vertexIndex].y = this.vision_orig_vertices[vertexIndex].y; this.vision.geometry.vertices[vertexIndex].z = this.vision_orig_vertices[vertexIndex].z; this.vision.geometry.verticesNeedUpdate=true; if ( collisionResults.length > 0 && collisionResults[0].distance < this.vision_distance) { // Display limited vision vertice if(collisionResults[0].object.name=='walls') { var distance = collisionResults[0].distance; this.vision.geometry.vertices[vertexIndex].x *= distance / this.vision_distance; this.vision.geometry.vertices[vertexIndex].y *= distance / this.vision_distance; this.vision.geometry.vertices[vertexIndex].z *= distance / this.vision_distance; } } } } // Loop check vision this.check_vision_loop=0; this.check_vision_timer= window.setTimeout(this.check_vision_end.bind(this), this.check_vision_every); }; Ennemy.prototype.check_vision_end = function() { this.check_vision_timer=null; this.check_vision(); }; Ennemy.prototype.move_weight = function() { if(this.move_weight_destination!==null) { var c = this.move_action.getEffectiveWeight(); var dest; if(c>this.move_weight_destination) { var dest = Math.max(0,c-0.3); this.move_action.setEffectiveWeight(dest); this.idle_action.setEffectiveWeight(1-dest); this.move_action_weight=dest; } else if(c=this.options.patrol_positions.length) { if(this.options.patrol_loop) { next_id=0; } else { next_id=this.options.patrol_positions.length-2; next_inc=-1; } } this.patrol_id=next_id; this.patrol_inc=next_inc; return this.options.patrol_positions[next_id]; };