Diplomacy - No Pointless Dialogs and Diplomacy - No Expansion Dialogs

Discussion in 'Civ5 - Mod Components' started by whoward69, Oct 18, 2015.

  1. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,590
    Location:
    Near Portsmouth, UK
    Diplomacy - No Pointless Dialogs
    Disables all pointless inter-turn diplomacy dialogs. These fall into two categories.
    * Those where you can only press the "Back" button. Typically things like "I'm disappointed" (after a previous response) or "I'm denouncing you" (you'll still get the notification)
    * Those where it makes not an iota of difference as to which of the two options you take as the AI ignores the response, as they already hate you a little bit more anyway. For example, after the "You bullied my CS" comment, both "I'm sorry" and "Get over it" have exactly the same effect (the AI hates you already!)

    Diplomacy - No Expansion Dialogs
    Disables the annoying inter-turn diplomacy dialogs for "You settled my land" and "You bought my land" ... I know and I don't care! (Defaults the reply to "Get over yourself" - or whatever the actual text is!)

    Both require V67 of my DLL
     
    DukeCivStarcon likes this.
  2. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,590
    Location:
    Near Portsmouth, UK
    V67 of the DLL adds three new diplomacy related tables - Responses, Diplomacy_Stfu and Diplomacy_StfuResponses

    Responses contains all the unique RESPONSE_XYZ keys with an ID. This is needed to ensure a consistent mapping from a RESPONSE_ type to a unique integer id (as the DLL doesn't pass strings very easily to Lua events) and also as a reference for Diplomacy_StfuResponses entries. With the exception of the 0th RESPONSE_DEFAULT entry, it is populated automatically via SQL
    Spoiler :
    Code:
    <Table name="Responses">
        <Column name="ID" type="integer" primarykey="true" autoincrement="true"/>
        <Column name="Type" type="text" notnull="true" unique="true"/>
    </Table>
    


    Diplomacy_Stfu contains definitions of what to do with RESPONSE_ types. STFU_DEFAULT (perform the standard process, ie, display the screen to the player), STFU_IGNORE (pretend the diplomacy action was never generated), STFU_EVENT (send a Lua event, and do whatever the event determines) and STFU_EVENT_ONLY (send a Lua event and then ignore the diplomacy action) are provided by the DLL, mods are free to add their own definitions to this table (see later).
    The order of processing is
    * If SendEvent, send a Lua GameEvent and use the result as a STFU_ type for further processing
    * If Ignore, end all further processing and pretend that the diplomacy action was never generated
    * If Respond, use the ResponseXyz values to fake a Lua Game.DoFromUIDiploEvent() call, this can be used to simulate pressing a particular button on the diplomacy discussion screen (LeaderHeadRoot)
    Spoiler :
    Code:
    <Table name="Diplomacy_Stfu">
        <Column name="ID" type="integer" primarykey="true" autoincrement="true"/>
        <Column name="Type" type="text" unique="true" notnull="true"/>
        <Column name="SendEvent" type="boolean" default="false"/>
        <Column name="Ignore" type="boolean" default="false"/>
        <Column name="Respond" type="boolean" default="false"/>
        <Column name="ResponseEvent" type="integer" default="0"/>
        <Column name="ResponseAI" type="boolean" default="false"/>
        <Column name="ResponseData1" type="integer" default="0"/>
        <Column name="ResponseData2" type="integer" default="0"/>
    </Table>
    
    <Diplomacy_Stfu>
        <!-- Generic actions available to Lua event handlers -->
        <Row>
            <!-- Do NOT remove or change this entry.  YOU HAVE BEEN WARNED!!! -->
            <Type>STFU_DEFAULT</Type>
            <ID>0</ID>
        </Row>
        <Row>
            <Type>STFU_IGNORE</Type>
            <Ignore>true</Ignore>
        </Row>
        <Row>
            <Type>STFU_EVENT</Type>
            <SendEvent>true</SendEvent>
        </Row>
        <Row>
            <Type>STFU_EVENT_ONLY</Type>
            <SendEvent>true</SendEvent>
            <Ignore>true</Ignore>
        </Row>
    </Diplomacy_Stfu>
    


    Diplomacy_StfuResponses contains entries to map RESPONSE_ types to STFU_ action types.
    Spoiler :
    Code:
    <Table name="Diplomacy_StfuResponses">
        <Column name="ResponseType" type="text" reference="Responses(Type)"/>
        <Column name="StfuType" type="text" reference="Diplomacy_Stfu(Type)"/>
    </Table>
    
    Multiple entries for a single RESPONSE_ type are merged, that is
    Code:
    <Diplomacy_StfuResponses>
        <Row ResponseType="RESPONSE_XYZ" StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_XYZ" StfuType="STFU_EVENT"/>
    </Diplomacy_StfuResponses>
    
    is the same as
    Code:
    <Diplomacy_StfuResponses>
        <Row ResponseType="RESPONSE_XYZ" StfuType="STFU_EVENT_ONLY"/>
    </Diplomacy_StfuResponses>
    
    This is by design, as it permits one mod to disable a response while another one requests an event for it


    So how do I use these?

    The answer to that is "carefully!"

    Don't just disable (STFU_IGNORE) a RESPONSE_ type without knowing what kind of answers the AI is expecting back from the player - you can easily screw up the internal state of AI's diplomacy structures!

    A simple example is to turn off all the inter-turn insults, the players only choice is to click "Back" so this is safe

    Code:
    <Diplomacy_StfuResponses>
        <!-- Disable all insults - the AI already hates us and we only have an "OK, so what" option anyway -->
        <Row ResponseType="RESPONSE_INSULT_GENERIC"     StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_BULLY"       StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_CITIES"      StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_CULTURE"     StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_MILITARY"    StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_NUKE"        StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_POPULATION"  StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_INSULT_UNHAPPINESS" StfuType="STFU_IGNORE"/>
    <Diplomacy_StfuResponses>
    
    The "Diplomacy - No Pointless Dialogs" turns off everything that can be safely ignored.

    A more complex example is turning off the plot buying warnings. In this case the AI says "Don't buy plots near me" and you have two possible replies - a) "I'm sorry, I won't do it for the foreseeable future" or b) "I don't care, deal with it". The player's actual reply is used to condition the AI's diplomacy state. If the player says they won't do it again, the AI remembers this and starts a counter decremented every turn. If the player then buys another plot before the counter has reached zero, they'll take a "broken promise" diplo hit. However, if the player doesn't care, they take an immediate diplo hit. Depending on the player's reply, the AI will display a good or bad final message.

    In order to disable the plot buying warnings we need to pre-decide what the response will be - in this example "I don't care" although it could just as easily be "I won't do it again"

    Firstly we need to create a new Diplomacy_Stfu entry to encode our chosen reply
    Code:
    </Diplomacy_Stfu>
        <Row>
            <Type>STFU_IGNORE_PLOT_BUYING_WARNING</Type>
            <!-- Need to fake up Game.DoFromUIDiploEvent(FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE, iAiPlayer, 1, 0) -->
            <Respond>true</Respond>
            <ResponseEvent>17</ResponseEvent>
            <ResponseAI>true</ResponseAI>
            <ResponseData1>1</ResponseData1>
            <ResponseData2>0</ResponseData2>
        </Row>
    </Diplomacy_Stfu>
    
    and then set RESPONSE_PLOT_BUYING_WARNING to refer to it
    Code:
    <Diplomacy_StfuResponses>
        <Row ResponseType="RESPONSE_PLOT_BUYING_WARNING"            StfuType="STFU_IGNORE_PLOT_BUYING_WARNING"/>
    </Diplomacy_StfuResponses>
    
    However, if we just did this, while we wouldn't see the warning we would still get the response to our choice, so we also need to disable the rest of the discussion sequence
    Code:
    <Diplomacy_StfuResponses>
        <Row ResponseType="RESPONSE_PLOT_BUYING_WARNING"            StfuType="STFU_IGNORE_PLOT_BUYING_WARNING"/>
        <Row ResponseType="RESPONSE_HUMAN_PLOT_BUYING_WARNING_BAD"  StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_HUMAN_PLOT_BUYING_WARNING_GOOD" StfuType="STFU_IGNORE"/>
        <Row ResponseType="RESPONSE_PLOT_BUYING_BROKEN_PROMISE"     StfuType="STFU_IGNORE"/>
    </Diplomacy_StfuResponses>
    
    (We don't actually need RESPONSE_HUMAN_PLOT_BUYING_WARNING_GOOD and RESPONSE_PLOT_BUYING_BROKEN_PROMISE as we decided to say "I don't care", but it's good practice to include the full discussion sequence in case we want to change from bad to good in future)

    The "Diplomacy - No Expansion Dialogs" turns off both the "settled near" and "bought near" whinges from the AI.
     
  3. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,590
    Location:
    Near Portsmouth, UK
    So how do you know what the four magic ResponseXyz parameters need to be?

    Unfortunately the answer to that involves digging into the Lua code, and, if you want to be very sure of what you're doing, you really need to also delve into the C++ code. Fortunately the delving is quite simple - it's just a case of searching various files and carefully following the breadcrumb trail.

    Firstly you need to know what the RESPONSE_ type is that the diplomacy screen is displaying.

    So you were playing happily, clicked "End Turn" and went to go get a coffee. On your return, instead of being ready for your next turn, the game is paused with an AI whinging about you buying land that they consider their's. The exact text is "Your purchase of land in my vicinity has caused some alarm. I caution you so that this issue does not become more serious." The key part here is "purchase of land", so search the core Civ5*.xml files for this text ... you'll find it in Civ5_Dialog__GENERIC.xml and it's associated with the TXT_KEY_PLOT_BUYING_WARNING_1 key. Ignore the _1 bit on the end and search the Civ5Diplomacy_Responses.xml file for "TXT_KEY_PLOT_BUYING_WARNING" ... it's associated with the RESPONSE_PLOT_BUYING_WARNING key. Eureka! We know what the RESPONSE_ type is.

    But that's only half the problem. We also need to find out what, if any, player replies are available. Unfortunately, the Lua UI doesn't use RESPONSE_ type keys, but a different DIPLO_UI_STATE_ type key ... and the mapping between these is in the C++ code. So you can either delve into the C++ code (not difficult, see below) or you'll have to figure out what the possible replies are by deconstructing the Lua (really not recommended). The C++ route is not hard and not open to interpretation errors.

    Using a plain text editor, open the CvDiplomacyAI.cpp file in the CvGameCoreDLL_Expansion2 source code sub-directory of the SDK and search it for RESPONSE_PLOT_BUYING_WARNING, you'll find a block of code like
    Code:
    // AI is warning player about his plot buying
      case DIPLO_MESSAGE_PLOT_BUYING_WARNING:
        strText = GetDiploTextFromTag("RESPONSE_PLOT_BUYING_WARNING");
        break;
    
    We want the DIPLO_MESSAGE_PLOT_BUYING_WARNING bit. Search the same file from the top for that and you'll find a block of code like
    Code:
        szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_WARNING);
        CvDiplomacyRequests::SendRequest(GetPlayer()->GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE);
    
    we want the DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING bit.

    NOTE: Avoid the temptation to assume that you can deduce the DIPLO_UI_STATE_ type from the RESPONSE_ type (as they appear to share the same ending) ... trust me, it doesn't work!

    Armed with DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING, we are now ready to delve into the Lua to find out the possible human player replies

    Open DiscussionDialog.lua (make sure you've got the BNW one!) with a plain text editor and search for DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING, you'll find several blocks of code
    Code:
        elseif (iDiploUIState == DiploUIStateTypes.DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING) then
            bMyMode = true;
    
    Code:
        elseif (g_DiploUIState == DiploUIStateTypes.DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING) then
            strButton1Text = Locale.ConvertTextKey( "TXT_KEY_DIPLO_DISCUSS_NOT_YOUR_BUSINESS" );
            strButton2Text = Locale.ConvertTextKey( "TXT_KEY_DIPLO_DISCUSS_SORRY_FOR_CLAIMING" );
            bHideBackButton = true;
    
    Code:
        -- AI warning us about plot buying - we tell him to go away
        elseif (g_DiploUIState == DiploUIStateTypes.DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING) then
            Game.DoFromUIDiploEvent( FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE, g_iAIPlayer, iButtonID, 0 );
    
    Code:
        -- AI warning us about plot buying - we apologize
        elseif (g_DiploUIState == DiploUIStateTypes.DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING) then
            Game.DoFromUIDiploEvent( FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE, g_iAIPlayer, iButtonID, 0 );
    
    The first block is just setting up the diplomacy screen, if you don't find something like it, you can't automate the reply with this process
    The second block is setting up the text on the reply buttons - typically these are buttons 1 and 2, but not always, so take note of which buttons are being configured
    The third and fourth blocks are what happens when the player clicks a button - block three for button 1 (go away) and block 4 for button 2 (I'm sorry). As we want to tell the AI to eff off, we're interested in block 3

    Specifically the DoFromUIDiploEvent() call
    Code:
    Game.DoFromUIDiploEvent(FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE, g_iAIPlayer, iButtonID, 0)
    
    as the four parameters to this call correspond directly to the four parameters we need to set in the Diplomacy_Stfu entry

    In the LiveTuner/FireTuner console, select WorldView and enter "FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE" and press return, you'll get the number 17 back. We don't care what the value of g_iAIPlayer is, just that it's not -1, and as this is the action for button 1 iButtonID will be 1 (you can find it higher up in the Lua code). Which is how we get all the required values
    Code:
        <!-- Need to fake up Game.DoFromUIDiploEvent(FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE, iAiPlayer, 1, 0) -->
        <Respond>true</Respond>
        <ResponseEvent>17</ResponseEvent>
        <ResponseAI>true</ResponseAI>
        <ResponseData1>1</ResponseData1>
        <ResponseData2>0</ResponseData2>
    
    NOTE: if the 2nd parameter (g_iAIPlayer) to DoFromUIDiploEvent had been -1, ResponseAI would have been set to false
     
  4. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,590
    Location:
    Near Portsmouth, UK
    As indicated, we can tell the system to send us a GameEvent before the diplomacy screen is opened, the template for this event is

    Code:
    function OnDiplomacyStfu(iAI, iResponseType, iDiploUIState, iAction, iExtraData)
      -- Do something useful
    
      return GameInfoTypes.STFU_DEFAULT
    end
    GameEvents.DiplomacyStfu.Add(OnDiplomacyStfu)
    
    Note that it is an "accumulator" event, it is very important that the handler returns GameInfoTypes.STFU_DEFAULT (0) and not true, false, nil, or just falls off the end!

    The return value from the event is used to control further processing. Return values are typically GameInfoTypes.STFU_DEFAULT and GameInfoTypes.STFU_IGNORE, but we could return GameInfoTypes.STFU_IGNORE_PLOT_BUYING_WARNING (although it would be pretty pointless as there are quicker ways to achieve that effect). Only one event will ever be generated, that is, returning GameInfoTypes.STFU_EVENT will not cause another GameEvent.

    We could write a handler to achieve the same result as above
    Code:
    function OnDiplomacyStfu(iAI, iResponseType, iDiploUIState, _, _)
      -- if (iDiploUIState == DiploUIStateTypes.DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING) then
      if (iResponseType == GameInfoTypes.RESPONSE_PLOT_BUYING_WARNING) then
        Game.DoFromUIDiploEvent(FromUIDiploEventTypes.FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE, iAI, 1, 0)
        return GameInfoTypes.STFU_IGNORE
      end
      
      return GameInfoTypes.STFU_DEFAULT
    end
    GameEvents.DiplomacyStfu.Add(OnDiplomacyStfu)
    
    There's no real advantage or disadvantage to either approach - the Lua will be fractionally slower than the XML (but faster than waiting for a player to click a button), the XML has to use hard-coded numbers (17) while Lua can use the defined constants.

    Both approaches require you to dig through the C++ and Lua code to ascertain what the correct format of the DoFromUIDiploEvent() call is.

    The real advantage to Lua comes when data-driven techniques are employed. It would, for example, be possible to rewrite the diplomacy screen to add a "Remember this reply" check box to dynamically configure per-AI auto-responses during a game.

    Assuming your brain isn't slowly dribbling out of your ears from information overload ... enjoy!

    W
     
  5. Nomad or What

    Nomad or What Wayfarer Supporter

    Joined:
    Nov 25, 2012
    Messages:
    713
    Location:
    Wherever the winds take me
    Thank you for this! Now I can enjoy my game whine-free (but never wine-free :D ). They can enjoy a nice hot cup of STFU while I am getting a nice hot cup of coffee to enjoy. :)
     
  6. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    7,241
    Location:
    Illinois, USA
    I almost had a carbonated beverage nose event when I read the names of your tables. :)

    This so could have been handy the last time I was doing an extended gameplay test of the latest mod I am working on. All I was interested in was getting a feel for how the effects of my mod are working, and I kept running into the annoying unrelated AI neediness and whinging...Nice to know there's a way to more or less globally tell them to stop with the whining and stfu.

    Can it be set-up to ignore AI-initiated trade deals, or is that not within the scope of the 'meaningless insults' and 'it don't matter what the human says' types of interactions the stfu are meant to provide for ?
     
  7. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,590
    Location:
    Near Portsmouth, UK
    It can pretty much stop any AI initiated diplomacy pop-up (although you may need v68 of the DLL as I missed some of the "opening code" in v67 - specifically for trade deals), but you'll need to follow the sequence of events through the code (as described above) to work out which requests and replies you need. Lua events would be the way to go, as that way you can double-check the current player and make sure you only auto-respond during the AI's turn. See also "UI - Diplomacy Log", as that does something very similar by using the Lua event
     
  8. TheOneHitPupper

    TheOneHitPupper Chieftain

    Joined:
    Jun 15, 2017
    Messages:
    97
    Sorry to revive an old thread, but it seems that the Diplomacy_StfuResponses table was removed from the DLL at some point. Is there another way to get this mod working?
     
  9. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,590
    Location:
    Near Portsmouth, UK
    I assume you're using CPP, so best to ask in their forum as that's nothing to do with me.
     
  10. Recursive

    Recursive Already Looping Moderator

    Joined:
    Dec 19, 2017
    Messages:
    3,032
    Gender:
    Male
    In Vox Populi, it was implemented in a different way in the file DiploAIOptions.sql.

    (I know you already know this @TheOneHitPupper, but for anyone else reading this...)
     
    TheOneHitPupper likes this.

Share This Page