/**** LEAPFROG SYSTEM **** Thread maps\_leapfrog::leapfrog() on any AI that uses a chain of nodes. Make sure to check the NOT_CHAIN check box on each node in the chain to allow chains to merge or loop. You need at least two chains for this to look good. level.leap_delay_min and max determines how fast they try to advance. set script_delay on a node to have the ai use a delay instead of waiting for a leap notify. script_delay = 0 will make them run past that node before going to the next one. *************************/ #include maps\_utility; main() { // Set delays to tweak how fast the advance should be. level.leap_delay_min = 6; level.leap_delay_max = 14; level.leap_delay_override = false; // there will never be more ai on one leap node then what this is set to. level.leapfrog_max_node_ai = 6; level.leap_node_array = []; level.leapfrog_random_int = randomint(5); // lower threat threatbias group for when leaping. createthreatbiasgroup("leapfrog"); setthreatbiasagainstall("leapfrog", -200); level thread leapfrog_masterthread(); } leapfrog_masterthread() { while ( true ) { if ( !level.leap_delay_override ) wait ( randomFloatRange( level.leap_delay_min, level.leap_delay_max ) ); else wait .05; level.leap_delay_override = false; // used to make ai take the same fork in a path when script_delay is set. level.leapfrog_random_int = randomint(5); node_arr = []; high_weight = -1000000; if ( !level.leap_node_array.size ) continue; for ( i=0; i high_weight ) high_weight = weight; } assertEx( isdefined(node_arr[high_weight]), "high_weight is: " + high_weight ); assertEx( isdefined(high_weight >= 0), "high_weight is below zero: " + high_weight ); node = node_arr[high_weight][ randomint( node_arr[high_weight].size ) ]; assert ( isdefined(node.target) ); // there should always be a new node or it shouldn't be in the array. node_arr = getnodearray(node.target,"targetname"); next_node = node_arr[ randomint(node_arr.size) ]; // reset future ai count if ( isdefined( next_node.leapfrog_ai_count ) ) next_node.leapfrog_future_ai_count = next_node.leapfrog_ai_count; else next_node.leapfrog_future_ai_count = 0; // increase the weight of all none chosen nodes. array_thread( level.leap_node_array, ::increment_leap_weight, node ); new_weight = int(node.leap_weight * -.25); if (isdefined(next_node.leap_weight)) new_weight += next_node.leap_weight; add_leap_node(next_node, new_weight); node notify("leapfrog", next_node); remove_leap_node(node); } } increment_leap_weight(node) { if (self == node) return; diff_weight = node.leap_weight - self.leap_weight; self.leap_weight += (int (diff_weight * 0.5) + 1); // old .75; } leapfrog() { self endon("death"); self endon("stop_leapfrog"); self notify("stop_going_to_node"); // get first node node_arr = getnodearray(self.target,"targetname"); node = node_arr[ randomint(node_arr.size) ]; while ( true ) { if (node.radius != 0) self.goalradius = node.radius; if ( isdefined(node.height) ) self.goalheight = node.height; self setgoalnode(node); old_maxsightdistsqrd = self.maxsightdistsqrd; self.maxsightdistsqrd = 350*350; old_group = self getthreatbiasgroup(); self setthreatbiasgroup("leapfrog"); self waittill("goal"); // Notify the node and pass the guy. Might be good for something node notify("trigger",self); self.maxsightdistsqrd = old_maxsightdistsqrd; self setthreatbiasgroup(old_group); self thread leapfrog_on_death(node); if ( !isdefined(node.target) ) break; if ( isdefined(node.script_delay) ) { node script_delay(); node_arr = getnodearray(node.target,"targetname"); next_node = node_arr[ level.leapfrog_random_int % node_arr.size ]; } else { if ( !add_leap_node(node) ) break; node waittill("leapfrog", next_node); next_node.leapfrog_future_ai_count++; max_node_ai = level.leapfrog_max_node_ai; if ( isdefined(node.script_noteworthy) ) max_node_ai = int( node.script_noteworthy ); if (next_node.leapfrog_future_ai_count > max_node_ai) { level.leap_delay_override = true; if ( isdefined(next_node.leap_weight) ) { next_node.leap_weight += 1; // make the full node more likely to leap. } next_node = node; // stay on old node. } } node = next_node; } // notify level and pass the guy that reached his final leapfrog node. level notify("leapfrog_completed", self); } leapfrog_on_death(node) { node endon("leapfrog"); if ( !isdefined( node.leapfrog_ai_count ) ) node.leapfrog_ai_count = 0; node.leapfrog_ai_count++; self waittill("death"); node.leapfrog_ai_count--; if ( isdefined(node.leap_weight) ) { new_weight = node.leap_weight - 1; if (new_weight < 1) new_weight = 1; node.leap_weight = new_weight; } if (!node.leapfrog_ai_count) remove_leap_node(node); } add_leap_node(node, weight) { if ( !isdefined(node.target) || isdefined(node.script_delay)) return false; if ( getdvar("debug") == "1") node thread debug_leap_node(); if ( !is_in_array (level.leap_node_array, node) ) { level.leap_node_array = array_add(level.leap_node_array, node); node.leap_weight = 0; } if ( isdefined(weight) ) node.leap_weight = weight; else node.leap_weight += 2; return true; } remove_leap_node(node) { node.leap_weight = undefined; node.leapfrog_ai_count = undefined; level.leap_node_array = array_remove(level.leap_node_array, node); } /* debug stuff */ debug_leap_node() { if ( isdefined(self.debug_leapnode) ) return; self.debug_leapnode = true; while (true) { if ( isdefined(self.leap_weight) ) self thread print3Dmessage(self.leap_weight, .5); if ( isdefined(self.leapfrog_ai_count) ) self thread print3Dmessage(self.leapfrog_ai_count, 0.5 , (1,0,0), (0,0,128) , 3); wait .5; } } print3Dmessage(message, show_time, color, offset, scale) { if ( !isdefined(color) ) color = (0.5,1,0.5); if ( !isdefined(offset) ) offset = (0,0,56); if ( !isdefined(scale) ) scale = 6; show_time = gettime() + (show_time * 1000); while ( gettime() < show_time) { print3d (self.origin + offset, message, color, 1, scale); wait (0.05); } }