Yeah I remember running across it at one point. Don't remember if it was in the xml or lua. I'm leaning lua, but either way, I'm mostly sure it can be modded.
ContextPtr:LoadNewContext("CustomNotification")
LuaEvents.CustomNotificationLoaded(Controls, ProcessStackSizes)
<?xml version="1.0" encoding="utf-8"?>
<Context Name="CustomNotification">
<Instance Name="CityGrowth2Item" >
<Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="CityGrowth2Container" ConsumeMouseButton="1" >
<SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
<AlphaAnim Style="NewFinger" />
<Button Anchor="R,C" Size="80,80" Offset="0,0" Texture="assets\UI\Art\Notification\NotificationFrameBase.dds" ID="CityGrowth2Button" Hidden="0" >
<ShowOnMouseOver>
<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationFrameBase.dds" />
<AlphaAnim Anchor="C,C" AnchorSide="O.O" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\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\NotificationCityGrowth.dds" />
<AlphaAnim Anchor="C,C" Offset="0,0" Size="80.80" TextureOffset="0.0" Texture="assets\UI\Art\Notification\NotificationCityGrowthGlow.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="0" AlphaEnd="1"/>
<Label Anchor="C,C" Offset="0,0" String="" Font="TwCenMT20" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" ID="CityGrowth2Count" />
</Button>
</SlideAnim>
</Container>
</Instance>
<Instance Name="CityTileItem" >
<Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="CityTileContainer" ConsumeMouseButton="1" >
<SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
<AlphaAnim Style="NewFinger" />
<Button Anchor="C,C" Size="80,80" Offset="0,0" Texture="assets\UI\Art\Notification\NotificationClearBase.dds" ID="CityTileButton" Hidden="0" >
<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationTileFrame.dds" />
<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationTileGlass.dds" />
<AlphaAnim Anchor="C,C" Offset="0,0" Size="80.80" TextureOffset="0.0" Texture="assets\UI\Art\Notification\NotificationTileGlow.dds" Pause="0" Cycle="Bounce" Speed="1" AlphaStart="0" AlphaEnd="1"/>
<Label Anchor="C,C" Offset="0,0" String="" Font="TwCenMT20" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" ID="CityTileCount" />
</Button>
</SlideAnim>
</Container>
</Instance>
</Context>
--[[
CustomNotification.lua
Creator: alpaca
Last Change: 28.11.2010
Description: Adds support for the city growth and culture tile spread notifications to the game
]]--
include("lib")
customNotifications = {}
customInstances = {}
nextID = 1
function addCustomNotification(noty)
customNotifications[nextID] = noty
noty.ID = nextID
nextID = nextID + 1
end
------------------------------------------------------------------
-- Notification class
------------------------------------------------------------------
Notification = class(
function(o, name, summary, toolTip, leftClick, rightClick)
o.Name = name
o.Instance = nil
o.Summary = summary
o.ToolTip = toolTip
end
)
--[[
Gets the instance of this notification
Returns:
instance
]]--
function Notification:GetInstance()
if self.Instance == nil then
self.Instance = {}
ContextPtr:BuildInstanceForControl( self.Name.."Item", self.Instance, Controls.SmallStack );
end
return self.Instance
end
function Notification.LeftClick(id)
return
end
function Notification.RightClick(id)
customNotifications[id]:Remove()
end
function Notification:Show()
addCustomNotification(self)
local container = self:GetInstance()[self.Name.."Container"]
local button = self:GetInstance()[self.Name.."Button"]
self:GetInstance().FingerTitle:SetText( self.Summary )
container:BranchResetAnimation();
button:SetHide(false)
button:SetVoid1(self.ID)
button:RegisterCallback(Mouse.eLClick, self.LeftClick)
--button:RegisterCallback(Mouse.eRClick, testClick)
button:RegisterCallback(Mouse.eRClick, self.RightClick)
button:SetToolTipString(self.ToolTip)
ProcessStackSizes();
end
function Notification:Remove()
Controls.SmallStack:ReleaseChild( self:GetInstance()[self.Name.."Container"] )
customNotifications[self.ID] = nil
ProcessStackSizes()
end
------------------------------------------------------------------
-- CityZoomNotification class
------------------------------------------------------------------
CityZoomNotification = class(Notification,
function (o, name, summary, toolTip, plot)
o.Plot = plot
Notification.init(o, name, summary, toolTip)
end
)
function CityZoomNotification.LeftClick(id)
local self = customNotifications[id]
UI.DoSelectCityAtPlot(self.Plot)
ProcessStackSizes();
end
------------------------------------------------------------------
-- Event handling
------------------------------------------------------------------
------------------------------------------------------------------
-- City Growth notification (also above pop 5)
------------------------------------------------------------------
-- initialise city sizes
citySizes = {}
for pCity in Players[Game.GetActivePlayer()]:Cities() do
citySizes[pCity:GetID()] = pCity:GetPopulation()
end
--[[
Detects city growth and fires a notification
]]--
function CityGrowthNotificationOnActivePlayerTurnStart()
for pCity in Players[Game.GetActivePlayer()]:Cities() do
local newPop = pCity:GetPopulation()
print("Checking population: new pop is "..tostring(newPop)..", old pop is "..tostring(citySizes[pCity:GetID()]))
if citySizes[pCity:GetID()] == nil then
citySizes[pCity:GetID()] = newPop
else
if newPop ~= citySizes[pCity:GetID()] then
citySizes[pCity:GetID()] = newPop
local pPlot = pCity:Plot()
notification = CityZoomNotification("CityGrowth2", Locale.ConvertTextKey("TXT_KEY_NOTIFICATION_SUMMARY_CITY_GROWTH_2", pCity:GetName()), Locale.ConvertTextKey("TXT_KEY_NOTIFICATION_CITY_GROWTH_2", pCity:GetName(), newPop), pPlot)
notification:Show()
end
end
end
end
Events.ActivePlayerTurnStart.Add(CityGrowthNotificationOnActivePlayerTurnStart)
------------------------------------------------------------------
-- Border Growth notification
------------------------------------------------------------------
-- initialise culture levels
cityCultureLevels = {}
for pCity in Players[Game.GetActivePlayer()]:Cities() do
cityCultureLevels[pCity:GetID()] = pCity:GetJONSCultureLevel()
end
--[[
Detects city growth and fires a notification
]]--
function CultureLevelNotificationOnActivePlayerTurnStart()
for pCity in Players[Game.GetActivePlayer()]:Cities() do
local newLevel = pCity:GetJONSCultureLevel()
if cityCultureLevels[pCity:GetID()] == nil then
cityCultureLevels[pCity:GetID()] = newLevel
else
if newLevel ~= cityCultureLevels[pCity:GetID()] then
cityCultureLevels[pCity:GetID()] = newLevel
local pPlot = pCity:Plot()
notification = CityZoomNotification("CityTile", Locale.ConvertTextKey("TXT_KEY_NOTIFICATION_SUMMARY_CITY_TILE", pCity:GetName()), Locale.ConvertTextKey("TXT_KEY_NOTIFICATION_CITY_TILE", pCity:GetName()), pPlot)
notification:Show()
end
end
end
end
Events.ActivePlayerTurnStart.Add(CultureLevelNotificationOnActivePlayerTurnStart)
------------------------------------------------------------------
-- Overhead
------------------------------------------------------------------
function OnTurnEnd()
for id, pNotification in pairs(customNotifications) do
pNotification:RightClick()
end
end
Events.ActivePlayerTurnEnd.Add(OnTurnEnd)
function OnCustomNotificationsLoaded(controls, processStackSizes)
Controls = controls
ProcessStackSizes = processStackSizes
end
LuaEvents.CustomNotificationLoaded.Add(OnCustomNotificationsLoaded)
--[[
lib.lua
Creator: alpaca
Last Change: 27.11.2010
Description: Adds some useful functions
]]--
-- Constants
MAX_LOGGING_DEPTH = 10
SPACES_PER_TAB = 4
--[[
Recursively prints a table. For userdata, the metatable is logged
Arguments:
tab: Table. Table to log
depth: Number. Current depth (for recursion)
maxDepth: Number. Maximal recursion depth (to avoid infinite loops through back-referencing)
callStack: Used internally
Returns:
true: on success
]]--
function printTable(tab, depth, maxDepth, callStack)
local depth = depth or 0
local maxDepth = maxDepth or MAX_LOGGING_DEPTH
local iD = iD or "default"
local callStack = callStack or {}
if tab == nil then
print(intToSpaces(depth).."<nil>")
return
end
if tab == {} then
print(intToSpaces(depth).."<empty>")
return
end
if depth > maxDepth then
return "MaxDepth reached"
end
tab = (type(tab) == "userdata") and getmetatable(tab) or tab -- for userdata, we use the metatable
for k,v in pairs(tab) do
if type(v) == "table" then
print(intToSpaces(depth).."(table) "..tostring(k))
-- avoid infinite recursion by making sure the table is only printed once
if callStack[v] == nil then
callStack[v] = v
printTable(v, depth + 1, maxDepth, iD, callStack)
end
else
print(intToSpaces(depth).."("..type(k)..") "..tostring(k)..": ".."("..type(v)..") "..tostring(v))
end
end
end
--[[
Converts an integer into a string containing n times so many spaces (for indentation).
Arguments:
num: Number. Number of "tabs" to write
Returns:
String containing the spaces
]]--
function intToSpaces(num)
local retValue = ""
for var = 1, num*SPACES_PER_TAB do
retValue = retValue.." "
end
return retValue
end
--[[
Defines a class that can be instantiated. Taken from the LuA users wiki: http://lua-users.org/wiki/SimpleLuaClasses (April 8th 2010)
An example for implementing a class with this can be found in the wiki.
Arguments:
base: Class. Base class to derive this class from
init: Function. Initialisation function to call - a constructor if you will
Returns:
The class object
]]--
function class(base, init)
local c = {} -- a new class instance
if not init and type(base) == 'function' then
init = base
base = nil
elseif type(base) == 'table' then
-- our new class is a shallow copy of the base class!
for i,v in pairs(base) do
c[i] = v
end
c._base = base
end
-- the class will be the metatable for all its objects,
-- and they will look up their methods in it.
c.__index = c
-- expose a constructor which can be called by <classname>(<args>)
local mt = {}
mt.__call = function(class_tbl, ...)
local obj = {}
setmetatable(obj,c)
if init then
init(obj,...)
else
-- make sure that any stuff from the base class is initialized!
if base and base.init then
base.init(obj, ...)
end
end
return obj
end
c.init = init
c.is_a = function(self, klass)
local m = getmetatable(self)
while m do
if m == klass then return true end
m = m._base
end
return false
end
setmetatable(c, mt)
return c
end
<?xml version="1.0" encoding="utf-8"?>
<!-- Created by ModBuddy on 11/28/2010 5:33:32 PM -->
<GameData>
<Language_en_US>
<Row Tag="TXT_KEY_NOTIFICATION_CITY_GROWTH_2">
<Text>The City of {1_CityName} now has {2_Pop} [ICON_CITIZEN] Citizens! The new Citizen will automatically work the land near the City for additional [ICON_FOOD] Food, [ICON_PRODUCTION] Production or [ICON_GOLD] Gold.[NEWLINE][NEWLINE]Click on this message to open its city view.</Text>
</Row>
<Row Tag="TXT_KEY_NOTIFICATION_SUMMARY_CITY_GROWTH_2">
<Text>{1_CityName} has grown!</Text>
</Row>
<Row Tag="TXT_KEY_NOTIFICATION_CITY_TILE">
<Text>The City of {1_CityName} has claimed a new tile for [ICON_CITIZEN] to work on![NEWLINE][NEWLINE]Click on this message to open its city view.</Text>
</Row>
<Row Tag="TXT_KEY_NOTIFICATION_SUMMARY_CITY_TILE">
<Text>{1_CityName} has claimed a new tile!</Text>
</Row>
</Language_en_US>
</GameData>
I'm getting an error, "attempt to index field FingerTitle (a nil value)" on line 83 of CustomNotificationPanel.lua.
LuaEvents.CustomNotification(1003,
[B][COLOR="Red"]"City-State Captured"[/COLOR][/B],
"Looted food supplies from a captured Maritime city-state allowed your Capital to gain 1 citizen.",
{ location={pPlot:GetX(), pPlot:GetY()} }
);
<Instance Name="CityStateItem" >
LuaEvents.CustomNotification(ID,[B][COLOR="Red"] "CityState" [/COLOR][/B], "Tooltip text" , location
);
I read this thread for the first time. if you still have problem then the cause of this problem is the following:
Actually, it's more than that. There was a problem created in the December megapatch where the old version of this mod would give that error every time, even if the titles were handled correctly.
The problem was the VFS change. To get this to work, you have to set the VFS to true (for all three files) and NOT add the Lua through the usual InGameUIAddIn or InGame.xml methods. At that point it should work fine.
<!-- mainfile.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!-- Author: Moaf -->
<Context>
<LuaContext FileName="CCNotifications" ID="CCNotifications" Hidden="1" />
</Context>
<!-- CCNotifications.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!-- Author: Moaf -->
<Context>
<!--=======================================================================================================================-->
<!-- Cultural Capital Notification -->
<!--=======================================================================================================================-->
<Instance Name="CCItem" >
<Container Anchor="R,C" Size="80,80" Offset="0,0" Hidden="0" ID="CCContainer" ConsumeMouseButton="1" >
<SlideAnim Anchor="L,T" Style="NotificationSlide" ID="NotificationSlide" >
<AlphaAnim Style="NewFinger" />
<Button Anchor="R,C" Size="80,80" Offset="0,0" Texture="assets\UI\Art\Notification\NotificationFrameBase.dds" ID="CCButton" Hidden="0" >
<ShowOnMouseOver>
<Image Anchor="C,C" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\NotificationFrameBase.dds" />
<AlphaAnim Anchor="C,C" AnchorSide="O.O" Offset="0,0" Size="80,80" Texture="assets\UI\Art\Notification\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\NotificationBlack.dds" />
<AlphaAnim Anchor="C,C" Offset="0,-1" Size="80,80" Texture="assets\UI\Art\Notification\SocialPolicyActive80.dds" ID="CCImage" Pause="0" Cycle="Bounce" Speed="1" AlphaStart=".5" AlphaEnd="1" />
<FlipAnim Size="64,64" Offset="0,6" Anchor="C,C" Columns="8" Speed="20" Pause=".5" StepSize="64,64" FrameCount="31" Texture="assets\UI\Art\Notification\CapitalAnimation.dds" />
<Label Anchor="C,C" Offset="0,0" String="" Font="TwCenMT20" ColorSet="Beige_Black_Alpha" FontStyle="Shadow" ID="CCCount" />
</Button>
</SlideAnim>
</Container>
</Instance>
</Context>
-- mainfile.lua
import("CCxNotifications");
function OnLoadScreenClose()
CCNot_Init();
end
Events.LoadScreenClose.Add(OnLoadScreenClose);
include("MoafsUtils");
include("FLuaVector");
include( "WhysUtils" );
-- vymdt.01.2010.11.12.1515
-- Created by: Whys Alives, modified by Moaf
--===========================================================================
--[[
Global Variables.
]]
local g_customActiveNotifications = {};
local g_customInstances = {};
local g_customData = {};
local g_customId = 0;
--handled custom notification types.
local g_customNameTable = {};
g_customNameTable[GameInfo.CCNotifications.NOTIFICATION_CULTURALCAPITAL] = "CC";
--===========================================================================
--===========================================================================
--===========================================================================
-------------------------------------------------------------------------------
-- details for dynamically sizing the small notification stack, copied from notificationpanel.lua
-------------------------------------------------------------------------------
local DIPLO_SIZE_GUESS = 120;
local _, screenY = UIManager:GetScreenSizeVal();
local offsetY;
local g_SmallScrollMax;
-- Init function
function CCNot_Init()
print("CCNot_Init");
local context = ContextPtr:LookUpControl("../CCNotifications/");
context:ChangeParent(ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/"));
_, offsetY = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/OuterStack"):GetOffsetVal();
g_SmallScrollMax = screenY - offsetY - DIPLO_SIZE_GUESS;
end
-------------------------------------------------
-------------------------------------------------
-- copied and modified from notificationpanel.lua
function CCNot_ProcessStackSizes()
local smallStack = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/SmallStack");
local smallScrollPanel = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/SmallScrollPanel");
local bigStack = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/BigStack");
local outerStack = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/OuterStack");
bigStack:CalculateSize();
local bigY = bigStack:GetSizeY();
smallScrollPanel:SetSizeY( g_SmallScrollMax - bigY );
smallStack:CalculateSize();
smallStack:ReprocessAnchoring();
smallScrollPanel:CalculateInternalSize();
if( smallScrollPanel:GetRatio() ~= 1 ) then
smallScrollPanel:SetOffsetVal( 20, 0 );
else
smallScrollPanel:SetOffsetVal( 0, 0 );
end
outerStack:CalculateSize();
outerStack:ReprocessAnchoring();
end
--===========================================================================
--===========================================================================
--===========================================================================
--[[
handles default mouse left-click for custom notifications. Uses fourth
argument in LuaEvents.CustomNotification() to goto {x,y} map location.
]]
function CCNot_CustomLeftClick( id )
if type( g_customData[id] ) == "table" and
type( g_customData[id]["location"] ) == "table" then
local plot = Map.GetPlot( g_customData[id]["location"][1]
, g_customData[id]["location"][2] );
if plot ~= nil then
UI.LookAt( plot, 0 );
Events.SerialEventHexHighlight(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY())), true, Vector4(1.0, 0.0, 1.0, 1.0));
end
end
end
--===========================================================================
--[[
handles default mouse right-click for custom notifications. Deactivates
custom notification.
]]
function CCNot_CustomRightClick( id )
if g_customActiveNotifications[id] == nil then
print("Unknown Custom Notification Id: "..tostring(id));
return;
end
local name = g_customNameTable[g_customActiveNotifications[id]];
local instance = g_customInstances[id];
if instance ~= nil then
local smallStack = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/SmallStack");
smallStack:ReleaseChild( instance[name.."Container"] );
g_customInstances[id] = nil;
end
CCNot_ProcessStackSizes();
end
--===========================================================================
--===========================================================================
function CCNot_OpenCCInfoOnLeftClick(id)
CCNot_CustomLeftClick(id);
end
--===========================================================================
--[[
Activates custom notification.
]]
function CCNot_OnCustomNotificationAdded( iType, summary, toolTip, data )
local smallStack = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/SmallStack");
local notificationPanelContext = ContextPtr:LookUpControl("/InGame/WorldView/ActionInfoPanel/NotificationPanel/");
local ccNotContext = ContextPtr:LookUpControl("../CCNotifications/");
g_customId = g_customId + 1;
if g_customActiveNotifications[g_customId] ~= nil then
print("Redundant Custom Notification Type: "..tostring(iType));
return;
end
local instance = {};
local name = g_customNameTable[iType];
ccNotContext:BuildInstanceForControl(name.."Item", instance, smallStack);
g_customInstances[g_customId] = instance;
g_customData[g_customId] = data;
local root = instance[name.."Container"];
local button = instance[name.."Button"];
instance.FingerTitle:SetText( summary );
root:BranchResetAnimation();
local leftClick, rightClick = nil, nil;
--.........................................................................
-- Custom Notification Handling
if iType == GameInfo.CCNotifications.NOTIFICATION_CULTURALCAPITAL then
leftClick = CCNot_OpenCCInfoOnLeftClick;
end
--END Custom Notification Handling
--.........................................................................
if type( leftClick ) ~= "function" then leftClick = CCNot_CustomLeftClick; end
if type( rightClick ) ~= "function" then rightClick = CCNot_CustomRightClick; end
button:SetHide( false );
button:SetVoid1( g_customId );
button:RegisterCallback( Mouse.eLClick, leftClick );
button:RegisterCallback( Mouse.eRClick, rightClick );
button:SetToolTipString( toolTip );
g_customActiveNotifications[g_customId] = iType;
CCNot_ProcessStackSizes();
end
LuaEvents.CustomNotification.Add( CCNot_OnCustomNotificationAdded );
--===========================================================================
--[[
Deactivates all custom notifications on end of turn.
]]
function CCNot_OnTurnEnd()
for id,type in pairs(g_customInstances) do CCNot_CustomRightClick(id); end
end
Events.ActivePlayerTurnEnd.Add(CCNot_OnTurnEnd);