Aller au contenu

Photo

Which is more efficient and less bug prone: GetFirstPC() or GetOwnedCharacter() ?


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

#1
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
I discussed with someone, MasterChanger, perhaps, about different ways to identify objects and was told that GetFirstPC() should only be used for debug commands.  It seems that this would suggest that GetOwnedCharacter() would be a better way to identify the PC.  Is this the case?  Thanks for any help.

#2
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
I think this was discussed in this thread

It depends on the script, there is no one answer. Master changer is correct, GetFirstPC is used right before GetNextPC to loop thru all the players, to just use it to get the first PC only works if there is ONLY one PC. While it might work, perhaps if you only do SP things, well it also is going to cause obscure bugs which are not going to be fixed until you use the correct functions. Might even seem to work in SP until you get players possessing familars, companions and henchmen.

In casting a spell the PC is OBJECT_SELF. But it can also be an NPC. It is also sometimes the speaker in a conversation. Each type of script makes WHO the pc is, vary, even OBJECT_SELF can vary who it refers to. Frankly the answer will vary, and cannot be intelligently answered unless more is specifically known about what you are doing. You HAVE to refer to a reference of some sort which tells you how to refer to the actors based on the type of script that is being run.

To a large degree you want a script to be usable by a NPC just like they were a PC once the AI is extended to allow that to happen, which means the question should not be who is the PC, but who is the caster, who is the speaker, who is the target, who is the item activator, etc.

GetOwnedCharacter will also not do the currently controlled character, but the character the player joined the game with. I think i've used that once at most, it just is not the correct solution since if they possess someone it will make what happens not make any sense.

I would suggest reading thru how to reference objects each time you want to know HOW to refer to things in a script, or look for other scripts which others have done which are similar to what you are doing.

#3
Shallina

Shallina
  • Members
  • 1 011 messages
GetFirstPC() should be avoided if you plan to have your work able to support multiplayer.

The both works very well.

#4
GhostOfGod

GhostOfGod
  • Members
  • 863 messages

Shallina wrote...

GetFirstPC() should be avoided if you plan to have your work able to support multiplayer.


Unless of course you are using it in a loop to get all the players in a multiplayer environment. ;)

#5
Kaldor Silverwand

Kaldor Silverwand
  • Members
  • 1 585 messages
I don't see why using GetFirstPC is an issue. If you use it in single player then it returns the first player created character in the party. It shouldn't return henchmen or companions or other faction members because they aren't PC's. In multi-player it should (does it really?) return the first player created character that joined the game. So if you want to store variables on an object or you want to identify the main PC, I don't see a problem using it in either single player or multi-player.

If there is a better option for multi-player, then what is it?

Regards

#6
The Fred

The Fred
  • Members
  • 2 516 messages
Couldn't the first PC leave the game, meaning all the variables are lost? This could really confuse things, as the first PC wouldn't always be the same PC.

GetFirstPC() does indeed work fine in SP but even without trying not to use it, I don't think I've ever had need to do so. It's usually just more appropriate to use something else, like GetPCSpeaker(). As long as you are careful it should be fine, but you also have to take into account the fact that one day, you or someone else just *might* try to convert this to MP, and you're creating an extra headache for yourself/them, it's just kinda lazy really.

#7
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
There are instances where I script things in in the environment to be useable and cause damage to opponents. For example, I have a crate that will explode when it is damaged and will create a fireball effect there.

In order to make sure that the damage shows up properly and that the PC gets XP for any opponents killed by the explosion, I have to make the PC run the damage script. How would I reference the owned PC object without using GetFirstPC()? I didn't see any references in the thread on referencing objects. Would I have to make a loop and check all the objects in the vicinity to see if they are owned by the PC?

#8
Kaldor Silverwand

Kaldor Silverwand
  • Members
  • 1 585 messages
Fred,

I object to the term "lazy". None of us are lazy. Please explain how GetPCSpeaker is going to help in a situation where you need to store general (not character specific) variables persistently. The PC Speaker is the character engaged in a conversation with an NPC. That may not always be the same character (it is in the OC when playing single-player, but it need not be in other campaigns). Using global variables doesn't always work because they are stored independently of saved games. I believe the common practice is to store the variables on the FirstPC. That works. I don't think GetPCSpeaker would unless only one PC is ever allowed to speak to NPCs.

On what persistent object can party variables be safely stored (for example, the last time the party rested)? Is there any?  Conversations with NPC's are the easy case because you can store variables on them and use node settings. We need a solution for the harder case.

Regards

Modifié par Kaldor Silverwand, 26 avril 2011 - 03:11 .


#9
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Hey M, You still haven't said whether or not this is single or multi player(or did I miss that?).

If this is a single player module then there are a lot of things that would work fine using GetFirstPC();.

As far as pure efficiency gos I'm not sure which is better. Not sure how different the engine handles things like GetPCSpeaker or GetFirstPC. GetFirstPC seems efficient enough. Just grabs the first PC on the PC list.

As far as your exploding crate gos...Is this when the player damages the crate? If so then you can use GetLastDamager to find who damaged it.

One other trick to single player that you could do is GetObjectByTag("");/GetNearestObjectByTag("", oTarget); As long as all the objects in your module have a tag this will only return the player since players do not have tags. However this might not be as efficient since I believe the module has to search through everything till an object with no tag comes up. But it does come in handy sometimes using the GetNearest version.

Like Kaldor I don't really have any issues when using GetFirstPC and use it for quite a few different things...mostly just debugging though. ;)

Modifié par GhostOfGod, 26 avril 2011 - 03:58 .


#10
MasterChanger

MasterChanger
  • Members
  • 686 messages
Ha ha, I don't think I had any part in that original conversation. But If have seen far, it's because I have scripted atop the shoulders of fire giants, and frost giants, and maybe even a cloud giant or two... :P

Basically, unless I'm just testing stuff with a single character, I use GetFirstPC (with a GetNextPC, as Pain notes) as a last resort. I keep in the back of my mind that I can always fall back on using GetFirstPC if no more exact or elegant way of retrieving the PC in question is available.

To give you an example, sometimes when I've layered scripts several layers deep with UI's the last script will need to have access to the target identified by an earlier script in the chain. It would not be easy for the last script to re-identify the target object in question, and it's inconvenient to pass the object directly from the script that has access to it because there are XMLs in the way. So, I store the target as a local object and have the last script in the chain retrieve that.

In your situation of firing a fireball...isn't there an area (or more exactly, a Shape) within which damage will be caused?

#11
Shaughn78

Shaughn78
  • Members
  • 637 messages
MC, the firball script the M.Rieder is using sounds like he is IDing the PC as the source of the fireball. It likely uses the area/shape to id creatures to damage. But having it come from the PC, they will get the xp if a creature is killed by the fireball.

Instead of GetFirstPC with that have you tried GetFactionLeader(oDamager)?

#12
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages

Kaldor Silverwand wrote...

I don't see why using GetFirstPC is an issue. If you use it in single player then it returns the first player created character in the party. It shouldn't return henchmen or companions or other faction members because they aren't PC's. In multi-player it should (does it really?) return the first player created character that joined the game. So if you want to store variables on an object or you want to identify the main PC, I don't see a problem using it in either single player or multi-player.

If there is a better option for multi-player, then what is it?

Regards


There is no one answer to how to refer to the characters, it really varies, hence i link to a forum post which answers how. GetFirstPC's only benefit is that you don't really have to learn how to use the particular type of script, there is for example no reason on earth to use GetFirstPC in a spell script, OBJECT_SELF, getspelltarget() or something along those lines are the ways to reference things in spell scripts. The speaker and OBJECT_SELF, or a string which is passed in via a GA script are what you use in conversation scripting. If you acquire an object there is again a custom function. You have to learn how to use the engine properly in each case.

The issue is you can get away with GetFirstPC for the above, which will work in testing, but it's not correct, it just works if the tester happens to be the first PC. If something else is using the given event, well then you get really hard to track down errors and events which seem to be happening at random. You could just say it seems to work, but i don't see how you can call it correct if there is in fact a correct function or OBJECT_SELF which is designed to work with the particular script type.

If i see code for GetFirstPC, and it's not a loop with GetNextPC, it is a HUGE red flag that the person does not really understand how to refer to objects inside scripts. I will replace it with the correct function. They will generally have issues which they don't really know the cause in their module. While there is nothing really "wrong" with using that function, it actually does what it says, it's that scripting requires a lot of attention to detail and there is almost always a more correct function you should use. Likewise there are functions for each event in the module, and generally you can just compare to what is already there to find the correct function.

The issues created by this often are impossible to see, and only show up when you use the scripts for something the developer was not specifically testing for, such as when the NPC starts using the given spell after you figure out much later how to do AI scripting ( or you upgrade to TonyK's AI which also caused a lot of issues ), then you find really odd issues with targeting. At this point its often very difficult to get everything working correctly. Many of the bugs which i've fixed for SP games are only visible because i've sat there with another player on a MP server and cast the spell at them, and we each verified the spell was working properly. In SP it's very difficult to even notice a lot of the issues, but that does not mean they are only happening in MP, once you locate an issue it's painfully obvious while you are playing.

Now if you do SP scripting, well that is not a good excuse to use this since there are MANY players who like to play the OC, MOTB or SOZ cooperatively, and the second you use GetFirstPC it really creates an issue. There is NO difference between how i code for SP and how i code for MP, the engine is the exact same, it's just used differently, and to do things properly is not very hard to learn. Every SP module can be played cooperatively as a LAN game, and by not coding properly you are artificially limiting your player base.

The only exception though, is if you are debugging things, and you are not sure how to refer to objects in a new type of script. Doing a SendMessageToPC( GetFirstPC(), "Target object is"+GetName(OBJECT_SELF) ); can be very helpul, however those all should be commented out after your testing is done.  

No one is saying anyone is lazy, but using GetFirstPC() outside of a loop is a shortcut plain and simple. If i was digging a hole, i would want a shovel, and yeah a hoe or a knife is useful for digging holes, i still know i should use the tool which is advertised as solving the problem i am trying to solve. Showing the hole i dug with a knife, and how deep it is, well just because you can make things happen with the wrong tool does not make it the correct tool. Look up the script type in question, and read which functions are set up to deal with objects in that script type, and i can guarantee that you will never need to use GetFirstPC() again outside of a loop ( or in quick and dirty testing ).

Modifié par painofdungeoneternal, 26 avril 2011 - 10:18 .


#13
Kaldor Silverwand

Kaldor Silverwand
  • Members
  • 1 585 messages
pain,

There are times when you want to store a persistent variable representing the state of something that has happened to the party. How would you do so? On what object? How would you identify that object?

Regards

#14
MasterChanger

MasterChanger
  • Members
  • 686 messages

Shaughn78 wrote...

MC, the firball script the M.Rieder is using sounds like he is IDing the PC as the source of the fireball. It likely uses the area/shape to id creatures to damage. But having it come from the PC, they will get the xp if a creature is killed by the fireball.

Instead of GetFirstPC with that have you tried GetFactionLeader(oDamager)?


That seems perfect. To figure out how to grab the object you want, it helps to start out with words or spaghetti code:

I want the dude that broke this box, or the dude that's the boss of the first dude

(just reporting my thought process here ;)).

Good programming/algebra/experiment design requires coming up with a clear idea of what you want to find out, and then creating operational definitions for those pieces of information. That's why there's no one answer for the "best" way to retrieve a specific PC object in all cases. As Pain's list shows, the question depends on the event that precipitates the question. If you want something to happen when an object is clicked, you probably want the object to ask who clicked on it. Or when a box is broken, its dying thought would likely be, "Which Talos-lovin' fool did that?!"

#15
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
All of these responses have been very helpful in helping me understand the importance of being more precise in my scripting. I think the longer discussions explaining the many implications and dangers of using GetFirstPC() instead of other identifiers helped me grasp it a little better. Thanks for your many responses.

@Pain,

I know I made you repeat yourself about 5 times, but I finally got my head around it. Thanks. The point I was missing is that there is almost always a different way to identify the object you want. It is a simple concept but I seemed to keep missing it. I got it now, though.

Of course, now I wonder what evil is lurking in the five score scripts I have already made for my module. Thank god it is SP. At least I know now and won't repeat any mistakes.

Modifié par M. Rieder, 27 avril 2011 - 12:44 .


#16
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
Well it's all a learning curve, you should see me when skywing repeatedly tells me something, and eventually I have an aha moment.

The other issue is while people ask questions, they just don't understand what they are dealing with enough to know which questions to ask.

Most of my first scripts are pretty bad, and my way of coding is one of repeatedly re-writing things over and over, ( refactoring ), with the goal of periodically rewriting everything. It's ok to have things which are fugly, which work, you should take some pride in that. But as you learn new things, you need to revisit the old, because your idea of what is bad keeps growing as your standards keep getting better.

The one thing i wonder though, is why more folks are not in IRC. The forums are just too slow. The really old school scripters, the grumpy ones like elven, zebranky, and their ilk, who poke fun at any script you put on pastebin seem to be underappreciated. While it might not always be enjoyable to have things nit picked apart, they are much much further along on the road of oops, and hearing their comments will allow you to sidestep a lot of future land mines you just have no concept at this time that can be an issue. They also can help you come up with a game plan for implementing what you are working on which prevents a lot of going in circles.

#17
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages

Kaldor Silverwand wrote...

pain,

There are times when you want to store a persistent variable representing the state of something that has happened to the party. How would you do so? On what object? How would you identify that object?

Regards


The question you ask cannot have a answer because there are many possible solutions, and of those the most appropriate should be chosen.

There is a fundamental flaw most technical people succumb to, where they focus on a specific solution before they even truly understand the underlying problem. It's akin to the carpenter holding a hammer, who sees every problem as a nail. Usually when folks ask me a question they are hammering away and wondering why it's not working, not realizing the question itself is itself part of the problem.

The correct answer often just depends on too many things, how portable you want to be, and which advantages/disadvantages are most important to you.

Variables can be stored on the party leader, on the module itself, or in a database, or stored on all members in the party. You can also store things on the quest giving NPC, or a custom object created just for this ( an ipoint object ). I use custom objects extensively as it speeds up lookups ( the engine has to iterate all the variables when you get a local var, which is very quick but i have massive tables caching the data in 2da files ).

The journal is actually built to handle this, but it's a bit of a pain-and i personally avoid it. However it's got the best UI solution for the player, the player knows to look for things there.

You can also use the campaign database in SP, or keep track in a mysql table on a PW.

Other alternatives are storing things as history feats, or just building the logic into the module itself, if this object/creature exists the quest still is in this stage. Once the dragon is slain then the townsfolk can offer new things because the dragon object is no longer there.

There also is the old fake heartbeat, where you just loop a value with a delay, and store the state in that.

But i really don't know what you are doing, i'd have to know way more to answer. I would suggest discussing this on IRC sometime, that way it won't just be my opinion, but you can get a broader perspective.


While everyone is listening, i have to mention skywing has been upgrading the torlack compiler so it removes a number of bugs the official compiler is leaving in compiled code. Mismatched prototypes are creating buggy compiled code, as are global variables in the AI, and a few other things. ( i think i mentioned this before but these are a big problem, this will actually throw errors when they are incorrect with this compiler ) These are bugs which can only be removed by using his new compiler, which is going to also be a toolset plugin, and integrate with his improved resource loader, and of course your compile speed will go way up. I am trying to point out the various issues so there are no drawbacks. Just a heads up, but i would say this new compiler revision is as much a requirement for scripters as the xp_bugfix is required to just run a Pw.

#18
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

Kaldor Silverwand wrote...

There are times when you want to store a persistent variable representing the state of something that has happened to the party. How would you do so? On what object? How would you identify that object?


Hi Kaldor,

The module I am building is designed for MP play and I have need to store variables across modules. Unless I am misunderstanding what you ask, or have misunderstood the function, using SetGlobalInt works fine. Is there any reason why you have not used this? (In my own testing, a global int set in this way persists across a module changeover. Is there a situation that you know of where it is lost?)

Furthermore, using the journal acts in a similar manner. i.e. The journal, if used to update each player progress, updates a local int on the player with respect to the quest in question.

A more difficult thing to track that I have found in a MP game with more than one module is plot objects. For this, I had to write two "plot objects" tracking scripts that used every PC and the modules between change overs to store the items as string variables. The scripts are then attached to actions where PCs either leave a mod, join a mod, acquire or unacquire objects, so that the "plot database" is kept up to date. Please note, however, the database is not external, but stored on the current mod in play.

painofdungeoneternal wrote...

There is NO difference between how i code for SP and how i code for MP, the engine is the exact same, it's just used differently, and to do things properly is not very hard to learn. Every SP module can be played cooperatively as a LAN game, and by not coding properly you are artificially limiting your player base.


I am surprised to find myself disagreeing with you on this aspect, unless I have misinterpreted your meaning. From my experience, coding for a SP game is very different from a MP one. For this reason, not every SP game can be played in a MP way. A careful attention to transitions, conversations, player states, etc, all play a big part on how you code between the two games. Maybe, if the game is very "standard" with very little scripting involved and one that does not use many variables, then one might get away with a SP game being able to be played MP as well, but I would be surprised not to find a script that did not work for the MP in a SP module. Take a look at my answer to Kaldor above as just one example of how scripting for MP requires much more work than a SP game.

Lance.

EDIT: As for using GetFirstPC, I have used it in a MP environment to check if there is a player in the game, or to simply gain a reference to the players irresepctive of the actual player.

Modifié par Lance Botelle, 27 avril 2011 - 11:58 .


#19
The Fred

The Fred
  • Members
  • 2 516 messages

Kaldor Silverwand wrote...
I object to the term "lazy".

I realised when I wrote it that "lazy" probably wasn't appropriate... what I mean is that it's often easy to use GetFirstPC() for everything, and this is lazy. Obviously there are times when this is not the case. Personally, though, I generally code for SP, and so persistance of variables isn't an issue for me.

M. Rieder wrote...
In order to make sure that the damage shows up properly and that the PC gets XP for any opponents killed by the explosion, I have to make the PC run the damage script. How would I reference the owned PC object without using GetFirstPC()?

Use GetNearestCreature(). If you set the parameters to CREATURE_TYPE_PLAYER_CHAR and PLAYER_CHAR_IS_PC, it essentially becomes GetNearestPC(). I think there is even such a wrapper in one of the include files, or at least it wouldn't surprise me.

#20
Eguintir Eligard

Eguintir Eligard
  • Members
  • 1 832 messages

Using global variables doesn't always work because they are stored independently of saved games.


Since when? I used a handful for islander and the game always worked start to finish (because the globals were stored with the saved game). If they werent the game would have failed to proceed from cut scene #1 on.

#21
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages

Lance Botelle wrote...

I am surprised to find myself disagreeing with you on this aspect, unless I have misinterpreted your meaning. From my experience, coding for a SP game is very different from a MP one. For this reason, not every SP game can be played in a MP way. A careful attention to transitions, conversations, player states, etc, all play a big part on how you code between the two games. Maybe, if the game is very "standard" with very little scripting involved and one that does not use many variables, then one might get away with a SP game being able to be played MP as well, but I would be surprised not to find a script that did not work for the MP in a SP module. Take a look at my answer to Kaldor above as just one example of how scripting for MP requires much more work than a SP game.


It does not require that much more work, you just cannot do short cuts which only work in SP like GetFirstPC() is the biggest issue. A module which is made for just SP will have people wanting to play it Co-op. Just a little bit of thought allows you to handle both. If i am looking at a SP games coding, i can quickly change things which i see as wrong, after i fix them it will work in SP and MP, and it will almost certainly be less buggy than before for the SP player.

This does not mean the module will make sense if it has more than one player if it's set up as a solo adventure any more than a PNP module will work if it is labeled as solo, but it not have major technical issues and will be able to handle any number of players.

Just look at how many people try to play the OC on a lan with their friends. This is not real MP play on a PW, it's a SP game which a husband and wife, or a few buddies want to play as a group, and the OC was never designed in any way for this. If you do modules you will have people like this, supporting this does not mean you are doing things anything like i do in a PW, and these players needs are much more akin to SP than anything else.

You said it yourself, the MP code should work with no issues in SP, but the SP code often will only work in SP. This is generally because the SP refers to object incorrectly in events. Correcting these is generally something a lot of SP module makers just don't pay attention to, and if they did then you'd give players more options on ways to play  your module.

Modifié par painofdungeoneternal, 27 avril 2011 - 01:08 .


#22
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

painofdungeoneternal wrote...

This does not mean the module will make sense if it has more than one player if it's set up as a solo adventure any more than a PNP module will work if it is labeled as solo, but it not have major technical issues and will be able to handle any number of players.


Hi Pain,

This is my point exactly. If the game does not make sense, then it is *not* set to be played MP. I do not want a game that plays "technically" but does not make sense, because the game has not be coded to play as it should in a MP environment.

To make it play and make sense in a MP environment takes a *lot* more care when coding. I know this from experience. I am fairly certain I would have knocked a considerable amount of time off my current coding for my module if I was only coding for a SP game. I have even blogged about the added difficulties in trying to code for a MP environment. Posted Image

What is the point of saying a game is playable as MP if it dies not make sense when it is played?

Lance.

Modifié par Lance Botelle, 28 avril 2011 - 11:34 .


#23
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
@Pain,

Is there a place where someone has written down considerations for MP play? I would like my campaigns to be accessible to MP if possible.

#24
Shallina

Shallina
  • Members
  • 1 011 messages
There are times when you want to store a persistent variable representing the state of something that has happened to the party. How would you do so? On what object? How would you identify that object?

Regards

You can use Global Var and not local var for this.

Global var can be accessed everywhere in the campaign and isn't module related.

As soon as you use GetFirstPC() in a multiplayer environement you are basically screwed for a simple reason, the GetFirstPC() could refer different player depending on many thing.

The only way GetFirstPC() can be sure to refer always the same player is when there is only one player.

But, there are case where you can't do without GetFirstPlayer(), and when that happend that mean you'll have a buggy MP. In BGR there are many case where I can't do without GetFirstPlayer for exemple. SOZ made use of GetFirstPlayer() as well , that's why it doesn't officially support MP.

Modifié par Shallina, 27 avril 2011 - 03:45 .


#25
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages

Shallina wrote...

As soon as you use GetFirstPC() in a multiplayer environement you are basically screwed for a simple reason, the GetFirstPC() could refer different player depending on many thing.

The only way GetFirstPC() can be sure to refer always the same player is when there is only one player.

But, there are case where you can't do without GetFirstPlayer(), and when that happend that mean you'll have a buggy MP. In BGR there are many case where I can't do without GetFirstPlayer for exemple. SOZ made use of GetFirstPlayer() as well , that's why it doesn't officially support MP.


Hi Shallina,

Actually, using GetFirstPC is perfectly OK to use in a MP environment as long as you only want to get *any* player in the party (as the player in the first PC slot may change). This is often required and a desireable use of GetFirstPC in coding, as there may not be any other way to get to a player in the game. In a HB script for example, where you need to get at least one player in the party to do stuff with.

Lance.

Modifié par Lance Botelle, 27 avril 2011 - 04:58 .