Aller au contenu

Photo

local integers


  • Veuillez vous connecter pour répondre
45 réponses à ce sujet

#1
PJ156

PJ156
  • Members
  • 2 980 messages
I know, I know it's all been said before but I have never got a local int working for me and now when I really need one it's no exception.

I want to set a local int on the pc when an npc dies (is attacked in my case as once that has happened death is inevitable)

There are three baddies one will add 1 to the integer, one 2 and the other 4 that way I can track how many are dead and incude that information in a later convo.

I have this script created for me by Lilac soul. Line 3 is fine since this is a solo mod.

void main()
{
object oPC = GetLastHostileActor();
if (!GetIsPC(oPC)) return;
int nInt;
nInt = GetLocalInt(oPC, "0001_s_cs_killed");
nInt += 1;
SetLocalInt(oPC, "0001_s_cs_killed", nInt);
}

In the convo I test on gc_local_int and look for nos 1-7 against oPC.

Please tell me what I am doing wrong. I always use Global ints normaly but here I can set it as a global as getglobalint is  not a function that I can find. I dont want to fudge a convo here though I can fire off a barkstring so I have that as a fall back option.

Help would be appreciated.

PJ

Modifié par PJ156, 24 octobre 2011 - 10:45 .


#2
_Knightmare_

_Knightmare_
  • Members
  • 643 messages
I don't see anything wrong with the script. Should work. How are you getting the int in the convo (how are you defining it aqs the PC)? That gc_ script defaults to checking the "owner" of the convo, which would be the NPC who it is attached to.

Try a simple check script of your own to see if it finds it, example:

int StartingConditional (string sVariableName, int nValue)
{
object oPC = GetFirstPC();
if (GetLocalInt (oPC, sVariableName) == nValue) return TRUE;
return FALSE;
}

#3
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
Where are you putting the script, PJ? What event is if firing from? Is it from the On Death event of the NPC? It also looks like the script is only going to add +1 to the integer every time the script fires. Based on what you said in your description, you want it to add +1 for one NPC, +2 for another, and +4 for the third. To fix it you need to add this code:


(This will only work if the NPC's in question are the ones calling the script.)


void main()
{
object oPC = GetLastHostileActor();
if (!GetIsPC(oPC)) return;
int nInt;
nInt = GetLocalInt(oPC, "0001_s_cs_killed");

///NEW CODE ADDED///


object oSelf = OBJECT_SELF;
string sTag = GetTag(oSelf);

if (sTag=="tag_of_first_NPC")
{
nInt +=1;
}

if (sTag=="tag_of_second_NPC")
{
nInt +=2;
}


if (sTag=="tag_of_third_NPC")
{
nInt +=4;
}
/////END NEW CODE.

SetLocalInt(oPC, "0001_s_cs_killed", nInt);
}

Modifié par M. Rieder, 25 octobre 2011 - 12:04 .


#4
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
Now that I look at it again, I am wondering if there is a problem with this line too:

if (!GetIsPC(oPC)) return;

If the last hostile actor is a member of the party, but is not currently being controlled by the player, then this will cause the script not to fire and it is possible that the NPC could be killed without the integer being incremented, which would create a bug.

you probably would want to check if oPC is in the player's faction. try this code instead:

If(!GetIsOwnedByPlayer(GetFactionLeader(oPC))) return;

this checks the faction leader of the last hostile actor and sees if it is owned by the player. If the last hostile actor is a party member, then this will be true and the script will fire. If the last hostile actor is not a party member, then the script will return.

Note, If there are other members of the Defender faction who could possibly kill one of the NPCs, then this script might fail if the other Defender who isn't a party member kills the NPCs.

#5
Dann-J

Dann-J
  • Members
  • 3 161 messages
You might have more luck with GetLastKiller().

There's really no need for 'if (!GetIsPC(oPC)) return;'. If someone other than the PC kills one of them, then the killer just gets another local integer of their own. In fact, you could then compare kills between party members (there was a quest like that in Dark Waters).

#6
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
If PJ is going to be checking for the local integer on the PC later, then it might matter who gets the kill. I think it may be easier just to store the integer on the area, that way it doesn't matter who kills the guy, just so long as he/she dies. then PJ can just check the local integer on the area.

#7
Dann-J

Dann-J
  • Members
  • 3 161 messages

M. Rieder wrote...

If PJ is going to be checking for the local integer on the PC later, then it might matter who gets the kill. I think it may be easier just to store the integer on the area, that way it doesn't matter who kills the guy, just so long as he/she dies. then PJ can just check the local integer on the area.


If it's an overall count that is wanted, then the template GroupOnDeath script (or some similar name) in the toolset has a function that keeps track of the number of group members that are dead. It requires adding all the potentially hostile NPCs into a group first, which is easy if they use a custom faction - an OnSpawn script that uses FactionToGroup() for at least one of them does the trick (I'm using that in a mini-module I'm building at the moment). Putting them all into a group also makes turning them hostile all at once very easy, as there's a function for that as well. The group include file is one of my favourites.

If you want to keep a tally of how many the PC specifically killed, then the local int doesn't need to check who was responsible for the kill at all. Different local ints just get written to anyone who killed one of them.

As for storing a local int on the area - you might as well be using SetGlobalInt().

#8
PJ156

PJ156
  • Members
  • 2 980 messages
Thanks for the answers guys, I need to digest these this evening.

I put the script on the on damaged location so I guess from M.R's questions it may be counting up the script, thus it might well be working but the final value of the integer is way higher than anything I am testing for.

Getlastkiller() on the on death script hook seems to be more appropriate.

Thanks guys I will look at this later.

PJ

#9
M. Rieder

M. Rieder
  • Members
  • 2 530 messages

DannJ wrote...


If you want to keep a tally of how many the PC specifically killed, then the local int doesn't need to check who was responsible for the kill at all. Different local ints just get written to anyone who killed one of them.


Wait, I'm confused now.  If the local integer is being stored on the killer, then doesn't PJ need to know who the killer is if he is going to check the local int later on to see if the NPC has been killed?  If the integer can be stored on any number of different objects and PJ doesn't have a way to be sure of which object it is, then how can he reliably check the integer? 

#10
bealzebub

bealzebub
  • Members
  • 352 messages
I have a similar problem, and think PJ's solution may be mine as well. This script works fine as long as the PC is the killer. If it's a companion, or a pc possesed companion- forget it. I put this OnDeath, to start a conversation... on death. Any ideas?

void main()
{
object oPC = GetFirstPC();
while (GetIsObjectValid(GetMaster(oPC)))
   {
   oPC=GetMaster(oPC);
   }
if (!GetIsPC(oPC)) return;
object oTarget;
oTarget = GetObjectByTag("t16b_slave");
AssignCommand(oTarget, ActionStartConversation(oPC, "t16b_slave"));
ExecuteScript("nw_c2_default7",OBJECT_SELF);
}

#11
The Fred

The Fred
  • Members
  • 2 516 messages
The thing is that the OnDeath event is a bit wonky. If someone else kills the NPC, you have a situation where either a) the local is getting set on someone who's not the PC or B) the local's not being set. This would make the quest uncompletable, even if the PC did do the killing, they just didn't land the final blow.

Firstly, you can check to see if the killer is in a PC's party and set it on the faction leader and/or all party members.

However, if you just want to know that the NPC has been killed (and don't really care that it mightn't have been the PC themselves) you could, as suggested, store the local on the area or even the module.

GetLastKiller() is more appropriate for an OnDeath event, but given the above, it might be more prudent to skip this entirely.

#12
M. Rieder

M. Rieder
  • Members
  • 2 530 messages

bealzebub wrote...

I have a similar problem, and think PJ's solution may be mine as well. This script works fine as long as the PC is the killer. If it's a companion, or a pc possesed companion- forget it. I put this OnDeath, to start a conversation... on death. Any ideas?

void main()
{
object oPC = GetFirstPC();
while (GetIsObjectValid(GetMaster(oPC)))
   {
   oPC=GetMaster(oPC);
   }
if (!GetIsPC(oPC)) return;
object oTarget;
oTarget = GetObjectByTag("t16b_slave");
AssignCommand(oTarget, ActionStartConversation(oPC, "t16b_slave"));
ExecuteScript("nw_c2_default7",OBJECT_SELF);
}




Change object oPC definition.

object oPC = GetFactionLeader(GetLastKiller());  //this is a better way of identifying the PC, unless there is a chance that someone not in the party may kill the NPC,


you also must clear oPC's actions before the conversation can happen.  use this code:

AssignCommand(oPC,ClearAllActions(TRUE));


You also might want to put a little delay in before the conversation starts, to make it less abrupt.  I usually do about 3 seconds.  If you do a delay, sometimes you have to clear all actions 0.1 seconds before starting the conversation. 

Modifié par M. Rieder, 25 octobre 2011 - 02:39 .


#13
Lugaid of the Red Stripes

Lugaid of the Red Stripes
  • Members
  • 955 messages
For a solo mod, GetFirstPC() will always work, no matter how much people hate on it.

#14
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
Don't let Pain find you you said that! ;)

#15
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
Lugaid, we should be educating scripters on the proper way to do things, not just throwing out something that works despite it being incorrect. The suggestions would improve the given script, and suggesting GetFirstPC as being valid when those suggestions are improvements, which actually solve stated problems with companions seems a bit backwards.

Now let me ask as i am just poking my nose into this, what event are these scripts being used in, is it on death event? Are you using the NPC on death event ( nw_c2_default7.nss ) or the PC focused one which is a module event.

I am a bit confused by what exactly is wanted to happen.

Is the PC the one who kills the npc, or do you want it to be the main actor involved? ( sometimes getnearest with a PC only filter tends to work as well )

Do you have a way to inspect the variables which are being applied on the target to see if they are working.

#16
Claudius33

Claudius33
  • Members
  • 256 messages
Hello PJ,

I haven't understood is the PC must absolutely be the killer or not. I'm assuming it has not importance. I assume also you want to know who was/were killed and not how many were killed.

As for the GetFirstPC(), I confirm it totally works for a solo campaign, but I won't enter in the debate.

You do not need to attach a local variable to the PC, unless you need the variable in another module (.mod) of your campaign.

You can use any creature or placeable in the module to carry the local variable. I use often signs.

Let's say you a sign tagged 'sign' (or whatever tag suits better), property 'plot' set to TRUE (so it can't be damaged). Attach an integer variable called 'Count' (or whatever other name), initialized at 0.

Attach an integer variable to your 3 NPCs called 'Value'. Intitialize it to 1 for the first, 2 for the second, 4 for the third.

Now the script put on the onDeath of all tree NPCs becomes pretty obvious :

void main()
{
object oSign = GetObjectByTag("sign");
int n = GetLocalInt(oSign, "Count") + GetLocalInt(OBJECT_SELF, "Value");
SetLocalInt(oSign, "Count", n);

// note you can add ExecuteScript("nw_c2_default7", OBJECT_SELF)
// if you want the standard script to be run in order to alert the other hostiles
}

You can now test in your convo : gc_local_int("Count", value to test, "sign") assuming the convo takes place in the same module as the fight.

Advantage: you can add/remove hostiles at will, the script won't change. Of course your convo will have to be amended.

#17
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
Or you can attach your script name as a variable on the npc with value of "DeathScript", this is triggered by nw_c2_default7. This would use existing features to do the same thing, and i don't think you want to remove the actual event and the jobs it's doing. What you are describing is basically flip flopping how things are done at the moment.

I suggest using a waypoint to store those variables for the sign, less memory/load time overhead on the module for the player, unless you are using a something which has some other purpose like if it's an actual sign, and it has it's own getter function as well i believe ( which means the game does not have to work as hard to get it ). A ipoint placeable is useful however since you can set a custom heartbeat on the fly. You can create waypoints/placeables on the fly as needed as well, which is useful for scripters making a script they can just use without needing to prepare a module to work with it. ( note that waypoints, placeables, module all can be used, problems happen when you have vast numbers of variables on a item, or vast numbers of waypoints or placeables which affect performance, so i suggest using multiple techniques to prevent any single way of doing things getting out of control. )

Another tactic is to use the journal as well since you are in SP, which is kind of what that feature was designed for. There is a parameter to set the journal entries for everyone in the party. And you can put in the integer value of the journal to match how many are killed. This would be much better experience for the player.

#18
PJ156

PJ156
  • Members
  • 2 980 messages
Thanks again for all the posts. I have been banished from modding time till Thursday eve so I will look at this again then. This is a solo mod so I think who does the killing is not going to matter however I have had the issue on onther mods so I have takent he points made on board.

I shall use the one death script and uyse getlastkiller() I think.

I shall use an I point rather than the PC to carry the integer as this seems better practice.

As to what I am wanting to do:

The character approaches a building outside which are three hostiles who thay can hunt and kill or sneak past. Inside they enter into a conversation, I want the pc node to have the option to say I killed three two or one of the bad guys. Looking at the conversation above I may further develop that by allowing them to say i killed the one out the front or the one out the back etc.

Thanks again for all the help this has been useful.

PJ

#19
bealzebub

bealzebub
  • Members
  • 352 messages
I'm going to try getlastkiller() and follow M. Reider's recomendations. Thanks for the help, and thanks PJ for letting me piggy-back your topic.

#20
M. Rieder

M. Rieder
  • Members
  • 2 530 messages

painofdungeoneternal wrote...

Or you can attach your script name as a variable on the npc with value of "DeathScript", this is triggered by nw_c2_default7. This would use existing features to do the same thing, and i don't think you want to remove the actual event and the jobs it's doing. What you are describing is basically flip flopping how things are done at the moment.


Ahhh yes... the DeathScript.  I keep forgetting about that.  I really need to start using that. 

#21
Shallina

Shallina
  • Members
  • 1 011 messages
if you plan on doing a single player campaign with MP as a bonus, GetFirstPC() is the way to go beceause it always works whatever is the event and will save you a lot of time.

If you plan of having Multiplayer working, you need to check wich is the one calling the event and so on, but if you want to do it this way, know that that for some event it's not the proper "event marker" that works. for exemple on use and on click can be mitmatched and so on.....

Doing a script solid for MP is great, but it's more time and more difficult. Pain would want everyone scripting like that, it's sure a great way of doing it, but I think it's too difficult for someone who is just starting  "advanced scripting".

If it's a first "scripted" project, start by doing it SP with GetFirstPC(). You ll learn to use the event, and you'l be able to do a proper MP one next time. Catching event right is not something "trivial", even if it's the proper way to do it.

It's better to do something "bulky" but wich works, than trying to do things as a pro but fail.

Also for some events you won't be able to do it without GetFirstPC() or you'll need to put in place something very difficult..

Better to start easy and have things working and then going to more difficult way of handling things.

if you are starting to do full script on your own you'll got more than enought worry without having the one to check if you catch the event propely. Before beeing beautifull a script need to work.

Modifié par Shallina, 26 octobre 2011 - 07:42 .


#22
kevL

kevL
  • Members
  • 4 052 messages

Shallina wrote...

Doing a script solid for MP is great, but it's more time and more difficult.

for sure! There's about a dozen functions, many of them poorly described ... and maybe it's just me again, but I got a return for GetFactionLeader( ) as the controlled Companion recently ...

- another jaw-dropping moment in NwN2 toolset ..

#23
Shallina

Shallina
  • Members
  • 1 011 messages
As soon as you use henchmen, scripting without GetFirstPC() and with only event catcher can becomme a real headhache. Because some event return the NPC and player and others only players.

And when you need a function on a henchmen to be MP proof, and it works only on player, you are in to rewrite it fully.

#24
kevL

kevL
  • Members
  • 4 052 messages
i think I went with GetOwnedCharacter( ) ....


( i shudder at the thought of defining "henchman" )

#25
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages

Shallina wrote...

If it's a first "scripted" project, start by doing it SP with GetFirstPC(). You ll learn to use the event, and you'l be able to do a proper MP one next time. Catching event right is not something "trivial", even if it's the proper way to do it.


The real problem is that if you don't understand the events, your code is not going to work as you intend no matter if you get it to compile or not. This issues are not obvious in initial testing, when you assume there are no companions, summons, or that there is only one summon. But all the players want to be able to add features willy nilly to your SP module. Unlike MP where i control everything and test it all, it's much more crazy, and from what i've seen the SP code out there tends to really need help from skilled scripters just to ensure those players have a good experience.

Really i set up that list of functions to make it easy, so as to help people learn the correct function. I ask what is the event, and reply back with a copy paste from that thread. This is very situational and i would just ask that new scripters who are not sure about how to do things post a question. Generally you will get far more advice on what to look out for, for each event.

What i suggest is looking at what others have done. Usually the top of the script has things like 

oPC = OBJECT_SELF;
oPC = GetWhatever();

The thing is this is a death script, the first thing you should do is look at a death script prior to anything else. Not sure where getFirstPC() is even brought up, just open the event on a given creature, and see what script code is currently in use. Generally all the knowledge you need is right there at the top.

GetFirstPC() does have some usage, but so do heartbeats, it is used far far too often in SP coding and it seems that some think a lower standard where things are not working is acceptable.

for companions and such

object oPM = GetFirstFactionMember( oPC, FALSE );
while ( GetIsObjectValid( oPM ) == TRUE )
{

oPM = GetNextFactionMember( oPC, FALSE );
}

and to get them one at a time ( there is a third parameter if there are multiple ones but usually it's restricted to one, you can do a for loop to iterate them all if needed )
// Get all the possible associates of this PC
object oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC);
object oDomin = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC);
object oFamil = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC);
object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC);
object oAnimalComp = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC);

example of when you have more than one ( this gets all the total fiends you have summoned. )
int SCGetControlledFiendTotalHD(object oPC = OBJECT_SELF){    int nTotalHD;    int i = 1;    object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);    while(GetIsObjectValid(oSummonTest))    {        if( CSLGetIsFiend(oSummonTest) )        {            nTotalHD += GetHitDice(oSummonTest);        }        if(DEBUGGING)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC);        i++;        oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);    }    return nTotalHD;}