Creature NOT rendering in Game

All,

I am having some weird issues with getting an ankheg to render in game. @raymondsebas was good enough to walk me through how to create a blueprint manually in this thread:

https://forum.neverwintervault.org/t/how-to-download-non-zipped-hak-files/

I downloaded the files from the vault, created a hak pak for the ankheg, associated the hak with the mod, and even was able to create the scripts from the .nss files using notepad. Here’s where stuff gets odd…

When I place the ankheg in an area w/in the toolset, I see this…

The ankheg blueprint has these properties:

But when I run the module, here’s what I see in game (the road marker and palm are just there for reference):

And to add insult to injury, things only get weirder when I try to spawn the ankheg from an encounter:

However, when it spawns in game the ankheg is a human fighter… DOH!

Now I know I’m screwing something up, but I just can’t figure out exactly what… So I defer to the experience of the community to help me find my way out of another fine mess I’ve gotten myself into.

Thanks in advance.

Dave

1 Like

My guess is it has something to do with appearance.2da. Have you modified that? I have on occasion made the error that I’ve had two 2da files with the same name, one in my override folder and one in a hak pack, and that always cause problems.

2 Likes

Maybe, however it’s showing in the ToolSet, so it might be something else.

See scripts I use and leave blank the other part on creature (see orange circle on ToolSet picture). Just tested in a black studio test area and all seem to work. At least for me.


// ankheg_ondamage script

#include "ginc_ai"
#include "x0_i0_position"
#include "ginc_behavior"

void PlayCustomAnimationWrapper(object oMember, string sAnim, int bLoop)
{
	PlayCustomAnimation(oMember,sAnim,bLoop);
}

void Burrow(object oTarget)
{
	SetLocalInt(OBJECT_SELF, "Underground", 1);
	ClearAllActions(TRUE);
	// Make it ethereal to disengage existing attackers
	effect eVis = EffectNWN2SpecialEffectFile("fx_global_earth_elemental_arise.sef");
	effect eBurrow = EffectEthereal();
//	eBurrow = EffectLinkEffects(EffectBlindness(), eBurrow);
	eBurrow = SupernaturalEffect(SetEffectSpellId(eBurrow, 100));
    ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF);
    DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBurrow, OBJECT_SELF));
	// Down we go
	DelayCommand(0.1, PlayCustomAnimationWrapper(OBJECT_SELF, "Disappear", 0));
	DelayCommand(0.2, ActionWait(1.8));
	DelayCommand(1.0, SetPlotFlag(OBJECT_SELF, 1));
    DelayCommand(1.9, SetScriptHidden(OBJECT_SELF, 1, 0));
	DelayCommand(1.99, PlayCustomAnimationWrapper(OBJECT_SELF, "DisappearIdle", 1));
	if (!GetIsObjectValid(oTarget)) return;
	
	//If damage was from a distance, pop up behind our enemy
	location lDestination = GetBehindLocation(oTarget, 2.5);
	if (!GetIsLocationValid(lDestination))
		CalcSafeLocation(OBJECT_SELF, lDestination, 2.0, FALSE, FALSE);
	AssignCommand(OBJECT_SELF, DelayCommand(2.5, ActionJumpToLocation(lDestination)));
	effect eListen = EffectSkillIncrease(SKILL_LISTEN, 20);
	DelayCommand(2.51, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eListen, OBJECT_SELF, 6.5));
}

void main()
{
	object oSelf = OBJECT_SELF;
    object oDamager = GetLastDamager(oSelf);
	SetLocalObject(oSelf, "LastDamager", oDamager);
	int iUnderground = GetLocalInt(oSelf, "Underground");
	object oTarget = OBJECT_INVALID;
	float fDist = GetDistanceBetween(oSelf, oDamager);
	int iHPPercent = GetHealthPercent(oSelf);
	// Attack was from a distance
	if (fDist > 5.0) oTarget = oDamager;
	
	// If dead, don't burrow
	if (GetCurrentHitPoints(oSelf) < 1)
	return;
	
	//If less than 50% hit points, always burrow
	if (iUnderground == 0)
	{
    if ( iHPPercent < 50  || d4(1) == 1)
	{
	Burrow(oTarget);
	return;	
	}
	}	
			
    ExecuteScript("nw_c2_default6", oSelf);
}

//////////////////////////////////////////////////////////

// ankheg_ondeath script


void CreateObjectWrapper(location lLoc)
{
CreateObject(OBJECT_TYPE_PLACEABLE, "plc_ankheg", lLoc);
}

void main()
{
location lLoc = GetLocation(OBJECT_SELF);
DelayCommand(4.0, CreateObjectWrapper(lLoc));
ExecuteScript("nw_c2_default7", OBJECT_SELF);
}



////////////////////////////////////////////////////////

//  ankheg_hb heartbeat script

#include "NW_I0_SPELLS"
#include "ginc_cutscene"
#include "x0_i0_position"
#include "ginc_ai"

void AcidSpit();

void PlayCustomAnimationWrapper(object oMember, string sAnim, int bLoop)
{
	PlayCustomAnimation(oMember,sAnim,bLoop);
}

void main()
{
object oMyArea = GetArea(OBJECT_SELF);
object oPCArea = GetArea(GetFirstPC());
if (oMyArea != oPCArea)// If no players in area, abort
	return;

int iHidden = GetScriptHidden(OBJECT_SELF);
if (iHidden == 0)// We're above ground
	{
	//If not in combat or underground, turn to face last attacker
	int iUnderground = GetLocalInt(OBJECT_SELF, "Underground");
	object oLastDamager = GetLocalObject(OBJECT_SELF, "LastDamager");
	if (GetCurrentAction(OBJECT_SELF) != 3 && !IsFacingWithin(20.0, oLastDamager)
		&& !GetIsDead(oLastDamager) && iUnderground == 0)
	TurnToFaceObject(oLastDamager, OBJECT_SELF);
	//Fall through to default script
	ExecuteScript("nw_c2_default1", OBJECT_SELF);
	return;
	}
else// We're underground & listening
	{
	object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, GetLocation(OBJECT_SELF), FALSE, OBJECT_TYPE_CREATURE);
    while(GetIsObjectValid(oTarget))
    {
	int iMoveSilent = GetSkillRank(SKILL_MOVE_SILENTLY, oTarget, FALSE)+d20(1);
	int iHeard = GetIsSkillSuccessful(OBJECT_SELF, SKILL_LISTEN, iMoveSilent, FALSE);
	int iHPPercent = GetHealthPercent(OBJECT_SELF);
	int iAcid = GetLocalInt(OBJECT_SELF, "Acid");
        if(oTarget != OBJECT_SELF 
			&& spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF) 
			&& iHeard )// We heard an enemy
        {
		location lDestination = GetBehindLocation(oTarget, 2.5);
		if (!GetIsLocationValid(lDestination))
			CalcSafeLocation(OBJECT_SELF, lDestination, 2.0, FALSE, FALSE);
		float fDelay = 2.0;
		ClearAllActions(TRUE);
        AssignCommand(OBJECT_SELF, ActionJumpToLocation(lDestination));		
		effect eVis = EffectNWN2SpecialEffectFile("fx_global_earth_elemental_arise.sef");
		DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF));
		DelayCommand(0.5, SetScriptHidden(OBJECT_SELF, 0, 0));
		DelayCommand(0.5, PlayCustomAnimationWrapper(OBJECT_SELF, "Appear", 0));
		DelayCommand(0.51, ActionWait(1.48));
		DelayCommand(1.0, RemoveEffectsFromSpell(OBJECT_SELF, 100));
		DelayCommand(1.1, SetPlotFlag(OBJECT_SELF, 0));
		DelayCommand(1.9, PlayCustomAnimationWrapper(OBJECT_SELF, "%", 1));
	
		if (iHPPercent < 50 && iAcid == 0)
			{//Spit acid once if badly injured
			AssignCommand(OBJECT_SELF, DelayCommand(2.0, ClearAllActions(TRUE)));
			AssignCommand(OBJECT_SELF, DelayCommand(2.5, PlayCustomAnimationWrapper(OBJECT_SELF, "gen_cast", 0)));
			AssignCommand(OBJECT_SELF, DelayCommand(2.6, AcidSpit()));
			SetLocalInt(OBJECT_SELF, "Acid", 1);
			fDelay = 5.0;
			}
		else
			AssignCommand(OBJECT_SELF, DelayCommand(2.0, ActionAttack(oTarget)));
		
		DelayCommand(fDelay, SetLocalInt(OBJECT_SELF, "Underground", 0));
		return;
		}
	 oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, GetLocation(OBJECT_SELF), FALSE, OBJECT_TYPE_CREATURE);
	}//end while
	}//end else	
}

void AcidSpit()
{
effect eAcidBeam = EffectVisualEffect(VFX_BEAM_GREEN_DRAGON_ACID);
location lLocation = GetLocation(OBJECT_SELF);
location lNewLocation = GetAheadLocation(OBJECT_SELF, 60.0f);
object oEndTarget = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_ipoint ", lNewLocation);
DelayCommand(1.0f, DestroyObject(oEndTarget));
DelayCommand(0.5f, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eAcidBeam, oEndTarget, 3.0f));
float fDelay;
// Use hit dice to determine damage & DC
int nDC = 10 + GetHitDice(OBJECT_SELF);
int nDamStrike;
effect eVis = EffectVisualEffect(VFX_IMP_ACID_L);
object oTarget = GetFirstObjectInShape(SHAPE_SPELLCYLINDER, 9.0, lNewLocation, TRUE, OBJECT_TYPE_CREATURE, GetPosition(OBJECT_SELF));
    while(GetIsObjectValid(oTarget))
    {
        if(oTarget != OBJECT_SELF && spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF))
        {
            //Reset the damage to full
            nDamStrike = d4(GetHitDice(OBJECT_SELF));
			
            // calc delay based on distance
            fDelay = GetDistanceBetween(oTarget, OBJECT_SELF)/20;

            //Fire cast spell at event for the specified target
            SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELLABILITY_DRAGON_BREATH_ACID));

            //Adjust the damage based on the Reflex Save, Evasion and Improved Evasion.
            if(MySavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, SAVING_THROW_TYPE_ACID))
            {
                nDamStrike = nDamStrike/2;
                if(GetHasFeat(FEAT_EVASION, oTarget) || GetHasFeat(FEAT_IMPROVED_EVASION, oTarget))
                {
                    nDamStrike = 0;
                }
            }
            else if(GetHasFeat(FEAT_IMPROVED_EVASION, oTarget))
            {
                nDamStrike = nDamStrike/2;
            }
			effect eBreath = EffectDamage(nDamStrike, DAMAGE_TYPE_ACID);
			if (nDamStrike > 0)
            {
                //Apply the VFX impact and effects
                DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget, 3.0f));
                DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget));
             }
        }
        //Get next target in spell area
        oTarget = GetNextObjectInShape(SHAPE_SPELLCYLINDER, 9.0, lNewLocation, TRUE, OBJECT_TYPE_CREATURE, GetPosition(OBJECT_SELF));
    }
}



///////////////////////////////////////////////////

//   ankheg_onspawn  script


void PlayCustomAnimationWrapper(object oMember, string sAnim, int bLoop)
{
	PlayCustomAnimation(oMember,sAnim,bLoop);
}

//Starts ankheg underground
void main()
{
DelayCommand(1.0, PlayCustomAnimationWrapper(OBJECT_SELF, "DisappearIdle", 1));
SetPlotFlag(OBJECT_SELF, 1);
SetLocalInt(OBJECT_SELF, "Underground", 1);
DelayCommand(0.1, SetScriptHidden(OBJECT_SELF, 1, 0));
}

Hi,

To make sure the creature appears at all … Just leave all its scripts blank and just make sure you can see the model in the game, even if it does not doing anything at this stage.

Cheers, Lance.

Hi Raymond,

I am just taking a closer look at the scripts for this creature and noticed that there is supposed to be a placeable object for a dead ankheg. Is that something you have, as I don’t think it came with the download I have of it. i.e. Nothing is going to be left behind with this script … and may not even be required if we use lootable corpses instead anyway.

As I say, I have only just started looking at these, so (for myself) I may end up doing a few changes to the scripts that came with it.

Cheers, Lance.

It is to leave an object (a shell craftable into Ankheg plate armor with the properties that we know). That’s all. It can be optional. :slightly_smiling_face:

1 Like

Oh, I see … I tend to leave corpses with the ankheg armour skin on it to collect. Just a different method.

EDIT: Do you know if you use all of the scripts, or have you modified / just gone with them? Eg: Does “DisappearIdle” work?

Cheers, Lance.

1 Like

If you have a script that does this, post it here, I’m interested. But normally, if you have selected in Body bag option => “Body”, the creature’s body remains there, as long as you have not searched it. Well if I remember right. Maybe I’m wrong.

As far as I am aware, all you need is the “Lootable Corpse” TRUE (very last line in your screenshot) and the one you have as “body”, I leave as “default”. Then when the creature dies, it just leaves its own corpse. Or have I misunderstood your request?

EDIT: I have some rather complex ondeath handling when it comes to corpses and how the are handled … fade etc.

Cheers, Lance.

I know. Lootable Corpse = ” TRUE" already set (see last picture).

And you can put this on click dead body (I’m not the author of this this dialogue and all the scripts, I have no credit in this).

Hi Raymond,

EDIT: Just confirmed I have that conv too. Must have been with the original download.

QUESTION: I assume “n2_crft_ankhegplate” is something they made up as a new resref for a placeable drop, as I do not see the item in my lists?

Slightly different to what I do :slight_smile: … I use a skill check when a PC kills a creature and then allow craftable item to drop then (on the lootable corpse). Of course, I need to add the ankheg to the current list of drops, but I can do that once I have finished setting the creature up to work with my own AI scripts.

The scripts appear to be somewhat different to the standard way I do things and so it may take me some time to work out what parts I will use and which parts I won’t. It handles the acid spit independently from normal AI code in a HB. Not sure if I want to go that way or not, but cannot tell if I will have to or not until I experiment a little with it.

Cheers, Lance.

1 Like

Hi All,

OK, I am playing around with the scripts at the moment, and I have rewritten quite a bit, leaving out stuff we don’t really need (due to being able to place it in the creature properties at build time) - and also messing around with when and how the creature appears. (No spawn script required.)

Bottom line … the creature does render in my module, but the timing of “burrows” is a little haphazard for my liking at the moment. I think it is to do with the way the ankheg “detects” (or fails to detect) nearby targets.

Also, I have assumed (having looked at the model), that the ankheg is supposed to be “immobile” (*) and simply burrows, moves underground (unseen) and burrows up and attacks again. That is how I always understood it anyway, but I just want to make sure we all had that noticed. (*) Although setting to immobile or none appears to break its combat ability, so I may have to set a minimal “very slow” to ensure combat works.

When I have the scripts to my liking, I will post them.

My current approach uses:-

  1. alb_functions_ai: Contains the ankheg burrowing and spit functions.
  2. A simple on heartbeat check to one of the above functions. (Burrow up and attack.)
  3. An AI addition for the combat round based on the creature appearance. (Burrow away chance.)

EDIT: The detection range of the ankheg is quite “small” (only 30 feet in original script) considering how far a PC can walk in a single heartbeat (6 seconds). i.e. As it currently stands … If the timing is “wrong”, a player could move their PCs in and out of the creature’s detection range before a check was even made! This “detection” is what I am currently working on. I am going to increase from COLOSSAL (30’) to VAST (60’) to start with. < - Even using VAST does not improve the overall effect enough for my liking. I am going to look at the whole detection and appear/disappear routines again, to improve their timings.

Cheers, Lance.

Hi AYG,

You do realise that the ankheg is “underground” when you first enter the area and so will not be “seen”. What happens when you try to stand (walk towards) the spot where the ankheg is supposed to be?

By the way, I am not saying it will work, but just wanted to make sure you tried the obvious. In my own testing, I don’t think the ankheg will detect the PC that well as the original scripts stood.

Cheers, Lance.

UPDATE 07/02/20:

Hi All,

After recognising pseudo heartbeats cannot be easily refreshed for testing in a saved game, I am back with a clearer approach to sorting this AI today.

At the moment, the scripts I have do work relatively well: the ankheg detects every time, and can “chase” the PCs if they try to run, blocking their path and tries to fight them from its burrowed up position. I also have the acid spray when low on hit points.

However, there are still some timing issues and combat attacks that stop me from saying it is working fully. I have some ideas that I hope may fix that today, and will post if I have it working.

EDIT OK, I think I’m about there … I wanted to give the ankheg a little more “aggression” and altered its AI to target PCs with better tremorsense ability than the original code did. To ensure it does fight properly, I also had to give it a very slow speed as if “ploughing” through the ground, albeit very slowly. However, most of the time it does not move this way, but simply burrows away and burrows up again as it requires.

Basically, PC detection for the ankheg has been greatly increased so that it can now intercept PCs that cross within its 60’ tremorsense ability … which basically means, if the PCs cross its path, the ankheg will emerge from the ground and begin its attack.

If the creature drops below 50% hit points, it will burrow away and return with a spit. This range has also been recalculated to allow for any location safe calculation result.

Considerations

  1. The ankheg must have “very slow” movement.
  2. It is placed in the area with ScriptHidden TRUE, but DisableAIWhileHidden is FALSE.
  3. Variable on placed ankheg must be “UNDERGROUND” value of 1. (Starts underground.)
  4. AI scipt must be employed in X2_SPECIAL_COMBAT_AI_SCRIPT variable on creature.
  5. Lines added to monster heartbeat script to start tremorsense detection.
  6. All associated functions then called from alb_functions_ai script.

I am just going to do some last minute testing and put a video together to demo. Will post a link for viewing when done.

NEW VIDEO : The 60 Second Party Wipeout

Thanks, Lance.

1 Like

All,

Thanks! I haven’t been able to devote much time to fixing this, work and family stuff. However, I did make progress this morning. @raymondsebas and @Lance_Botelle, first round is on me! Thank you both! At lance’s suggestion, I first stripped out all of the old scripts for the ankheg and got it to render in the game. I then used the scripts Raymond provided (which were VERY different from the ones I had, not sure why!) and got it to perform exactly as it did in the posted video.

The only oddity I now have is that it’s very difficult to get the ankheg to spawn… I sometimes have to have the PC running around the area for 5 minutes or more. Is there a way to adjust the script to make PC detection by the ankheg more sensitive? Or, as is more likely, am I somehow using it wrong? I’d like to have the creature spawn w/in a few seconds of the party entering the area.

Thanks again for the help.

1 Like

Hi AYG,

You need to read the two posts above your last one. :wink: It speaks about this exact issue.

i.e. The scripts you currently have may use the animations to show and hide the ankheg, but they suffer from poor detection in my opinion. So, you won’t have the same result as the video, which shows immediate detection, even if the animation for appearance and disappearance are the same once activated. In fact, I had to change quite a bit to make it work, and the only bit I didn’t change was the animation sequence I believe. :slight_smile:

Cheers, Lance

P.S. If people would prefer to use the scripts I have written, then let me know and I will post them. The only caveat I would draw your attention to, is you need to understand that one script requires knowledge on how to implement a user AI script.

1 Like

@Lance_Botelle, thanks. I re-read the posts in question and since I have VERY limited scripting ability I would love to try the scripts you used for the video. As far as implementing the AI script, I know I have an AI script, but not sure if/how it’s associated with the creature.

Any advice and guidance would be much appreciated. And please remember, a village somewhere is missing me when it comes to this stuff! So don’t be afraid to be explicit.

Thanks again,

angry_yard_gnome

(Dave)

Hi Dave,

I’m not at my computer at the moment, but will post something when I am. :slightly_smiling_face:

Catch you later.
Lance

Hi Dave,

I have added a link to download an erf containing three scripts below. (Forum would not format the code properly for me when I tried to post them here.) In the scripts, I had to change from my own targeting functions and replaced them with official campaign ones so the scripts compile for you. The end result should be similar, if not the same. I just had my own reasons to use the functions I had. Let me know if there are any problems.

Download from this link.

Cheers, Lance.

1 Like

Lance, thanks! I’ll download them when I get out of work. I may not get to try them until tomorrow, but I’ll let you know how things work when I do.

Take care,

Dave

1 Like