Blind monster

So I’ve got this cave where there’s supposed to be a blind monster that you are supposed to sneak by without it noticing you. If failing everyone is supposed to die, it’s game over so to speak.Any ideas for how to set this thing up? How do you make an NPC like this permanently blind? Do you cast some effect on it and put it on the OnSpawn of the character, or do you modify OnPerception somewhat?
I could do some triggers in the cave that just checks against a dice throw if you succeed on being hidden and unnoticed, but I wondered if there’s a better way of going about it that’s more…eh…dynamic, or something, that more depends on where the creature is and where the PC is, and if the monster notices you.

Any thoughts on how to best approach this?

What if I put a script like this on the OnClientEnter of the area? Would that be enough?

void main()
{

object oPC = GetEnteringObject();

if(!GetIsPC(oPC)) return;

object oMonster = GetObjectByTag("monster");

effect eBlind = EffectBlindness();

ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBlind, oMonster );


}

andgalf… Set it’s perception range to short and make sure it’s skills are crap at spot that’ll make it blind. This way it’ll only attack if you get too close and the game’s sneaky code will do the rest.

1 Like

You are on uncharted territory, the only way to know if it’s enought or not is to test it.

If it’s not enought I am afraid you’ ll have to edit the script of that creature. or completly cheat this with a trigger.

1 Like

EffectBlindness(). I’m not sure if that handles the Spot skill penalty.

andgalf… Shallina’s given me an idea but it’s not a complete cheat. Make a safe path with things blocking the line of sight like rocks etc and moss on the ground to stop noise then put twigs and other things like litter elsewhere ( noisy things ). Make a speak trigger that fires an “Oh bugger it’s heard us” conversation along the outside of the safe path if the player strays on it and turn the creature hostile from that.

One thing it will do is stop people just attacking the hostile creature from the start and getting angry when they can’t kill it.

1 Like

I think I’ll go with your first suggestion @Tsongo. If that won’t work I’ll go with @Shallina 's way and just do it like I did in my unreleased module where I just had a bunch of triggers.

I did a bit of testing. When using the script that I posted here, together with Tsongo’s suggestion of making perseption range short and having the spot skill at 0, you could stand next to the monster and it wouldn’t notice a thing, so that was too easy for the PC. However, when removing the script I found it pretty satisfying. The thing that I think I need to do now is somehow modify (or rather make a new version of) the default perseption script since I want the PC to die if he fails to sneak by the monster. Even if the monster is at level 25 at the moment and the PC and the party are roughly level 4, I know that in my first module I thought I had made an impossible fight, and I still got reports that players had managed to defeat the adversaries, so that can’t happen in this case.

EDIT: I modified the default perception script which was pretty easy it turned out, with just adding a ShowProperDeathScreen if one fails. This quest might be a bit easy, but I’m afraid that if I put the perception range to anything other than short it might be too hard instead. I don’t know. Maybe I’ll do some more testing. I want to force the player to use stealth mode so…

EDIT: Changed the perception to medium since I found that short made it a bit too easy. It’s still not hard now but adequate I think for the situation.

1 Like

I have a new situation that is related to this, so instead of starting a new thread I’ll just continue here.

In this cave area I have a trigger. The trigger triggers a conversation. In that conversation the companions are teleported to a waypoint, and then the PC (or a certain companion) does the quest on his/her own.

My problem is this: If I, after this teleporting, walk a bit with the controlled companion or the PC (the one that’s supposed to do the quest) and then save the game, when I reload the whole party is gathered where my controlled companion or PC was. To prevent this I put a script on the OnClientEnter of the area. However, this script doesn’t seem to run for some unexplicable reason (or maybe it runs, I get no messages and I don’t know where to look for the PrintString messages that’s supposed to be in a log file somewhere I can’t find). I don’t get it. Here’s the OnClientEnter script:

void TeleportPCCompanions();

void TeleportCompanions();

void main()
{

object oPC = GetEnteringObject();

object oPC1 = GetFirstPC();

if(!GetIsPC(oPC)) return;

object oMathias = GetObjectByTag("mathias");


	if(GetGlobalInt("Donewcavtlk") && GetGlobalInt("mathiasclimb"))
	{

	SetOwnersControlledCompanion(oPC1,oMathias);
	DelayCommand(0.3,TeleportPCCompanions());

	}
	 
	else if(GetGlobalInt("Donewcavtlk") && GetGlobalInt("pcclimb"))
	{

	DelayCommand(0.3,TeleportCompanions());
	
    }

}





void TeleportPCCompanions()
{

	object oPC1 = GetFirstPC();
	object oMathias = GetObjectByTag("mathias");	
  	object oDestParty = GetWaypointByTag("partywait_wp");
	object oDestMathias = GetWaypointByTag("wpmathcave");

    object oFaction = GetFirstFactionMember(oPC1, FALSE);
    	while (GetIsObjectValid(oFaction))
    	{
		
			if(oFaction == oMathias)
			{
        	AssignCommand(oFaction, ClearAllActions());
        	AssignCommand(oFaction, JumpToObject(oDestMathias));
			
			}
			else
			{
        	AssignCommand(oFaction, ClearAllActions());
        	AssignCommand(oFaction, JumpToObject(oDestParty));
			
			}
			oFaction = GetNextFactionMember(oPC1, FALSE);
			
    	}

}

void TeleportCompanions()
{



	object oPC1 = GetFirstPC();
	

  		object oDestParty = GetWaypointByTag("partywait_wp");

    	object oFaction = GetFirstFactionMember(oPC1, FALSE);
    	while (GetIsObjectValid(oFaction))
    	{

		
			if(oFaction == oPC1)
			{
		
			oFaction = GetNextFactionMember(oPC1, FALSE);
		
			}
		
			else
			{
       	 	AssignCommand(oFaction, ClearAllActions());
        	AssignCommand(oFaction, JumpToObject(oDestParty));

        	oFaction = GetNextFactionMember(oPC1, FALSE);
			}
		}
}

EDIT: Now this is weird! I managed to solve this…by putting this script on the OnEnter of the area! Why does that work and not OnClientEnter? Everyone says you should use OnClientEnter rather than OnEnter but…well, if this works all of a sudden, I think I’ll have to use it here at least. I hope I don’t cause other bugs by doing so.

EDIT2: Added the last additions I made to the script before abandoning trying to fix this even more.

PrintString() prints here →

C:\Users\User\AppData\Local\Temp\NWN2\LOGS\nwclientLog1.txt

you might want the message to be more descriptive … there’s a lot of text in that file

 
re. OnClientEnter vs OnEnter … idk. but it should be stable as long as you got those lines

object oPC = GetEnteringObject();
if (!GetIsPC(oPC)) return;
1 Like

Ok. Since I have those lines, I’ll just have to use OnEnter since that’s the only thing that makes the script running when loading a savegame.

Another problem I noticed while doing more testing with my code, is that if I control a companion instead of the PC (the one called Mathias) then if I load a save, even if everyone else teleports to the waypoint, Mathias does that too, probably since he automatically (seems like the game is coded that way) teleports to the PC whenever you load a game. So I don’t quite know how to make the game first check where he is BEFORE teleporting to the PC…or maybe there’s something else going on here…Maybe if I use SetControllersCompanion (or whatever that function was called) it will work…I’ll have to do more testing tomorrow though. It’s goodnight for me now.

1 Like

I didn’t find a proper solution for this so I had to do it in a roundabout way. The companion Mathias won’t stand where you left him if you save the game and then reload, but instead teleport automatically to the PC.

The only way I found to solve this was just to put a waypoint somewhere in the middle of the area away from the monster, where I teleport Mathias at the same time as the others are teleported, but so you can still move him (the others are trapped behind a walkmesh cutter). SetOwnersControlledCompanion, which I also added in the script, just made the game choose him as your controlled character at the moment. It wouldn’t help with anything else when it came to teleporting. Hopefully the player won’t save in the middle of this scene, but you never know. If the player does that, the player will just have to be ok with this compromise with Mathias not being in the exact spot as when you saved. It will just have to do.

@kevL_s - Thanks for letting me know the location of the logfile. I would never have guessed it was under C: (even though many programs put stuff here, which I really don’t like) since all the other stuff are either in MyDocuments or the main game folder under Programs (in my case).

andgalf… I had the same issue with companions locked in cells and the PC had to sneak about to find the key. If you saved and reloaded they got out of jail without the key and joined the PC.

I admire your perseverance I just gave up and left it as it was !

2 Likes

@Tsongo - I just wish there was a way to fix this through scripting. I do all this stuff with custom scripts anyway. Maybe @Lance_Botelle would have a solution since he has really flipped the game on its head with all his custom stuff…but since others here like Shallina, kevL_s or travus hasn’t commented on this (as they are too very knowledgable to what’s possible with the NWN2 game) then maybe there just isn’t a real solution for this.

@andgalf oh there’s a solution alright no doubt about it. :)

but what the solution is, more exactly, isn’t obvious – there’s a bit of trial and error involved, then stress testing in situ

needless to say you’re in the best position to do that, m’friend

 
but Lance might have a routine that for example disbands and reforms the party …

ps. did you try jumping the non-actor party members after a short delay, from 1…3 seconds? Delay a function that both defines and jumps party members that shouldn’t be at the reload point. the delay is to give the game time to load all characters … and not try to fire everything up immediately onEnter

am thinking that not all characters will necessarily be instantiated yet when your script runs,

1 Like

@andgalf

Just picked up your post.

As @kevL_s said, I would consider disbanding the party, leaving just the main pc and the companion you wish to control to work out.

Also, it might be worth looking at the overland map code, which hides party members while player controls just one member on the map.

I have not had time to look at this to any degree yet, (for such a purpose), but I recall some functions to deal who was the current “actor”.

However, as you know, because I ensure my modules are MP compatible, this in an area I am not able to easily adopt. IE All players need to be involved and so isolating each player (within a specific companion) would be a headache.

I think SP PC isolation should be doable, but you may need to employ a means to store the pc location maybe every Hb, which you can recall via the on enter script.

Timing of events needs care, but it should be possible, and would require testing on various situations as others have said.

I may yet mess around with some code to see what can be done, but I cannot say when that may happen. I am curious as to what can be done now.

1 Like

i think i’d try something like this …

when the situation begins (ie. sneak past a monster using only 1 character in the party?)

id set a variable on the area or module that somehow defines who that actor is. I’d have waypoints in the area, one for every member that could be in the party. And in a delayed function that runs from OnEnter/OnClientEnter, send all members except that actor to their designated waypoint.

Note that in OnClientEnter, this checks is reload: if (!FiredFromPartyTransition()) (more or less, usually more than less but apparently it on depends how you’ve setup your area-transitions)

//RWT-OEI 02/23/06
//This function is for use in OnClientEnter scripts. It returns TRUE if the
//OnClientEnter script was fired because a party was moved into the area via
//the JumpPartyToArea script function.
//It returns false if OnClientEnter was fired from a single player being jumped
//into an area, a player logging into the area, or a normal, non-party transition.
//It is only valid during the execution of OnClientEnter, will return false
//at all other times.
int FiredFromPartyTransition();

Clear the variable when it’s not needed anymore, and that way it can also be used to check if this special onreload code should execute … along with the check for !FiredFromPartyTransition()

1 Like

Hmmm, it still sounds rather complicated. Yesterday I did some extended testing and now I think everything works ok, even if it’s not perfect. I have tried to screw it up on purpose with a conversation that should firing at a trigger and I tested loading and entering the area from all possible situations.

The way it works now is that I have a waypoint in the middle of the map (where the monster can’t notice the companion) and I have the companion teleport there whenever you load the area (but only if the quest with him going alone has started). The player might have gone somewhere else with him in the rather small map, but it’s such a minor nuissance that I think noone will complain that much. It’s not the “oh, darn, I have to do all that stuff all over again”, but more like “eh…but I was over here when I saved. Ah, ok, that’s no biggie, I guess.”

So the whole scene, and now we’re in spoiler territory, but who cares, so few play my modules anyway, goes like this:

  • The player and the party enter the area.
  • By just walking a few steps there’s a trigger on the ground checking if it’s the PC or the companion doing the quest. The trigger starts a conversation. When trying to mess this up on purpose, I noticed that the conversation would only trigger 75 % of the time, so I had to go with CreateIPSpeaker to be confident it would fire (which it does now, hasn’t failed me a single time yet).
  • This conversation teleports the whole party (except the one doing the quest) to a single waypoint near the door. This small area within the whole area has a walkmesh cutter preventing the party from going anywhere. The PC or the companion (the one doing the quest, which is determined earlier in the adventure) is teleported to another waypoint nearby where he can then move further in to sneak by the monster, which is shambling around, to get to a chest with an item that you need to procure. If the monster notices you, I’ve made it so the whole party gets a massive amount of damage so it’s game over.
  • If you reload from this point, having the one companion or PC ready to go on his own, I have a bunch of checks that you see in my script, checking if it is the companon or PC doing the quest, and if one of them has the item that is to be procured. When loading, the whole party is teleported back to the waypoint and the companion (if he’s the one doing the quest) is teleported to the middle-of-the-map-not-noticable-by-the-monster waypoint. If you control the PC, only the party is teleported since then the game behaves the way I want with the PC being where he’s supposed to be.
  • After sneaking by the monster and getting back, the same trigger I used before, checks for if anyone has the item, then starts the conversation again and the party automatically teleports outside the cave. Again, this conversation needed the CreateIPSpeaker to work. Even with CreateIPSpeaker I noticed a way to screw this up, by clicking on the exit before entering the trigger, and then switch control to somebody else in the party. Then the conversation won’t fire, even though I use SetCutsceneMode and CreateIPSpeaker, so I did a bullet proof thing with having the same conversation start if you click on the exit (this script checks if you have the item and has not yet spoken to the companions again).

So I think I have things working adequately now.

2 Likes

@andgalf

Do you leave the party portrait bar active (visible) allowing the player to still switch PCs?

Does it cause an issue or have you ensured player cannot switch during this time by selecting another PC?

Just curious.

Hi Andgalf,

I’m not 100% sure of what you want to achieve. I won’t comment about the blind effect, others have covered it or MP campaign specific issues (I know nothing about).

I did something like that for the Al Andalus’ murder quest in Alexandria (module AA_Fustat)

During the conversation with the quest giver (cv_fu_nabil):

1/ We remove/despawn the party members : ga_roster_party_remove_all(1, 1)
2/ We generate a temporary companion Madjouline according to the player’s choice and spawn her from the relevant blueprint:

  • She is an expert in hand to hand fighting [Monk].
  • She sneaks in everywhere without being spotted [Shadow Thief of Amn].
  • She prefers easily concealed light weapons and catching her enemy by surprise [Invisible Blade].

3/we add her to the party, a marker carried by the PC being set:

  • aa_add_companion(“madjouline”, 6)
  • ga_local_int(“Madjouline”, 1, $PC)

After some research, the PC and Madjouline find out that a NPC, Umm, is involved. Madjouline and the PC are ordered to spy on her. She can often be found at the hamman. Umm, tagged plot, carries the in hamman, mission in progress flag.

If the party enters the hamman while the mission is not in progress, full party, nothing specific happens, you can talk with the manager (Rachida), but can’t get to the baths.

If the mission is in progress the following script aa_fu_init_hamman is run. Basically, the PC is moved to a cell box (isolated), and control is given to Madjouline.

// init hammam, move PC, undress and move Madjouline, set her in stealthy mode, set Madjouline controlled.

void MoveTo(object oC, string sWP)
{
    AssignCommand(oC, ClearAllActions());
    AssignCommand(oC, ActionJumpToObject(GetWaypointByTag(sWP)));
}

void main()
{
    object oPC = GetFirstPC();
    object oMadjouline = GetObjectByTag("madjouline");
    object oUmm = GetObjectByTag("umm");
    SetLocalInt(oUmm, "Hammam", 1);
    
    SetOwnersControlledCompanion(GetFirstPC(FALSE), oMadjouline);
    MoveTo(oPC, "WP_PC_hammam");    
    object oSuit = GetItemInSlot(INVENTORY_SLOT_CHEST, oMadjouline);  AssignCommand(oMadjouline, ClearAllActions());
    AssignCommand(oMadjouline, ActionUnequipItem(oSuit));
    DelayCommand(0.1f, MoveTo(oMadjouline, "WP_madjouline_hammam"));
    DelayCommand(0.15f,SetActionMode(oMadjouline, ACTION_MODE_STEALTH, TRUE));
}

Madjouline must remain unnoticed, find Umm and hear a clue (the 4th line of the poem). Yes, pervs, the poet and the poem are historically true :star_struck:
The script aa_fu_umm (Umm’s Heartbeat) is used for that purpose :

// recurrent Umm - anim + specific

void main()
{
    object oUmm = OBJECT_SELF;
    if (!GetLocalInt(oUmm, "Hammam")) return;            // hammam mission not started or ended
    
    object oMadjouline = GetObjectByTag("madjouline");
    
    ClearAllActions();                                    // play animation    
    SetFacing(IntToFloat(GetLocalInt(oUmm,"Bearing")));
    PlayCustomAnimation(oUmm, GetLocalString(oUmm, "Anim"), 1, 1.0);
    
    if (!GetObjectSeen(oUmm, oMadjouline)) return;        // Madjouline can't see Umm
    if (!GetObjectHeard(oUmm, oMadjouline)) return;        // Madjouline can't hear Umm
    
    object oPC = GetFirstPC();
    int c = GetLocalInt(oUmm, "Count");
    string s;
    
    if (GetGlobalInt("FR"))                                //speak string, (perhaps) hear clue
        switch(c)
        {
            case 0 : s = "Avances ta main : gras et saillant,"; break;
            case 1 : s = "Son sexe généreux emplira toute ta main."; break;
            case 2 : s = "Ainsi parlait le poète Nabigha Dubyani."; break;
            case 3 : s = "Mais j'y pense ... Le bois a-t-il bien été amnené à la déesse cobra?"; break;
            case 4 : s = "Caresses plus bas et plus profond, imbécile!"; break;
        }
        
    else
        switch(c)
        {
            case 0 : s = "Move your hand forward : oily and protudent,"; break;
            case 1 : s = "His generous genitals your hand will fill."; break;
            case 2 : s = "So talked poet Nabigha Dubyani."; break;
            case 3 : s = "Well, I'm thinking ... Has the wood been brought to cobra Godess?"; break;
            case 4 : s = "Fondle me lower and deeper, you idiot!"; break;
        }
    SpeakString(s);
    
    if ( (c == 3) && (GetJournalEntry("q_murder", oPC) < 70) ) AddJournalQuestEntry("q_murder", 70, oPC); // Clue heard, update journal
    
    c++; if (c > 4) c = 0;
    SetLocalInt(oUmm, "Count", c);         
}

If Madjouline is spotted and kills someone, you lose prestige but can complete the quest. Script aa_fu_killed_hamman (OnDeath of every civvie).

// kill a civvie in the hammam, mark mission failed

void main()
{
    object oUmm = GetObjectByTag("umm");
    SetLocalInt(oUmm, "Fail", 1);
}

As a safety measure, in case the player reloads while in the hamman, the following script aa_fu_enter_hamman (onEnterConnect) is run whenever the party enters the hamman. If the mission is in progress, the PC is warped to the cell box and Madjouline to her starting position. She is set in stealth mode and gets control again.

// safety : if reloading during the hammam mission, place actors (OnEnter hammam)

void MoveTo(object oC, string sWP)
{
    AssignCommand(oC, ClearAllActions());
    AssignCommand(oC, ActionJumpToObject(GetWaypointByTag(sWP)));
}

void main()
{
    object oUmm = GetObjectByTag("umm");
    if (GetLocalInt(oUmm, "Hammam") < 1) return;    // not in mission
    
    object oPC = GetFirstPC();
    object oMadjouline = GetObjectByTag("madjouline");
    
    SetOwnersControlledCompanion(GetFirstPC(FALSE), oMadjouline);
    MoveTo(oPC, "WP_PC_hammam");
    DelayCommand(0.1f, MoveTo(oMadjouline, "WP_madjouline_hammam"));
    DelayCommand(0.15f,SetActionMode(oMadjouline, ACTION_MODE_STEALTH, TRUE));
}

@tsongo : about your companions as prisoners issue, you should remove/despawn them from the party, then spawn them in the cells. Whenever the PC gets to them, you add them to the party thru convo or whatever means.

1 Like