Larian Banner: Baldur's Gate Patch 9
Print Thread
Joined: Jul 2014
R
apprentice
OP Offline
apprentice
R
Joined: Jul 2014
So, as many of you may or may not have noticed, combat works in the main campaign. That's pretty sweet. What's not so sweet is that if you make a new mod that isn't modding the main campaign (that is, Main is only checked as a dependency not as a Mod), combat stops working.

Well, I mean, it still probably works, there's just no way to start it to find out.

As far as I can tell, combat starts when you move near someone who is hostile (but roguelike, you say, what about when I shoot something from far away and it starts combat? Yeah, yeah, I'm still working on it.)

So, what determines if two characters are hostile to one another and will start a combat if they get close? Well, I'll save you the suspense and tell you that there's an engine check when the game starts and there's a story function that you can call for turning things hostile after the game has started.

The engine check is the simpler of the two and we can get it working just by going and grabbing the /Story/Alignments/Alignment.lsx file, dropping it in our mod and assigning one character to "Evil Npc" and one character to "Good".

If we want to see how that worked, we can open up Alignment.lsx in our favorite text editor and see that all it does is define a handful of alignments, a bunch of 'entities' which seem to inherit an alignment, and then a whole bunch of relations which can exist between entities, entities and alignments, or just alignments. The only relation values that appear to matter are '0' which is hostile, '50' which seems to be indifferent, and '100' which seems to be assists in combat. I haven't actually tested any of these, but the important thing to take away is that good and evil characters hate each other.

So that allows you place down characters like the zombies outside of Cyseal that will attack as soon as you see them, but what if you want a merchant who, for instance, starts to fight if you attack her?

Well, here's where it gets interesting. Obviously, in the main campaign, this just works. And by just works, I mean we can open up /Story/RawFiles/Goals/Attitude.txt and see about a million functions along the lines of:

IF
CharacterAttackedByCharacter(_Npc,_Player)
AND
_Npc!=_Player
AND
CharacterGetRelationToCharacter(_Player,_Npc,_Rel)
AND
_Rel>=25
AND
CharacterIsDead(_Player,0)
AND
_Player.isPlayer()
AND
NOT _Npc.isPlayer()
AND
CharacterIsDead(_Npc,0)
AND
NOT dbCombat(_Npc,_)
AND
NOT DB_DisableAttackReaction(_Npc)
AND
NOT GenericsBlockedFor(_Player)
AND
NOT IsGuard(_Npc)
AND
NOT _Npc.isInDialog()
AND
NOT IsNotMessingAround(_Npc)
AND
NOT DB_Pets(_Npc,_)
THEN
WarnPlayerOfAttack(_Npc,_Player);


Note that the lack of 'else' or 'else if' statement equivalents means that each function only has one control flow, so I hope you enjoy programming a lot of similar functions. Welcome to Hell.

Anyway, we can close Attitude.txt now because the function we actually care about lives in _PROC.txt and it looks like:

IF
CharacterAttackedByCharacter(_Char,_Source)
AND
_Source.isPlayer()
AND
DB_StoryMoving(_Char,1)
AND
NOT dbCombat(_Char,_)
THEN
ProcMakeNPCHostile(_Source,_Char);

And in fact, we can just grab that, go to our mod, drop it in to a new file in the story editor, build everything, and voila! When we attack a neutral npc (make sure to change their alignment to Neutral or something so they don't start out hostile), absolutely nothing happens.

I'll save you about five minutes of debugging and let you know that the problem is that DB_StoryMoving and _Source.isPlayer always evaluate to false and that this function:

IF
CharacterAttackedByCharacter(_Char,_Source)
AND
NOT dbCombat(_Char,_)
THEN
ProcMakeNPCHostile(_Source,_Char);

Works just fine.

Well, fine in the sense that if we walk up to a neutral npc and whack them, they will now become hostile. Not so fine in the sense that not only did we probably just delete at least one critical bug fix, but area of effects spells cast by the AI will now cause a ripple effect of hostility, probably cause entire villages to murder themselves. Also, this might make groups turn on each other. Like, if the evil wizard casts a fireball on his henchmen, this may turn them hostile to him. But whatever.

If anyone wants to figure out how to make .isPlayer() work and the db commands, it ought to be possible to import the entire attitude system from attitude.txt.

I haven't tried it yet, but I think if you wanted combat from dialog to work (i.e. [Attack] as a dialog option), you would need to change the npc's attitude, then have a story function that listens for attitude changes and turns npcs hostile. You might be able to get away with just flag too if you don't feel like reimplementing the entire attitude system.


EDIT:
Whoops, if you haven't been hacking _Proc.txt to work, you'll need to cast the "_" in dbCombat to a string "(STRING)_" and you also need to drop in this function from _Proc into your story script:

PROC
ProcMakeNPCHostile((CHARACTER)_Player,(CHARACTER)_Npc)
AND
CharacterIsDead(_Npc,0)
THEN
AlignmentChangedByStory(_Player,_Npc);
CharacterSetTemporaryHostileRelation(_Npc,_Player);

Last edited by roguelike; 11/07/14 04:10 AM.
Joined: Jul 2014
N
apprentice
Offline
apprentice
N
Joined: Jul 2014
Thanks, it works great! Watching the major bosses fight it out - so much fun ^^

Joined: Sep 2014
S
stranger
Offline
stranger
S
Joined: Sep 2014
Sorry I don't understand with your edit, just can you give me the script and if I need to change something in one file or not. Sorry, I don't speak very well english, so I don't understand correctly your explanation.

Thanks for your future help.

Joined: Oct 2014
Location: Hogwarts
member
Offline
member
Joined: Oct 2014
Location: Hogwarts
This is great! Have you made any progress?

Joined: Oct 2014
B
enthusiast
Offline
enthusiast
B
Joined: Oct 2014
Can't test it at the moment but why wouldn't the below work?

//Check for player 1 attacking NPC
IF
CharacterAttackedByCharacter(_Char,CHARACTER_Player1)
AND
NOT dbCombat(_Char,_)
THEN
ProcMakeNPCHostile(_Source,_Char);


/Separate function for player 2 because I'm bad
IF
CharacterAttackedByCharacter(_Char,CHARACTER_Player2)
AND
NOT dbCombat(_Char,_)
THEN
ProcMakeNPCHostile(_Source,_Char);

Last edited by Burgee; 11/11/14 01:34 AM.

Link Copied to Clipboard
Powered by UBB.threads™ PHP Forum Software 7.7.5