Aller au contenu

Photo

Script Idea - Using a Gong placable to attract the enemy


  • Veuillez vous connecter pour répondre
9 réponses à ce sujet

#1
Friar

Friar
  • Members
  • 293 messages

I was looking for a script that runs everytime the PC successfully hits an indestructable gong.

 

I want the enemy who is blocking the path to an objective hear the gong and go to the gong/waypoint to investigate the sound.

 

I have found one script that I have not been able to implement. As much as I hate to admit it, it confuses me.

 

void main()
{

object oPC = GetLastHostileActor();

if (!GetIsPC(oPC)) return;

object oTarget;
oTarget = GetObjectByTag("as_mg_gong_02");

SoundObjectPlay(oTarget);

SoundObjectSetVolume(oTarget, 100);

AmbientSoundPlay(GetArea(oPC));

}

int NW_FLAG_BEHAVIOR_SPECIAL       = 0x00000001;

void SetBehaviorState(int nCondition, int bValid = TRUE)
{
    int nPlot = GetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER");
    if(bValid == TRUE)
    {
        nPlot = nPlot | nCondition;
        SetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER", nPlot);
    }
    else if (bValid == FALSE)
    {
        nPlot = nPlot & ~nCondition;
        SetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER", nPlot);
    }
}


int isGong( object gong ) {
    return GetTag(gong) == "TsGong";
}

int isBaton( object baton ) {
    int type = GetBaseItemType(baton);
    return
        type == BASE_ITEM_TORCH ||
        type == BASE_ITEM_CLUB ||
        type == BASE_ITEM_LIGHTFLAIL ||
        type == BASE_ITEM_HEAVYFLAIL ||
        type == BASE_ITEM_MORNINGSTAR;
}

void moveToObject( object what, int run ) {
    if( isBusy() || isSleeping() )
        return;
    ClearAllActions();
    ActionMoveToObject( what, run, 5.0 );
}

int getIsSentient( object who ) {
    int r = GetRacialType( who );
    if( r == RACIAL_TYPE_DWARF || r == RACIAL_TYPE_ELF ||
        r == RACIAL_TYPE_FEY || r == RACIAL_TYPE_GNOME ||
        r == RACIAL_TYPE_HALFELF || r == RACIAL_TYPE_HALFLING ||
        r == RACIAL_TYPE_HUMAN ||
        r == RACIAL_TYPE_DRAGON || r == RACIAL_TYPE_GIANT ||
        r == RACIAL_TYPE_HALFORC || r == RACIAL_TYPE_HUMANOID_GOBLINOID ||
        r == RACIAL_TYPE_HUMANOID_ORC || r == RACIAL_TYPE_HUMANOID_REPTILIAN )
        return 1;
    return 0;
}

void sayChoice( int chanceToSay, string dumb, string s1, string s2, string s3, string s4, object who = OBJECT_SELF ) {

    if( isSleeping( who ) ) {
        return;
    }

    if( !getIsSentient( who ) )
        return;

    if( d100() >= chanceToSay )
        return;

    string s = dumb;
    if( GetAbilityScore( who, ABILITY_INTELLIGENCE ) >= 6 ) {
        switch( d4() ) {
            case 1: s = s1; break;
            case 2: s = s2; break;
            case 3: s = s3; break;
            case 4: s = s4; break;
        }
    }
    if( who != OBJECT_SELF )
        AssignCommand( who, SpeakString(s) );
    else
        SpeakString( s );
}


void returnToPostWhenNotInCombat() {
    debugSay( "returning to post when not in combat." );

    if( !GetIsObjectValid( OBJECT_SELF ) )
        return;

    if( GetIsInCombat() ) {
        DelayCommand( 6.0, returnToPostWhenNotInCombat() );
        return;
    }

    // Now return to your post.
    object preGongPost = GetLocalObject( OBJECT_SELF, "tsPreGongPost" );
    if( GetIsObjectValid( preGongPost ) ) {
        debugSay( "attempting to return to my post." );
        moveToObject( preGongPost, FALSE );
        DestroyObject( preGongPost, 7.0 );
    }
    else {
        debugSay( "no post to return to." );
    }
    DeleteLocalObject( OBJECT_SELF, "tsPreGongPost" );
    DeleteLocalInt( OBJECT_SELF, "tsGongResponse" );
}

int testFight( object who = OBJECT_SELF ) {
    object enemy = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, who, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
    tell( GetName(who)+" test fight. Found="+GetName(enemy) );
    if( GetIsObjectValid(enemy) ) {
        ActionMoveToObject( enemy, TRUE );
        ActionAttack( enemy );
        DelayCommand( 24.0, returnToPostWhenNotInCombat() );
        return 1;
    }

    return 0;
}

void reactToVisitingGong( object gong ) {
    if( !GetIsObjectValid( OBJECT_SELF ) )
        return;
    // When I reach the gong, what do I do or say?  If I get there and don't
    // end up in combat, maybe I won't be fooled the next time.

    int gongReactionTicks = GetLocalInt( OBJECT_SELF, "tsGongReactionTicks" );
    if( gongReactionTicks >= 3 ) {
        // If I didn't get to the gong within 18 seconds, then
        // forget about reacting.
        debugSay( "I give up. I'm going back to my post." );
        DeleteLocalInt( OBJECT_SELF, "tsGongReactionTicks" );
        if( !testFight() )
            returnToPostWhenNotInCombat();
        return;
    }
    SetLocalInt( OBJECT_SELF, "tsGongReactionTicks", gongReactionTicks+1 );

    tell( "Current action = "+IntToString(GetCurrentAction()) );
    if( GetCurrentAction() == ACTION_MOVETOPOINT || GetDistanceBetween( OBJECT_SELF, gong ) > 5.0 ) {
        // We aren't close enough yet, so keep trying.
        debugSay( "Still moving to the gong." );
        if( !testFight() )
            DelayCommand( 6.0, reactToVisitingGong( gong ) );
        return;
    }

    DeleteLocalInt( OBJECT_SELF, "tsGongReactionTicks" );
    debugSay( "reacting to my visit to the gong." );
    SetBehaviorState( NW_FLAG_BEHAVIOR_SPECIAL, FALSE );

    // OK, we got to the gong. What did we find?
    if( testFight() ) {
        sayChoice( 70,
            "You make noise!",
            "So, it was you who made the noise!",
            "You gave yourself away!",
            "Striking that gong was unwise!",
            "That noise will cost you your life!"
        );
        return;
    }

    sayChoice( 70,
        "Uhhh, nobody here?",
        "I thought I heard...",
        "That is strange...",
        "I guess it was the wind.",
        "Something suspicious is going on."
    );

    DelayCommand( TS_SECONDS_TO_RETURN_TO_POST, returnToPostWhenNotInCombat() );
}

void gongResponse() {
    if( !GetIsObjectValid( OBJECT_SELF ) )
        return;

    if( isBusy() ) {
        debugSay( "too busy to respond to that gong." );
        return;
    }

    SetBehaviorState( NW_FLAG_BEHAVIOR_SPECIAL, TRUE );
    removeEffectType( OBJECT_SELF, EFFECT_TYPE_SLEEP );

    debugSay( "responding to a gong." );

    ClearAllActions();
    sayChoice( 70,
        "Duh, what?",
        "What was that?",
        "An alarm!",
        "I heard a gong!",
        "I'm checking that noise out."
    );

    // Store where we are standing so we can return here if we find nothing near
    // the gong.
    object preGongPost = GetLocalObject( OBJECT_SELF, "tsPreGongPost" );
    if( !GetIsObjectValid( preGongPost ) ) {
        preGongPost = CreateObject( OBJECT_TYPE_PLACEABLE, "plc_invisobj", GetLocation(OBJECT_SELF) );
        if( !GetIsObjectValid(preGongPost) )
            tell( "ERROR: unable to create invisible object to return to." );
        SetLocalObject( OBJECT_SELF, "tsPreGongPost", preGongPost );
    }

    object gong = GetLocalObject( OBJECT_SELF, "tsGongIHeard" );
    moveToObject( gong, TRUE );

    DelayCommand( 6.0, reactToVisitingGong( gong ) );

    DeleteLocalObject( OBJECT_SELF, "tsGongIHeard" );
}

 

 

 


 



#2
rjshae

rjshae
  • Members
  • 4 506 messages

Can you tell us what calls the gongResponse() routine?

 

I wonder how well just a shout to hostile creatures would work? I've never experimented with that mechanic.



#3
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

I was looking for a script that runs everytime the PC successfully hits an indestructable gong.
 
I want the enemy who is blocking the path to an objective hear the gong and go to the gong/waypoint to investigate the sound.
 
I have found one script that I have not been able to implement. As much as I hate to admit it, it confuses me.


Hi Friar,

That appears to be an amazingly overly complicated script to do what you are trying to do .... I am not surprised you are confused. I also copied and pasted the script into my module to take a closer look at it, and it does not compile either ... but that is probably just due to missing includes at the top of the script.

That all said, let's look at your premise and work simply from that:- "I was looking for a script that runs everytime the PC successfully hits an indestructable gong. I want the enemy who is blocking the path to an objective hear the gong and go to the gong/waypoint to investigate the sound."

Rather than immediately simply write the script for you, I would recommend having a go yourself first, even if it means posting what would appear a lot of "garbage". :) To give you a little guidance, however, note these points:-

1) Indestructible gong ... This should be easy enough for you to create in the toolset. a) Look for the gong object you are after. Place it within the area you wish to have it, and adjust the placed object's properties to ensure that it is "plot", which makes it indestructible. You will also need to make sure it is "Useable" so that it can be interacted with by the player.

2) Now, with respect to what the player is actually doing to the gong determines which "hooks" you are going to need to attach your scripts to for this object. Do you really mean "when the player hits the gong" or do you mean "when the player uses the gong". You may actually mean both in this instance, in which case, two scripts (or a combined one that accounts for both using and hitting) may be appropriate. For the time being, let me just assume you do mean "hit". Therefore, the script hook you need to work with is the OnMeleeAttacked hook. Therefore, this is where you need to place your script once it has been written.

3) Let's assume you call your script, "alb_gong_hit" ... That's just the format I would use, as it uses a combination of my initials and makes obvious what it does. These are the main points you need to include when writing the script (according to what you have requested):-

a ) The object (oPC) who is hitting the gong. (Actually, you may not even need to define this in your case.)
B ) The object (creature) who you want to respond to the gong.
c ) The object of the gong itself possibly as well. (So the creature knows where you are guiding it to.)

4) With all this information in mind, you can now construct your script to react (run) according to what the PC does when they "hit" the gong in question. To get you started, here is a quick outline of the start for that script:-
 
void main()
{
    object oPC = GetLastAttacker(); // This line defines who is hitting the gong itself. (MAY NOT EVEN NEED THIS)
    object oCreature = GetObjectByTag("YourCreatureTagHere"); // The creature you want distracted.
    object oGong = OBJECT_SELF;

    // NOW HAVE A GO AT FINISHING THE SCRIPT THAT GOES HERE
    // CLUE: IT INVOLVES USING A FUNCTION THAT FORCES THE CREATURE TO MOVE TO THE GONG

}
All the best ... Give it a go and I will see how you are doing. (As I am sure others will be keen to do as well.)

EDIT: You will later need to consider what happens if the PC keeps hitting the gong after the creature has already started to move.

Cheers,
Lance

#4
Friar

Friar
  • Members
  • 293 messages

You have been very helpful Lance, a number of times, and I appreciate that!

 

I do mean "hit."

I'd like the player to use the slingshot their character constructed to snap the gong from a distance. If they get too close to the gong and hit it, then it could mean trouble. Whereas, if they are at a distance they should be able to slip by unscathed. They have to move kind of quickly though or else the creature will return to it's favorite spot.

 

A very classic puzzle for seasoned roleplayers, but one I want to include anyway.

 

I am going to work with what you got me started with. I'm glad to read that it doesn't have to be as complicated as the earlier script.



#5
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

You have been very helpful Lance, a number of times, and I appreciate that!
 
I do mean "hit."
I'd like the player to use the slingshot their character constructed to snap the gong from a distance. If they get too close to the gong and hit it, then it could mean trouble. Whereas, if they are at a distance they should be able to slip by unscathed. They have to move kind of quickly though or else the creature will return to it's favorite spot.
 
A very classic puzzle for seasoned roleplayers, but one I want to include anyway.
 
I am going to work with what you got me started with. I'm glad to read that it doesn't have to be as complicated as the earlier script.


Hi Friar,

You are welcome ... I like the old gong classic puzzle as well. :)

In theory, I don't see any reason why you should not have the script up and running with only one or two more lines. :)

I will check back later for you.
Lance.

#6
Friar

Friar
  • Members
  • 293 messages

Okay we did it!

Reminding me to simplify it helped my mind calm down.

Once I managed to do that I started to focus only on things I wanted to do rather scatter my thoughts on all the possiblities I could do.

Here is the code I decided to go with. I might need to put more delays in it since some of the creatures timing isn't exactly where I want it.

void main()
{

object oPC = GetLastAttacker();

if (!GetIsPC(oPC)) return;

AssignCommand(GetObjectByTag("01_medusa"), ActionSpeakString("Sssomething disturbs my sssolitude!"));

object oTarget;
oTarget = GetObjectByTag("01_medusa");

AssignCommand(oTarget, ActionMoveToObject(GetObjectByTag("tsGongResponse")));

DelayCommand(80.0, AssignCommand(GetObjectByTag("01_medusa"), ActionSpeakString("Ssome clever creature plans to be my next meal.")));

AssignCommand(oTarget, ActionMoveToObject(GetObjectByTag("tsPreGongPost")));

}



#7
Dann-J

Dann-J
  • Members
  • 3 161 messages

Another way to accomplish this is to have two waypoints using different naming conventions; one in the creature's usual hangout, and another near the gong. Then have the gong's OnDamage script change the set of walk waypoints the creature uses, via the function SetWWPController(). You might also have to stop and start the creature using walk waypoints to initiate the new waypoint set. Perhaps something like:

 

AssignCommand(oCreature, SetWalkCondition(NW_WALK_FLAG_PAUSED, TRUE));

SetWWPController(sNewWaypointTag, oCreature);

DelayCommand(0.1, AssignCommand(oCreature, SetWalkCondition(NW_WALK_FLAG_PAUSED, FALSE)));

 

The default creature heartbeat script would then take care of the rest. A trigger around the gong could change the walk waypoints back again, so the creature would then walk back to its usual hangout. The trigger could also have it pause for a while, and perhaps look around or make a comment.

 

See also:

http://forum.bioware...ypoints-repost/



#8
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

Okay we did it!
Reminding me to simplify it helped my mind calm down.
Once I managed to do that I started to focus only on things I wanted to do rather scatter my thoughts on all the possiblities I could do.
Here is the code I decided to go with. I might need to put more delays in it since some of the creatures timing isn't exactly where I want it. <CODE SNIPPED>


Hi Friar,

Well done for giving this a go ... you will accomplish so much more when you master scripting. :)

For your reference, here is your script rewritten by me with some added notes. Take note of the differences in structure, especially how to define the variables in the first place, which prevents having to keep redefining the same object throughout the script.

After the script, I have added some comments for you to be aware of.
 
void main()
{
	// DEFINE VARIABLES WE ARE GOING TO USE HERE. NOTE THAT YOU CAN PREDEFINE THE VARIABLES 
	// AT THE TOP OF THE SCRIPT TO PREVENT KEEP HAVING TO DO IT AGAIN LATER. ALSO AS THE 
	// SCRIPT IS ATTACHED TO THE GONG, THEN WE CAN USE OBJECT_SELF TO DEFINE THEGONG OBJECT HERE.
	
	object oPC = GetLastAttacker();	
	object oMedusa = GetObjectByTag("01_medusa");
	object oGong = OBJECT_SELF;	// I HAVE ASSUMED "tsGongResponse" IS THE GONG ITSELF HERE.	
	
	// NOTE THAT WE CAN GRAB THE START LOCATION FOR THE MEDUSA THIS WAY AS WELL.
	// THIS WAY WE CAN RELY ON THE INFORMATION DYNAMICALLY. I.E. DONT HAVE TO SPECIFY POST POINTS.
	location lStartPost = GetLocation(oMedusa);
	
	// ONLY WORKS FOR PCS
	if (!GetIsPC(oPC)) return;
	
	// NOW WE CAN TELL WHAT THE MEDUSA (DEFINED ABOVE) IS GOING TO DO:-
	
	// FIRST SAY SOMETHING ...
	AssignCommand(oMedusa, ActionSpeakString("Sssomething disturbs my sssolitude!"));	
	
	// NEXT, MOVE TO THE GONG (CONSIDER USING ActionForceMoveToObject FUNCTION HERE INSTEAD)
	AssignCommand(oMedusa, ActionMoveToObject(oGong));
	
	// NOW SAYING SOMETHING ELSE 80 SECONDS LATER.
	DelayCommand(80.0, AssignCommand(oMedusa, ActionSpeakString("Ssome clever creature plans to be my next meal.")));
	
	// NOW RETURN MEDUSA TO THEIR STARTING LOCATION
	AssignCommand(oMedusa, ActionMoveToLocation(lStartPost));
}
Important things to note about this script:-

1) It assumes only one "medusa" of the given TAG, and works only with that one.
2) The strings spoken are using TALK levels and may or may not give the desired effect.
3) Furthermore, the medusa responds immediately - talk about lightning response. ;)
4) There is a small risk of the medusa getting stuck on walking to gong if not checked carefully. Walkmesh issues.
5) The last response (80 seconds) appears quite a long delay. Is this due to a long walk ... or simply just another comment later?
6) No checks are made for multiple hitting the gong.
7) What happens if the medusa is attacked before reaching gong and finishing their "speeches"?
8) In my code comments, I give the impression that the medusa events occur as they are written in the code, which is not true strictly speaking, as DelayCommands affect the timing obviously, but I hope you understand what I am trying to get at.

As DannJ points out, there are other ways to handle speeches and comments, and trigger responses is probably the easiest to work with. Personally, I found some problems with WalkWayPoints (unreliable at times) and rewrote the entire set of scripts that handle that to be more reliable.

Great to see you getting going with this though ... and the best thing to do is to start with a simple script (get it to do the basics of what you need it to do) and then consider (do not have to do necessarily) the other considerations that impact that script as additional code later.

All the best,
Lance.

#9
ColorsFade

ColorsFade
  • Members
  • 1 271 messages

Just going to chime in here. I know I'm late to this particular party, but someone might find this helpful. 

 

If this were me, I'd make use of a custom AI script for the creature, and a separate script for the gong hit. 

 

You can use the X2_SPECIAL_COMBAT_AI_SCRIPT variable on a creature to assign it a custom AI script. This script can execute whatever logic you want for the creature (barkstrings, movement, etc.), an then it can fall back to the creature's default AI if conditions are not appropriate. 

 

So, for example, you could have a script for the gong that, when it is struck, it assigns a value to a local variable on the area (or the creature, or whatever you want to check). 

 

The creature's AI could be written in such a way that it checks for this variable's value. If it's true, it runs it's custom AI, meaning it walks to a waypoint, issues a bark, etc. 

 

If the variable is false, then the creature's default AI controls its behavior (so if it's walking a waypoint string, it should continue doing that). 

 

You could then have the creature switch the variable back to false after a delay, or you could use an area HB to switch the variable back to false after X turns. Either way, when the variable is false, the custom AI script doesn't run, and returns false itself, to let the default AI know to take over. 

 

With such a custom AI script and various variables, you could do pretty much whatever you want. You could really fine-tune the creature's behavior to suit your needs. 



#10
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

Just going to chime in here. I know I'm late to this particular party, but someone might find this helpful. 
 
If this were me, I'd make use of a custom AI script for the creature, and a separate script for the gong hit. 
 
You can use the X2_SPECIAL_COMBAT_AI_SCRIPT variable on a creature to assign it a custom AI script. This script can execute whatever logic you want for the creature (barkstrings, movement, etc.), an then it can fall back to the creature's default AI if conditions are not appropriate. 
 
So, for example, you could have a script for the gong that, when it is struck, it assigns a value to a local variable on the area (or the creature, or whatever you want to check). 
 
The creature's AI could be written in such a way that it checks for this variable's value. If it's true, it runs it's custom AI, meaning it walks to a waypoint, issues a bark, etc. 
 
If the variable is false, then the creature's default AI controls its behavior (so if it's walking a waypoint string, it should continue doing that). 
 
You could then have the creature switch the variable back to false after a delay, or you could use an area HB to switch the variable back to false after X turns. Either way, when the variable is false, the custom AI script doesn't run, and returns false itself, to let the default AI know to take over. 
 
With such a custom AI script and various variables, you could do pretty much whatever you want. You could really fine-tune the creature's behavior to suit your needs.


Hi ColoursFade,

That seems to be a complicated way of doing it ... and, personally, I think the AI script is better reserved for the "COMBAT" situation, which its variable holder name alludes to.

That said, I suppose the bottom line is to go with whatever method you feel most comfortable with and works well. :)

Cheers,
Lance.