Jump to content

Message Calls Are The Devil


Lazarus.

Recommended Posts

I was writing my first script involving messaging today, (I've avoided it like the plague) and have encountered some difficulties.

 

The idea is fairly simple. One creature(the overseer) sends out a message of 1 to all nearby characters. Another script(the slave) turns to hostile B if it doesn't receive a message for two consecutive turns. Basically its Kel's "traitor" but with messages instead of SDFs.

 

The problem I've encountered is that the slave creature is constantly forgetting its message. I tried saving the message as a variable, but the script runs multiple times each turn and just winds up writing over it with a -1. See for yourself, here's the relevant part of the slave script.

 

Code:
 beginstate START_STATE;	message = my_current_message();	if(message == 1){		nomess = 0;		set_attitude(ME,10);		}	if(message == -1){		if(nomess == 1){			message_dialog("The ogre, realizing that its tormentor is dead, promptly turns on its former masters.","");			set_attitude(ME,11);		}		if(nomess == 0)			nomess = 1;	}	if (target_ok()) {		if (dist_to_char(get_target()) <= 16)			else set_target(ME,-1);		}		// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		}			// Have I been hit? Strike back!	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		}break;
I can get it to work if I replace the do_attack()'s with end_combat_turn, but obviously that makes it just sit there. The most frustrating thing is that the behavior seems fairly random, one slave will revolt while others remain hostile, and then several turns later they too will revolt. Any suggestions would be appreciated.
Link to comment
Share on other sites

I am pretty sure you are just experimenting, so this script is fine for that purpose. However, if you need this to do something important, there are simpler and more reliable ways to do this.

 

I haven't confirmed your statement about the reassignment of message in the same turn when START_STATE runs again, but I'll assume your take on the situation is right and I present this code which should work:

 

Code:
  beginstate START_STATE;	message = my_current_message();	if(message == 1){		nomess = get_current_tick();		set_attitude(ME,10) // is this really necessary?		}	if(message == -1){		if(tick_difference(nomess,get_current_tick()) >= 2){			message_dialog("The ogre, realizing that its tormentor is dead, promptly turns on its former masters.","");			set_attitude(ME,11);		}	if (target_ok()) {		if (dist_to_char(get_target()) <= 16)			else set_target(ME,-1);		}		// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		}			// Have I been hit? Strike back!	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		}break; 
Link to comment
Share on other sites

Actually this was for real. I'm using it in my new scenario.

 

So anyway, I had to fix the code a little. Heres what the working version looked like.

Code:
 	message = my_current_message();	if(message == 1){		nomess = -1;		set_attitude(ME,10); // For the record, this is here in case an overseer comes back into range, so the ogre will straighten itself out.	}			if(message == -1){		if((tick_difference(nomess,get_current_tick()) >= 1) && (nomess != -1)){			message_dialog("The ogre, realizing that its tormentor is dead, promptly turns on its former masters.","");			set_attitude(ME,11);		}	nomess = get_current_tick();	}	if (target_ok()) {		if (dist_to_char(get_target()) <= 16)			else set_target(ME,-1);		}		// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		}			// Have I been hit? Strike back!	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		} 
Your tick suggestion really put me on the right track. Thanks a bunch. By the way, what easier ways to do you mean? I'm always a fan of shortcuts, and I'd run through a lot of different ideas and couldn't come up with anything that would work for how I'm using this code.

 

Ok, slightly funny story, I made the changes above, and knew it should be working, but it wasn't. After slamming my head on the wall for about five minutes it hit me. Much to my chagrin, I was testing it in a town that I had frozen in time (doh).

Link to comment
Share on other sites

Actually, if you have not modified the rest of the control structure of this script, there are a handful of possible problems.

 

As wz pointed out, the NPC must act after its overseer in order for this to work.

 

The message dialog seems to assume that the overseer must be dead if it has not been around for a round. However, because you said the monster script reverts it attitude when it comes back, you imply that the overseer might just be out of the area when the ogre goes rogue.

 

Since this code is in START_STATE, it will not have any effect while the ogre is attacking one target -- the ogre will be in state 3 for the entire duration of which. You probably do not want this because I tend to think it will be too busy attacking you to change attitudes.

 

Also, if you wanted to do this simply, why didn't you just use the SDF version? If you do not like SDFs for this, you could also use a set_memory_cell call on a group.

Link to comment
Share on other sites

The message dialog sucks because I wrote it in about 2 seconds, mostly for the purpose of alerting me when the ogre changed sides when testing the script.

 

As for the rest, I already had taken out state 3, but I've now added a set_target(ME,-1) to when the ogre switches sides. Also, whether the overseer acts first doesn't matter particularly much, since he has an extra turn to catch up to them. Example: Ogre doesn't get a message because it acts first, begins countdown, then next turn gets a message and doesn't revolt.

 

The real problem is that a slowed, forcecaged or paralyzed overseer's script doesn't get run, which means the ogres go nuts every other turn. Its not to much of a concern since I'm stripping all the default spells so this probably won't come up, but still. I'm making the overseers immune to those things just in case.

 

And the reason I'm not using SDF's is because I'll be using this in outdoor combats a couple of times. Since you can't set memory cells before hand in outdoor combats, or add creatures to groups, it would require making a different script for each one (Since number of overseers that have to die, etc. changes from situation to situation.) In a town I could get away with this by just using a memory cell to mark how many overseers a particular ogre should expect to have, and what flag that group would be using. In an outdoors it would take a whole new script.

Link to comment
Share on other sites

Nothing to apologize for. Here's the scripts for both the slave and the overseer. The slave script is pretty much what I posted last time.

 

Slave:

Code:
 begincreaturescript;variables;short i,target;short check,message,nomess,new;short best,status,targbody;beginstate INIT_STATE;set_char_script_mode(ME,2);	nomess = 0;	new = 1;	if (get_memory_cell(0) == 2)		set_mobility(ME,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;	message = my_current_message();	if(message == 1){		nomess = -1;		set_attitude(ME,10);	}			if(message == -1){		if((tick_difference(nomess,get_current_tick()) >= 2) && (nomess > 0)){			if(get_flag(5,0) == 0){				message_dialog("Ogres are slow and dim witted, nevertheless it only takes this one a short amount of time to realize that there are no overseers around, and thus no threat of whips and pain. It promptly starts attacking anything in sight.","");				set_flag(5,0,1);			}			set_attitude(ME,11);			set_target(ME,-1);		}		if(nomess == -1)			nomess = get_current_tick();	}	if (target_ok()) {		if (dist_to_char(get_target()) <= 16)			else set_target(ME,-1);		}		// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		}			// Have I been hit? Strike back!	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		}break; 
Overseer:

Code:
 begincreaturescript;variables;short i,target;body;beginstate INIT_STATE;set_char_script_mode(ME,2);	if (get_memory_cell(0) == 2)		set_mobility(ME,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 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);		}		// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		set_state(3);		}	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		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, and that eats up CPU time.	if (am_i_doing_action() == FALSE)		end_combat_turn();break;beginstate 3; // attackingprint_str("Sent");broadcast_char_message(15,1,0);	if (target_ok() == FALSE)		set_state(START_STATE);	do_attack();	end();break; 
It seems to be working fairly well like this, although I'm still a bit distrustful of it. I've seen it act goofy too many times in the past. Oh, and I think I'm going to edit it to run a town script when the creature dies, I'll decide when I'm done with the dungeon and know whether I'll need it.

Edit: That message dialog was WAY to long, broke it up a bit.

Link to comment
Share on other sites

There are only a few changes in the following. They should account for every possibility, indoors and outdoors.

 

Code:
begincreaturescript;variables;short i,target;short check,message,nomess = -1,new = 0; // since you can assign them values right awayshort best,status,targ;body;beginstate INIT_STATE;set_char_script_mode(ME,2);	if (get_memory_cell(0) == 2)		set_mobility(ME,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;	message = my_current_message();	if(message == 1){		nomess = -1;		set_attitude(ME,10);	}			if(message == -1){		if((tick_difference(nomess,get_current_tick()) >= 2) && (nomess > -1)){			if(get_flag(5,0) == 0){				message_dialog("Ogres are slow and dim witted, nevertheless it only takes this one a short amount of time to realize that there are no overseers around, and thus no threat of whips and pain. It promptly starts attacking anything in sight.","");				set_flag(5,0,1);			}			set_attitude(ME,11);			set_target(ME,-1);		}		if(nomess == -1)			nomess = get_current_tick();	}		if (target_ok()) { // the do_attack() call was missing		if (dist_to_char(get_target()) <= 16)			do_attack();			else set_target(ME,-1);		}					// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		}			// Have I been hit? Strike back!	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		}break; 
Code:
begincreaturescript;variables;short i,target;body;beginstate INIT_STATE;set_char_script_mode(ME,4); // setting this to run every turn period accounts for at least one special case I can think of	if (get_memory_cell(0) == 2)		set_mobility(ME,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 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);		}		// Look for a target, attack it if visible	if (select_target(ME,8,0)) {		do_attack();		set_state(3);		}	if (who_hit_me() >= 0) {		set_target(ME,who_hit_me());		do_attack();		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, and that eats up CPU time.	if (am_i_doing_action() == FALSE)		end_combat_turn();break;beginstate 3; // attacking	print_str("Sent");	broadcast_char_message(15,1,0);		if (target_ok() == FALSE)		set_state(START_STATE);	do_attack();break;
If you want to make a nice live-action scene with an overseer beating up an ogre, I have the perfect script for that in case you'd like to see it.
Link to comment
Share on other sites

Thanks for the help, I haven't tried the new version yet but I don't see why it shouldn't work.

 

I think I'll have to take a pass on the ogre getting whooped on. The first time the party encounters them will be outdoors, which eliminates putting the sequence there. By the time you reach the dungeon it will already have been established through message dialogs and Friendly NPC dialog that the ogres are mistreated. Throwing a sequence like that in the middle of a hack n' slash dungeon, while tempting, would probably just break up the atmosphere (plus I assume you mean the sparring script you wrote at the codex, in which case the party will just wind up killing the ogre and overseer before they realize what is going on :p )

 

Edit: Actually having an overseer beating up on ogres and miners alike would be kinda interesting. I'll put it in and see how it looks.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...