UI - Notification Options

whoward69

DLL Minion
Joined
May 30, 2011
Messages
8,699
Location
Near Portsmouth, UK
Shift-Right-Click on a notification to remove all notifications of that type from the current list - so if you have lots of "CS X has declared war on CS Y" type messages you can Shift-Right-Click to remove them all from the notification panel

Ctrl-Right-Click behaves like Shift-Right-Click and also disables future notifications of that type (can be re-enabled via the dialog below)

Adds a dialog to permit specific notifications to be enabled/disabled



Selecting/Deselecting a notification in the options dialog updates the notification panel immediately (so you can easily see which notifications are affected)

Settings are saved from one game to the next.

Provides an API (via LuaEvents) for adding custom notifications and/or overriding existing ones.

Modifies the "City can bombard" notification to show a better icon

Also incorporates the City Growth (Population and Land) notifications by Sneaks and alpaca from "Custom Notifications (v 3)"



For an example of a completely new notification (and also how to revert to generic notifications if this mod isn't installed) see the mod "Units - Mounted Units (v 4)"
 
To override an existing notification, in this case to replace the generic exclamation mark icon for the "city can bombard" notification with something more immediately recognisable, first create a notification instance.

BombardNotification.xml

Code:
<?xml version="1.0" encoding="utf-8"?>
<Context Name="BombardNotification">
  <Instance Name="[COLOR="Red"][I]Bombard[/I]Item[/COLOR]" >
    <Container ID="[COLOR="red"][I]Bombard[/I]Container[/COLOR]" Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ConsumeMouseButton="1" >
      <SlideAnim ID="NotificationSlide" Anchor="L,T" Style="NotificationSlide" >
        <AlphaAnim Style="NewFinger" />
        <Button ID="[COLOR="red"][I]Bombard[/I]Button[/COLOR]" Texture="[COLOR="seagreen"]NotificationClearBase.dds[/COLOR]" Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" >
[COLOR="SeaGreen"]          <Image Anchor="C,C" Offset="0,0" Size="64,64" Texture="CityBombard.dds" TextureOffset="0,64" />
          <AlphaAnim Anchor="C,C" Size="64,64" Texture="CityBannerStrengthAlpha.dds" Pause="0" Cycle="Bounce" Speed="2" AlphaStart="1" AlphaEnd="0"/>[/COLOR]
        </Button>
      </SlideAnim>
    </Container>
  </Instance>
</Context>

The IDs in red must all start with the same prefix ("Bombard" in this case), the elements in green can be changed to suit, the rest must be as given above.

Then create the associated Lua

BombardNotification.lua

Code:
local Bombard = {
  id   = NotificationTypes.NOTIFICATION_CITY_RANGE_ATTACK, -- specified as replacing an existing notification
  item = "[I][COLOR="Red"]Bombard[/COLOR][/I]"
}

-- Generate the notification "shell" to be shown to the player, most of the content is filled in by the standard code
function OnBombardNotification(cbData)
  local instance = {}
  ContextPtr:BuildInstanceForControl("[COLOR="red"][I]Bombard[/I]Item[/COLOR]", instance, cbData.parent)
  cbData.instance = instance

  -- What to do when the player left-clicks on the notification, in this case the default action
  instance.BombardButton:RegisterCallback(Mouse.eLClick, function() UI.ActivateNotification(cbData.id) end)
end
Bombard.callback = OnBombardNotification

-- Register the updated notification
LuaEvents.CustomNotificationAddin(Bombard)

Both files need VFS=true

For BombardNotification.lua add a CustomNotificationAddin action

Related .modinfo entries

Code:
<Files>
  <File import="1">UI/BombardNotification.lua</File>
  <File import="1">UI/BombardNotification.xml</File>
</Files>
<EntryPoints>
  <EntryPoint type="CustomNotificationAddin" file="UI/BombardNotification.lua">
    <Name>Bombard Notification</Name>
    <Description/>
  </EntryPoint>
</EntryPoints>

Simples!
 
The CityGrowthTile XML instance

Spoiler :
Code:
<?xml version="1.0" encoding="utf-8"?>
<Context Name="CityNotifications">
  <Instance Name="CityGrowthItem" >
    <Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="CityGrowthContainer" ConsumeMouseButton="1" >
      <SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
        <AlphaAnim Style="NewFinger" />
        <Button Anchor="R,C" Size="80,80" Offset="0,0" Texture="NotificationFrameBase.dds" ID="CityGrowthButton" Hidden="0" >
          <ShowOnMouseOver>
            <Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="NotificationFrameBase.dds" />
            <AlphaAnim Anchor="C,C" AnchorSide="O,O" Offset="0,0" Size="80,80" Texture="NotificationFrameGlow2.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="1" AlphaEnd="0" Hidden="0"/>
          </ShowOnMouseOver>
          <Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="NotificationCityGrowth.dds" />
          <AlphaAnim Anchor="C,C" Offset="0,0" Size="80,80" TextureOffset="0,0" Texture="NotificationCityGrowthGlow.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="0" AlphaEnd="1"/>
        </Button>
      </SlideAnim>
    </Container>
  </Instance>
</Context>

The CityGrowthTile Lua code

Spoiler :
Code:
local CityTile = {
  -- id = nil, -- This will be assigned when the notification is registered
  label = "TXT_KEY_CITY_NOTIFICATIONS_TILE",
  key   = "CITY_NOTIFICATIONS_TILE", 
  item  = "CityTile", 
  show  = true, 
  ui    = true, 
  -- callback = OnCityNotificationTile -- This is filled in after the callback function is defined
}


-- Store all the current culture levels for all cities for all human players
local m_cityCulture = {}
function InitCityCulture()
  for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS do
    local pPlayer = Players[iPlayer]
    if (pPlayer:IsEverAlive() and pPlayer:IsHuman()) then
      local culture = {}
      m_cityCulture[iPlayer] = culture

      for pCity in pPlayer:Cities() do
        culture[pCity:GetID()] = pCity:GetJONSCultureLevel()
      end
    end
  end
end

-- When a tile's culture changes, generate a notification
function OnHexCultureChanged(iHexX, iHexY, iPlayer)
  if (iPlayer == Game.GetActivePlayer()) then
    local pPlot = Map.GetPlot(ToGridFromHex(iHexX, iHexY))
    local pCity = pPlot:GetWorkingCity()

    if (pCity ~= nil) then
      local newLevel = pCity:GetJONSCultureLevel()
      if (m_cityCulture[iPlayer][pCity:GetID()] ~= newLevel) then
        m_cityCulture[iPlayer][pCity:GetID()] = newLevel

        if (newLevel ~= 0) then
          local sHeading = Locale.ConvertTextKey("TXT_KEY_CITY_NOTIFICATIONS_TILE_HEADING", pCity:GetName())
          local sText = Locale.ConvertTextKey("TXT_KEY_CITY_NOTIFICATIONS_TILE_TEXT", pCity:GetName())
          Players[Game.GetActivePlayer()]:AddNotification(CityTile.id, sText, sHeading, pPlot:GetX(), pPlot:GetY(), iHexX, iHexY)
        end
      end
    else
      local sHeading = Locale.ConvertTextKey("TXT_KEY_CITY_NOTIFICATIONS_TILE_EMPIRE_HEADING")
      local sText = Locale.ConvertTextKey("TXT_KEY_CITY_NOTIFICATIONS_TILE_EMPIRE_TEXT")
      Players[Game.GetActivePlayer()]:AddNotification(CityTile.id, sText, sHeading, pPlot:GetX(), pPlot:GetY(), iHexX, iHexY)
    end
  end
end
Events.SerialEventHexCultureChanged.Add(OnHexCultureChanged)

-- When the player left-clicks the notification, highlight the hex and look at it
function OnCityTileClick(iHexX, iHexY)
  Events.SerialEventHexHighlight({x=iHexX, y=iHexY}, true, Color(1, 0, 1, 1))
  UI.LookAt(Map.GetPlot(ToGridFromHex(iHexX, iHexY)))
end

-- Generate the notification "shell" to be shown to the player, most of the content is filled in by the standard code
function OnCityNotificationTile(cbData)
  local instance = {}
  ContextPtr:BuildInstanceForControl("CityTileItem", instance, cbData.parent)
  cbData.instance = instance
        
  -- The only item we specifically have to supply is what to do when the player left-clicks on the notification
  instance.CityTileButton:RegisterCallback(Mouse.eLClick, function() OnCityTileClick(cbData.iExtra1, cbData.iExtra2) end)
end
CityTile.callback = OnCityNotificationTile


-- Switch off and remove from the options panel (so the user can't switch it back on) the existing event
-- (NOTIFICATION_CITY_TILE is never actually called, but it doesn't hurt to ensure it's off!)
LuaEvents.CustomNotificationAddin({id=NotificationTypes.NOTIFICATION_CITY_TILE, show=false, ui=false})

-- Register the new notification
LuaEvents.CustomNotificationAddin(CityTile)

-- Initialise the city culture stores
InitCityCulture()

The interesting parts

Code:
local CityTile = {
  -- id = nil, -- This will be assigned when the notification is registered
  label = "TXT_KEY_CITY_NOTIFICATIONS_TILE",
  key   = "CITY_NOTIFICATIONS_TILE", 
  item  = "CityTile", 
  show  = true, 
  ui    = true, 
  -- callback = OnCityNotificationTile -- This is filled in after the callback function is defined
}
Defines the new notification
  • id - will be automatically assigned as this is a new notification
  • label - the text (or TXT_KEY) to display in the options dialog
  • key - the unique key (used to save the player's setting)
  • item - the notifications prefix (must be the same as the prefix used in the XML IDs)
  • show - default show/hide visibility of this type of notification
  • ui - does this appear in the options dialog (if false, the player won't be able to change the visibility of the notifications)

Notifications are sent via the standard pPlayer:AddNotification() method
Code:
Players[Game.GetActivePlayer()]:[COLOR="red"]AddNotification[/COLOR](CityTile.id, sText, sHeading, pPlot:GetX(), pPlot:GetY(), iHexX, iHexY)
this has the major advantage that they will be automatically re-broadcast when a saved game is loaded or they can be manually re-broadcast via Lua (which is how the options dialog adds/removes them as the player changes their options)

The callback creates the notification instance and hooks-up the left click, the rest of the instance (text, heading, etc) is filled in by the system
Code:
function OnCityNotificationTile(cbData)
  local instance = {}
  ContextPtr:BuildInstanceForControl("CityTileItem", instance, [COLOR="Red"]cbData.parent[/COLOR])
  [COLOR="red"]cbData.instance = instance[/COLOR]
        
  -- The only item we specifically have to supply is what to do when the player left-clicks on the notification
  instance.CityTileButton:RegisterCallback(Mouse.eLClick, function() OnCityTileClick([COLOR="red"]cbData.iExtra1, cbData.iExtra2[/COLOR]) end)
end
[COLOR="red"]CityTile.callback = OnCityNotificationTile[/COLOR]

Finally we disable the existing event (just in case!) and register our new event
Code:
-- Switch off and remove from the options panel (so the user can't switch it back on) the existing event
-- (NOTIFICATION_CITY_TILE is never actually called, but it doesn't hurt to ensure it's off!)
LuaEvents.CustomNotificationAddin({id=NotificationTypes.NOTIFICATION_CITY_TILE, show=false, ui=false})

-- Register the new notification
LuaEvents.CustomNotificationAddin(CityTile)
 
Seems the mod don't work with CivUp, i have no entry to open the option screen.
No Keyboard shortcut to open it?
 
So now I'm here... as you told me whoward69. I would like to contribute a translation of the three custom City Growth (Population and Land) notifications into german, which are only in english right now (see your "./XML/CityNotificationsText.xml"). Oh, and I have modified these three text messages, so that they match the original ones of the latest Civ5 G&K (german and english). Only the file "./XML/CityNotificationsText.xml" has to be replaced with my version. It's tested and works fine. Anything else is unchanged.

So I just want you to close the gap in your mod for us german players related to those custom notifications. The other notifications work fine for german and english, because your mod usually uses the original Civ5 G&K message-engine which supports multi-language - except for the three custom ones.

So now you can update your mod (see attachments). Questions? So ask me :cool:

Thanks.
 

Attachments

  • CityNotificationsText.zip
    919 bytes · Views: 153
Can you please put your mods here as well for download. Steam is a pain for me. I`m really after the `remove GDR` mod.

Thanks.
 
So now I'm here...

Questions? So ask me :cool:

Thanks.

I've finally got around to including these! (as I'm in the process of making all my mods compatible with 1.0.2.21 now Firaxis have fixed the "switch all mods off every time" bug)

Couple of points, accented characters needs to be encoded (otherwise Steam does very weird things with them!)

Eg

ä is ä
ü is ü
 
And if posts #2 and #3 arn't enough, some more tutorial

1) Add a LUA/XML pair of files to your mod for the custom notification entry that will appear in the notification list, eg to send a notification when a unit causes pollution (whatever that may be) add the files UI/PollutionNotification.lua and UI/PollutionNotification.xml

2) Set VFS=true on both of these files

3) On the Content tab, and an entry with
Type = "CustomNotificationAddin" (you will have to type that as it's not on the drop-down list)
Name = "Pollution Notification"
Description = "Pollution Notification"
FileName = "UI/PollutionNotification.lua"

4) In the UI/PollutionNotification.xml file add the following, you can call the bits in italic what you want, but do NOT change the other ID values
Code:
<?xml version="1.0" encoding="utf-8"?>
<Context Name="[I]PollutionNotification[/I]">
  <Instance Name="[I]PollutionItem[/I]" >
    <Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="[I]PollutionContainer[/I]" ConsumeMouseButton="1" >
      <SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
        <AlphaAnim Style="NewFinger" />
        <Button Anchor="R,C" Size="80,80" Offset="0,0" Texture="NotificationFrameBase.dds" ID="[I]PollutionButton[/I]" Hidden="0" >
          <ShowOnMouseOver>
            <Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="NotificationFrameBase.dds" />
            <AlphaAnim Anchor="C,C" AnchorSide="O,O" Offset="0,0" Size="80,80" Texture="NotificationFrameGlow2.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="1" AlphaEnd="0" Hidden="0"/>
          </ShowOnMouseOver>
          <Image ID="Resource" Anchor="C,C" Offset="0,0" Size="80,80" TextureOffset="80,0" Texture="Resources640.dds"/>
		  <!-- Adds a unit icon over the main notification icon -->
          <Image ID="UnitFlag" Size="38,38" Offset="0,0" Anchor="R,B" Texture="unitbackteamcolor.dds">
            <Image ID="UnitIcon" Size="32,32" Offset="0,0" Anchor="C,C" Texture="UnitFlagAtlasWhite.dds" />
          </Image>
        </Button>
      </SlideAnim>
    </Container>
  </Instance>
</Context>

5) In the UI/PollutionNotification.lua file add the following,
Code:
--
-- Pollution Notification - custom notification for pollution events
--

include("IconSupport")

-- Data structure about the notification, passed in the LuaEvent below
local Pollution = {
  label = "TXT_KEY_POLLUTION_NOTIFICATIONS", 
  key   = "POLLUTION_NOTIFICATIONS", 
  item  = "Pollution",
  show  = true, 
  ui    = true, 
}

-- Helper function to scroll the map to a plot, and select any unit that may be there
function OnLookAt(pPlot)
  if (pPlot ~= nil) then
    UI.LookAt(pPlot)

    local pUnit = pPlot:GetUnit(0)
    if (pUnit ~= nil) then
      UI.SelectUnit(pUnit)
    end
  end
end

-- This is executed when the notification is issued, build the entry for the list and hook up any events
function OnPollutionNotification(cbData)
  print(string.format("OnPollution(%i, %i, %s, %s)", cbData.iExtra1, cbData.iExtra2, cbData.sHeading, cbData.sText))
  local instance = {}
  ContextPtr:BuildInstanceForControl("PollutionItem", instance, cbData.parent)
  cbData.instance = instance

  instance.UnitFlag:SetHide(true)

  local pPlayer = Players[cbData.iExtra1]
  if (pPlayer ~= nil) then
    local pUnit = pPlayer:GetUnitByID(cbData.iExtra2)
    if (pUnit ~= nil) then
      local unitInfo = GameInfo.Units[pUnit:GetUnitType()]
      local textureOffset, textureAtlas = IconLookup(unitInfo.UnitFlagIconOffset, 32, unitInfo.UnitFlagAtlas)
      local iconColor, flagColor = pPlayer:GetPlayerColors()
      instance.UnitFlag:SetHide(false)
      instance.UnitFlag:SetColor(flagColor)
      instance.UnitIcon:SetTexture(textureAtlas)
      instance.UnitIcon:SetTextureOffset(textureOffset)
      instance.UnitIcon:SetColor(iconColor)

      -- Need to localise this, as by the time we click on the notification the unit may no longer be around
      local pPlot = pUnit:GetPlot()
	  -- MUST use a closure (anonymous function) as the click event
      instance.PollutionButton:RegisterCallback(Mouse.eLClick, function() OnLookAt(pPlot) end)
    end
  end    
end
Pollution.callback = OnPollutionNotification

-- We have to get a bit defensive here as Firaxis keep changing the order that events fire.
-- This approach means we don't care if this code executes before or after the "UI - Notifications Options" code
function OnPollutionNotificationIdRequest()
  if (Pollution.id ~= nil) then
    LuaEvents.PollutionNotificationId(Pollution.id)
  end
end
LuaEvents.PollutionNotificationIdRequest.Add(OnPollutionNotificationIdRequest)

function Register()
  LuaEvents.CustomNotificationAddin(Pollution) -- Name of the data structure at the top of the file
  OnPollutionNotificationIdRequest()
end

Register()

6) In your Lua file that will generate the event (which will almost certainly have an InGameUIAddin entry), add the following
Code:
-- By default our notifications will be generic, unless the user is also using "UI - Notification Options" in which case we'll send custom ones
local g_PollutionNotificationType = NotificationTypes.NOTIFICATION_GENERIC

-- Now get the custom pollution notification code to use, if the user is also using "UI - Notification Options"
function OnPollutionNotificationId(id)
  print(string.format("Setting pollution notification id to %i", id))
  g_PollutionNotificationType = id
end
LuaEvents.PollutionNotificationId.Add(OnPollutionNotificationId)
LuaEvents.PollutionNotificationIdRequest()

7) And finally send the notification, in whatever code is appropriate, eg
Code:
pPlayer:AddNotification(g_PollutionNotificationType, text, heading, pPlot:GetX(), pPlot:GetY(), pUnit:GetOwner(), pUnit:GetID())

Note: As the notifications are sent through the standard notification method, they are added to the internal queue and saved with the game state, hence they will re-notify when the game is loaded from a save (if they were sent during that turn) and can also be manipulated like any other core notification
 
Quick FYI - I downloaded it from your Pick'N'Mix Mods site, and one of the options is:

TXT_KEY_OPTIONS_NOTIFICATION_REQU

It cuts off - I could probably work out what the missing text key is by just increasing the size of the panel temporarily so that it doesn't cut off, shouldn't be hard to figure out or fix and not a major issue anyway, but I figured you'd want to hear about it.
 
Quick FYI - I downloaded it from your Pick'N'Mix Mods site, and one of the options is:

TXT_KEY_OPTIONS_NOTIFICATION_REQU

It cuts off - I could probably work out what the missing text key is by just increasing the size of the panel temporarily so that it doesn't cut off, shouldn't be hard to figure out or fix and not a major issue anyway, but I figured you'd want to hear about it.

I had this issue too, not sure what it's all about, so I just left it alone.
 
Missing TXT_KEY for a new notification

the omitted line should read

Code:
    <Row Tag="TXT_KEY_OPTIONS_NOTIFICATION_REQUEST_RESOURCE" Text="Request Resource"/>
 
What do I have to do to change the image shown for the notification when it comes up? I'm getting the ! icon no matter what I try.

Everything else has worked fine, I don't know why this issue has been such a bear.
 
What do I have to do to change the image shown for the notification when it comes up?

supply a different image

Code:
<?xml version="1.0" encoding="utf-8"?>
<Context Name="BombardNotification">
  <Instance Name="BombardItem" >
    <Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="BombardContainer" ConsumeMouseButton="1" >
      <SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
        <AlphaAnim Style="NewFinger" />
        <Button Anchor="R,C" Size="80,80" Offset="0,0" Texture="NotificationClearBase.dds" ID="BombardButton" Hidden="0" >
          [COLOR="Red"][B]<Image Anchor="C,C" Offset="0,0" Size="64,64" Texture="CityBombard.dds" TextureOffset="0,64" />[/B][/COLOR]
          <AlphaAnim Anchor="C,C" Size="64,64" Texture="CityBannerStrengthAlpha.dds" Pause="0" Cycle="Bounce" Speed="2" AlphaStart="1" AlphaEnd="0"/>
        </Button>
      </SlideAnim>
    </Container>
  </Instance>
</Context>
 
Any reason this wouldn't work? The Image and AlphaAnim elements are identical to the ones in the Emigration v.3 mod.

Code:
<?xml version="1.0" encoding="utf-8"?>
<Context Name="EmigrationNotification">
	<Instance Name="EmigrationItem" >
		<Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="EmigrationContainer" ConsumeMouseButton="1" >
			<SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
				<AlphaAnim Style="NewFinger" />
				<Button Anchor="R,C" Size="80,80" Offset="0,0" Texture="NotificationFrameBase.dds" ID="EmigrationButton" Hidden="0" >
					<ShowOnMouseOver>
						<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="NotificationFrameBase.dds" />
						<AlphaAnim Anchor="C,C" AnchorSide="O,O" Offset="0,0" Size="80,80" Texture="NotificationFrameGlow2.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="1" AlphaEnd="0" Hidden="0"/>
					</ShowOnMouseOver>
					<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationCityGrowthRed.dds" />
					<AlphaAnim Anchor="C,C" Offset="0,0" Size="80,80"  TextureOffset="0.0" Texture="assets\UI\Art\Notification\NotificationCityGrowthRedGlow.dds"  Pause="0" Cycle="Bounce" Speed="1" AlphaStart="0" AlphaEnd="1"/>
				</Button>
			</SlideAnim>
		</Container>
	</Instance>
</Context>
 
Nothing obvious in the XML, you have the Lua to build the instance of the custom notification?

Code:
-- Generate the notification "shell" to be shown to the player, most of the content is filled in by the standard code
function OnBombardNotification(cbData)
  -- print(string.format("OnBombard(%i, %i, %s, %s)", cbData.iExtra1, cbData.iExtra2, cbData.sHeading, cbData.sText))
[COLOR="Red"][B]  local instance = {}
  ContextPtr:BuildInstanceForControl("BombardItem", instance, cbData.parent)
  cbData.instance = instance[/B][/COLOR]

  -- What to do when the player left-clicks on the notification, in this case the default action
  instance.BombardButton:RegisterCallback(Mouse.eLClick, function() UI.ActivateNotification(cbData.id) end)
end
Bombard.callback = OnBombardNotification
 
Top Bottom