The following code will launch objects from the primitive that the script is in towards any scannable avatar in a 96
m range while allowing to select different velocities, both theta angles and firing type.
The cannon script asks for:
The projectile, being physical, is susceptible to wind which will induce an error and might deviate the projectile's trajectory. If your designed projectiles have a low mass (directly given by the object's size), then this might be an issue. To counter wind effects, increase the object's mass and/or increase the firing velocity.
/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2011 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // INTERNALS // /////////////////////////////////////////////////////////////////////////// string object = ""; integer person = 0; list peopleK = []; list peopleN = []; integer comHandle = 0; list menu_items = []; integer numObjects = 0; float V0 = 0; string angle = ""; /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasMenuIndex = 0; list wasDialogMenu(list input, list actions, string direction) { integer cut = 11-wasListCountExclude(actions, [""]); if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) { ++wasMenuIndex; jump slice; } if(direction == "<" && wasMenuIndex-1 >= 0) { --wasMenuIndex; jump slice; } @slice; integer multiple = wasMenuIndex*cut; input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex); input = wasListMerge(input, actions, ""); return input; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasListCountExclude(list input, list exclude) { if(llGetListLength(input) == 0) return 0; if(llListFindList(exclude, (list)llList2String(input, 0)) == -1) return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude); return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// list wasListMerge(list l, list m, string merge) { if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return []; string a = llList2String(m, 0); if(a != merge) return [ a ] + wasListMerge(l, llDeleteSubList(m, 0, 0), merge); return [ llList2String(l, 0) ] + wasListMerge(llDeleteSubList(l, 0, 0), llDeleteSubList(m, 0, 0), merge); } default { touch_start(integer total_number) { if(llDetectedKey(0) != llGetOwner()) return; list objects = []; menu_items = []; integer i = llGetInventoryNumber(INVENTORY_OBJECT)-1; do { menu_items += llGetInventoryName(INVENTORY_OBJECT, i); } while(--i>-1); integer comChannel = (integer)("0x8" + llGetSubString(llGetOwner(), 0, 6)); comHandle = llListen(comChannel, "", llGetOwner(), ""); llDialog(llGetOwner(), "Please choose an object from the inventory:", wasDialogMenu(menu_items, ["<= Back", "", "Next =>"], ""), comChannel); } on_rez(integer num) { llResetScript(); } listen(integer channel, string name, key id, string message) { if(message == "<= Back") { llDialog(id, "Please browse and select:\n", wasDialogMenu(menu_items, ["<= Back", "", "Next =>"], "<"), channel); return; } if(message == "Next =>") { llDialog(id, "Please browse and select:\n", wasDialogMenu(menu_items, ["<= Back", "", "Next =>"], ">"), channel); return; } integer comChannel = (integer)("0x8" + llGetSubString(llGetOwner(), 0, 6)); if(channel == comChannel) { object = message; llSensor("", "", AGENT, 96, TWO_PI); return; } if(channel == comChannel+1) { llListenRemove(comHandle); person = (integer)message; list am = []; integer i = 11; do { am += (string)i; } while(--i>0); am += "Continuous"; comHandle = llListen(comChannel+2, "", llGetOwner(), ""); llDialog(llGetOwner(), "Please select an amount of charges to fire:\n", am, comChannel+2); return; } if(channel == comChannel+2) { llListenRemove(comHandle); if(message == "Continuous") { numObjects = -1; jump create_menu; } numObjects = (integer)message; @create_menu; menu_items = []; integer i = 20; do { menu_items += (string)i; } while(--i>0); comHandle = llListen(comChannel+3, "", llGetOwner(), ""); llDialog(llGetOwner(), "Please select the velocity in meters/second:\n", wasDialogMenu(menu_items, ["<= Back", "", "Next =>"], ""), comChannel+3); return; } if(channel == comChannel+3) { V0 = (integer) message; llListenRemove(comHandle); comHandle = llListen(comChannel+4, "", llGetOwner(), ""); llDialog(llGetOwner(), "Please the possible angles:", ["High", "Low"], comChannel+4); return; } if(channel == comChannel+4) { angle = message; state fire; } llListenRemove(comHandle); } sensor(integer num) { peopleK = []; peopleN = []; list val = []; --num; do { peopleK = llListInsertList(peopleK, (list)llDetectedKey(num), 0); peopleN = llListInsertList(peopleN, (list)((string)num + ".) " + llDetectedName(num)), 0); val = llListInsertList(val, (list)((string)num), 0); } while(--num>-1); integer comChannel = (integer)("0x8" + llGetSubString(llGetOwner(), 0, 6)); comHandle = llListen(comChannel+1, "", llGetOwner(), ""); llDialog(llGetOwner(), "Please select a target:\n"+llDumpList2String(peopleN, "\n"), val, comChannel+1); } } state fire { state_entry() { llSetTimerEvent(1.0); } timer() { vector target = llList2Vector(llGetObjectDetails(llList2Key(peopleK, person), [OBJECT_POS]), 0); vector origin = llGetLocalPos(); float dΔ=llVecDist(<target.x,target.y,0>,<origin.x,origin.y,0>); float valSin = 9.81*dΔ/llPow(V0, 2); if(valSin < -1 || valSin > 1) { llSetTimerEvent(0); llOwnerSay("Not enough velocity to reach target, please increase velocity."); state default; } float low_Θ = RAD_TO_DEG*llAsin(valSin)/2; float high_Θ = 90-low_Θ; rotation qΔ = ZERO_ROTATION; if(angle == "High") qΔ = llRotBetween(<1,0,0>,llVecNorm(<target.x-origin.x,target.y-origin.y, dΔ*llTan(high_Θ * DEG_TO_RAD) + llFabs(target.z-origin.z)>)); else qΔ = llRotBetween(<1,0,0>,llVecNorm(<target.x-origin.x,target.y-origin.y, dΔ*llTan(low_Θ * DEG_TO_RAD) + target.z-origin.z>)); float t = 2*V0/9.81; float h_max = llPow(V0, 2)*llPow(llSin(high_Θ), 2)/(2*9.81); llOwnerSay("---------- FIRE ROUND START ----------"); llOwnerSay("+Distance to target: " + (string)dΔ + "m"); llOwnerSay("+Required angle delta to hit: " + (string)low_Θ + "˚"); llOwnerSay("+Orientation detlta to target: " + (string)qΔ + "m"); llOwnerSay("+Time to impact: " + (string)t + "s"); llOwnerSay("+Maximum flight height: " + (string)h_max + "m"); llOwnerSay("----------- FIRE ROUND END -----------"); llRezObject(object, llGetLocalPos(), llVecNorm(<1,0,0>*qΔ)*V0, qΔ, 0); if(~numObjects != 0) { if(--numObjects == 0) { llSetTimerEvent(0); return; } } llSetTimerEvent(t + llGetRegionTimeDilation()); } touch_start(integer num) { if(llDetectedKey(0) != llGetOwner()) return; llOwnerSay("Firing ceased, touch to reconfigure."); state default; } on_rez(integer num) { llResetScript(); } }