Jump to content

Threat Calculation


WizWom

Recommended Posts

For My Tutu NPC, I've made a fairly sophisticated threat calculator, mainly for Mages.

 

generally, it decides how dangerous to th mage the nearest enemy closer than 8 (a distance which should take 2 seconds to cross) is and how dangerous the furthest spellcaster is (priority goes Mage, cleric, druid, bard, if I'm scripting right). It takes into account the mages resilience and the spellcaster's power. It then chooses between these two if both are present.

 

And since I keep the threat calculation, I can then have the script use different spells for differeing levels of danger - of course, I like putting out a magic missile or acid arrow to disrupt preferentially. Clerics, of course, should open with a disrupting insect swarm.

 

Anyway, it also can be extended to fighters, in that they can just choose to attack the target if they are ranging, or the nearest non-trivial if they are meleeing.

 

I'm basing it off the TAZQ series of scripts, but I'm ending up chopping stuff out because it will never be seen in tutu, and recoding almost all the tests :-)

Link to comment

WizWorm,

 

Threat assessment is pretty tricky, cool that you've worked something out that is effective.

 

Ideally you would be able to have a 'global' threat level = the amount of trouble the whole part is in e.g.,

 

Red = 1 Tarasque or 2 summoned demons + 2 mages + 2 Fighters + 2 Clerics

Yellow = 5 Fire Giants or 8 10th+ lvl creatures

Green = 10 Kobolds or 3 Carrion Crawlers

 

And then have an individual threat level based on who/what is currently attacking a particular party memember.

 

Then you would figure out some way to mesh the two systems to react appropriately e.g., the mage realizing that he is in serious trouble runs off towards your fighters to get some help with the Iron Golem that is trying to maul him/her.

 

Care to post some snippets with an explanation or is the code proprietary?

 

Does your threat assessment scale well? Tutu vs. BG2?

 

TAZQ series of scripts? ...Never heard of this system

 

Thanks,

Link to comment

Well, the whole point was to make a scaling threat assessment, for Tutu. since what's really dangerous as 1st level isn't going to phase you when you've make 5th or 6th. So, you need to use your magic/items to battle a wolf at first level, but you probably won't break a sweat fighting a Dire wolf at 4th.

 

The scripts were an extension on someone's q-series scripts, off of sorcerer's place.

BG2 aiscripts by Tapio Kivikkola <zap@mad.scientist.com>

It included upgraded summons AI & somewhat better

Similiar to orginal Blucher's scripts, but usage of scrolls, some items and most of area spells are removed (in my game, however, Aerie still casts Fireball).

Also alchemy is removed.

Link to comment

Just to chime in, I'm now testing my own threat level system in IWD2. My brief experience seems to indicate that at least the following factors should be considered:

  • Number of non-disabled enemies
  • Number of nearby allies
  • Health (and status) of party
  • Presence of enemy spellcasters
  • Checks for (a few) particularly nasty enemy types
  • and, very importantly, Personal health

For my own system, I add or subtract from my threat levels based on the above, and then perform some sanity checks to ensure the threat level has not gotten too high or too low. After that, I translate the threat level value into a maximum spell level that I'm willing to use.

 

That made it much more trivial to think appropriately for spell and item usage: I'll gladly cast Mirror Image (spell level 2) or drink a Potion of Aid (spell level 2) if my MaxSpellLevel is 2 or higher.

 

An added bonus is that as I refine the list of threat contributors above, I merely have to adjust the maximum spell level appropriately as I find the threat system too lenient or too strict, and all the items/spells I have coded will "take care of themselves".

 

My testing so far has been pretty limited, but I've been happy with the system. I will admit that IWD2's scripting engine -- and allocation of enemies -- actually makes threat assessment a little easier than the BG2 engine IMO, but that's small consolation for the multitude of bugs that are in the engine overall. :)

 

As a secondary note, the inclusion of character-specific modifiers to threat level (most notably personal health) meant that a global threat level seemed less appropriate than a personal one. In addition, I found that global threat calculation had to be handled very carefully (to ensure that everyone isn't constantly trying to adjust it), so I've abandoned global threat level for now.

Link to comment

well, generally, my script starts with a "fix-up" section, which tests for low HP or poison and tries to remedy.

 

The, if I'm not in combat and see nothing threatening, I break.

 

Combat threat calculation is:

IF
True()
THEN
RESPONSE #100
	SetGlobal("WxNearDanger","LOCALS",0)
	SetGlobal("WxMageDanger","LOCALS",0)
	Continue()
END

IF
Global("Debug","GLOBAL",1)
THEN
RESPONSE #100
	PauseGame()
	Continue()
END

IF
See(NearestEnemyOf(Myself))
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	SetGlobal("WxNearDanger","LOCALS",3)
	Continue()
END

IF
HPLT(LastSeenBy(Myself),10)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",-1)
	Continue()
END

IF
HPLT(LastSeenBy(Myself),5)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",-1)
	Continue()
END

IF
HPGT(LastSeenBy(Myself),20)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",1)
	Continue()
END

IF
HPGT(LastSeenBy(Myself),35)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",1)
	Continue()
END

IF
HPGT(LastSeenBy(Myself),50)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",1)
	Continue()
END

IF
HPGT(LastSeenBy(Myself),65)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",1)
	Continue()
END

IF
HPGT(Myself,40)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",-1)
	Continue()
END

IF
HPGT(Myself,30)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",-1)
	Continue()
END

IF
HPGT(Myself,15)
Range(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxNearDanger","LOCALS",-1)
	Continue()
END

IF
GlobalLT("WxNearDanger","LOCALS",0)
THEN
RESPONSE #100
	SetGlobal("WxNearDanger","LOCALS",0)
	ChangeSpecifics(NearestEnemyOf(Myself),WWXJON_TRIVIAL)
	Continue()
END

IF
GlobalGT("WxNearDanger","LOCALS",3)
THEN
RESPONSE #100
	Shout(124)
	ChangeSpecifics(NearestEnemyOf(Myself),WXJON_TARGET)
	Continue()
END

IF
OR(4)
	See([ENEMY.0.0.CLERIC_ALL])
	See([ENEMY.0.0.LONG_BOW])
	See([ENEMY.0.0.BARD_ALL])
	See([ENEMY.0.0.DRUID_ALL])
THEN
RESPONSE #100
	SetGlobal("WxMageDanger","LOCALS",3)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
LevelLT(LastSeenBy(Myself),3)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",-1)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
LevelLT(LastSeenBy(Myself),5)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",-1)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
LevelGT(LastSeenBy(Myself),6)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",2)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
LevelGT(LastSeenBy(Myself),8)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",2)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
CheckStatGT(Myself,10,SAVEVSSPELL)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",1)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
CheckStatGT(Myself,13,SAVEVSSPELL)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",1)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
CheckStatGT(Myself,16,SAVEVSSPELL)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",1)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
CheckStatLT(Myself,6,SAVEVSSPELL)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",-1)
	Continue()
END

IF
HPGT(Myself,40)
GlobalGT("WxMagedanger","LOCALS",0)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",-1)
	Continue()
END

IF
HPGT(Myself,30)
GlobalGT("WxMagedanger","LOCALS",0)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",-1)
	Continue()
END

IF
HPGT(Myself,15)
GlobalGT("WxMagedanger","LOCALS",0)
THEN
RESPONSE #100
	IncrementGlobal("WxMageDanger","LOCALS",-1)
	Continue()
END

IF
GlobalLT("WxMagedanger","LOCALS",0)
THEN
RESPONSE #100
	SetGlobal("WxMageDanger","LOCALS",0)
	ChangeSpecifics(LastSeenBy(Myself),WWXJON_TRIVIAL)
	Continue()
END

IF
GlobalGT("WxMagedanger","LOCALS",0)
!See([0.0.0.0.WXJON_TARGET])
THEN
RESPONSE #100
	ChangeSpecifics(LastSeenBy(Myself),WXJON_TARGET)
	Continue()
END

IF
GlobalGT("WxMageDanger","LOCALS",0)
See([0.0.0.0.WXJON_TARGET])
OR(4)
	See([ENEMY.0.0.CLERIC_ALL])
	See([ENEMY.0.0.LONG_BOW])
	See([ENEMY.0.0.BARD_ALL])
	See([ENEMY.0.0.DRUID_ALL])
LocalsGT("WxMageDanger","WxNearDanger")
THEN
RESPONSE #100
	ChangeSpecifics(LastSeenBy(Myself),WXJON_TARGET)
	ChangeSpecifics(NearestEnemyOf(Myself),0)
	Continue()
END

 

So, if I have no enemy with a WXJON_TARGET, then I know I am not threatened.

If I see an enemy with WXJON_TARGET, then I can decide whether it is a mage or a melee combatant, if it is a mage, I can use blocks like:

 

IF
LocalsGT("WxMageDanger","WxNearDanger")
See([0.0.0.0.WXJON_TARGET])
CheckStatLT(Myself,35,SPELLFAILUREMAGE)
CheckStatLT(LastSeenBy(Myself),50,RESISTMAGIC)
!HasBounceEffects(LastSeenBy(Myself))
!HasItemEquiped("mageamul",LastSeenBy(Myself)) // Necklace
!StateCheck(LastSeenBy(Myself),STATE_INVISIBLE)
!StateCheck(LastSeenBy(Myself),STATE_IMPROVEDINVISIBILITY)
CheckStatLT(LastSeenBy(Myself),25,RESISTACID)
HaveSpell(WIZARD_MELF_ACID_ARROW)
THEN
RESPONSE #100
	SetInterrupt(FALSE)
	SetGlobalTimer("GBHalfAttack","LOCALS",ONE_ROUND)
	Spell(LastSeenBy(Myself),WIZARD_MELF_ACID_ARROW)
END

 

The "GBHalfAttack" bit is to let the next pass through the script use a ranged weapon, so I don't waste the rest of the round.

Link to comment

Wizworm,

 

Not sure about BG1, but it BG2, you can break the game by changing specifics of a character. That is why I dropped the ChangeSpecifics targeting from the eSeries and went to using Detectable Spells (not that it doesn't have its issues).

Link to comment

As far as it goes, I change specifics of NPC, not characters...

 

I'm running this under tutu, so the engine is BG2. I saw the note about multiplayer using specific; I would expect they use it to tell who controlls what sprites.

 

It's a shame, because othewise I can't really make a turing machine, as I have no real way to store data. It's a bit of a hack, but it was seeming to work.

Link to comment
As far as it goes, I change specifics of NPC, not characters...

 

I'm running this under tutu, so the engine is BG2. I saw the note about multiplayer using specific; I would expect they use it to tell who controlls what sprites.

 

It's a shame, because othewise I can't really make a turing machine, as I have no real way to store data. It's a bit of a hack, but it was seeming to work.

 

--ChangeSpecifics(NearestEnemyOf(Myself),WWXJON_TRIVIAL)

 

I meant NPCs, darn esoteric terminology...

 

Wizworm, ah, TuTu. Is there someway to search as see if any ingame scripts are checking for Specifics to move the plot along? If BG1 (albeit in the BG2 engine) isn't using Specifics to advance quests or the plot line along, then you would still be okay using it.

 

Seems like you are saying that multiplayer definitely does. In that case, it would probably be a bad idea to use the scripts for multiplayer games.

Link to comment

Well, BG2 uses specifics for certain NPCs, from what i can see. So, I'm not sure it's wise to do this, anyway.

 

Since I know whether to target the NearestEnemy() or the LastSeen of a mage class, then I suppose I'll have to have the targeting in the blocks :-(

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...