/*
*  shark attack.lsl
*  Part of the Sloodle project (www.sloodle.org)
*
*  Copyright (c) 2011-06 contributors (see below)
*  Released under the GNU GPL v3
*  -------------------------------------------
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see .
*
*  All scripts must maintain this copyrite information, including the contributer information listed
*
*  DESCRIPTION
*  This script is the artificial intellegence for the sharks in the pool.
*
*  
*  This script causes a shark to swim around
*
*  Contributors:
*  Paul Preibisch
*  Edmund Edgar
* screams sound file comes from http://www.freesound.org/people/thanvannispen/sounds/9431/ and is licensed under Creative Commons with Attribution: 
Than van Nispen tot Pannerden - Composer for (non-linear) Media 
*/
integer SLOODLE_CHANNEL_OBJECT_DIALOG = -3857343;//configuration channel
integer SLOODLE_CHANNEL_ENEMY_ATTACK= -163928666;//Channel to communicate on when attack occurs by an enemy
integer SLOODLE_CHANNEL_ENEMY_AIM = -163928665;//Channel to communicate on when enemy is aming at target, ie:delivering a laserbeam
float POOL_WIDTH=6;
float POOL_HEIGHT=6.525;
vector PUSH_STRENGTH=<25,15,10>;
// maximum swim distance for swimming up or down from last center
float height = 1.0;
// delay in seconds for next movement
float delay = 3.0;
// internal channel for communication
integer CHANNEL = -87;
integer MAX=30;
// last center position
vector center;
integer target_id;
list screams;
integer BLOOD=-999922110;
integer NOBLOOD=-999922111;
integer counter =0;
vector deathTarget;//the location of an avatar we want to attack
key deathKey;//the key of the avatar we will attack
integer attackTimes=0;
integer screamCounter=0;
vector rezzer_position_offset;
rotation rezzer_rotation_offset;
key rezzer_uuid=NULL_KEY;
integer    isconfigured;
integer  boundary_set;
integer SLOODLE_CHANNEL_POOL_BOUNDARIES_QUERY= -163928000;//sharks whisper on this channel requesting the boundaries from the shark pool
integer SLOODLE_CHANNEL_POOL_BOUNDARIES_RESPONSE= -163928001;//pool whispers on this channel telling sharks the boundaries from the shark pool
integer RANDOM_POS_SENSOR_RATE=5;
vector POOL_CENTER; //this is the location of the center of the pool and is determined by sensing an object called "Shark Pool"
list BOUNDING_BOX_POOL;
vector BOUNDING_BOX_MIN_CORNER;
vector BOUNDING_BOX_MAX_CORNER;
integer timeToMove=4;//this will get reset to a new value each time the shark moves, and will determined the next time that the shark shall move - so the shark doesnt move at predicatable intervals 
integer MAX_WAIT_TILL_MOVE_TIME=6;
integer MIN_WAIT_TILL_MOVE_TIME=2;
integer timeToMove_counter=0;
integer SAFETY_EDGE=2; //an extra padding so sharks noses dont stick out of the side of the pool
//sloodle_set_pos is an SL/OPENSIM friendly way of moving non-physical objects around 
sloodle_set_pos(vector targetposition){
    integer counter=0;
    while ((llVecDist(llGetPos(), targetposition) > 0.001)&&(counter<50)) {
        counter+=1;
        llSetPos(targetposition);
    }
}
debug (string message ){
      list params = llGetPrimitiveParams ([PRIM_MATERIAL ]);
      if (llList2Integer (params ,0)==PRIM_MATERIAL_FLESH){
           llOwnerSay(llGetScriptName ()+": " +message );
     }
} 
//sloodle_handle_command, is what we use to interpret data that comes through linked messages from our sloodle_rezzer_objects script (who talks to the server)
integer sloodle_handle_command(string str){
   
    list bits = llParseString2List(str,["|"],[]);
    integer numbits = llGetListLength(bits);
    string name;
    name = llList2String(bits,0);
    //set:position|<18.31369, 15.54828, 3.15605>|<0.00000, 0.00000, 0.25705, 0.96640>|0df19640-c2e9-43c1-4359-b805ef08dad0
    if (name == "set:position") {        
        rezzer_position_offset = (vector)llList2String(bits,1);
        rezzer_rotation_offset = (rotation)llList2String(bits,2);
        rezzer_uuid = llList2Key(bits,3);
        
        
    }
    if (rezzer_uuid!=NULL_KEY) return TRUE; else return FALSE; 
}
float randBetween(float min, float max){
    return llFrand(max - min) + min;
}
integer random_integer( integer min, integer max ){  return min + (integer)( llFrand ( max - min + 1 ) );}
//blood causes the shark to rez a bullet, which has particle effects for blood
blood(key av){
    llMessageLinked(LINK_SET, BLOOD, "", av);    
}
noblood(){
    llMessageLinked(LINK_SET, NOBLOOD, "", NULL_KEY);    
}
//getScreeam is used to play random sounds
string getScream(){
    screams = ["SND_SCREAM1","SND_SCREAM2","SND_SCREAM3","SND_SCREAM4"];
    integer screamLen = llGetListLength(screams);
    screamCounter++;
    if  (screamCounter>screamLen-1){
        screamCounter=0;
    }
    return llList2String(screams,screamCounter);
}
vector swim_in_boundary(vector dest, vector pos){
        if (dest.x > BOUNDING_BOX_MAX_CORNER.x-SAFETY_EDGE){
            dest.x=pos.x;
        }
        if (dest.x < BOUNDING_BOX_MIN_CORNER.x+SAFETY_EDGE){
            dest.x=pos.x;
        }
        if (dest.y > BOUNDING_BOX_MAX_CORNER.y-SAFETY_EDGE){
            dest.y=pos.y;
        }
        if (dest.y < BOUNDING_BOX_MIN_CORNER.y+SAFETY_EDGE){
            dest.y=pos.y;
        }                
        if (dest.z > BOUNDING_BOX_MAX_CORNER.z-0.5){
            dest.z=pos.z;
        }
        if (dest.z < BOUNDING_BOX_MIN_CORNER.z+0.5){
            dest.z=pos.z;
        }        
        return dest;        
}
integer is_in_boundary(vector dest){
    integer inBoundary =TRUE;
    if (dest.x > BOUNDING_BOX_MAX_CORNER.x-SAFETY_EDGE){
        
            inBoundary = TRUE;
        }
        if (dest.x < BOUNDING_BOX_MIN_CORNER.x+SAFETY_EDGE){
            inBoundary = FALSE;
        }
        if (dest.y > BOUNDING_BOX_MAX_CORNER.y-SAFETY_EDGE){
           inBoundary = FALSE;
        }
        if (dest.y < BOUNDING_BOX_MIN_CORNER.y+SAFETY_EDGE){
           inBoundary = FALSE;
        }                
        if (dest.z > BOUNDING_BOX_MAX_CORNER.z-0.5){
           inBoundary = FALSE;
        }
        if (dest.z < BOUNDING_BOX_MIN_CORNER.z+0.5){
            inBoundary = FALSE;
        }  
        if (inBoundary){
            debug("avatar is in the boundary");
        }else{
            debug("avatar is NOT in the boundary");
        }      
        return inBoundary;        
}
default{
   
    state_entry()
    {
        llTriggerSound("SND_JAWS", 1);
        isconfigured=FALSE;
        noblood(); //turn off particle effects
        counter=0;
    }
    
    link_message(integer sender_num, integer num, string str, key id) {
       
         // Split the data up into lines
        list lines = llParseStringKeepNulls(str, ["\n"], []);  
        integer numlines = llGetListLength(lines);
       
        if (num == SLOODLE_CHANNEL_OBJECT_DIALOG) {
            // Split the message into lines
            integer i = 0;
            for (i=0; i < numlines; i++) {
                isconfigured = sloodle_handle_command(llList2String(lines, i));
                if (isconfigured){
                  
                    state get_boundary;
                  }
              }
        }
    }
   
}
state get_boundary{
    on_rez(integer start_param) {
        llResetScript();
    }
    
    state_entry() {
        //llSensorRepeat("Shark Pool", "",PASSIVE,  10, TWO_PI, 5);
         debug("in get_boundary");
         llSetTimerEvent(2);
        
    }
    timer() {
        debug("trying to find: Shark Pool");
        llSensor("Shark Pool", "", SCRIPTED, 10, TWO_PI);
    }
    sensor(integer num_detected) {
        debug("found pool!");
        POOL_CENTER = llDetectedPos(0);
        BOUNDING_BOX_POOL= llGetBoundingBox(llDetectedKey(0));
        vector pool_lower_corner = llList2Vector(BOUNDING_BOX_POOL,0);
        vector pool_upper_corner = llList2Vector(BOUNDING_BOX_POOL,1);
        BOUNDING_BOX_MIN_CORNER =POOL_CENTER+pool_lower_corner;  
        BOUNDING_BOX_MAX_CORNER =POOL_CENTER+pool_upper_corner;
        POOL_WIDTH=pool_upper_corner.x+(-1*pool_lower_corner.x);
        POOL_HEIGHT=pool_upper_corner.z+(-1*pool_lower_corner.z);
        debug("found bounding box - height: "+(string)POOL_HEIGHT+" width: "+(string)POOL_WIDTH);
        
        state ready;     
    }
}
state ready{ 
    on_rez(integer start_param) {
        llResetScript();
    }
    state_entry() {
            llListen(232323, "", "","");
            debug("in the ready state");
            llSetTimerEvent(1); //the timer event is set so that our shark moves around the pool every second 
            noblood();//TURN off blood;
            deathKey=NULL_KEY;
            deathTarget = ZERO_VECTOR;
            llSetText("", <0,0,1>, 1);
            llSetBuoyancy(0.9);
            llSensorRepeat("", "", AGENT, POOL_WIDTH/2, PI,timeToMove); //start searching for victems to eat!
        
    }
    listen(integer channel, string name, key id, string message) {
        //if pool moves
        if (channel==232323){
            llSleep(3);
            state get_boundary;
        }
    }
    sensor(integer num_detected) {
        integer i;
        
        //we found a victem so look at them and attack
        
        for (i=0;i);
            llRotLookAt(rot, 1.0, 1.0);
            llMessageLinked(LINK_SET, SLOODLE_CHANNEL_ENEMY_AIM, "", deathKey); 
            state attack;
        }
        
    }
    timer(){
        timeToMove_counter++;
        if (timeToMove_counter);
        llRotLookAt(rot, 1.0, 1.0);
        sloodle_set_pos(dest);
    }
}
state attack{
on_rez(integer start_param) {
        llResetScript();
    }
    state_entry() {
        llListen(232323, "", "", "");
        debug("in the attack state");
        attackTimes = 0;
        target_id = llTarget(deathTarget, 0.5);
        llTriggerSound("SND_JAWS", 1);
        sloodle_set_pos(deathTarget);
        llSensorRepeat("", "", AGENT, 10, PI,3);
        llSetTimerEvent(10);
    
    }
    listen(integer channel, string name, key id, string message) {
        //if pool moves
        if (channel==232323){
            llSleep(3);
            state get_boundary;
        }
    }
    sensor(integer num_detected) {
        integer i;
        for (i=0;i);
            llRotLookAt(rot, 1.0, 1.0);
            llLookAt( deathTarget + <0.0, 0.0, 1.0>, 3.0, 1.0 );
            sloodle_set_pos(deathTarget);
        }
        
    }
    timer() {
        llSetTimerEvent(0);
          vector dest=POOL_CENTER;
        sloodle_set_pos(dest);
        state ready;
    }
    at_target(integer tnum, vector targetpos, vector ourpos)    {
        if (tnum == target_id){
            debug("at the target");
            // llRezObject("redBubbles", llGetPos(), llGetVel(), ZERO_ROTATION, 0);
             llRezObject("blood_bath", llGetPos(), llGetVel(), ZERO_ROTATION, 0);
             llMessageLinked(LINK_SET, SLOODLE_CHANNEL_ENEMY_ATTACK, "", deathKey);             
            llTriggerSound(getScream(), 1);
            llTriggerSound("SND_BITE", 1);
            attackTimes++;
            blood(deathKey);
            llPushObject(deathKey,PUSH_STRENGTH,PUSH_STRENGTH, TRUE);
            llTargetRemove(target_id);
            if (attackTimes>5) {
                attackTimes= 0;
                debug("attackTimes >5 going to ready state");
                state ready;
            }
        }
    }
}
// Please leave the following line intact to show where the script lives in Git:
// SLOODLE LSL Script Git Location: mod/gaming-1.0/objects/shark/assets/shark_artificial_intelligence.lslp