Force NPC to hit placeable

How do you do this? There must be a way…I tried making to two new factions that see each other as enemies, and let the placeable be one faction and the NPC the other. Then I went with a script like this, but it didn’t work:

#include "nw_i0_generic"

void main()
{

object oDummy = GetObjectByTag("dummyo");

object oNPC = GetObjectByTag("npc");

SetIsTemporaryEnemy(oNPC, oDummy);
AssignCommand(oNPC, ActionAttack(oDummy));
AssignCommand(oNPC, DetermineCombatRound(oDummy));

}

So, I found a function that maybe solves this: DoPlaceableObjectAction
I’ll try that out (the script is much longer in reality, but these are the important parts I’ve posted here).

EDIT: Tried it, but that didn’t work either:

void main()
{

object oDummy = GetObjectByTag("dummyo");
object oNPC = GetObjectByTag("npc");

DelayCommand(0.5, AssignCommand(oNPC, DoPlaceableObjectAction(oDummy,PLACEABLE_ACTION_BASH)));


}

EDIT2: Upon further examination it actually kind of works, but only once, and there is no sound when hitting the placeable. I would have liked the NPC to bash repeatedly on the placeable. Must I do that on the heartbeat script of the character or is there some other way?

EDIT3: So it almost works now. I placed a simple script upon the heartbeat of the placeable, however, I’d like it to be faster than every 6th second. I have some vague memory of Aqvilinus and @kevL_s speaking about pseudo heartbeat, but I can’t remember how you coded something like that…

Here’s the heartbeat script at the moment:

void main()
{

object oDummy = GetObjectByTag("dummyo");
object oNPC = GetObjectByTag("npc");

AssignCommand(oNPC, DoPlaceableObjectAction(oDummy,PLACEABLE_ACTION_BASH));


}

I found an old thread speaking about pseudoheartbeats. I’ll try this now:

void PseudoHeartbeat()
{

object oDummy = GetObjectByTag("dummyo");
object oNPC = GetObjectByTag("npc");

AssignCommand(oNPC, DoPlaceableObjectAction(oDummy,PLACEABLE_ACTION_BASH));


}


void main()
{
	PseudoHeartbeat();	
	DelayCommand(1.0f, PseudoHeartbeat());
	DelayCommand(2.0f, PseudoHeartbeat());
	DelayCommand(3.0f, PseudoHeartbeat());
	DelayCommand(4.0f, PseudoHeartbeat());
	DelayCommand(5.0f, PseudoHeartbeat());	
}

Since I still think he’s not bashing fast enough, so I did it like this, but that didn’t increase the speed… Maybe it’s some limitiation of sorts…

void PseudoHeartbeat()
{

object oDummy = GetObjectByTag("dummyo");
object oNPC = GetObjectByTag("npc");

AssignCommand(oNPC, DoPlaceableObjectAction(oDummy,PLACEABLE_ACTION_BASH));


}


void main()

	PseudoHeartbeat();	
	DelayCommand(0.5f, PseudoHeartbeat());
	DelayCommand(1.0f, PseudoHeartbeat());
	DelayCommand(1.5f, PseudoHeartbeat());
	DelayCommand(2.0f, PseudoHeartbeat());
	DelayCommand(2.5f, PseudoHeartbeat());	
	DelayCommand(3.0f, PseudoHeartbeat());	
	DelayCommand(3.5f, PseudoHeartbeat());	
	DelayCommand(4.0f, PseudoHeartbeat());	
	DelayCommand(4.5f, PseudoHeartbeat());	
	DelayCommand(5.0f, PseudoHeartbeat());

}

A pseudo Hb does not have to start from a Hb. You can have it start on a first hit of a placeable. Just make sure you only start it once. So starting one for every 2 seconds will make a pc strike 3 times a round.

Start it from any event.

I’m not following what you are trying to say here. Have I done it wrong somehow you mean?
I want it to continue until eternity until I decide to shut it off, therefore I think I need to have this on a heartbeat script. I’m confused. And I would like to at least bash it 5 times every round, but the NPC doesn’t seem to do that. That’s what I meant by “Maybe it’s some limitiation of sorts…”.
And no answer to this question yet, which would have been nice:

What do you mean by:

I’m making the assumption you are just making some eye-candy animation of someone hacking at some object, but don’t want to actually destroy the object. If that is the case, you could try these:

This will haste the npc. Though, not 5 swings a round. Just set the object to have huge current HP like 99999 or something. Don’t run it from the heartbeat. Use a trigger or a convo, or something.

void main()
{
	object oNPC = GetObjectByTag("npc");	
	object oDummy = GetObjectByTag("dummyo");		

	ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectHaste(), oNPC);
	AssignCommand(oNPC, DoPlaceableObjectAction(oDummy, PLACEABLE_ACTION_BASH));		
}

Or, if you just need a rapid animation of attacking…

void DoAnimation(object oNPC)
{
	PlayCustomAnimation(oNPC, "*1attack01", 1);
}

void main()
{
	object oNPC = GetObjectByTag("npc");	
	object oDummy = GetObjectByTag("dummyo");		

	AssignCommand(oNPC, ClearAllActions());	
	DelayCommand(0.0f, DoAnimation(oNPC));	
} 
2 Likes

@andgalf

A pseudo Hb is usually something you setup independently from the real Hb .

It’s basically just a script that calls itself to a determined time delay. When doing this, you have to make sure you don’t initiate the pseudo Hb more than once or you will overload your code by too many instances of the pseudo Hb running at any one time.

Yes, there is still going to be a limit due to animation speed, which is why @travus suggested using haste as well. To ensure the animations executed fast enough .

If that still does not explain it, I’ll write a demo next week, if someone does not do so in the meantime.

Have a good weekend. :zzz:

2 Likes

dont ask how long this took :|

bugs:
- SetFacing() can fail
- if the NPC is stopped on its way to the target, its bumpstate and orient-on-dialog doesn’t get reset

// 'lever_ou'
/*
	Lever OnUsed starts or stops a dwarf bashing an anvil.

	Tags
		dwarf = "dwarf_at_anvil"
		anvil = "anvil"

	var
		"dobash" held on the Dwarf-object - TRUE to bash FALSE to stop
*/

void bash_phb(object o, object oTarget);
void bash_startvars(object o);
void bash_stopvars(object o);


//
void main()
{
	// take care of the lever animation ->
	// NOTE: The only important thing here is the state of 'bStart'

	int bStart = !GetLocalInt(OBJECT_SELF, "X2_L_PLC_ACTIVATED_STATE");
	if (bStart)
	{
		ActionPlayAnimation(ANIMATION_PLACEABLE_ACTIVATE);
	}
	else
		ActionPlayAnimation(ANIMATION_PLACEABLE_DEACTIVATE);

	SetLocalInt(OBJECT_SELF, "X2_L_PLC_ACTIVATED_STATE", bStart);


	// dwarf's bash behavior ->
	object o = GetNearestObjectByTag("dwarf_at_anvil");
	if (GetIsObjectValid(o))
	{
		object oTarget;

		if (bStart
			&& !GetIsInCombat(o)
			&& !IsInConversation(o)
			&& GetCurrentAction(o) == ACTION_INVALID
			&& GetIsObjectValid(oTarget = GetNearestObjectByTag("anvil")))
		{
			bash_startvars(o);

			object oWp = GetNearestObjectByTag("wp_anvil"); // a waypoint for the dwarf to stand at
			AssignCommand(o, ActionForceMoveToObject(oWp, FALSE, 0.f));
			AssignCommand(o, ActionDoCommand(SetFacing(GetFacing(oWp))));
			AssignCommand(o, ActionDoCommand(DelayCommand(0.5f, bash_phb(o, oTarget))));
		}
		else
			bash_stopvars(o);
	}
}


// recursive funct run on the Dwarf
void bash_phb(object o, object oTarget)
{
	if (GetLocalInt(o, "dobash"))
	{
		if (GetIsObjectValid(oTarget)
			&& !GetIsInCombat(o)
			&& !IsInConversation(o)
			&& GetCurrentAction(o) == ACTION_INVALID)
		{
			PlayCustomAnimation(o, "*1attack01", FALSE, 2.f);
			AssignCommand(oTarget, DelayCommand(0.4f, PlaySound("cb_ht_bldplat04"))); // *clank
			DelayCommand(0.8f, bash_phb(o, oTarget)); // recurse
		}
		else
			bash_stopvars(o);
	}
}

void bash_startvars(object o)
{
	SetLocalInt(o, "dobash", TRUE);
	SetBumpState(o, BUMPSTATE_UNBUMPABLE);
	SetOrientOnDialog(o, FALSE); // you might not want this if NPC has a dialog (a bark is ok).
}

void bash_stopvars(object o)
{
	SetLocalInt(o, "dobash", FALSE);
	SetBumpState(o, BUMPSTATE_DEFAULT);
	SetOrientOnDialog(o, TRUE);
}
3 Likes

Thanks for all the answers!

Exactly! Looking at your scripts the second one with doing it as an animation sounds like the easiest way to do it. Though I would really like the sound of hitting the object, which I think would be absent if I’m using just an animation.

Don’t know if using haste will look odd. Maybe I don’t want the bashing to be too rapid (not sure until I see it) but I want it to be more rapid than it is at the moment, at least.

Ok, this makes sense, I guess. I’m just thinking that if I didn’t have the script I’m using at the moment on a heartbeat script of an object, it would just do the animation 5 times (if doing it like my first script) and then just stop. That’s why I don’t get how it could only be set up once if it’s to continue as a loop.

@kevL_s - Your script looks pretty advanced and I would have to take out the stuff I don’t need there (like the lever stuff), but I’m sure that can be done, of course. I’ll just have to read it through a bunch of times to really understand what you’re doing in the script.

In my situation I spawn in this character directly at the object and then the bashing is supposed to begin until it is interupted sometimes during a dialogue with another character.

@kevL_s - After quite a bit of work I managed to incorporate your script into mine. I ended up using @travus suggestion of effect Haste. It’s looking quite good, and it definitely needed Haste, although sometimes the NPC pauses and just looks at the target before bashing again for some reason. I’d rather that wasn’t happening, but I think this is a limitation in NWN2. There’s so much that can intervene with an animation so…and this is not a cutscene.

Anyway, as usual, thanks for all the help @kevL_s, @travus, @Lance_Botelle!

EDIT: One thing that I don’t get is that the animation is set to not loop. How then can this animation be looped? I mean he continues to bash…
Is it because of this code perhaps that it loops: DelayCommand(0.8f, bash_phb(o, oTarget)); // recurse
OK, it seems that way. I didn’t know what “recurse” meant since english is not my mother tounge.

EDIT2: And now all of the sudden nothing works…I hate NWN2 sometimes… :unamused:

EDIT3: Alright, I have made a big mistake in my latest script. Now it works and the Haste thing really shows and it’s in fact a bit too much. I’ll remove that…

2 Likes

recurse means to make reference to itself. In code, a function recurses if it calls itself.

in nwn/2 we call a delayed recursion a pseudo-heartbeat …

1 Like

In mathematics, the function factorial is a nice example of the principle.

2 Likes

Also what happens when the witch’s first curse doesn’t stick.

2 Likes

not to be confused with an iterative function

 
(took me forever to get the difference between recursive and iterative)

Here’s a link to a page that explains in simple language the difference between them with examples in javascript and vb script (still used in macros for ms office software).

TR

1 Like

a common example of recursion in the .net framework, with controls inside controls inside controls … is say you want to turn all button-controls blue on some event. Iterate through the toplevel controls with a function that checks if each control has a subcontrol; if it does, recurse the function by passing in any subcontrol as a new ‘toplevel’ control, to check if that is a container of other controls, and so on down the line …

 
buttons all the way down … /facepalm

1 Like

Exhausted perhaps? :wink: I think even the best fighter would occasionally “take a small break” before bashing again. If anything, this sounds more natural to me.

@kevL_s

Thanks for doing the actual script work. I liked the lever animation tick-tock approach.

1 Like

@Lance_Botelle - I did something wrong in the script before when I wrote that, so now everything’s fine. I ended up using DelayCommand(1.2f, bash_phb(oNPC, oDummy)); in kevL_s’ script. That was quite perfect in my opinion. With @kevL_s script I feel like I now have total control over the situation which I really like.

I also think I finally understand now the thing about psuedo heartbeat script that got me so confused. My first script was “wrong” since it only repeated the script/function about five times (and that’s why I needed to put it on the heartbeat of a character/object for it to work), but with kevL_s’ script I saw that when the function is run, it again runs itself at the end (and that’s what I think one means with a pseudo heartbeat) , and that’s why it becomes a loop, and why it is only needed to be started one time. That’s what I tried asking about before, but never felt got explained to me, probably because people didn’t understand what I was asking about.

Reading again what @Lance_Botelle said here:

I understand now that he was trying to explain what I just said here in this post. Sometimes when using certain words, and speaking about code, I have a hard time understanding (english not being my mother tongue and all that), and I’ve realized quite many times that I don’t think like a programmer does…at all.
The thing about “calling itself” is apparent to me now, but if that had been worded differently, I would have got it immediately, I think.

3 Likes