Aller au contenu

Photo

Getting the Main PC


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

#1
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
Working on getting the Main PC player. Working on redoing some of the scripts for the overland map which came with SOZ so they are more reliable and compatible with both single player and multiplayer, and started focusing on AddEpithetFeat which Volo uses to award feats.

This is more aimed at making things compatible no matter how the game is played, in a way that is transparent to the developer. It also ensures that those users who choose to play a single player module ( either the OC or another module created by the community using those OC scripts as many seem to do ) with a few friends will not be greeted with strange bugs. Of course those in multiplayer are already dealing with oddball issues since GetFirstPC() is in use throughout the game as it is.

Original code is basically as follows...
void AddEpithetFeat(int nFeatID){	object oPC			= GetFirstPC();	object oPartyMember	= GetFirstFactionMember(oPC, FALSE);		while (GetIsObjectValid(oPartyMember))	{		if (GetIsRosterMember(oPartyMember) || GetIsOwnedByPlayer(oPartyMember))		{			FeatAdd(oPartyMember, nFeatID, FALSE, TRUE, TRUE);		}		oPartyMember	= GetNextFactionMember(oPC, FALSE);	}}

Basically the above is only usable in Single player since in multiplayer GetFirstPC() can be someone not even trying to do the quest to begin with as you can have multiple parties. Since this returns a valid result in testing, and probably over half the time in actual mutliplayer play and never in a test module, it lead to very hard to find bugs. To replace this i came up with this, which finds the nearest PC if you are not in single player. I am assuming that if a script is running the player involved is near that script. The parameter allows you to gain some more control of that as well.
/**  * Use this instead of GetFirstPC() in modules. When in Single Player it retrieves the player easily as there is only one. However when in multiplayer it is often not clear which player is the correct one so it will get the nearest living player instead.  The target parameter allows you to specify who this is close to, otherwise it's object self.** Mainly intended in use for stories and plot related purposes, and allows code which only works in single player to also work in multiplayer** WARNING: do not use in a loop using GetFirstPC() and GetNextPC() as this will cause an infinite loop as each use of GetFirstPC() resets GetNextPC() to start over again.* @author Brian Meyer* @param oTarget ( Default* @return The main player in the module*/object CSLGetMainPC( object oTarget = OBJECT_SELF ){	if ( !GetIsSinglePlayer() ) // not single player then use this	{		return GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oTarget, CREATURE_TYPE_IS_ALIVE, CREATURE_ALIVE_TRUE);	}	

	return GetFirstPC(); // must be single player}


This makes the original function with minor changes work in both multiplayer and single player. Note that it replaces multiple functions which basically do the same thing, both AddEpithetFeat and AddTeamworkFeat are exactly the same with the only difference being the function name.
/**  * Adds feat to all members of the players party.* @param iFeatId Identifier of the Feat ( row number in feats.2da )* @param oPC Optional parameter ( normally assume this will not be supplied ) If not supplied then In Single player this is the player starting the game and his party, in multiplayer it's the closest player to where the script is fired.* @see * @replaces AddEpithetFeat, AddTeamworkFeat* @return */void CSLAddFeatToPlayersParty( int nFeatID, object oPC = OBJECT_INVALID ){	if ( !GetIsObjectValid(oPC) )	{		oPC = CSLGetMainPC();	}		// oTest = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oTarget, 1);	object oPartyMember	= GetFirstFactionMember(oPC, FALSE);		while (GetIsObjectValid(oPartyMember))	{		if (GetIsRosterMember(oPartyMember) || GetIsOwnedByPlayer(oPartyMember))		{			FeatAdd(oPartyMember, nFeatID, FALSE, TRUE, TRUE);		}		oPartyMember	= GetNextFactionMember(oPC, FALSE);	}}

// example usage in ka_banite_bane.nss
//	ka_banite_bane//  Grants the Bane of the Banites feat.#include "_CSLCore_Feats"
void main(){	CSLAddFeatToPlayersParty(FEAT_EPITHET_BANE_OF_THE_BANITES);}


#2
Shallina

Shallina
  • Members
  • 1 012 messages
You need to catch the player by events if you want to avoid the use of GetFirstPC. This won't always be easy but it's possible I think. Now if you got a script that access GetFirstPC without player in an Event, thing will becomme harder.

Now comme the real difficulty, if information are stored in GetFirstPC(), or if things are given only to GetFirstPC, or involve only the "FirstPC", you need to change the way it works or scrap the feature.

Finding an alternativ to "GetFirstPC" isn't always possible.

Also GetFactionMember result will probably depend on the order poeple have joined the group, and will change at each new module.

For a bulletproof MP scripting, it's something you can't use instead of GetFirstPC since it has almost the same limitations.

Modifié par Shallina, 08 janvier 2012 - 08:16 .


#3
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
I understand that, this is for those functions which exist in the vanilla game, which use the getfirstpc, soas to make it make more sense in more situations. Or when i find someone elses wonky code, who decide that getfirstpc() is how they are going to design things, and make it so it can handle a game where there is more than a single player logged in and have a chance of working.

#4
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
Thanks for this, pain, identifying objects is one of the most common source of bugs for me.

#5
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages
Hi Matt,

I had to write my own function that recalled the "Main PC" that was stored on each companion as they were acquired. This is because there were a number of issues with the OC that did not suffice for my needs. i.e. It was not always returning the PC I wanted.

Hre is the function code, but you need to add some code to ensure the Main PC is stored where it needs to be:

///////////////////////////////////////////////////////////////////////////////////////////////////
// object GetMainPC(object oPC); oPC can be the Main PC or Companion.
// The OC GetOwnedCharacter(oPlayer); is similar (CONSIDER USING INSTEAD)
// HOWEVER, This OC function only returns the Main PC when possessing the PC being tested, whereas my
// function returns the Main PC regardless of possession. i.e. The Main PC is always returned for tested PC.
// NB: Main PC object has been previously stored on the companion under "MYBOSS" variable holder.
// NB: Ensure this object variable is carefully kept up to date when adding or removing companions.
// Will return OBJECT_INVALID if used on a creature that is not player controlled.
///////////////////////////////////////////////////////////////////////////////////////////////////
object GetMainPC(object oPC);
object GetMainPC(object oPC)
{
// TRY TO FIND STORED PC FIRST
object oMainPC = GetLocalObject(oPC, "MYBOSS");

// IF FAILS, CHECK IF THIS IS THE MAIN PC
if(oMainPC == OBJECT_INVALID && GetIsOwnedByPlayer(oPC)){oMainPC = oPC;}

// IF FAILS, RETURN INVALID OBJECT
if(oMainPC == OBJECT_INVALID && !GetIsOwnedByPlayer(oPC)){oMainPC = OBJECT_INVALID;}

return oMainPC;
}

Lance.

#6
M. Rieder

M. Rieder
  • Members
  • 2 530 messages
Hi Lance,

That function looks handy. Do you store the object at the beginning of the mod or something?


I have been using GetOwnedCharacter(GetFactionLeader(memberofPC'sparty))) a lot for getting the main PC with good results so far.

#7
Lance Botelle

Lance Botelle
  • Members
  • 1 480 messages
Hi Matt and Pain,

(Sorry Pain, I had not realised you were also the original poster.)

Sorry about the late response, but you have probably seen the answer by now anyway. :)

I add/remove the variable when the player adds/removes a companion (as noted in the function comment).

The problem I was having when retrieving a Main PC was more to do with Multi-Player environments, when I wanted to retrieve any main "player" according to a PC being played. i.e. If there were two players playing in a game, and each of them was playing 3 companions, and each was not playing their original PC, how would you retrieve the second player's (not the host player) Main PC?

E.g. Have two players join a module, and then have both players possess a companion, and then try to get the second player.

I have made a distinction between Main PC between players and not just the host player. This function also still works if the players should swap *leadership*.

I went through hoops looking for a combination of official functions to get th Main PC for ever player for my design of mod, and this was *the only way* I found that worked.

Test your functions in the way I have said above ... and also test it if the players swap leadership. Then you will see the problems of getting the Main PC (correct player) according to companion played each time.

I also blogged about the issues I was having back in May 2009.

Lance.

Modifié par Lance Botelle, 23 janvier 2012 - 11:05 .