Swarm Script

Written by Kitsune
// Swarm script
// by Apotheus Silverman
// This script is my implementation of the well-known swarm algorithm
// which can be found in numerous open-source programs.
// Due to the specifics of the SL environment, I have strayed from some
// of the traditional rules slightly. Regardless, the end effect is
// indistiguishable from the original algorithm.
 
// Configurable parameters
 
// Determines whether or not to enable STATUS_SANDBOX.
integer sandbox = FALSE;
 
// Timer length
float timer_length = 0.5;
 
// Die after this many seconds
integer kill_time = 300;
 
// Enables or disables schooling behavior
integer school = TRUE;
 
// Schooling comm channel
integer school_comm_channel = 9284;
 
// Schooling behavior update interval (should be a multiple of timer_length)
float school_update_interval = 2.0;
 
// How much force to apply with each impulse
float force_modifier = 0.7;
 
// How much force to apply when repulsed by another like me
float repulse_force_modifier = 0.86;
 
// How much friction to use on a scale from 0 to 1.
// Note that friction takes effect each timer cycle, so the lower the timer length,
// the more the friction you specify here will take effect, thereby increasing actual
// friction applied.
float friction = 0.45;
 
// How much to modify the rotation strength. Higher numbers produce greater strength
// Note that if the modifier is too small, the object may not rotate at all.
float rotation_strength_modifier = 2.8;
 
// How much to modify rotation damping. Higher numbers produce slower rotation.
float rotation_damping_modifier = 5000000.0;
 
// Does this object "swim" in air or water?
// 2 = air
// 1 = water
// 0 = both
integer flight_mode = 2;
 
// Maximum distance from spawn point
float max_distance = 15.0;
 
// How far away to scan for others like me
float sensor_distance = 30.0;
 
// *** Don't change anything below unless you *really* know what you're doing ***
 
float mass;
vector spawn_location;
float school_timer = 0.0;
vector school_modifier = <0,0,0>;
 
// Update rotation function
do_rotation(vector mypos, vector myvel) {
        llLookAt(mypos + myvel, mass * rotation_strength_modifier, mass * rotation_damping_modifier);
}
 
// Collision function
collide(vector loc) {
        vector mypos = llGetPos();
        // Apply repulse force
        vector impulse = llVecNorm(mypos - loc);
        llApplyImpulse(impulse * repulse_force_modifier * mass, FALSE);
        //llSay(0, "collide() - impulse " + (string)impulse + " applied.");
        // Update rotation
        do_rotation(mypos, llGetVel());
}
 
// This function is called whether the sensor senses anything or not
sensor_any() {
        // Die after reaching kill_time
        if (kill_time != 0 && llGetTime() >= kill_time) {
                llDie();
        }
 
        // Get my velocity
        vector myvel = llGetVel();
 
        // Apply friction
        llApplyImpulse(-(myvel * friction * mass), FALSE);
 
        // Schooling behavior
        if (school && llGetTime() - school_timer > school_update_interval) {
                llSay(school_comm_channel, (string)myvel);
                school_timer = llGetTime();
        }
 
        // Get my position
        vector mypos = llGetPos();
 
        // Check for air/water breach
        if (flight_mode == 1) {
                // water
                if (mypos.z >= llWater(mypos) - llVecMag(llGetScale())) {
                        //llSay(0, "collide() called due to air/water breach.");
                        collide();
                }
        } else if (flight_mode == 2) {
                // air
                if (mypos.z <= llWater(mypos) + llVecMag(llGetScale())) {
                        //llSay(0, "collide() called due to air/water breach.");
                        collide();
                }
        }
 
        // Stay near spawn location
        if (llVecDist(mypos, spawn_location) > max_distance) {
                // Compensate for being near sim border
                if (spawn_location.x - mypos.x > 100) {
                        mypos.x += 255;
                }
                //llSay(0, "collide() called due to too much distance from my spawn point. mypos=" + (string)mypos + ", spawn_location = " + (string)spawn_location);
                collide(mypos - llVecNorm(spawn_location - mypos));
        }
 
        // Stay above ground level
        if (mypos.z <= llGround(ZERO_VECTOR)) {
                collide(mypos - llGroundNormal(ZERO_VECTOR));
        }
}
 
default {
        state_entry() {
                llResetTime();
                llSay(0, "Fishy spawned.");
 
                // School
                if (school) {
                        llListen(school_comm_channel, "", NULL_KEY, "");
                }
 
                // Sandbox
                llSetStatus(STATUS_SANDBOX, sandbox);
                llSetStatus(STATUS_BLOCK_GRAB, FALSE);
                spawn_location = llGetPos();
 
                // Initialize physics behavior
                mass = llGetMass();
                llSetBuoyancy(1.0);
                llSetStatus(STATUS_PHYSICS, TRUE);
                llVolumeDetect(TRUE);
 
                // Initialize sensor
                llSensorRepeat(llGetObjectName(), NULL_KEY, ACTIVE|SCRIPTED, sensor_distance, PI, timer_length);
 
        }
 
        collision_start(integer total_number) {
                //llSay(0, "collide() called due to physical object collision.");
                collide(llDetectedPos(0));
        }
 
        no_sensor() {
                sensor_any();
        }
 
        sensor(integer total_number) {
                sensor_any();
 
                // Populate neighbors with the positions of the two nearest neighbors.
                vector mypos = llGetPos();
                list neighbors = [];
                integer i;
 
                for (i = 0; i < total_number; i++) {
                        vector current_pos = llDetectedPos(i);
 
                        if (llGetListLength(neighbors) < 2) {
                                // Add to list
                                neighbors = llListInsertList(neighbors, [current_pos], llGetListLength(neighbors));
                        } else {
                                // Check to see if the current vector is closer than the list
                                // vector which is furthest away.
                                if (llVecDist(mypos, llList2Vector(neighbors, 0)) > llVecDist(mypos, llList2Vector(neighbors, 1))) {
                                        // check against first list item
                                        if (llVecDist(mypos, llList2Vector(neighbors, 0)) > llVecDist(mypos, current_pos)) {
                                                llListInsertList(neighbors, [current_pos], 0);
                                        }
                                } else {
                                        // check against second list item
                                        if (llVecDist(mypos, llList2Vector(neighbors, 1)) > llVecDist(mypos, current_pos)) {
                                                llListInsertList(neighbors, [current_pos], 1);
                                        }
                                }
                        }
                }
 
                // Process movement
 
                // Apply force
                if (llGetListLength(neighbors) == 2) {
                        vector neighbor1 = llList2Vector(neighbors, 0);
                        vector neighbor2 = llList2Vector(neighbors, 1);
                        vector target = neighbor2 + ((neighbor1 - neighbor2) * 0.5);
                        vector impulse = <0,0,0>;
                        if (school) {
                                impulse = llVecNorm(target + school_modifier - mypos);
                        } else {
                                impulse = llVecNorm(target - mypos);
                        }
                        //llSay(0, "setforce " + (string)(impulse * force_modifier * mass));
                        llSetForce(impulse * force_modifier * mass, FALSE);
                }
 
                // Update rotation
                do_rotation(llGetPos(), llGetVel());
        }
 
        listen(integer channel, string name, key id, string message) {
                list myList = llCSV2List(llGetSubString(message, 1, llStringLength(message) - 2));
                if (llGetListLength(myList) == 3) {
                        school_modifier = ;
                        school_timer = llGetTime();
                }
        }
 
        on_rez(integer start_param) {
                llResetScript();
                //        spawn_location = llGetPos();
                //        llResetTime();
        }
}