Multi-Human Player Delema

OrionVeteran

Deity
Joined
Dec 25, 2003
Messages
2,443
Location
Newport News VA
I have a tough one for you this time. I currently have a function that is making various changes to the current human player. Based upon a condition that becomes true, I need to run a popup function for each of the other Human players on their very next respective turn.

My Question Is: How can I get the function to run for each of the other human players on their very next respective turns.

One or more of the other human player's could be the same turn you are currently playing or perhaps the other human player's turns all start on the next game turn. I'm not sure how to approach this. :confused:

Orion Veteran :cool:
 
Have you tried sendModNetMessage and application of a QUEUED popup in the corresponding onModNetMessage?

I'm not sure how to make it work, as there are few examples out there. Here is one message I need to run separately for each human player:

Code:
def SetLH(self):
	# This popup allows change of Leaders
	player = gc.getPlayer(gc.getGame().getActivePlayer())
	popup = PyPopup.PyPopup(CvUtil.EventLHPopup, EventContextTypes.EVENTCONTEXT_ALL)
	popup.setHeaderString( "Select Leader" )
	szText = "Select a button to choose a Leader.\n"
	popup.setBodyString( szText )
	popup.addSeparator()
	popup.addButton( "Random Leader: You Only" )
	popup.addButton( "Random Leader: All Civs" )
	for i in range( gc.getNumLeaderHeadInfos() ):
		popup.addButton( gc.getLeaderHeadInfo(i).getDescription() )
	popup.addSeparator()
	popup.launch(False)

def LHBegin(self, argsList):
	self.SetLH()
	return

How do I use onModNetMessage to make this function run once for each human player?

Orion Veteran :cool:
 
Haven't used the ModNet extensively myself yet. But I have figured out a limited amount of how it work.

The player who the event happens for needs to have your python do exactly what it normally does for him, but ALSO send out the ModNetMessage. All of the other players will react to the ModNetMessage in whatever manner you tell them to. Or, wording that slightly differently, the player who SENDS the message, does not GET the message himself. Thus if you are trying to get something to remain in Sync, you need the code to happen twice.

Not quite as important of a point for you, since you are trying to get the other players to do something different. Actually it works in your favor because you don't want it to run for the initiating player anyway.


The tricky bit is that you only get 5 integer values to work with. Typically you use the first value to inform yourself how to handle the other 4 sets
ie:
if (iData1 == 5):
Run My Code....

Most likely you'll pass the player number with the iData2 so you know who initiated the code, so then you have 3 integers left to form your popup with. If there aren't any more variables you don't need them anyway. You'll just have the "Run My Code..." section above loop over all players and if any of them are human, provide a popup informing them whatever you wanted to inform them about.


Can't quite tell from the posted what is going on. Are you having all players change leaders, or just wanting to announce to all players when one of the humans changes out?
 
Haven't used the ModNet extensively myself yet. But I have figured out a limited amount of how it work.

The player who the event happens for needs to have your python do exactly what it normally does for him, but ALSO send out the ModNetMessage. All of the other players will react to the ModNetMessage in whatever manner you tell them to. Or, wording that slightly differently, the player who SENDS the message, does not GET the message himself. Thus if you are trying to get something to remain in Sync, you need the code to happen twice.

Not quite as important of a point for you, since you are trying to get the other players to do something different. Actually it works in your favor because you don't want it to run for the initiating player anyway.


The tricky bit is that you only get 5 integer values to work with. Typically you use the first value to inform yourself how to handle the other 4 sets
ie:
if (iData1 == 5):
Run My Code....

Most likely you'll pass the player number with the iData2 so you know who initiated the code, so then you have 3 integers left to form your popup with. If there aren't any more variables you don't need them anyway. You'll just have the "Run My Code..." section above loop over all players and if any of them are human, provide a popup informing them whatever you wanted to inform them about.


Can't quite tell from the posted what is going on. Are you having all players change leaders, or just wanting to announce to all players when one of the humans changes out?

I actually have two different conditions in multi-player where I have need for this function:

1. The first is the code I posted, which needs to be ran for each human player, at the beginning of their respective turns. The condition I have now is the code repeats itself once for every player on the first player's turn. So if I have 8 players, the code is repeating 8 times on the first player's turn. This is obviously bad and has denied the capability to use this function in multiplayer. I would really like to get this one fixed. The source code was in ver 1.6, the "Civ customiser", in Dales Combat Mod.

2. The second happens when an event occurs: The destruction of a Holy City. The AI Civs, with the same state religion will automatically declare war against the aggressor, human or not. This part of the code works really well.

The part that does not work: I want to ask each human theocracy player, who shares the Holy City religion, on that player's next respective turn, if the player will declare war against the aggressor, who destryed their Holy City. I have a popup designed to ask the question, but I don't know where to put it.

If the human player refuses to declare war against the aggressor, AI Civs, with the same religion will declare war against the human player, as the human player has betrayed the faith. If the human player accepts and declares war, then AI Civs, with the same state religion will have vastly improved relations with the human player. I can use the AIAttitudeAdjustment function to improve or anger relationships between Civs. My problem is where to put my popup message so that it will wait to run on the respective human player's next turn.

So there you have it, two big problems that could potentially be resolved with the onModNetMessage function. I just don't know how to code that function properly.

OrionVeteran :cool:
 
The Holy City one isn't too bad. Your INT values would be:

iData1 = Something to indicate you are doing the Holy City War function (let's say 200. You can use ANY value though, as long as nothing else tries to use it already, which shouldn't be the case since you don't have any examples to work off)
iData2 = iPlayer (who razed the city)
iData3 = iReligion (religion of Holy City Destroyed)
iData4, iData5 = Not required for this at all.


So in your function checking if the City is destroyed, you would do sendModNetMessage(200, iPlayer, iReligion, -1, -1)

Then in onModNetMessage you would have something like:

if (iData1 == 200)
if (ActivePlayer != iData2)
if (getStateReligion == iData3)
GiveHolyWarPopup to the ActivePlayer (with the war popup asking you to declare war on iData2 Player)


I'm not sure if you would be able to do anything fancy to keep track of the holy city name. Most likely it wouldn't be too easy to accomplish, but you don't really NEED that, it'd just be sorta nice.



Still not quite sure when you are wanting the other one to trigger, but it does sound like it is a bit more involved, so play with the religion bit first and get a feel for how to get things working. And don't forget you can play MP against yourself on one computer to test things like this. (use a shortcut to directly launch your Mod, and include the keyword "multiple" in the arguments of the shortcut: "C:\.....\Civ4BtS.exe" multiple mod="MyMod")
 
Take a look at BUG for code that will fire an event when a player's turn begins. CvMainInterface calls BugEventManager.fireEvent() passing in "BeginActivePlayerTurn" or something like that. You'll of course need to instead call your function.

So in onModNetMessage, you want to capture and store (in the game's script data or using SdToolkit) that on the next turn for the player it should fire the popup. Or perhaps you can make the popup queued as xienwolf suggested. I haven't tried that route yet. There are articles in this forum about using popups that require getting user input.
 
Oh yes, since you are dealing with human selections in the popup, you might need to sendModNetMessage to broadcast their answer when all is said and done as well. Though declaration of war should be safe since it is a base function for Civ which should have a built-in sync/announce system. But if you were doing anything else, like modification of relations, that might require a broadcasting to sync properly. In fact, that almost certainly requires it, and wouldn't show up in terms of anything that would cause an OOS for quite a long time, and would be hard to trace back to the cause...

So just make sure that whatever the player chooses from his popup is broadcast in another onModNetMessage

iData1 = 201 or something
iData2 = iPlayer (guy who had the popup)
iData3 = 0 or 1, whichever answer he selected
iData4 & iData5 useless again.

Remember for this one though that you have to run it in onModNetMessage for everyone who DIDN'T just make that choice, but run it somewhere else for the guy who made the choice to get the results on his own computer.

Hrm... don't like how I worded that last part... let me know if it isn't completely clear. As an illustrative example though:

Without ModNetMessaging: If Jack chooses YES, then Jack's computer is the only one who knows that, so the only one which gets the relationship shift done properly.

So you have to write a (send/on)ModNetMessage pair for Mary and Sam's computers to acknowledge Jack's choice and update their AI's attitudes toward Jack properly. But you cannot delete the original function which updated Jack's computer only, because Jack didn't send the ModNetMessage to himself, so if you delete the original function now Mary & Sam have properly updated Attitudes, but Jack himself does not....
 
I've never used ModNetMessage, but I'm wondering if the player that sends the message also receives it. If so, then you can move all the code that applies the effects to the receiving code and have everyone apply the effects. There would be no need to apply it for the sender in a different place from the receivers.

This would be a good thing to test quickly as it would make doing all your messages easier.
 
Ah, too bad. However, don't put the same code in both places. Instead, put the code into a module-level function and call it from both places.

Never duplicate code when you can avoid it.
 
Here is what I have so far:

Code:
CyMessageControl().sendModNetMessage(251, -1, -1, -1, -1)

Which calls this:

Code:
if iData1 == 251:
	# This popup allows change of Leaders
	iStart = 1
	iStop = gc.getNumLeaderHeadInfos()
	iStep = 1
	
	for iPlayer in range(gc.getMAX_PLAYERS()):
		player = gc.getPlayer(iPlayer)
		if player.isAlive() and player.isHuman():			
			popupInfo = CyPopupInfo()
			popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
			szText = CyTranslator().getText("TXT_KEY_OCC_LH_TEXT", ())
			popupInfo.setText(szText)
			popupInfo.setOnClickedPythonCallback("OCCLeaderHeadOnClickedCallback")
			for iLeader in range(iStart,iStop,iStep):
				popupInfo.addPythonButton(gc.getLeaderHeadInfo(iLeader).getDescription(), gc.getLeaderHeadInfo(iLeader).getButton())
						
			popupInfo.setData1(iStart)
			popupInfo.setData2(iStop)
			popupInfo.setData3(iStep)
			popupInfo.setData4(iPlayer)
			popupInfo.addPopup(iData2)

Which calls this:

Code:
def OCCLeaderHeadOnClickedCallback(argsList):
	iButtonId = argsList[0]
	iStart = argsList[1]
	iStop = argsList[2]
	iStep = argsList[3]
	szText = argsList[5]
	bOption1 = argsList[6]
	bOption2 = argsList[7]		
	iReal = 0

	for i in range(0, iStop, iStep):		
		iReal = iReal + 1
		if iButtonId == i:			
			iChoice = gc.getLeaderHeadInfo(iReal).getDescription()
			# Run command to change the iplayer to selected CIV
					
	return

All of this works nicely. What I need now is code that will execute the CIV change for the iplayer. Is there a changeLeader or ChangeCiv function available? :confused:

Orion Veteran :cool:
 
The leader type of each player is stored in CvInitCore, and it's not exposed to Python at all AFAICT. The only function that calls CvInitCore::setLeader() is CvGame::addPlayer(). You could modify the DLL to expose the function as a new function on CvPlayer, but I don't know if the game will react appropriately.

Edit: BTW, it looks like the code above will produce a popup for each human on each human's PC. Is that correct, and is that what you want?
 
The code actually calls up each player before the "dawn of man" popup is ever displayed. This is my attempt to convert a CIV custimiser to work with Multiplayer. Just as you said, I might have bitten off more than I can chew, as this line does not want to work:

Code:
gc.getInitCore().setLeader((PlayerTypes)iPlayer, (leaderHeadTypes)iChoice)

Are there any alternatives to modifying the DLL?

Orion Veteran :cool:
 
Can you explain more precisely what you want to accomplish? Do you have a 4000BC saved game that you want to play multiplayer with different leaders, but using the same map and starting locations?
 
Can you explain more precisely what you want to accomplish? Do you have a 4000BC saved game that you want to play multiplayer with different leaders, but using the same map and starting locations?

Sure. Basically I want to re-create the customiser that exists in Dale's Combat mod. Dale's customiser does not work in Muliplayer and that is what I want to do: Get the customiser to run completely before the "Dawn of Man" popup opens for the first Human player. This link is to Dale's Customiser mod component:

http://forums.civfanatics.com/showthread.php?t=237182

Orion Veteran :cool:
 
Did you look at how he did it? I'd think you could do the same thing. The difference will be that you need to broadcast each selection to the other players so they can all make it.

First, as you've done, broadcast the "pick your leader if you're human" message. As each human picks their leader, it sends out the "player X chose leader Y" message. All humans make that change on their on game for player X.

Is the first message even necessary? If you are playing with simultaneous turns, all humans should choose their leader on turn 0. If playing turn-based (can you?), I guess you'll need it. Can the other humans activate and use a popup when it's not their turn?
 
Top Bottom