// 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(); } }