[Lua] Finding if a specific screen is entered, killing it?

Irkalla

ENTP POWWWEEEEEER
Joined
Sep 25, 2012
Messages
1,009
Location
Way down around Vicksburg
I was thinking, instead of overwriting the files for a certain UI screen, why could I not catch the screen when it's triggered, kill it, and then bring my own screen up? The specific screen I want to do this to is the City-State diplomacy screen.

I've been scouring the wiki for stuff I could use, events and such. This seems to be rather fruitless.

Has anyone else had success in doing such things?
 
CityStateDiploPopup.lua

Code:
-------------------------------------------------
-- On Event Received
-------------------------------------------------
function OnEventReceived( popupInfo )
	
      [B]  -- Putmalk: do nothing, return
            print("Killed");        
            return;[/B]

	local bGreeting = popupInfo.Type == ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_GREETING;
	local bMessage = popupInfo.Type == ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_MESSAGE;
	local bDiplo = popupInfo.Type == ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_DIPLO;
	
	if(not bMessage and not bDiplo) then
		return;
	end
	
	m_PopupInfo = popupInfo;	
	
    local iPlayer = popupInfo.Data1;
    local pPlayer = Players[iPlayer];
	local iTeam = pPlayer:GetTeam();
	local pTeam = Teams[iTeam];
	
	local iQuestFlags = popupInfo.Data2;
    
    g_iMinorCivID = iPlayer;
    g_iMinorCivTeamID = iTeam;
	
	m_iLastAction = kiNoAction;
	m_iPendingAction = kiNoAction;
	
	if (iQuestFlags == 1) then
		m_bNewQuestAvailable = true;
	else
		m_bNewQuestAvailable = false;
	end
	
	OnDisplay();
	
	UIManager:QueuePopup( ContextPtr, PopupPriority.CityStateDiplo );
end
Events.SerialEventGameMessagePopup.Add( OnEventReceived );

CityStateGreetingPopup.lua

Code:
function OnPopup( popupInfo )
      -- Putmalk
      print("Killed");
      return;
end

This will prevent those popups from appearing (they won't be queued).

Then I would send a new popup (BUTTONPOPUP_MODDER_0) for example

Code:
Events.SerialEventGameMessagePopup( {
Type = ButtonPopupTypes.BUTTONPOPUP_MODDER_0
Data1 = Game.GetActivePlayer(),
Data2 = Players[23]
}
);

Those are example parameters and you'll have to find the exact ones you need (based on what you want them to do).
 
I was thinking, instead of overwriting the files for a certain UI screen, why could I not catch the screen when it's triggered, kill it, and then bring my own screen up? The specific screen I want to do this to is the City-State diplomacy screen.

I've been scouring the wiki for stuff I could use, events and such. This seems to be rather fruitless.

Has anyone else had success in doing such things?

Without replacing the core files, you can intercept that screen and close it (and call your replacement screen), or just replace some of it's elements by yours (that's what I use)

the event to use is Events.SerialEventGameMessagePopup as shown in Putmalk's post, but from another context, you don't want to close/change all popup, just the CS diplo screen.

So the code could either be
Code:
function UpdateCityStateScreen( popupInfo )

	if( popupInfo.Type ~= ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_DIPLO ) then
		return
	end
	-- do your changes here
	ContextPtr:LookUpControl("/InGame/CityStateDiploPopup/DescriptionLabel"):SetText( "Hello Brave New World !" )
end
Events.SerialEventGameMessagePopup.Add(UpdateCityStateScreen)

or
Code:
function KillCityStateScreen( popupInfo )

	if( popupInfo.Type == ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_DIPLO ) then
		UIManager:DequeuePopup( ContextPtr:LookUpControl("/InGame/CityStateDiploPopup") )
		-- Call your replacement screen here
	end
end
Events.SerialEventGameMessagePopup.Add(KillCityStateScreen)
 
Without replacing the core files, you can intercept that screen and close it (and call your replacement screen), or just replace some of it's elements by yours (that's what I use)

the event to use is Events.SerialEventGameMessagePopup as shown in Putmalk's post, but from another context, you don't want to close/change all popup, just the CS diplo screen.

So the code could either be
Code:
function UpdateCityStateScreen( popupInfo )

	if( popupInfo.Type ~= ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_DIPLO ) then
		return
	end
	-- do your changes here
	ContextPtr:LookUpControl("/InGame/CityStateDiploPopup/DescriptionLabel"):SetText( "Hello Brave New World !" )
end
Events.SerialEventGameMessagePopup.Add(UpdateCityStateScreen)

or
Code:
function KillCityStateScreen( popupInfo )

	if( popupInfo.Type == ButtonPopupTypes.BUTTONPOPUP_CITY_STATE_DIPLO ) then
		UIManager:DequeuePopup( ContextPtr:LookUpControl("/InGame/CityStateDiploPopup") )
		-- Call your replacement screen here
	end
end
Events.SerialEventGameMessagePopup.Add(KillCityStateScreen)

Here's an even better question, can I do this with notifications? I've got an idea as to how the AI can handle what I intend to implement, and that's to catch him doing one similar behavior, perform some sanity checks with Lua, and if so do the new thing. If insane, do original thing.

For the record, I'm trying to catch him bullying a city-state.

Modularity :P
 
Here's an even better question, can I do this with notifications? I've got an idea as to how the AI can handle what I intend to implement, and that's to catch him doing one similar behavior, perform some sanity checks with Lua, and if so do the new thing. If insane, do original thing.

For the record, I'm trying to catch him bullying a city-state.

Modularity :P

You can't catch a major civ bullying a minor civ without DLL interaction, since the action occurs in the source code and not through some notification (the notification is what is sent after the fact).
 
You can't catch a major civ bullying a minor civ without DLL interaction, since the action occurs in the source code and not through some notification (the notification is what is sent after the fact).

Well, I could catch the notification, kill it, then do my stuff, and if I wind up not doing my stuff, resend the notification, couldn't I?

The effects of the major civ bullying the minor civ, after I do what I intend to do, won't really matter unless they enslaved a worker. But does the AI ever do that?

Besides, I could just take the gold away from the major civ that bullied them. I'm not sure how to go about taking the worker back from them, though.

It's not like I'm wanting to hook, per se, I'm just wanting to find a reliable way to catch an AI player doing it; something that signifies that they've bullied a minor civ.
 
I actually change my answer slightly. No notification is sent out at all. You can't catch something that isn't there. You must modify the source directly.

Yes, the AI does bully for workers.
 
I actually change my answer slightly. No notification is sent out at all. You can't catch something that isn't there. You must modify the source directly.

Yes, the AI does bully for workers.

Alright, so I'll have to run some Lua stuff myself that emulates an AI deciding whether or not to do the stuff based on current AI flavors...

What I'm wanting to do is allow a civ to bully a minor civ into joining it. I was thinking, I could do this every turn:

  • Use the civ's current expansion flavor to seed a random roll for a boolean, which is loaded (rigged, etc.) depending on expansion flavor (via a loop, table, incrementor, and table.Random()) Sanitize our expansion flavor by clamping it 0-10. Odds are expansion flavor out of ten.
  • If this roll comes out positive, check and see if the player's happiness will be able to accomodate a puppet city of this size.
  • If it can, we loop through a minor civ iterator and check for strategically located (read: close) minor civs.
  • We then check these strategically located minor civs for influence
  • If not too much positive influence with them, check if they're receptive to bullying
  • If receptive to bullying, check their fear
  • If their fear exceeds a certain threshold, call city changed hands event with conquest set to false, send out a notification (Can I make a custom notification type with custom icon and all that jazz?)

The main thing I fear with this is that I won't be able to get a leader's CURRENT AI flavor.

EDIT: Seems as if my fears were well rooted.
 
Alright, so I'll have to run some Lua stuff myself that emulates an AI deciding whether or not to do the stuff based on current AI flavors...

What I'm wanting to do is allow a civ to bully a minor civ into joining it. I was thinking, I could do this every turn:

  • Use the civ's current expansion flavor to seed a random roll for a boolean, which is loaded (rigged, etc.) depending on expansion flavor (via a loop, table, incrementor, and table.Random()) Sanitize our expansion flavor by clamping it 0-10. Odds are expansion flavor out of ten.
  • If this roll comes out positive, check and see if the player's happiness will be able to accomodate a puppet city of this size.
  • If it can, we loop through a minor civ iterator and check for strategically located (read: close) minor civs.
  • We then check these strategically located minor civs for influence
  • If not too much positive influence with them, check if they're receptive to bullying
  • If receptive to bullying, check their fear
  • If their fear exceeds a certain threshold, call city changed hands event with conquest set to false, send out a notification (Can I make a custom notification type with custom icon and all that jazz?)

The main thing I fear with this is that I won't be able to get a leader's CURRENT AI flavor.

EDIT: Seems as if my fears were well rooted.

As I said, I doubt this is possible with LUA. You need to do this with a modified DLL (either through a game event called from the DLL, and then coded through LUA, or actually coding it that way in the DLL)
 
As I said, I doubt this is possible with LUA. You need to do this with a modified DLL (either through a game event called from the DLL, and then coded through LUA, or actually coding it that way in the DLL)

I should look and see what sort of calculations are done with the AI in regards to Austria's Diplomatic Marriage.

CvDiplomacyAI
Code:
        // **************************
	// Would we like to buyout a minor this turn?  (Austria UA)
	// **************************
	bool bWantsToBuyout = false;
	if(GetPlayer()->GetPlayerTraits()->IsAbleToAnnexCityStates())
	{
		if(bFoundCity || bExpandLikeCrazy || bExpandToOtherContinents ||
		        GetStateAllWars() == STATE_ALL_WARS_LOSING ||
		        IsGoingForWorldConquest() ||
		        m_pPlayer->calculateGoldRate() > 100)
		{
			bWantsToBuyout = true;
		}
		else
		{
			int iThreshold = iExpansionFlavor * 5; //antonjs: todo: xml
			int iRandRoll = GC.getGame().getJonRandNum(100, "Diplomacy AI: good turn to buyout a minor?");

			if(iRandRoll < iThreshold)
				bWantsToBuyout = true;
		}
	}

My logic interpretation of this code:

If the AI wants to found a city, expand rapidly, expand to other continents, is losing all wars (???), is making more than 100 Gold per turn (???), or is going for Military Victory then it can be said that the AI also wants to buy minor civs out. If this is not the case, then we water down the expansion flavor by half on a scale of 100, and load the dice according to this watered down expansion flavor. If the dice give us a good number, then it can also be said that the AI would like to buy a city-state out. Note in the latter scenario, odds can never exceed 1:1 (1/2, 50%)

I should see if some of these AI intentions are exposed to Lua, as I believe some AI intentions actually are, but very few. If this is not the case, I'll have to try to write my own Lua functions to calculate these AI intentions. EDIT: They're not, as expected.

With the civ that will have the ability to do this, we can assert that it will be going for military victory.

EDIT: I can't seem to find the nuts and bolts of what makes an AI select a specific strategy. Any insight?

bFoundCity will be true under these conditions:
  • There must be good settle spots
  • The player must not be human
  • This must not be an OCC game
  • The player must have a "loose" settler -- that is, an idle settler. I will go into detail about good settle spots when I figure it out. Given the context of this, good settle spots ought not matter. Advise if mistaken.
bExpandLikeCrazy will be true under these conditions:
  • The game is not OCC
  • The player is not human.
  • The player is not going for a cultural victory, unless there is not culture policy cost mod per city.
  • The player's Expansion flavor dictates that expansion is more important than their current strategy -- we might be able to feign a check by checking for war, checking for apollo program, influence on other civs, whether the world congress is the UN yet and if there's a leader vote going on; that sort of thing.
bExpandToOtherContinents will be true under these conditions:
  • The game is not OCC and the player is not human.
  • This cannot be active during an island start strategy -- island start is guaranteed to be finished after turn 75.
  • This cannot be run during early expansion.
  • The player must not be in a war which they are not doing okay with.
  • The player must not be going for a culture win -- in the context of this ability, it's highly unlikely that this will be the case -- unless we have, at most, our max culture cities*.
  • The player should not be without a capital city, except if he has nowhere to settle.
bGoingForWorldConquest should be fairly self explanatory: We should check to see if the player has another capital under his control and his military strength compared to others.

Another parameter of this is "Are they losing all wars?" In this sense, this doesn't mean they're losing every war, but that, all of their wars considered, they should be on the defensive. Starting to lose any war can trigger this, as can your capital taking damage. A player can be said to be losing all wars if any of these are true:
  • The player's capital has anything less than full health
  • The player isn't doing well in a specific war. This is a very complex process to find out, but might be able to get a rough calculation of this.


*Players seem to agree 4 is best. This number, however, scales with map size as so: The quotient of the product of the maximum number of cultural cities and the greater of 4160 and the current map's number of plots, and 4160. With our players agreeing 4 is best for a culture victory, our formula would look something like this, where X is the greater of the two numbers 4160 and the current map's number of plots: (4 * X) / 4160. 4160 isn't actually a magic number, it's the number of tiles in a standard map.
 
Back
Top Bottom