load_quiz.lslp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. //
  2. // The line above should be left blank to avoid script errors in OpenSim.
  3. /* load_quiz
  4. Copyright (c) 2006-9 Sloodle (various contributors)
  5. Released under the GNU GPL
  6. This files lists all the status codes we use for sloodle.
  7. They have been written in LSL format so that you can plunk them into your source
  8. code if needed.
  9. Contributors:
  10. Edmund Edgar
  11. Paul Preibisch
  12. */
  13. integer SLOODLE_CHANNEL_ERROR_TRANSLATION_REQUEST=-1828374651;
  14. integer doRepeat = 0; // whether we should run through the questions again when we're done
  15. integer doDialog = 1; // whether we should ask the questions using dialog rather than chat
  16. integer doPlaySound = 1; // whether we should play sound
  17. integer doRandomize = 1; // whether we should ask the questions in random order
  18. string sloodleserverroot = "";
  19. integer sloodlecontrollerid = 0;
  20. string sloodlepwd = "";
  21. integer sloodlemoduleid = 0;
  22. list server_requests;
  23. integer sloodleobjectaccessleveluse = 0; // Who can use this object?
  24. integer sloodleobjectaccesslevelctrl = 0; // Who can control this object?
  25. integer sloodleserveraccesslevel = 0; // Who can use the server resource? (Value passed straight back to Moodle)
  26. integer isconfigured = FALSE; // Do we have all the configuration data we need?
  27. integer eof = FALSE; // Have we reached the end of the configuration data?
  28. integer SLOODLE_CHANNEL_AVATAR_DIALOG = 1001;
  29. integer SLOODLE_CHANNEL_OBJECT_DIALOG = -3857343; // an arbitrary channel the sloodle scripts will use to talk to each other. Doesn't atter what it is, as long as the same thing is set in the sloodle_slave script.
  30. integer SLOODLE_CHANNEL_AVATAR_IGNORE = -1639279999;
  31. integer SLOODLE_CHANNEL_QUIZ_START_FOR_AVATAR = -1639271102; //Tells us to start a quiz for the avatar, if possible.; Ordinary quiz chair will have a second script that detects and avatar sitting on it and sends it. Awards-integrated version waits for a game ID to be set before doing this.
  32. integer SLOODLE_CHANNEL_QUIZ_STARTED_FOR_AVATAR = -1639271103; //Sent by main quiz script to tell UI scripts that quiz has started for avatar with key
  33. integer SLOODLE_CHANNEL_QUIZ_COMPLETED_FOR_AVATAR = -1639271104; //Sent by main quiz script to tell UI scripts that quiz has finished for avatar with key, with x/y correct in string
  34. integer SLOODLE_CHANNEL_QUESTION_ASKED_AVATAR = -1639271105; //Sent by main quiz script to tell UI scripts that question has been asked to avatar with key. String contains question ID + "|" + question text
  35. integer SLOODLE_CHANNEL_QUESTION_ANSWERED_AVATAR = -1639271106; //Sent by main quiz script to tell UI scripts that question has been answered by avatar with key. String contains selected option ID + "|" + option text + "|"
  36. integer SLOODLE_CHANNEL_QUIZ_LOADING_QUESTION = -1639271107;
  37. integer SLOODLE_CHANNEL_QUIZ_LOADED_QUESTION = -1639271108;
  38. integer SLOODLE_CHANNEL_QUIZ_LOADING_QUIZ = -1639271109;
  39. integer SLOODLE_CHANNEL_QUIZ_LOADED_QUIZ = -1639271110;
  40. integer SLOODLE_CHANNEL_QUIZ_GO_TO_STARTING_POSITION = -1639271111;
  41. integer SLOODLE_CHANNEL_QUIZ_ASK_QUESTION_CHAT = -1639271125; // Tells the question handler scripts to ask the question with the ID in str to the avatar with key VIA CHAT.
  42. integer SLOODLE_CHANNEL_QUIZ_ASK_QUESTION_TEXT_BOX=-1639277001;//asks via a text box
  43. integer SLOODLE_CHANNEL_QUIZ_ASK_QUESTION_DIALOG = -1639271126; // Tells the question handler scripts to ask the question with the ID in str to the avatar with key VIA DIALOG.
  44. integer SLOODLE_CHANNEL_ANSWER_SCORE_FOR_AVATAR = -1639271113; // Tells anyone who might be interested that we scored the answer. Score in string, avatar in key.
  45. integer SLOODLE_CHANNEL_QUIZ_STATE_ENTRY_DEFAULT = -1639271114; //mod quiz script is in state DEFAULT
  46. integer SLOODLE_CHANNEL_QUIZ_STATE_ENTRY_READY = -1639271115; //mod quiz script is in state READY
  47. integer SLOODLE_CHANNEL_QUIZ_STATE_ENTRY_LOAD_QUIZ_FOR_USER = -1639271116; //mod quiz script is in state CHECK_QUIZ
  48. integer SLOODLE_CHANNEL_QUIZ_STATE_ENTRY_QUIZZING = -1639271117; //mod quiz script is in state quizzing
  49. integer SLOODLE_CHANNEL_QUIZ_NO_PERMISSION_USE= -1639271118; //user has tried to use the chair but doesnt have permission to do so.
  50. integer SLOODLE_CHANNEL_QUIZ_STOP_FOR_AVATAR = -1639271119; //Tells us to STOP a quiz for the avatar
  51. integer SLOODLE_CHANNEL_QUIZ_SUCCESS_NOTHING_MORE_TO_DO_WITH_AVATAR= -1639271122;
  52. integer SLOODLE_CHANNEL_QUIZ_FAILURE_NOTHING_MORE_TO_DO_WITH_AVATAR= -1639271123;
  53. integer SLOODLE_CHANNEL_QUIZ_ERROR_INVALID_QUESION = -1639271121; //
  54. integer SLOODLE_CHANNEL_QUIZ_ERROR_ATTEMPTS_LEFT= -1639271123; //
  55. integer SLOODLE_CHANNEL_QUIZ_ERROR_NO_QUESTIONS= -1639271124; //
  56. integer SLOODLE_CHANNEL_QUIZ_FINISH_QUIZ=-1639277002;
  57. integer SLOODLE_CHANNEL_QUIZ_LOAD_QUIZ= -1639277003;//user touched object
  58. list question_ids;
  59. ///// COLORS ////
  60. vector RED =<1.00000, 0.00000, 0.00000>;
  61. vector ORANGE=<1.00000, 0.43763, 0.02414>;
  62. vector YELLOW=<1.00000, 1.00000, 0.00000>;
  63. vector GREEN=<0.00000, 1.00000, 0.00000>;
  64. vector BLUE=<0.00000, 0.00000, 1.00000>;
  65. vector BABYBLUE=<0.00000, 1.00000, 1.00000>;
  66. vector PINK=<1.00000, 0.00000, 1.00000>;
  67. vector PURPLE=<0.57338, 0.25486, 1.00000>;
  68. vector BLACK= <0.00000, 0.00000, 0.00000>;
  69. vector WHITE= <1.00000, 1.00000, 1.00000>;
  70. ///// TRANSLATION /////
  71. // Link message channels
  72. integer SLOODLE_CHANNEL_TRANSLATION_REQUEST = -1928374651;
  73. // Translation output methods
  74. string SLOODLE_TRANSLATE_HOVER_TEXT = "hovertext"; // 2 output parameters: colour <r,g,b>, and alpha value
  75. string SLOODLE_TRANSLATE_WHISPER = "whisper"; // 1 output parameter: chat channel number
  76. string SLOODLE_TRANSLATE_SAY = "say"; // 1 output parameter: chat channel number
  77. string SLOODLE_TRANSLATE_OWNER_SAY = "ownersay"; // No output parameters
  78. string SLOODLE_TRANSLATE_DIALOG = "dialog"; // Recipient avatar should be identified in link message keyval. At least 2 output parameters: first the channel number for the dialog, and then 1 to 12 button label strings.
  79. string SLOODLE_TRANSLATE_LOAD_URL = "loadurl"; // Recipient avatar should be identified in link message keyval. 1 output parameter giving URL to load.
  80. string SLOODLE_TRANSLATE_IM = "instantmessage"; // Recipient avatar should be identified in link message keyval. No output parameters.
  81. integer SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC = 0;
  82. integer SLOODLE_OBJECT_ACCESS_LEVEL_OWNER = 1;
  83. integer SLOODLE_OBJECT_ACCESS_LEVEL_GROUP = 2;
  84. string SLOODLE_OBJECT_TYPE = "quiz-1.0";
  85. string SLOODLE_EOF = "sloodleeof";
  86. string sloodle_quiz_url = "/mod/sloodle/mod/quiz-1.0/linker.php";
  87. key httpquizquery = NULL_KEY;
  88. float request_timeout = 20.0;
  89. string sloodlehttpvars;
  90. // ID and name of the current quiz
  91. integer quizid = -1;
  92. string quizname = "";
  93. integer num_questions = 0;
  94. //quiz id
  95. integer quiz_id;
  96. string quiz_name;
  97. // Stores the number of questions the user got correct on a given attempt
  98. integer askquestionscontinuously=0;
  99. integer correctToContinue=0; //must get question correct before next question is asked
  100. ///// FUNCTIONS /////
  101. debug (string message ){
  102. list params = llGetPrimitiveParams ([PRIM_MATERIAL ]);
  103. if ( llList2Integer (params ,0)==PRIM_MATERIAL_FLESH ){
  104. llOwnerSay(llGetScriptName ()+": " +message );
  105. }
  106. }
  107. /******************************************************************************************************************************
  108. * sloodle_error_code -
  109. * Author: Paul Preibisch
  110. * Description - This function sends a linked message on the SLOODLE_CHANNEL_ERROR_TRANSLATION_REQUEST channel
  111. * The error_messages script hears this, translates the status code and sends an instant message to the avuuid
  112. * Params: method - SLOODLE_TRANSLATE_SAY, SLOODLE_TRANSLATE_IM etc
  113. * Params: avuuid - this is the avatar UUID to that an instant message with the translated error code will be sent to
  114. * Params: status code - the status code of the error as on our wiki: http://slisweb.sjsu.edu/sl/index.php/Sloodle_status_codes
  115. * Params: a message from the server to use if there is none listed in the linker script.
  116. *******************************************************************************************************************************/
  117. sloodle_error_code(string method, key avuuid,integer statuscode, string msg){
  118. llMessageLinked(LINK_SET, SLOODLE_CHANNEL_ERROR_TRANSLATION_REQUEST, method+"|"+(string)avuuid+"|"+(string)statuscode+"|"+(string)msg, NULL_KEY);
  119. }
  120. sloodle_debug(string msg){
  121. llMessageLinked(LINK_THIS, DEBUG_CHANNEL, msg, NULL_KEY);
  122. }
  123. // Configure by receiving a linked message from another script in the object
  124. // Returns TRUE if the object has all the data it needs
  125. integer sloodle_handle_command(string str){
  126. list bits = llParseString2List(str,["|"],[]);
  127. integer numbits = llGetListLength(bits);
  128. string name = llList2String(bits,0);
  129. string value1 = "";
  130. string value2 = "";
  131. if (numbits > 1) value1 = llList2String(bits,1);
  132. if (numbits > 2) value2 = llList2String(bits,2);
  133. if (name == "set:sloodleserverroot") sloodleserverroot = value1;
  134. else if (name == "set:sloodlepwd") {
  135. // The password may be a single prim password, or a UUID and a password
  136. if (value2 != "") {
  137. sloodlepwd = value1 + "|" + value2;
  138. }
  139. else {
  140. sloodlepwd = value1;
  141. }
  142. }
  143. else if (name == "set:sloodlecontrollerid") sloodlecontrollerid = (integer)value1;
  144. else if (name == "set:sloodlemoduleid") sloodlemoduleid = (integer)value1;
  145. else if (name == "set:sloodleobjectaccessleveluse") sloodleobjectaccessleveluse = (integer)value1;
  146. else if (name == "set:sloodleserveraccesslevel") sloodleserveraccesslevel = (integer)value1;
  147. else if (name == "set:sloodlerepeat") doRepeat = (integer)value1;
  148. else if (name == "set:sloodlerandomize") doRandomize = (integer)value1;
  149. else if (name == "set:sloodledialog") doDialog = (integer)value1;
  150. else if (name == "set:sloodleplaysound") doPlaySound = (integer)value1;
  151. else if (name == "set:sloodleaskquestionscontinuously") askquestionscontinuously= (integer)value1;
  152. else if (name == "set:correctToContinue") correctToContinue = (integer)value1;
  153. else if (name == SLOODLE_EOF) eof = TRUE;
  154. return (sloodleserverroot != "" && sloodlepwd != "" && sloodlecontrollerid > 0 && sloodlemoduleid > 0);
  155. }
  156. // Send a translation request link message
  157. sloodle_translation_request(string output_method, list output_params, string string_name, list string_params, key keyval, string batch){
  158. llMessageLinked(LINK_THIS, SLOODLE_CHANNEL_TRANSLATION_REQUEST, output_method + "|" + llList2CSV(output_params) + "|" + string_name + "|" + llList2CSV(string_params) + "|" + batch, keyval);
  159. }
  160. default{
  161. on_rez(integer start_param) {
  162. llResetScript();
  163. }
  164. state_entry(){
  165. isconfigured = FALSE;
  166. eof = FALSE;
  167. // Reset our configuration data
  168. sloodleserverroot = "";
  169. sloodlepwd = "";
  170. sloodlecontrollerid = 0;
  171. sloodlemoduleid = 0;
  172. sloodleobjectaccessleveluse = 0;
  173. sloodleserveraccesslevel = 0;
  174. doRepeat = 1;
  175. doDialog = 1;
  176. doPlaySound = 1;
  177. doRandomize = 1;
  178. //tell other scripts we are in the default state.
  179. }
  180. link_message( integer sender_num, integer num, string str, key id){
  181. // Check the channel for configuration messages
  182. if (num == SLOODLE_CHANNEL_OBJECT_DIALOG) {
  183. // Split the message into lines
  184. list lines = llParseString2List(str, ["\n"], []);
  185. integer numlines = llGetListLength(lines);
  186. integer i = 0;
  187. for (i=0; i < numlines; i++) {
  188. isconfigured = sloodle_handle_command(llList2String(lines, i));
  189. }
  190. // If we've got all our data AND reached the end of the configuration data (eof), then move on
  191. if (eof == TRUE) {
  192. if (isconfigured == TRUE) {
  193. sloodle_translation_request(SLOODLE_TRANSLATE_SAY, [0], "configurationreceived", [], NULL_KEY, "");
  194. state ready;
  195. } else {
  196. // Go all configuration but, it's not complete... request reconfiguration
  197. sloodle_translation_request(SLOODLE_TRANSLATE_SAY, [0], "configdatamissing", [llGetScriptName()], NULL_KEY, "");
  198. llMessageLinked(LINK_THIS, SLOODLE_CHANNEL_OBJECT_DIALOG, "do:reconfigure", NULL_KEY);
  199. eof = FALSE;
  200. }
  201. }
  202. }
  203. }
  204. }
  205. state ready{
  206. on_rez(integer start_param) {
  207. llResetScript();
  208. }
  209. state_entry(){
  210. sloodlehttpvars = "sloodlecontrollerid=" + (string)sloodlecontrollerid;
  211. sloodlehttpvars += "&sloodlepwd=" + sloodlepwd;
  212. sloodlehttpvars += "&sloodlemoduleid=" + (string)sloodlemoduleid;
  213. sloodlehttpvars += "&sloodleserveraccesslevel=" + (string)sloodleserveraccesslevel;
  214. }
  215. link_message(integer sender_num, integer num, string str, key user_key){
  216. if (num == SLOODLE_CHANNEL_QUIZ_LOAD_QUIZ) {
  217. string body=sloodlehttpvars;
  218. sloodle_translation_request(SLOODLE_TRANSLATE_IM, [0], "fetchingquiz", [llKey2Name(user_key)], user_key, "quizzer");
  219. // Request the quiz data from Moodle
  220. body += "&sloodlerequestdesc="+"LOADING_QUIZ";
  221. body += "&sloodleuuid=" + (string)user_key;
  222. body += "&sloodleavname=" + llEscapeURL(llKey2Name(user_key));
  223. body += "&request_timestamp="+(string)llGetUnixTime();
  224. server_requests+= llHTTPRequest(sloodleserverroot + sloodle_quiz_url, [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"], body);
  225. debug("loading quiz: "+sloodleserverroot + sloodle_quiz_url+"/?"+body);
  226. }else
  227. if (num == SLOODLE_CHANNEL_OBJECT_DIALOG) {
  228. // Is it a reset command?
  229. if (str == "do:reset") {
  230. llResetScript();
  231. }
  232. return;
  233. }
  234. }
  235. http_response(key request_id, integer status, list meta, string quiz_data){
  236. /*
  237. This response is a result of us calling load_quiz in the touch start event. In load_quiz, we asked the server
  238. for the quiz_name, and question_ids. Now let's parse the data that was returned
  239. */
  240. // Is this the response we are expecting?
  241. integer placeinlist=llListFindList(server_requests, [request_id]);
  242. if (placeinlist!=-1){
  243. server_requests= llDeleteSubList(server_requests, placeinlist, placeinlist);
  244. }else {
  245. return;
  246. }
  247. debug("quiz data: "+quiz_data);
  248. // Make sure the response was OK
  249. if (status != 200) {
  250. sloodle_error_code(SLOODLE_TRANSLATE_SAY, NULL_KEY,status, ""); //send message to error_message.lsl
  251. // llMessageLinked(LINK_SET, SLOODLE_CHANNEL_QUIZ_FAILURE_NOTHING_MORE_TO_DO_WITH_AVATAR, "", user_key);//todo add to dia
  252. }
  253. // Split the response into several lines
  254. list lines = llParseString2List(quiz_data, ["\n"], []);
  255. integer numlines = llGetListLength(lines);
  256. quiz_data = "";
  257. list statusfields = llParseStringKeepNulls(llList2String(lines,0), ["|"], []);
  258. integer statuscode = (integer)llStringTrim(llList2String(statusfields, 0), STRING_TRIM);
  259. key user_key = llList2Key(statusfields,6);
  260. // Was it an error code?
  261. if (statuscode == -10301) {
  262. sloodle_translation_request(SLOODLE_TRANSLATE_IM, [0], "noattemptsleft", [llKey2Name(user_key)],user_key, "");
  263. // llMessageLinked(LINK_SET, SLOODLE_CHANNEL_QUIZ_FAILURE_NOTHING_MORE_TO_DO_WITH_AVATAR, "", user_key);//todo add to dia
  264. return;
  265. } else if (statuscode == -10302) {
  266. sloodle_translation_request(SLOODLE_TRANSLATE_IM, [0], "noquestions", [llKey2Name(user_key)],user_key, "quizzer");
  267. // llMessageLinked(LINK_SET, SLOODLE_CHANNEL_QUIZ_FAILURE_NOTHING_MORE_TO_DO_WITH_AVATAR, "", user_key);
  268. return;
  269. } else if (statuscode <= 0) {
  270. //sloodle_translation_request(SLOODLE_TRANSLATE_SAY, [0], "servererror", [statuscode], null_key, "");
  271. string msg;
  272. if (numlines > 1) {
  273. msg = llList2String(lines, 1);
  274. }
  275. sloodle_debug("quiz_data error: "+msg);
  276. //Sloodle 2.0 Change - output custom errorcode to other scripts
  277. sloodle_error_code(SLOODLE_TRANSLATE_IM, user_key,statuscode, msg); //send message to error_message.lsl
  278. return;
  279. }
  280. // We shouldn't need the status line anymore... get rid of it
  281. statusfields = [];
  282. // Go through each line of the response
  283. integer i;
  284. list question_ids_list;
  285. for (i = 1; i < numlines; i++) {
  286. // Extract and parse the current line
  287. string thislinestr = llList2String(lines, i);
  288. list thisline = llParseString2List(thislinestr,["|"],[]);
  289. string rowtype = llList2String( thisline, 0 );
  290. // Check what type of line this is
  291. if ( rowtype == "quiz" ) {
  292. // Get the quiz ID and name
  293. quiz_id = (integer)llList2String(thisline, 4);
  294. quiz_name = llList2String(thisline, 2);
  295. //sloodle_translation_request(SLOODLE_TRANSLATE_HOVER_TEXT, [BLUE, 1.0], "quizname", [quiz_name], llGetOwner(), "quizzer");
  296. } else if ( rowtype == "quizpages" ) {
  297. // Extract the list of questions ID's
  298. question_ids_list = llCSV2List(llList2String(thisline, 3));
  299. num_questions = llGetListLength(question_ids_list);
  300. integer qiter = 0;
  301. question_ids = [];
  302. // Store all our question IDs
  303. for (qiter = 0; qiter < num_questions; qiter++) {
  304. question_ids += [(integer)llList2String(question_ids_list, qiter)];
  305. }
  306. // Are we to randomize the order of the questions?
  307. if (doRandomize) question_ids = llListRandomize(question_ids, 1);
  308. }
  309. }
  310. string question_ids_str = llList2CSV(question_ids_list);
  311. // Make sure we have all the data we need
  312. if (quiz_name == "" || num_questions == 0) {
  313. sloodle_translation_request(SLOODLE_TRANSLATE_SAY, [0], "noquestions", [llKey2Name(user_key)],user_key, "hex_quizzer");
  314. // llMessageLinked(LINK_SET, SLOODLE_CHANNEL_QUIZ_FAILURE_NOTHING_MORE_TO_DO_WITH_AVATAR, "", user_key);//add to dia
  315. return;
  316. }
  317. // Report the status to the user
  318. sloodle_translation_request(SLOODLE_TRANSLATE_SAY, [0], "ready", [llKey2Name(user_key)],user_key, "hex_quizzer");
  319. llMessageLinked(LINK_SET, SLOODLE_CHANNEL_QUIZ_LOADED_QUIZ, (string)quiz_id+"|"+quiz_name+"|"+(string)num_questions+"|"+question_ids_str, user_key);
  320. }
  321. }
  322. // Please leave the following line intact to show where the script lives in Git:
  323. // SLOODLE LSL Script Git Location: mod/quiz-1.0/objects/multi_user_quiz/assets/load_quiz.lslp