var Ennemy = function(game, options) { var self=this; self.options = options; self.game=game; self.move_speed= 0.2; self.run_speed= 1.1; self.check_vision_every= 80; self.is_running= false; self.running_timer= null; self.id='ennemy'+Math.random(); self.move_action_weight=0; self.is_moving=false; self.in_cells=[]; self.vision_distance=game.opt.door_size*1.5; 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(); this.create(); }; }; 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.MeshBasicMaterial( { color: 0x0000ff, transparent:true, opacity: 0.3 } ); if(game.opt.debug_level>1) { cube_material = new THREE.MeshBasicMaterial( { color: 0x0000ff, transparent:true, opacity: 1 } ); } var cube_geo = new THREE.BoxGeometry(6 , 6, 6); this.container_mesh = new THREE.Mesh(cube_geo, cube_material); this.container_mesh.rotation.x = Math.radians(90); this.container_mesh.rotation.z = Math.radians(45); this.container_mesh.position.y=1; this.container.add(this.container_mesh); 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 deg_angle= 90; var current_deg_angle = 0; var angle = Math.radians(45-current_deg_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=[]; this.vision_geom.vertices.push(v1); this.vision_orig_vertices.push(v1.clone()); this.vision_geom.vertices.push(v2); this.vision_orig_vertices.push(v2.clone()); this.vision_geom.vertices.push(v3); this.vision_orig_vertices.push(v3.clone()); var vIndex=2; for(true; current_deg_angle1) { vision_material = new THREE.MeshBasicMaterial( { color: 0x00ff00, wireframe:true, transparent:true, opacity: 1 } ); } 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.move_action = this.mixer.clipAction(this.walkingClip, null ).setDuration(0.45); this.idle_action = this.mixer.clipAction(this.iddlingClip, null ).setDuration(5); this.move_action.play(); this.idle_action.play(); this.move_action.setEffectiveWeight(0); this.idle_action.setEffectiveWeight(1); this.walk(); }; Ennemy.prototype.run = function(destination) { window.clearTimeout(this.patrol_waiting_timer); window.clearTimeout(this.running_timer); this.patrol_waiting_timer=null; this.running_timer=null; this.move_destination = destination; this.moveTo(this.move_destination); this.lookAt(this.move_destination, this.move_destination); if(!this.is_running) { this.is_running=true; this.move_action.setDuration(0.45); } this.running_timer = window.setTimeout(this.walk.bind(this), 2000); }; Ennemy.prototype.walk = function() { window.clearTimeout(this.running_timer); window.clearTimeout(this.patrol_waiting_timer); this.patrol_waiting_timer=null; this.running_timer=null; if(this.is_running) { this.is_running=false; if(this.move_destination) { this.moveTo(this.move_destination); this.move_action.setDuration(0.8); } } }; Ennemy.prototype.lookAt= function(pos,view_pos) { pos.y=0; this.container.lookAt(pos); this.vision.lookAt(view_pos); } Ennemy.prototype.moveTo= function(pos) { var current_pos = this.container.position; if(current_pos.equals(pos)) { return; } // Last part of moving this.is_moving=true; var distance = Math.sqrt(Math.pow(pos.x-current_pos.x,2)+Math.pow(pos.z-current_pos.z,2)); var directionX = (pos.x-current_pos.x) / distance; var directionZ = (pos.z-current_pos.z) / distance; var speed = this.is_running ? this.run_speed : this.move_speed; move_step_x = speed * directionX; move_step_z = speed * directionZ; this.move_step_vector_x = new THREE.Vector2(); this.move_step_vector_z = new THREE.Vector2(); this.move_step_vector_x.x = move_step_x; this.move_step_vector_x.z=0; this.move_step_vector_z.x=0; this.move_step_vector_z.z = move_step_z; // Actually moving... this.move_destination = pos; if(this.move_action_weight!=1) { this.move_weight_destination = 1; } }; Ennemy.prototype.move_step= function() { if(this.is_moving) { var moving=0; // Patrol right/left if(!this.is_running) { 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 diff_x = Math.abs(this.container.position.x>this.move_destination.x ? this.container.position.x-this.move_destination.x : this.move_destination.x - this.container.position.x); var diff_z = Math.abs(this.container.position.z>this.move_destination.z ? this.container.position.z-this.move_destination.z : this.move_destination.z - this.container.position.z); var distance = Math.sqrt(diff_x + diff_z); if(distance>2) { this.lookAt(this.move_destination,{ x: this.move_destination.x + Math.cos(rad_ang) * distance, y: this.move_destination.y, z: this.move_destination.z + Math.sin(rad_ang) * distance }); } } if(this.container.position.x!=this.move_destination.x || this.container.position.z!=this.move_destination.z) { moving++; } // 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; } 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 diff_x = Math.abs(this.container.position.x>this.next_pos.x ? this.container.position.x-this.next_pos.x : this.next_pos.x - this.container.position.x); var diff_z = Math.abs(this.container.position.z>this.next_pos.z ? this.container.position.z-this.next_pos.z : this.next_pos.z - this.container.position.z); var distance = Math.sqrt(diff_x + diff_z); this.lookAt(this.next_pos,{ x: this.next_pos.x + Math.cos(rad_ang) * distance, y: this.next_pos.y, z: this.next_pos.z + Math.sin(rad_ang) * distance }); } }; Ennemy.prototype.update_vision = function() { this.check_vision_loop=1; if(!this.check_vision_timer) { this.check_vision(); } }; Ennemy.prototype.check_vision = function() { if(!this.check_vision_loop) { return; } // Collision callbacks var originPoint = this.container.position; var obstacles = game.getObstaclesWithPlayer(); var collisions=[]; 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(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; } if(collisionResults[0].object.name=='p') { collisions.push(collisionResults[0].point); } } } if(collisions.length>0) { this.run(game.focus_perso.container.position.clone()); } // 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) { 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]; };