// :CATEGORY:XS Pet // :NAME:XS Pet Xundra Snowpaw Original Quail // :AUTHOR:Xundra Snowpaw // :CREATED:2013-09-06 // :EDITED:2013-09-18 15:39:11 // :ID:989 // :NUM:1479 // :REV:1 // :WORLD:Second Life, Opensim // :DESCRIPTION: // Original Pet Quail // :CODE: float VERSION = 0.22; integer API_CHANNEL = -999198; string SECRET_PASSWORD = "top secret"; integer SECRET_NUMBER = 99999; key YOUR_UUID = "00000000-0000-0000-0000-000000000000"; integer ANIMAL_CHANNEL = -999192; integer EGG_CHANNEL = -999193; integer BOX_CHANNEL = -999195; string SPECIAL = "Normal"; //integer TIME_TO_HATCH = 7200; // DON'T CHANGE THE FOLLOWING! (Unless you know what you are doing!) integer XTEA_DELTA = 0x9E3779B9; // (sqrt(5) - 1) * 2^31 integer xtea_num_rounds = 6; list xtea_key = [0, 0, 0, 0]; integer hex2int(string hex) { if(llGetSubString(hex,0,1) == "0x") return (integer)hex; if(llGetSubString(hex,0,0) == "x") return (integer)("0"+hex); return(integer)("0x"+hex); } // Convers any string to a 32 char MD5 string and then to a list of // 4 * 32 bit integers = 128 bit Key. MD5 ensures always a specific // 128 bit key is generated for any string passed. list xtea_key_from_string( string str ) { str = llMD5String(str,0); // Use Nonce = 0 return [ hex2int(llGetSubString( str, 0, 7)), hex2int(llGetSubString( str, 8, 15)), hex2int(llGetSubString( str, 16, 23)), hex2int(llGetSubString( str, 24, 31))]; } // Encipher two integers and return the result as a 12-byte string // containing two base64-encoded integers. string xtea_encipher( integer v0, integer v1 ) { integer num_rounds = xtea_num_rounds; integer sum = 0; do { // LSL does not have unsigned integers, so when shifting right we // have to mask out sign-extension bits. v0 += (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^ (sum + llList2Integer(xtea_key, sum & 3)); sum += XTEA_DELTA; v1 += (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^ (sum + llList2Integer(xtea_key, (sum >> 11) & 3)); } while( num_rounds = ~-num_rounds ); //return only first 6 chars to remove "=="'s and compact encrypted text. return llGetSubString(llIntegerToBase64(v0),0,5) + llGetSubString(llIntegerToBase64(v1),0,5); } // Decipher two base64-encoded integers and return the FIRST 30 BITS of // each as one 10-byte base64-encoded string. string xtea_decipher( integer v0, integer v1 ) { integer num_rounds = xtea_num_rounds; integer sum = XTEA_DELTA*xtea_num_rounds; do { // LSL does not have unsigned integers, so when shifting right we // have to mask out sign-extension bits. v1 -= (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^ (sum + llList2Integer(xtea_key, (sum>>11) & 3)); sum -= XTEA_DELTA; v0 -= (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^ (sum + llList2Integer(xtea_key, sum & 3)); } while ( num_rounds = ~-num_rounds ); return llGetSubString(llIntegerToBase64(v0), 0, 4) + llGetSubString(llIntegerToBase64(v1), 0, 4); } // Encrypt a full string using XTEA. string xtea_encrypt_string( string str ) { // encode string str = llStringToBase64(str); // remove trailing =s so we can do our own 0 padding integer i = llSubStringIndex( str, "=" ); if ( i != -1 ) str = llDeleteSubString( str, i, -1 ); // we don't want to process padding, so get length before adding it integer len = llStringLength(str); // zero pad str += "AAAAAAAAAA="; string result; i = 0; do { // encipher 30 (5*6) bits at a time. result += xtea_encipher(llBase64ToInteger(llGetSubString(str, i, i + 4) + "A="), llBase64ToInteger(llGetSubString(str, i+5, i + 9) + "A=")); i+=10; } while ( i < len ); return result; } // Decrypt a full string using XTEA string xtea_decrypt_string( string str ) { integer len = llStringLength(str); integer i=0; string result; //llOwnerSay(str); do { integer v0; integer v1; v0 = llBase64ToInteger(llGetSubString(str, i, i + 5) + "=="); i+= 6; v1 = llBase64ToInteger(llGetSubString(str, i, i + 5) + "=="); i+= 6; result += xtea_decipher(v0, v1); } while ( i < len ); // Replace multiple trailing zeroes with a single one i = llStringLength(result) - 1; while ( llGetSubString(result, i - 1, i) == "AA" ){ result = llDeleteSubString(result, i, i); i--; } i = llStringLength(result) - 1; // while (llGetSubString(result, i, i + 1) == "A" ) { // i--; // } result = llGetSubString(result, 0, i+1); i = llStringLength(result); integer mod = i%4; //Depending on encoded length diffrent appends are needed if(mod == 1) result += "A=="; else if(mod == 2 ) result += "=="; else if(mod == 3) result += "="; return llBase64ToString(result); } string base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; vector colour1; vector colour2; integer sex; integer gen; integer shine; float glow; integer api_listener; integer locked; integer born_hatched; integer owner_touch; integer menu_listener; set_colours(vector c1, vector c2, integer shine, float glow) { llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, c2, 1.0, PRIM_BUMP_SHINY, ALL_SIDES, shine, PRIM_BUMP_NONE, PRIM_GLOW, ALL_SIDES, glow]); llSetLinkPrimitiveParams(2, [PRIM_COLOR, ALL_SIDES, c1, 1.0, PRIM_BUMP_SHINY, ALL_SIDES, shine, PRIM_BUMP_NONE, PRIM_GLOW, ALL_SIDES, glow]); } say_details() { string myshine; if (shine == 0) { myshine = "None"; } else if (shine == 1) { myshine = "Low"; } else if (shine == 2) { myshine = "Medium"; } else if (shine == 3) { myshine = "High"; } llWhisper(0, "Head/Body Colour: " + (string)colour1); llWhisper(0, "Tail/Wing Colour: " + (string)colour2); llWhisper(0, "Shine: " + myshine); llWhisper(0, "Glow: " + (string)((integer)(glow * 100)) + "%"); llWhisper(0, "Special: " + SPECIAL); llWhisper(0, "Generation: " + (string)gen); llSay(API_CHANNEL, "XSQuail^" + (string)llGetKey() + "^" + (string)colour1 + "^" + (string)colour2 + "^" + (string)shine + "^" + (string)glow + "^" + SPECIAL + "^" + (string)gen + "^XSQuailEnd"); } default { state_entry() { llSetText("", <1,1,1>, 1.0); llSetColor(<1,1,1>, ALL_SIDES); llSetLinkColor(2, <1,1,1>, ALL_SIDES); llParticleSystem([]); xtea_key = xtea_key_from_string(SECRET_PASSWORD); } changed(integer change) { if (change & CHANGED_ALLOWED_DROP) { if (llGetInventoryType("Quail") != INVENTORY_NONE && llGetInventoryType("XS Egg") != INVENTORY_NONE) { llAllowInventoryDrop(FALSE); } } } on_rez(integer param) { llSetText("Rezzing...", <1,0,0>, 1.0); llSleep(5.0); if (param == 0) { if (llGetOwner() == YOUR_UUID) { integer colour = (integer)llFrand(9.0) + 1; if (colour == 1) { colour1 = <1,0,0>; colour2 = <1,0,0>; } else if (colour == 2) { colour1 = <1,0,0>; colour2 = <0,1,0>; } else if (colour == 3) { colour1 = <1,0,0>; colour2 = <0,0,1>; } else if (colour == 4) { colour1 = <0,1,0>; colour2 = <1,0,0>; } else if (colour == 5) { colour1 = <0,1,0>; colour2 = <0,1,0>; } else if (colour == 6) { colour1 = <0,1,0>; colour2 = <0,0,1>; } else if (colour == 7) { colour1 = <0,0,1>; colour2 = <1,0,0>; } else if (colour == 8) { colour1 = <0,0,1>; colour2 = <0,1,0>; } else if (colour == 9) { colour1 = <0,0,1>; colour2 = <0,0,1>; } set_colours(colour1, colour2, shine, glow); state rezzed; } else { llWhisper(0, "Oh no! This egg was broken in your inventory! Please use an egg-cup next time."); state dead; } } else if (param == SECRET_NUMBER) { menu_listener = llListen(EGG_CHANNEL, "", "", ""); if (llGetOwner() != YOUR_UUID) { llAllowInventoryDrop(TRUE); } llSay(ANIMAL_CHANNEL, xtea_encrypt_string("XSPET^EGG_READY^" + (string)llGetKey() + "^SMILE")); llSay(BOX_CHANNEL, xtea_encrypt_string("XSPET^" + (string)llGetKey() + "^EGG_READY^SMILE")); } } listen(integer channel, string sender, key id, string msg) { list data = llParseString2List(xtea_decrypt_string(msg), ["^"], []); if (llList2String(data, 0) == "XSPET" && llList2Key(data, 1) == llGetKey()) { if (llList2String(data, 2) == "UNBOX") { colour1 = (vector)llList2String(data, 3); colour2 = (vector)llList2String(data, 4); gen = llList2Integer(data,5); shine = llList2Integer(data, 6); glow = llList2Float(data, 7); SPECIAL = llList2String(data, 8); llListenRemove(menu_listener); set_colours(colour1, colour2, shine, glow); state rezzed; } else if (llList2String(data, 2) == "BORN") { vector fcolour1 = (vector)llList2String(data, 3); vector fcolour2 = (vector)llList2String(data, 4); integer fgen = llList2Integer(data,5); integer fshine = llList2Integer(data, 6); float fglow = llList2Float(data, 7); integer fglow_gene = llList2Integer(data, 8); vector mcolour1 = (vector)llList2String(data, 9); vector mcolour2 = (vector)llList2String(data, 10); integer mgen = llList2Integer(data, 11); integer mshine = llList2Integer(data, 12); float mglow = llList2Float(data, 13); integer mglow_gene = llList2Integer(data, 14); integer mix = (integer)llFrand(2) + 1; float deviation1 = (llFrand(10) - 5) / 100; float deviation2 = (llFrand(10) - 5) / 100; if (mix == 1) { colour1.x = (fcolour1.x / 2 + mcolour1.x / 2) + deviation1; colour1.y = (fcolour1.y / 2 + mcolour1.y / 2) + deviation1; colour1.z = (fcolour1.z / 2 + mcolour1.z / 2) + deviation1; colour2.x = (fcolour2.x / 2 + mcolour2.x / 2) + deviation2; colour2.y = (fcolour2.y / 2 + mcolour2.y / 2) + deviation2; colour2.z = (fcolour2.z / 2 + mcolour2.z / 2) + deviation2; } else { colour1.x = (fcolour1.x / 2 + mcolour2.x / 2) + deviation1; colour1.y = (fcolour1.y / 2 + mcolour2.y / 2) + deviation1; colour1.z = (fcolour1.z / 2 + mcolour2.z / 2) + deviation1; colour2.x = (fcolour2.x / 2 + mcolour1.x / 2) + deviation2; colour2.y = (fcolour2.y / 2 + mcolour1.y / 2) + deviation2; colour2.z = (fcolour2.z / 2 + mcolour1.z / 2) + deviation2; } if (colour1.x < 0) colour1.x = 0; if (colour1.x > 1) colour1.x = 1; if (colour1.y < 0) colour1.y = 0; if (colour1.y > 1) colour1.y = 1; if (colour1.z < 0) colour1.z = 0; if (colour1.z > 1) colour1.z = 1; if (colour2.x < 0) colour2.x = 0; if (colour2.x > 1) colour2.x = 1; if (colour2.y < 0) colour2.y = 0; if (colour2.y > 1) colour2.y = 1; if (colour2.z < 0) colour2.z = 0; if (colour2.z > 1) colour2.z = 1; if (mgen > fgen) { gen = mgen + 1; } else { gen = fgen + 1; } // TODO: shine / glow shine = 0; if (gen > 2) { integer pos = (integer)llFrand(100) + 1; if (fshine == mshine && fshine > 0) { if (pos < 25) { shine = fshine + 1; if (shine > 3) { shine = 3; } } } else { if (pos < 10) { // increase shine if (mshine > fshine) { shine = mshine + 1; } else if (fshine > mshine) { shine = fshine + 1; } else { shine = 1; } if (shine > 3) { shine = 3; } } } } glow = 0.0; if (mglow <= 0.3 && fglow <= 0.3) { if (mglow > fglow) { glow = mglow - 0.1; } else if (fglow > mglow) { glow = fglow - 0.1; } } if (fglow_gene >= 10 && mglow_gene >= 10) { integer poss = (integer)llFrand(100) + 1; if (poss < 25) { glow = 0.3; } } else if (fglow_gene >= 10 || mglow_gene >= 10) { integer poss = (integer)llFrand(100) + 1; if (poss < 10) { glow = 0.3; } } llListenRemove(menu_listener); born_hatched = 1; set_colours(colour1, colour2, shine, glow); llSetTimerEvent(1.0); } } } timer() { if (llGetInventoryType("Quail") != INVENTORY_NONE && llGetInventoryType("XS Egg") != INVENTORY_NONE) { state rezzed; } } } state rezzed { on_rez(integer param) { if (llGetOwner() != YOUR_UUID) { llWhisper(0, "Oh no! This egg was broken in your inventory! Please use an egg-cup next time."); state dead; } } state_entry() { if (SPECIAL == "Normal") { llSetText("", <1,1,1>, 1.0); } else { llSetText(SPECIAL, <1,1,1>, 1.0); } api_listener = llListen(API_CHANNEL, "", "", ""); } touch_start(integer num) { owner_touch = 0; say_details(); if (llDetectedKey(0) == llGetOwner()) { owner_touch = 1; integer chan = (integer)llFrand(100000.0) + 1000; menu_listener = llListen(chan, "", llGetOwner(), ""); llDialog(llGetOwner(), "I'm an egg. What would you like to do with me?", ["Hatch", "Package", "Nothing"], chan); } llSetTimerEvent(30.0); } timer() { llSetTimerEvent(0); if (owner_touch == 1) { llOwnerSay("Menu timeout."); llListenRemove(menu_listener); owner_touch = 0; } } listen(integer channel, string sender, key id, string msg) { if (channel == API_CHANNEL) { list data = llParseString2List(msg, ["^"], []); if (llList2String(data, 0) == "XSQuail" && llList2Key(data, 1) == llGetKey()) { say_details(); } } else { if (id == llGetOwner()) { llSetTimerEvent(0); llListenRemove(menu_listener); if (msg == "Hatch") { state hatching; } else if (msg == "Package") { state package; } } } } state_exit() { llListenRemove(api_listener); } } state hatching { state_entry() { llRezObject("Quail", llGetPos() + <0.0, 0.0, 0.25>, ZERO_VECTOR, ZERO_ROTATION, SECRET_NUMBER); } object_rez(key id) { menu_listener = llListen(EGG_CHANNEL, "", id, ""); } listen(integer channel, string sender, key id, string msg) { list decrypted = llParseString2List(xtea_decrypt_string(msg), ["^"], []); if (llList2String(decrypted, 0) == "XSPET" && llList2String(decrypted, 1) == "READY" && llList2String(decrypted, 2) == (string)id) { llGiveInventory(id, "Quail"); llGiveInventory(id, "XS Egg"); sex = (integer)llFrand(2) + 1; llSay(ANIMAL_CHANNEL, xtea_encrypt_string("XSPET^CONFIG^" + (string)id + "^" + (string)colour1 + "^" + (string)colour2 + "^" + (string)sex + "^" + (string)shine + "^" + (string)glow + "^" + (string)gen + "^0^0^Quail^0^" + SPECIAL)); llDie(); } } } state package { state_entry() { llSetTimerEvent(20.0); llWhisper(0, "Looking for an egg cup..."); menu_listener = llListen(EGG_CHANNEL, "", "", ""); llSay(BOX_CHANNEL, xtea_encrypt_string("XSPET^" + (string)llGetKey() + "^CUP_PING^" + (string)VERSION)); } timer() { llListenRemove(menu_listener); llSetTimerEvent(0); llWhisper(0, "No egg cup found."); state rezzed; } listen(integer channel, string sender, key id, string msg) { list data = llParseString2List(xtea_decrypt_string(msg), ["^"], []); if (llList2String(data, 0) == "XSPET" && llList2Key(data, 1) == llGetKey() && llList2String(data, 2) == "CUP_PONG" && llList2Float(data, 3) >= VERSION && locked == 0) { llSay(BOX_CHANNEL, xtea_encrypt_string("XSPET^" + (string)id + "^" + (string)colour1 + "^" + (string)colour2 + "^" + (string)shine + "^" + (string)glow + "^" + (string)gen + "^" + SPECIAL)); locked = 1; } else if (llList2String(data, 0) == "XSPET" && llList2Key(data, 1) == llGetKey() && llList2String(data, 2) == "CUP_DIE") { llDie(); } } } state dead { state_entry() { llSetText("Broken egg.", <1,0,0>, 1.0); } }