/*
* 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