/*********************************************** * Script: Bodyguard (pair with Guarded NPC) * * Author: Walker White * ***********************************************/ // bodyguard.txt // A basicnpc that responds to targetting requests // Walker M. White // Revision 1.0, // March 30, 2004 // Revision history: // 1.0: Initial release into the wild. // Permission is granted for free use of this script in scenarios, provided that // these header comments are retained and modifications are dutifully documented // throughout. // This script extends basicnpc.txt, except that it may receive messages from // other members of its group (namely guardednpcs). The message sent is a // character number. The character then goes after the character ID, making it // the new target (to protect the guardednpc). If the guardednpc has made a // target request previously, and that target is still alive (and presumably // hurting the guardednpc), then bodyguard may or may not switch targets, // according to the tolerance value in the memory cells. // The memory cells used in this file are as follows: // Cell 0: Mobility (as per basicnpc) // Cell 1: Stuff done flag 1 (as per basicnpc) // Cell 2: Stuff done flag 2 (as per basicnpc) // Cell 3: Dialog node (as per basicnpc) // Cell 4: Group to add this character to. The character is added to this group // upon initialization. The character must be in the same group as the // guardednpc or it will not receive any messages. Be careful not to // have any group conflicts when using this script. // Cell 5: Tolerance rating. This is a percentile value (0 - 100%) that // represents the probability of changing targets once the character // has already respond to a message. If this value is 0, then the // guard will change targets only if the original target was chosen // personally and not by the guardednpc. This keeps the bodyguard // from jumping about because the guardednpc keeps being hit by // different characters. Hence a high tolerance may result in very // erratice target selection. // NOTE: // There are a couple of important observations to make about this script. // First of all, note that we never use set_state_continue(), even though we do // in guardednpc. The call set_state_continue() does cut down on some overhead, // but it is dangerous in scripts that receive messages. All states need to // react to the message, and if set_state_continue() is called, the message is // not cleared before this state is invoked. This can cause an infinite loop. // Hence, while it is a little less efficient, you should always use set_state() // in a script that receives messages. // The other observation, is the you should never use this script in areas where // characters are _broadcasting_ messages. There is currently no way in // AvernumScript to check the sender of the message (as terrain can send // messages, there is no such thing). We control this issue in the script by // sending messages to the group instead of broadcasting. So this script // assumes that if it receives a message, this is a valid target request. // This can be bad when arbitrary messages are passed. The error checking for // target acquisition is pretty minimal, so this could have some bad results. // Currently this script assumes that all messages are valid targets. An easy // modification of this script (which I leave to the reader) is to respond to a // broadcast of -1 as a message that the guardednpc is dead. begincreaturescript; variables; short i,target; // Variable to implement tolerance rating. short protecting; body; beginstate INIT_STATE; if (get_memory_cell(0) == 2) { set_mobility(ME,0); } // If memory cell 4 is non-zero, add us to that group. if (get_memory_cell(4) > 1 && get_memory_cell(4) < 8) { add_char_to_group(my_number(),get_memory_cell(4)); protecting = FALSE; } // If tolerance is invalid, set it to 0. if (get_memory_cell(5) < 0 || get_memory_cell(5) > 100) { print_str("Error (bodyguard): Invalid tolerance rating."); set_memory_cell(5,0); } break; beginstate DEAD_STATE; // Set the appropriate stuff done flag for this character being dead if ((get_memory_cell(1) != 0) || (get_memory_cell(2) != 0)) { set_flag(get_memory_cell(1),get_memory_cell(2),1); } break; beginstate START_STATE; // If there is a yell for help, respond. if (my_current_message() != -1) { // If we are already protecting, check the tolerance. if (protecting == FALSE || (get_ran(1,1,100) <= get_memory_cell(5))) { protecting = TRUE; set_target(ME,my_current_message()); } } // if I have a target for some reason, go attack it if (target_ok()) { if (dist_to_char(get_target()) <= 16) { set_state(3); } else { set_target(ME,-1); } } // There is currently no target, so we are not protecting. protecting = FALSE; // Have I been hit by nearby living creature? Strike back! if (who_hit_me() >= 0) { if (dist_to_char(who_hit_me()) <= 2 && char_ok(who_hit_me()) == TRUE) { set_target(ME,who_hit_me()); set_state(3); } } // Look for a target, and attack it if visible if (select_target(ME,8,0)) { set_state_continue(3); } // If no one close, we defend ourselves against ranged attackers. if (who_hit_me() >= 0) { // Target may have been killed since it struck. if (char_ok(who_hit_me()) == TRUE) { set_target(ME,who_hit_me()); set_state(3); } } // Otherwise, just peacefully move around. Go back to start, if I'm too far // from where I started. if ((my_dist_from_start() >= 6) || ((my_dist_from_start() > 0) && (get_memory_cell(0) > 0))) { if (get_ran(1,1,100) < 40) { return_to_start(ME,1); } } else if (get_memory_cell(0) == 0) { fidget(ME,25); } // if we're in combat and the above didn't give me anything to do, just // stop now. Otherwise, game will keep running script. if (am_i_doing_action() == FALSE) { end_combat_turn(); } break; beginstate 3; // attacking // If there is a message, reassess target. if (my_current_message() != -1) { set_state_continue(START_STATE); } if (target_ok() == FALSE) { protecting = FALSE; set_state(START_STATE); } do_attack(); break; beginstate TALKING_STATE; if (get_memory_cell(3) == 0) { print_str("Talking: It doesn't respond."); end(); } begin_talk_mode(get_memory_cell(3)); break;