normalized perms on all files, to avoid relying on any stored executable bits in...
[feisty_meow.git] / huffware / huffotronic_scripts / party_culiar_v6.1.txt
1 
2 // huffware script: "party culiar", by fred huffhines
3 //
4 // this is yet another particle system script, based on ideas seen
5 // in several scripts by various authors and assisted by the lsl wiki.
6 // it has the useful characteristic of being able to load its parameters
7 // from a notecard, thus making script modifications for the particle
8 // system unnecessary.  
9 // on initial rez, if a notecard exists, then it is read for particle
10 // system parameters named similarly to the variables below (see the
11 // particle archetype notecard for more details).  if the object is
12 // rezzed with an existing particle system already read from a notecard
13 // that's still present, it will keep playing that particle system.
14 // if a notecard is added or changed, then the particle variables are
15 // read again.
16 //
17 // this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
18 // do not use it in objects without fully realizing you are implicitly accepting that license.
19 //
20
21 // party culiar link message API.
22 //////////////
23 integer PARTYCULIAR_HUFFWARE_ID = 10018;
24     // a unique ID within the huffware system for this script.
25 string HUFFWARE_PARM_SEPARATOR = "~~~";
26     // three tildes is an uncommon thing to have otherwise, so we use it to separate
27     // our commands in linked messages.
28 //
29 string PARTYCULIAR_POWER_COMMAND = "#powrpcl";
30     // tells the particle system to either turn on or off.  the single parameter is
31     // either "on", "1", "true", or "off", "0", "false".
32 //////////////
33
34 // constants...
35 float MIN_ROTATION = 0.1;  // rotations used for omega value to rotate particles.
36 float MAX_ROTATION = 4.0;
37
38 integer MAX_NOTECARD_READ_TIME = 34;
39
40 // items controlled by the notecard...
41
42 // noisiness...
43 integer debug = FALSE;  // controls whether logging occurs.
44
45 integer start_enabled = FALSE;  // if true, particle system starts up right away.
46
47 // color...
48 integer interpolate_colors = TRUE;
49 integer randomize_colors = FALSE;
50 vector starting_color = <0.5, 0.7, 0.9>;
51 vector ending_color = <0.0, 1.0, 0.3>;
52
53 // particle size...
54 integer interpolate_size = TRUE;
55 vector final_dimensions = <1.8, 1.8, 1.8>;
56 vector initial_dimensions = <0.72, 0.72, 0.72>;
57
58 // transparency...
59 //   1.0 means completely opaque and 0.0 means completely transparent.
60 float initial_opacity = 1.0;
61 float final_opacity = 0.3;
62
63 // target following (or not)...
64 integer follow_target_key = FALSE;
65 key target_key = NULL_KEY;
66
67 // speed and acceleration of particles...
68 float minimum_velocity = 0.4;
69 float maximum_velocity = 1.4;
70 vector acceleration = <0.0, 0.4, 0.7>;
71 integer wind_blown = TRUE;  // are particles affected by wind?
72
73 // freaky effects for particles...
74 integer bounce_off_z_height = FALSE;  // start at height of containing object.
75 integer glowing = FALSE;  // particles will glow.
76 integer source_centered = TRUE;  // particles start at object's center.
77 integer point_toward_velocity = TRUE;  // rotate vertical axis toward velocity.
78
79 string particle_texture = "";  // image used for the particle, if any.
80
81 // when randomizing colors, these control how long a choice lasts.
82 float minimum_hold_time = 5.0;
83 float maximum_hold_time = 20.0;
84
85 // timing (in seconds)...
86 float lifespan_for_particle = 3.0;
87 float lifespan_for_system = 0.0;  // 0 means unlimited.
88 float creation_rate = 0.2;  // delay between particle creations.
89 integer siblings = 7;  // how many to create at once.
90
91 // how to exude particles.
92 integer emission_pattern
93     = PSYS_SRC_PATTERN_ANGLE;  // 2D emission between given angles.
94     // = PSYS_SRC_PATTERN_DROP;  // just plop them at center of object.
95     // = PSYS_SRC_PATTERN_EXPLODE;  // spew them out as if exploded.
96     // = PSYS_SRC_PATTERN_ANGLE_CONE;  // 3D emission between given angles.
97     // = PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY;  // inverse of angle cone.
98
99 // pattern details...
100 float starting_angle = 1.0;  // initial angle where particles are emitted.
101 float ending_angle = 0.0;  // bounding angle for emission.
102 vector rotation_between_bursts = <0.2, 0.2, 0.2>;  // used in angle patterns.
103 float burst_radius = 2.0;  // emission distance from source, unused for source centered.
104
105 // add-in...  huffware script: notecard library, by fred huffhines
106
107 string current_notecard_name;  // the name of the card we're reading now.
108 key current_query_id;  // the query ID for the current notecard.
109 list query_contents;  // the lines we have read from the notecard.
110 integer line_number;  // which line are we at in notecard?
111 integer notecard_good = FALSE;  // is the notecard usable?
112 integer current_notecard;  // what notecard are we at in inventory?
113
114 initialize()
115 {
116     current_notecard_name = "";
117     current_notecard = 0;
118     notecard_good = FALSE;
119     current_query_id = NULL_KEY;
120     query_contents = [];
121     line_number = 0;
122 }
123
124 string dump_list(list to_show)
125 {
126     integer len = llGetListLength(to_show);
127     integer i;
128     string text;
129     for (i = len - 1; i >= 0; i--) {
130         text += llList2String(to_show, i) + "\n";
131     }
132     return text;
133 }
134
135 //////////////
136
137 create_particle_system()
138 {
139     if (randomize_colors) {
140         starting_color = <randomize_within_range(0.28, 0.95, FALSE),
141             randomize_within_range(0.28, 0.95, FALSE),
142             randomize_within_range(0.28, 0.95, FALSE)>;
143         ending_color = <randomize_within_range(0.14, 0.85, FALSE),
144             randomize_within_range(0.14, 0.85, FALSE),
145             randomize_within_range(0.14, 0.85, FALSE)>;
146     }
147     
148     list system_parameters;
149     integer flags_for_particle_effects = 0;
150
151     if (interpolate_colors)
152         flags_for_particle_effects = PSYS_PART_INTERP_COLOR_MASK | flags_for_particle_effects;
153     system_parameters += [ PSYS_PART_START_COLOR, starting_color,
154         PSYS_PART_END_COLOR, ending_color ];    
155
156     if (interpolate_size)
157         flags_for_particle_effects = PSYS_PART_INTERP_SCALE_MASK | flags_for_particle_effects;
158     system_parameters += [ PSYS_PART_START_SCALE, initial_dimensions,
159         PSYS_PART_END_SCALE, final_dimensions ];
160
161     system_parameters += [ PSYS_PART_START_ALPHA, initial_opacity,
162         PSYS_PART_END_ALPHA, final_opacity ];
163
164     if (follow_target_key && (target_key != NULL_KEY) ) {
165         flags_for_particle_effects = PSYS_PART_TARGET_POS_MASK | flags_for_particle_effects;
166         system_parameters += [ PSYS_SRC_TARGET_KEY, target_key ];
167     }
168
169     system_parameters += [ PSYS_SRC_BURST_SPEED_MIN, minimum_velocity,
170         PSYS_SRC_BURST_SPEED_MAX, maximum_velocity,
171         PSYS_SRC_ACCEL, acceleration ];
172     if (wind_blown)
173         flags_for_particle_effects = PSYS_PART_WIND_MASK | flags_for_particle_effects;
174
175     if (particle_texture != "")
176         system_parameters += [ PSYS_SRC_TEXTURE, particle_texture ];
177
178     if (emission_pattern != 0)
179         system_parameters += [ PSYS_SRC_PATTERN, emission_pattern ];
180
181     system_parameters += [ PSYS_PART_MAX_AGE, lifespan_for_particle,
182         PSYS_SRC_MAX_AGE, lifespan_for_system,
183         PSYS_SRC_BURST_PART_COUNT, siblings,
184         PSYS_SRC_BURST_RATE, creation_rate ];
185
186 //hmmm: only add if used?
187     system_parameters += [ PSYS_SRC_ANGLE_BEGIN, starting_angle,
188         PSYS_SRC_ANGLE_END, ending_angle,
189         PSYS_SRC_OMEGA, rotation_between_bursts ];
190         
191     // assorted effects...
192     if (bounce_off_z_height)
193         flags_for_particle_effects = PSYS_PART_BOUNCE_MASK | flags_for_particle_effects;
194     if (glowing)
195         flags_for_particle_effects = PSYS_PART_EMISSIVE_MASK | flags_for_particle_effects;
196     if (point_toward_velocity)
197         flags_for_particle_effects = PSYS_PART_FOLLOW_VELOCITY_MASK | flags_for_particle_effects;
198     if (source_centered)
199         flags_for_particle_effects = PSYS_PART_FOLLOW_SRC_MASK | flags_for_particle_effects;
200     else
201         system_parameters += [ PSYS_SRC_BURST_RADIUS, burst_radius ];  // okay to use.
202
203     // now that we're done accumulating the flags, we can add them to our list.
204     system_parameters += [ PSYS_PART_FLAGS, flags_for_particle_effects ];  // must be last.
205
206     // and finally, we are ready to create the particle system of our dreams...    
207     llParticleSystem(system_parameters);
208 }
209
210 // returns a non-empty string if "to_check" defines contents for "variable_name".
211 string defines_variable(string to_check, string variable_name)
212 {
213     // clean initial spaces.
214     while (llGetSubString(to_check, 0, 0) == " ")
215         to_check = llDeleteSubString(to_check, 0, 0);
216     if (!is_prefix(to_check, variable_name)) return "";
217     to_check = llDeleteSubString(to_check, 0, llStringLength(variable_name) - 1);
218     // clean any spaces or valid assignment characters.
219     while ( (llGetSubString(to_check, 0, 0) == " ")
220             || (llGetSubString(to_check, 0, 0) == "=")
221             || (llGetSubString(to_check, 0, 0) == ",") )
222         to_check = llDeleteSubString(to_check, 0, 0);
223     if (debug)
224         log_it("set " + variable_name + " = " + to_check);    
225     // return what's left of the string.
226     return to_check;
227 }
228
229 parse_variable_definition(string to_parse)
230 {
231     string content;  // filled after finding a variable name.
232     string texture_name;  // temporary used in reading texture name.
233     
234     if ( (content = defines_variable(to_parse, "debug")) != "")
235         debug = (integer)content;
236     else if ( (content = defines_variable(to_parse, "interpolate_colors")) != "")
237         interpolate_colors = (integer)content;
238     else if ( (content = defines_variable(to_parse, "randomize_colors")) != "")
239         randomize_colors = (integer)content;
240     else if ( (content = defines_variable(to_parse, "starting_color")) != "")
241         starting_color = (vector)content;
242     else if ( (content = defines_variable(to_parse, "ending_color")) != "")
243         ending_color = (vector)content;
244     else if ( (content = defines_variable(to_parse, "interpolate_size")) != "")
245         interpolate_size = (integer)content;
246     else if ( (content = defines_variable(to_parse, "initial_dimensions")) != "")
247         initial_dimensions = (vector)content;
248     else if ( (content = defines_variable(to_parse, "final_dimensions")) != "")
249         final_dimensions = (vector)content;
250     else if ( (content = defines_variable(to_parse, "initial_opacity")) != "")
251         initial_opacity = (float)content;
252     else if ( (content = defines_variable(to_parse, "final_opacity")) != "")
253         final_opacity = (float)content;
254     else if ( (content = defines_variable(to_parse, "follow_target_key")) != "")
255         follow_target_key = (integer)content;
256     else if ( (content = defines_variable(to_parse, "target_key")) != "")
257         target_key = (string)content;
258     else if ( (content = defines_variable(to_parse, "minimum_velocity")) != "")
259         minimum_velocity = (float)content;
260     else if ( (content = defines_variable(to_parse, "maximum_velocity")) != "")
261         maximum_velocity = (float)content;
262     else if ( (content = defines_variable(to_parse, "wind_blown")) != "")
263         wind_blown = (integer)content;
264     else if ( (content = defines_variable(to_parse, "acceleration")) != "")
265         acceleration = (vector)content;
266     else if ( (content = defines_variable(to_parse, "bounce_off_z_height")) != "")
267         bounce_off_z_height = (integer)content;
268     else if ( (content = defines_variable(to_parse, "glowing")) != "")
269         glowing = (integer)content;
270     else if ( (content = defines_variable(to_parse, "source_centered")) != "")
271         source_centered = (integer)content;
272     else if ( (content = defines_variable(to_parse, "point_toward_velocity")) != "")
273         point_toward_velocity = (integer)content;
274     else if ( (content = defines_variable(to_parse, "particle_texture")) != "")
275         particle_texture = (string)content;
276     else if ( (content = defines_variable(to_parse, "lifespan_for_particle")) != "")
277         lifespan_for_particle = (float)content;
278     else if ( (content = defines_variable(to_parse, "lifespan_for_system")) != "")
279         lifespan_for_system = (float)content;
280     else if ( (content = defines_variable(to_parse, "creation_rate")) != "")
281         creation_rate = (float)content;
282     else if ( (content = defines_variable(to_parse, "siblings")) != "")
283         siblings = (integer)content;
284     else if ( (content = defines_variable(to_parse, "minimum_hold_time")) != "")
285         minimum_hold_time = (float)content;
286     else if ( (content = defines_variable(to_parse, "maximum_hold_time")) != "")
287         maximum_hold_time = (float)content;
288     else if ( (content = defines_variable(to_parse, "emission_pattern")) != "") {
289         texture_name = (string)content;
290         // translate the short hand name into an emission_pattern value.
291         if (texture_name == "ANGLE") emission_pattern = PSYS_SRC_PATTERN_ANGLE;
292         else if (texture_name == "DROP") emission_pattern = PSYS_SRC_PATTERN_DROP;
293         else if (texture_name == "EXPLODE") emission_pattern = PSYS_SRC_PATTERN_EXPLODE;
294         else if (texture_name == "ANGLE_CONE") emission_pattern = PSYS_SRC_PATTERN_ANGLE_CONE;
295         else if (texture_name == "ANGLE_CONE_EMPTY")
296             emission_pattern = PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY;
297 //log_it("emission pattern is now " + (string)emission_pattern);
298     }
299     else if ( (content = defines_variable(to_parse, "starting_angle")) != "")
300         starting_angle = (float)content;
301     else if ( (content = defines_variable(to_parse, "ending_angle")) != "")
302         ending_angle = (float)content;
303     else if ( (content = defines_variable(to_parse, "rotation_between_bursts")) != "") {
304         if (content == "random")
305             rotation_between_bursts = <randomize_within_range(MIN_ROTATION, MAX_ROTATION, TRUE),
306                 randomize_within_range(MIN_ROTATION, MAX_ROTATION, TRUE),
307                 randomize_within_range(MIN_ROTATION, MAX_ROTATION, TRUE)>;
308         else
309             rotation_between_bursts = (vector)content;
310     } else if ( (content = defines_variable(to_parse, "burst_radius")) != "")
311         burst_radius = (float)content;
312     else if ( (content = defines_variable(to_parse, "running")) != "")
313         start_enabled = (integer)content;
314
315     // special cases for key to follow...
316     if (target_key == "owner") target_key = llGetOwner();
317     else if (target_key == "self") target_key = llGetKey();
318     // special cases for texture.
319     if (particle_texture == "inventory")
320         particle_texture = llGetInventoryName(INVENTORY_TEXTURE, 0);
321
322 }
323
324 process_particle_settings(list particle_definitions)
325 {
326     integer current_item = 0;
327     integer max_items = llGetListLength(particle_definitions);
328     while (current_item < max_items) {
329         string curr_line = llList2String(particle_definitions, current_item);
330         parse_variable_definition(curr_line);
331         current_item++;
332     }
333 }
334
335 randomize_timer()
336 {
337     if (randomize_colors) {
338         llSetTimerEvent(randomize_within_range(maximum_hold_time,
339             minimum_hold_time, FALSE));
340     }
341 }
342
343 stop_timer() { llSetTimerEvent(0.0); }
344
345 check_for_notecard()
346 {
347     // if there's a notecard, then we will start reading it.
348     if (llGetInventoryNumber(INVENTORY_NOTECARD) > current_notecard) {
349         current_notecard_name = llGetInventoryName(INVENTORY_NOTECARD, current_notecard++);
350         line_number = 0;
351         query_contents = [];
352         current_query_id = llGetNotecardLine(current_notecard_name, 0);
353         llSetTimerEvent(MAX_NOTECARD_READ_TIME);
354     } else {
355         log_it("No appropriate notecard was found.  Shutting down.");
356         llSetTimerEvent(0);
357     }
358 }
359
360 // this should be invoked from the link_message event handler to process the requests
361 // for whatever service this library script provides.
362 handle_link_message(integer sender, integer huff_id, string msg, key id)
363 {
364     if (huff_id != PARTYCULIAR_HUFFWARE_ID) {
365         return;
366     }
367 //llOwnerSay("link msg: " + (string)sender + " " + (string)huff_id + " msg=" + msg + " id=" + (string)id);
368     if (msg == PARTYCULIAR_POWER_COMMAND) {
369         string cmd = id;
370         if ( (find_substring(id, "on") == 0)
371             || (find_substring(id, "1") == 0)
372             || (find_substring(id, "true") == 0) ) {
373             // they want to crank the particle system up.
374             create_particle_system();
375             randomize_timer();
376         } else if ( (find_substring(id, "off") == 0)
377             || (find_substring(id, "0") == 0)
378             || (find_substring(id, "false") == 0) ) {
379             // the request is to shut the party down now.
380             llParticleSystem([]);
381             stop_timer();
382         }
383     }
384 }
385
386 //////////////
387 // from hufflets...
388
389 //////////////
390
391 integer debug_num = 0;
392
393 // a debugging output method.  can be disabled entirely in one place.
394 log_it(string to_say)
395 {
396     debug_num++;
397     // tell this to the owner.    
398     llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
399     // say this on open chat, but use an unusual channel.
400 //    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
401 }
402
403 //////////////
404
405 // joins a list of parameters using the parameter sentinel for the library.
406 string wrap_parameters(list to_flatten)
407 { return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
408
409 // handles when blank strings need to come through the pipe.
410 string wrap_blank_string(string to_wrap)
411 {
412     if (llStringLength(to_wrap)) return to_wrap;  // that one is okay.
413     return "\"\"";  // return a quoted nothing as a signal for a blank.
414 }
415
416 // undoes a previously wrapped blank string.
417 string interpret_blank_string(string to_unwrap)
418 {
419     if (to_unwrap == "\"\"") return "";  // that was an encoded blank.
420     return to_unwrap;  // no encoding.
421 }
422
423 // a simple version of a reply for a command that has been executed.  the parameters
424 // might contain an outcome or result of the operation that was requested.
425 send_reply(integer destination, list parms, string command)
426 {
427     llMessageLinked(destination, PARTYCULIAR_HUFFWARE_ID, command,
428         llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
429 }
430
431 //////////////
432
433 // returns a number at most maximum and at least minimum.
434 // if "allow_negative" is TRUE, then the return may be positive or negative.
435 float randomize_within_range(float minimum, float maximum, integer allow_negative)
436 {
437     float to_return = minimum + llFrand(maximum - minimum);
438     if (allow_negative) {
439         if (llFrand(1.0) < 0.5) to_return *= -1.0;
440     }
441     return to_return;
442 }
443
444 // the string processing methods are not case sensitive.
445   
446 // returns TRUE if the "pattern" is found in the "full_string".
447 integer matches_substring(string full_string, string pattern)
448 { return (find_substring(full_string, pattern) >= 0); }
449
450 // returns the index of the first occurrence of "pattern" inside
451 // the "full_string".  if it is not found, then a negative number is returned.
452 integer find_substring(string full_string, string pattern)
453 { return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
454
455 // returns TRUE if the "prefix" string is the first part of "compare_with".
456 integer is_prefix(string compare_with, string prefix)
457 { return find_substring(compare_with, prefix) == 0; }
458
459 //////////////
460 // huffware script: auto-retire, by fred huffhines, version 2.5.
461 // distributed under BSD-like license.
462 //   !!  keep in mind that this code must be *copied* into another
463 //   !!  script that you wish to add auto-retirement capability to.
464 // when a script has auto_retire in it, it can be dropped into an
465 // object and the most recent version of the script will destroy
466 // all older versions.
467 //
468 // the version numbers are embedded into the script names themselves.
469 // the notation for versions uses a letter 'v', followed by two numbers
470 // in the form "major.minor".
471 // major and minor versions are implicitly considered as a floating point
472 // number that increases with each newer version of the script.  thus,
473 // "hazmap v0.1" might be the first script in the "hazmap" script continuum,
474 // and "hazmap v3.2" is a more recent version.
475 //
476 // example usage of the auto-retirement script:
477 //     default {
478 //         state_entry() {
479 //            auto_retire();  // make sure newest addition is only version of script.
480 //        }
481 //     }
482 // this script is partly based on the self-upgrading scripts from markov brodsky
483 // and jippen faddoul.
484 //////////////
485 auto_retire() {
486     string self = llGetScriptName();  // the name of this script.
487     list split = compute_basename_and_version(self);
488     if (llGetListLength(split) != 2) return;  // nothing to do for this script.
489     string basename = llList2String(split, 0);  // script name with no version attached.
490     string version_string = llList2String(split, 1);  // the version found.
491     integer posn;
492     // find any scripts that match the basename.  they are variants of this script.
493     for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
494 //log_it("invpo=" + (string)posn);
495         string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
496         if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
497             // found a basic match at least.
498             list inv_split = compute_basename_and_version(curr_script);
499             if (llGetListLength(inv_split) == 2) {
500                 // see if this script is more ancient.
501                 string inv_version_string = llList2String(inv_split, 1);  // the version found.
502                 // must make sure that the retiring script is completely the identical basename;
503                 // just matching in the front doesn't make it a relative.
504                 if ( (llList2String(inv_split, 0) == basename)
505                     && ((float)inv_version_string < (float)version_string) ) {
506                     // remove script with same name from inventory that has inferior version.
507                     llRemoveInventory(curr_script);
508                 }
509             }
510         }
511     }
512 }
513 //
514 // separates the base script name and version number.  used by auto_retire.
515 list compute_basename_and_version(string to_chop_up)
516 {
517     // minimum script name is 2 characters plus a version.
518     integer space_v_posn;
519     // find the last useful space and 'v' combo.
520     for (space_v_posn = llStringLength(to_chop_up) - 3;
521         (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
522         space_v_posn--) {
523         // look for space and v but do nothing else.
524 //log_it("pos=" + (string)space_v_posn);
525     }
526     if (space_v_posn < 2) return [];  // no space found.
527 //log_it("space v@" + (string)space_v_posn);
528     // now we zoom through the stuff after our beloved v character and find any evil
529     // space characters, which are most likely from SL having found a duplicate item
530     // name and not so helpfully renamed it for us.
531     integer indy;
532     for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
533 //log_it("indy=" + (string)space_v_posn);
534         if (llGetSubString(to_chop_up, indy, indy) == " ") {
535             // found one; zap it.  since we're going backwards we don't need to
536             // adjust the loop at all.
537             to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
538 //log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
539         }
540     }
541     string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
542     // ditch the space character for our numerical check.
543     string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
544     // strip out a 'v' if there is one.
545     if (llGetSubString(chop_suffix, 0, 0) == "v")
546         chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
547     // if valid floating point number and greater than zero, that works for our version.
548     string basename = to_chop_up;  // script name with no version attached.
549     if ((float)chop_suffix > 0.0) {
550         // this is a big success right here.
551         basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
552         return [ basename, chop_suffix ];
553     }
554     // seems like we found nothing useful.
555     return [];
556 }
557 //
558 //////////////
559
560 default {
561     state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
562     on_rez(integer parm) { state rerun; }
563 }
564 state rerun { state_entry() { state default; } }
565
566 state real_default
567 {
568     state_entry()
569     {
570         auto_retire();
571         initialize();
572         if (start_enabled) {
573             create_particle_system();
574             randomize_timer();
575         } else {
576             llParticleSystem([]);
577             stop_timer();
578         }
579         check_for_notecard();
580     }
581     
582     timer() {
583         if ( (current_query_id != NULL_KEY) || !notecard_good) {
584             // there's been a failure of some sort; we were supposed to get a notecard to read.
585             llWhisper(0, "Failed to read the config notecard; restarting.");
586             llResetScript();
587         }
588         create_particle_system();
589         randomize_timer();
590     }
591     
592     on_rez(integer parm) {
593         target_key = llGetKey();  // reset to get rid of weird wrong keys.
594         llResetScript();
595     }
596
597     link_message(integer sender, integer num, string msg, key id) {
598         handle_link_message(sender, num, msg, id);
599     }
600
601     changed(integer change_type) {
602         if (change_type != CHANGED_INVENTORY) {
603             // we only care about inventory changes here.
604             return;
605         }
606         llResetScript();
607     }
608     
609     dataserver(key query_id, string data) {
610         if (query_id != current_query_id) {
611             log_it("not our query id somehow?  weird query had: id="
612                 + (string)query_id + " data=" + (string)data);
613             // to heck with all this weirdness; if there's a failure, start over.
614             llResetScript();
615         }
616         // if we're not at the end of the notecard we're reading...
617         if (data != EOF) {
618             if (!line_number) {
619                 if (data != "#party culiar") {
620                     // this card has the wrong signature at the top.  quit bothering
621                     // with it now.
622                     log_it("wrong notecard signature found in " + current_notecard_name
623                         + "; skipping to next card.");
624                     check_for_notecard();
625                     return;
626                 }
627                 log_it("starting to read notecard " + current_notecard_name + "...");    
628             }
629             if (data != "") {
630                  // add the non-blank line to our destination list.
631                 query_contents += data;
632 //log_it("line " + (string)line_number + ": data=" + data);
633             }
634             line_number++;  // increase the line count.
635             // request the next line from the notecard.
636             current_query_id = llGetNotecardLine(current_notecard_name, line_number);
637         } else {
638             // no more data, so we're done with this card.
639             current_query_id = NULL_KEY;
640             if (!llGetListLength(query_contents)) {
641                 // nothing was read?  the heck with this card.
642                 current_notecard_name = "";  // toss bad card.
643                 return;
644             }
645             // we set this just for getting to the point of having read the whole thing.
646             notecard_good = TRUE;
647 //log_it("notecard said:\n" + dump_list(query_contents));
648             process_particle_settings(query_contents);
649             if (start_enabled) {
650                 create_particle_system();
651                 randomize_timer();
652             } else {
653                 llParticleSystem([]);
654                 stop_timer();
655             }
656             log_it("done reading notecard " + current_notecard_name + ".");
657         }
658     }
659 }
660