diff --git a/.gitignore b/.gitignore
index 9491a2fda28342ab358eaf234e1afe0c07a53d62..afeec89c9bf1ece1bca5688d3a46b4ba23cd8321 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,9 @@
 ##
 ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 
+#Zip files
+*.zip
+
 # User-specific files
 *.rsuser
 *.suo
diff --git a/CityBannerManager.lua.cuc b/CityBannerManager.lua.cuc
new file mode 100644
index 0000000000000000000000000000000000000000..41e1049b6af71af42a6fa060740f2d9189eba0b9
--- /dev/null
+++ b/CityBannerManager.lua.cuc
@@ -0,0 +1,1241 @@
+--==========================================================
+-- CityBannerManager
+-- Re-written by bc1 using Notepad++
+-- code is common using gk_mode and bnw_mode switches
+--==========================================================
+
+Events.SequenceGameInitComplete.Add(function()
+
+include "GameInfoCache" -- warning! booleans are true, not 1, and use iterator ONLY with table field conditions, NOT string SQL query
+local GameInfo = GameInfoCache
+
+include "IconHookup"
+local IconHookup = IconHookup
+local CivIconHookup = CivIconHookup
+local Color = Color
+local PrimaryColors = PrimaryColors
+local BackgroundColors = BackgroundColors
+local ColorGreen = Color( 0, 1, 0, 1 )
+local ColorYellow = Color( 1, 1, 0, 1 )
+local ColorRed = Color( 1, 0, 0, 1 )
+local ColorCulture = Color( 1, 0, 1, 1 )
+
+include "CityStateStatusHelper"
+local GetCityStateStatusRow = GetCityStateStatusRow
+local GetActiveQuestText = GetActiveQuestText
+
+--==========================================================
+-- Minor lua optimizations
+--==========================================================
+
+local ipairs = ipairs
+local floor = math.floor
+local max = math.max
+local min = math.min
+local pairs = pairs
+local print = print
+local insert = table.insert
+local remove = table.remove
+local format = string.format
+
+local ButtonPopupTypes = ButtonPopupTypes
+local CityUpdateTypes = CityUpdateTypes
+local ContextPtr = ContextPtr
+local Controls = Controls
+local Events = Events
+local EventsClearHexHighlightStyle = Events.ClearHexHighlightStyle.Call
+local EventsRequestYieldDisplay = Events.RequestYieldDisplay.Call
+local EventsSerialEventHexHighlight = Events.SerialEventHexHighlight.Call
+local Game = Game
+local GridToWorld = GridToWorld
+local InStrategicView = InStrategicView
+local InterfaceModeTypes = InterfaceModeTypes
+local L = Locale.ConvertTextKey
+local ToUpper = Locale.ToUpper
+local GetPlot = Map.GetPlot
+local GetPlotByIndex = Map.GetPlotByIndex
+local MinorCivQuestTypes = MinorCivQuestTypes
+local Mouse = Mouse
+local SendUpdateCityCitizens = Network.SendUpdateCityCitizens
+local IsCivilianYields = OptionsManager.IsCivilianYields
+local Players = Players
+local Teams = Teams
+local ToGridFromHex = ToGridFromHex
+local ToHexFromGrid = ToHexFromGrid
+local UI = UI
+local GetUnitPortraitIcon = UI.GetUnitPortraitIcon
+local UnitMoving = UnitMoving
+local YieldDisplayTypes = YieldDisplayTypes
+local MAX_CITY_HIT_POINTS = GameDefines.MAX_CITY_HIT_POINTS
+local CITY_PLOTS_RADIUS = GameDefines.CITY_PLOTS_RADIUS
+
+--==========================================================
+-- Globals
+--==========================================================
+
+local RefreshCityBanner
+
+local gk_mode = Game.GetReligionName ~= nil
+local bnw_mode = Game.GetActiveLeague ~= nil
+
+local g_activePlayerID = Game.GetActivePlayer()
+local g_activePlayer = Players[ g_activePlayerID ]
+local g_activeTeamID = Game.GetActiveTeam()
+local g_activeTeam = Teams[ g_activeTeamID ]
+
+local g_cityBanners = {}
+--local g_outpostBanners = {}
+--local g_stationBanners = {}
+local g_svStrikeButtons = {}
+
+local g_scrapTeamBanners = {}
+local g_scrapOtherBanners = {}
+local g_scrapSVStrikeButtons = {}
+
+local g_WorldPositionOffsetZ = InStrategicView and 35 or 55
+
+
+local g_cityHexHighlight
+
+--local IsCivBE = Game.GetAvailableBeliefs ~= nil
+--local g_CovertOpsBannerContainer = IsCivBE and ContextPtr:LookUpControl( "../CovertOpsBannerContainer" )
+--local g_CovertOpsIntelReportContainer = IsCivBE and ContextPtr:LookUpControl( "../CovertOpsIntelReportContainer" )
+
+local g_cityFocusIcons = {
+--[CityAIFocusTypes.NO_CITY_AI_FOCUS_TYPE or -1] = "",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FOOD or -1] = "[ICON_FOOD]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_PRODUCTION or -1] = "[ICON_PRODUCTION]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GOLD or -1] = "[ICON_GOLD]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_SCIENCE or -1] = "[ICON_RESEARCH]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_CULTURE or -1] = "[ICON_CULTURE]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GREAT_PEOPLE or -1] = "[ICON_GREAT_PEOPLE]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FAITH or -1] = "[ICON_PEACE]",
+} g_cityFocusIcons[-1] = nil
+
+local g_cityFocusTooltips = {
+[CityAIFocusTypes.NO_CITY_AI_FOCUS_TYPE or -1] = L"TXT_KEY_CITYVIEW_FOCUS_BALANCED_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FOOD or -1] = L"TXT_KEY_CITYVIEW_FOCUS_FOOD_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_PRODUCTION or -1] = L"TXT_KEY_CITYVIEW_FOCUS_PROD_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GOLD or -1] = L"TXT_KEY_CITYVIEW_FOCUS_GOLD_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_SCIENCE or -1] = L"TXT_KEY_CITYVIEW_FOCUS_RESEARCH_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_CULTURE or -1] = L"TXT_KEY_CITYVIEW_FOCUS_CULTURE_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GREAT_PEOPLE or -1] = L"TXT_KEY_CITYVIEW_FOCUS_GREAT_PERSON_TEXT",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FAITH or -1] = L"TXT_KEY_CITYVIEW_FOCUS_FAITH_TEXT",
+} g_cityFocusTooltips[-1] = nil
+
+local function IsTurnActive( player )
+	return player and player:IsTurnActive() and not Game.IsProcessingMessages()
+end
+
+local function BannerError( where, arg )
+	if Game.IsDebugMode() then
+		local txt = ""
+		if arg and arg.PlotIndex then
+			txt = "city banner"
+			arg = arg and GetPlotByIndex(arg.PlotIndex)
+		end
+		if arg and arg.GetPlotCity then
+			txt = "plot " .. (arg:IsCity() and "with" or "without") .. " city"
+			arg = arg:GetPlotCity()
+		end
+		if arg and arg.GetCityPlotIndex then
+			txt = "city " .. arg:GetName()
+		end
+		print( "glitch", where, txt, debug and debug.traceback and debug.traceback() )
+	end
+end
+
+--==========================================================
+-- Clear Hex Highlighting
+--==========================================================
+
+local function ClearHexHighlights()
+	EventsClearHexHighlightStyle( "HexContour" )
+	EventsClearHexHighlightStyle( "WorkedFill" )
+	EventsClearHexHighlightStyle( "WorkedOutline" )
+	EventsClearHexHighlightStyle( "UnlockedFill" )
+	EventsClearHexHighlightStyle( "UnlockedOutline" )
+	EventsClearHexHighlightStyle( "OwnedFill")
+	EventsClearHexHighlightStyle( "OwnedOutline" )
+	EventsClearHexHighlightStyle( "CityLimits" )
+	EventsClearHexHighlightStyle( "EnemyFill" )
+	EventsClearHexHighlightStyle( "EnemyOutline" )
+	g_cityHexHighlight = false
+end
+
+--==========================================================
+-- Show/hide the garrison frame icon
+--==========================================================
+
+local function HideGarrisonFrame( instance, isHide )
+	-- Only the active team has a Garrison ring
+	if instance and instance[1] then
+		instance.GarrisonFrame:SetHide( isHide )
+	end
+end
+
+--==========================================================
+-- Show/hide the range strike icon
+--==========================================================
+
+local function UpdateRangeIcons( plotIndex, city, instance )
+
+	if city and instance then
+
+		local hideRangeStrikeButton = city:GetOwner() ~= g_activePlayerID or not city:CanRangeStrikeNow()
+		if instance.CityRangeStrikeButton then
+			instance.CityRangeStrikeButton:SetHide( hideRangeStrikeButton )
+		end
+
+		instance = g_svStrikeButtons[ plotIndex ]
+		if instance then
+			instance.CityRangeStrikeButton:SetHide( hideRangeStrikeButton )
+		end
+	end
+end
+
+--==========================================================
+-- Refresh the City Damage bar
+--==========================================================
+
+local function RefreshCityDamage( city, instance, cityDamage )
+
+	if instance then
+
+		local maxCityHitPoints = gk_mode and city and city:GetMaxHitPoints() or MAX_CITY_HIT_POINTS
+		local iHealthPercent = 1 - cityDamage / maxCityHitPoints
+
+		instance.CityBannerHealthBar:SetPercent(iHealthPercent)
+		instance.CityBannerHealthBar:SetToolTipString( format("%g / %g", maxCityHitPoints - cityDamage, maxCityHitPoints) )
+
+		---- Health bar color based on amount of damage
+		local barColor = {}
+
+		if iHealthPercent > 0.66 then
+			barColor = ColorGreen
+		elseif iHealthPercent > 0.33 then
+			barColor = ColorYellow
+		else
+			barColor = ColorRed
+		end
+		instance.CityBannerHealthBar:SetFGColor( barColor )
+
+		-- Show or hide the Health Bar as necessary
+		instance.CityBannerHealthBarBase:SetHide( cityDamage == 0 )
+	end
+end
+
+--==========================================================
+-- Click On City State Quest Info
+--==========================================================
+
+local questKillCamp = MinorCivQuestTypes.MINOR_CIV_QUEST_KILL_CAMP
+local IsActiveQuestKillCamp
+if bnw_mode then
+	IsActiveQuestKillCamp = function( minorPlayer )
+		return minorPlayer and minorPlayer:IsMinorCivDisplayedQuestForPlayer( g_activePlayerID, questKillCamp )
+	end
+elseif gk_mode then
+	IsActiveQuestKillCamp = function( minorPlayer )
+		return minorPlayer and minorPlayer:IsMinorCivActiveQuestForPlayer( g_activePlayerID, questKillCamp )
+	end
+else
+	IsActiveQuestKillCamp = function( minorPlayer )
+		return minorPlayer and minorPlayer:GetActiveQuestForPlayer( g_activePlayerID ) == questKillCamp
+	end
+end
+
+local function OnQuestInfoClicked( plotIndex )
+	local plot = GetPlotByIndex( plotIndex )
+	local city = plot and plot:GetPlotCity()
+	local cityOwner = city and Players[ city:GetOwner() ]
+	if cityOwner and cityOwner:IsMinorCiv() and IsActiveQuestKillCamp( cityOwner ) then
+		local questData1 = cityOwner:GetQuestData1( g_activePlayerID, questKillCamp )
+		local questData2 = cityOwner:GetQuestData2( g_activePlayerID, questKillCamp )
+		local plot = GetPlot( questData1, questData2 )
+		if plot then
+			UI.LookAt( plot )
+			local hex = ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }
+			Events.GameplayFX( hex.x, hex.y, -1 )
+		end
+	end
+end
+
+local function AnnexPopup( plotIndex )
+	local plot = GetPlotByIndex( plotIndex )
+	local city = plot and plot:GetPlotCity()
+	if city and city:GetOwner() == g_activePlayerID and not( bnw_mode and g_activePlayer:MayNotAnnex() ) then
+		Events.SerialEventGameMessagePopup{
+			Type = ButtonPopupTypes.BUTTONPOPUP_ANNEX_CITY,
+			Data1 = city:GetID(),
+			Data2 = -1,
+			Data3 = -1,
+			Option1 = false,
+			Option2 = false
+		}
+	end
+end
+
+local function EspionagePopup( plotIndex )
+	local plot = GetPlotByIndex( plotIndex )
+	local city = plot and plot:GetPlotCity()
+	if city and not Players[city:GetOwner()]:IsMinorCiv() then
+		ClearHexHighlights()
+		UI.SetInterfaceMode( InterfaceModeTypes.INTERFACEMODE_SELECTION )
+		UI.DoSelectCityAtPlot( plot )
+	else
+		Events.SerialEventGameMessagePopup{ Type = ButtonPopupTypes.BUTTONPOPUP_ESPIONAGE_OVERVIEW }
+	end
+end
+
+--==========================================================
+-- Click on City Range Strike Button
+--==========================================================
+
+local function OnCityRangeStrikeButtonClick( plotIndex )
+	local plot = GetPlotByIndex( plotIndex )
+	local city = plot and plot:GetPlotCity()
+
+	if city and city:GetOwner() == g_activePlayerID then
+		UI.ClearSelectionList()
+		UI.SelectCity( city )
+		UI.SetInterfaceMode( InterfaceModeTypes.INTERFACEMODE_CITY_RANGE_ATTACK )
+--		Events.InitCityRangeStrike( city:GetOwner(), city:GetID() )
+	end
+end
+
+--==========================================================
+-- Left Click on city banner
+--==========================================================
+
+local function OnBannerClick( plotIndex )
+	local plot = GetPlotByIndex( plotIndex )
+	local city = plot and plot:GetPlotCity()
+	if city then
+		UI.SetInterfaceMode( InterfaceModeTypes.INTERFACEMODE_SELECTION )
+		local cityOwnerID = city:GetOwner()
+		local cityOwner = Players[ cityOwnerID ]
+
+		-- Active player city
+		if cityOwnerID == g_activePlayerID then
+
+			-- always open city screen, puppets are not that special
+			ClearHexHighlights()
+			UI.DoSelectCityAtPlot( plot )
+
+		-- Observers get to look at anything
+		elseif Game.IsDebugMode() or g_activePlayer:IsObserver() then
+			UI.SelectCity( city )
+			UI.LookAt( plot )
+			UI.SetCityScreenUp( true )
+
+		-- Other player, which has been met
+		elseif g_activeTeam:IsHasMet( city:GetTeam() ) then
+
+			if cityOwner:IsMinorCiv() then
+				UI.DoSelectCityAtPlot( plot )
+			elseif IsTurnActive( g_activePlayer ) then
+				if cityOwner:IsHuman() then
+					Events.OpenPlayerDealScreenEvent( cityOwnerID )
+				elseif not cityOwner:IsBarbarian() then
+					UI.SetRepeatActionPlayer( cityOwnerID )
+					UI.ChangeStartDiploRepeatCount(1)
+					cityOwner:DoBeginDiploWithHuman()
+				end
+			end
+		end
+	else
+		BannerError( "OnBannerClick", plot )
+	end
+end
+
+--==========================================================
+-- Destroy City Banner
+--==========================================================
+
+local function DestroyCityBanner( plotIndex, instance )
+
+	-- Release city banner
+	if instance then
+		insert( instance[1] and g_scrapTeamBanners or g_scrapOtherBanners, instance )
+		g_cityBanners[ plotIndex or -1 ] = nil
+		instance.Anchor:ChangeParent( Controls.Scrap )
+	end
+
+	-- Release sv strike button
+	instance = g_svStrikeButtons[ plotIndex ]
+	if instance then
+		instance.Anchor:ChangeParent( Controls.Scrap )
+		insert( g_scrapSVStrikeButtons, instance )
+		g_svStrikeButtons[ plotIndex ] = nil
+	end
+end
+
+--==========================================================
+-- City banner mouse over
+--==========================================================
+
+local function OnBannerMouseExit()
+	if not UI.IsCityScreenUp() then
+
+		ClearHexHighlights()
+		-- duplicate code from InGame.lua function RequestYieldDisplay()
+
+		local isDisplayCivilianYields = IsCivilianYields()
+		local unit = UI.GetHeadSelectedUnit()
+
+		if isDisplayCivilianYields and UI.CanSelectionListWork() and not( unit and (GameInfo.Units[unit:GetUnitType()] or {}).DontShowYields ) then
+			EventsRequestYieldDisplay( YieldDisplayTypes.EMPIRE )
+
+		elseif isDisplayCivilianYields and UI.CanSelectionListFound() and unit then
+			EventsRequestYieldDisplay( YieldDisplayTypes.AREA, 2, unit:GetX(), unit:GetY() )
+		else
+			EventsRequestYieldDisplay( YieldDisplayTypes.AREA, 0 )
+		end
+	end
+end
+
+local function OnBannerMouseEnter( plotIndex )
+	local plot = GetPlotByIndex( plotIndex )
+	if plot then
+		local city = plot:GetPlotCity()
+		g_cityHexHighlight = plotIndex
+		if city and city:GetOwner() == g_activePlayerID and not( Game.IsNetworkMultiPlayer() and g_activePlayer:HasReceivedNetTurnComplete() ) then -- required to prevent turn interrupt
+			SendUpdateCityCitizens( city:GetID() )
+		end
+		return RefreshCityBanner( city )
+	end
+end
+
+local CityTooltip = LuaEvents.CityToolTips.Call
+local TeamCityTooltips = {
+	CityBannerButton = "EUI_ItemTooltip",
+	CityBannerRightBackground = "EUI_ItemTooltip",
+	BuildGrowth = "EUI_ItemTooltip",
+	CityGrowth = "EUI_ItemTooltip",
+--	BorderGrowth = "EUI_ItemTooltip",
+	CityReligion = "EUI_ItemTooltip",
+	CityFocus = "EUI_ItemTooltip",
+	CityQuests = "EUI_ItemTooltip",
+	CityIsPuppet = "EUI_ItemTooltip",
+	CityIsRazing = "EUI_ItemTooltip",
+	CityIsResistance = "EUI_ItemTooltip",
+	CityIsConnected = "EUI_ItemTooltip",
+	CityIsBlockaded = "EUI_ItemTooltip",
+	CityIsOccupied = "EUI_ItemTooltip",
+	CityIsCapital = "EUI_ItemTooltip",
+	CityIsOriginalCapital = "EUI_ItemTooltip",
+	CivIndicator = "EUI_ItemTooltip",
+	CityProductionBG = "EUI_CityProductionTooltip",
+	CityPopulation = "EUI_CityGrowthTooltip",
+}
+local OtherCityTooltips = {
+	CityBannerButton = "EUI_ItemTooltip",
+	CityBannerRightBackground = "EUI_ItemTooltip",
+--	BuildGrowth = "EUI_ItemTooltip",
+--	CityGrowth = "EUI_ItemTooltip",
+--	BorderGrowth = "EUI_ItemTooltip",
+	CityReligion = "EUI_ItemTooltip",
+--	CityFocus = "EUI_ItemTooltip",
+	CityQuests = "EUI_ItemTooltip",
+	CityIsPuppet = "EUI_ItemTooltip",
+	CityIsRazing = "EUI_ItemTooltip",
+	CityIsResistance = "EUI_ItemTooltip",
+--	CityIsConnected = "EUI_ItemTooltip",
+	CityIsBlockaded = "EUI_ItemTooltip",
+	CityIsOccupied = "EUI_ItemTooltip",
+	CityIsCapital = "EUI_ItemTooltip",
+	CityIsOriginalCapital = "EUI_ItemTooltip",
+	CivIndicator = "EUI_ItemTooltip",
+--	CityProductionBG = "EUI_CityProductionTooltip",
+--	CityPopulation = "EUI_CityGrowthTooltip",
+}
+local function InitBannerCallbacks( instance, tooltips )
+	local button = instance.CityBannerButton
+	button:RegisterCallback( Mouse.eLClick, OnBannerClick )
+	button:RegisterCallback( Mouse.eMouseEnter, OnBannerMouseEnter )
+	button:RegisterCallback( Mouse.eMouseExit, OnBannerMouseExit )
+--	instance.CityName:SetColor( Color( 0, 0, 0, 0.5 ), 1 )	-- #1 = shadow color
+--	instance.CityName:SetColor( Color( 1, 1, 1, 0.5 ), 2 )	-- #2 = soft color
+	instance.CityDiplomat:RegisterCallback( Mouse.eLClick, EspionagePopup )
+	instance.CitySpy:RegisterCallback( Mouse.eLClick, EspionagePopup )
+	-- Setup Tootip Callbacks
+	for controlID, toolTipType in pairs( tooltips ) do
+		instance[ controlID ]:SetToolTipCallback( function( control )
+			control:SetToolTipCallback( function( control ) return CityTooltip( control, GetPlotByIndex( button:GetVoid1() ):GetPlotCity() ) end )
+			control:SetToolTipType( toolTipType )
+		end)
+	end
+end
+
+--==========================================================
+-- Update banners to reflect latest city info
+--==========================================================
+
+function RefreshCityBanner( city )
+
+	if city then
+
+		local isDebug = Game.IsDebugMode() or g_activePlayer:IsObserver()
+		local plot = city:Plot()
+		local plotIndex = plot:GetPlotIndex()
+		local instance = g_cityBanners[ plotIndex ]
+		local cityOwnerID = city:GetOwner()
+		local cityOwner = Players[ cityOwnerID ]
+		local isActiveType = isDebug or city:GetTeam() == g_activeTeamID
+		local isActivePlayerCity = cityOwnerID == g_activePlayerID
+
+		-- Incompatible banner type ? Destroy !
+		if instance and isActiveType ~= instance[1] then
+			DestroyCityBanner( plotIndex, instance )
+			instance = nil
+		end
+
+		---------------------
+		-- Create City Banner
+		if not instance then
+			local worldX, worldY, worldZ = GridToWorld( plot:GetX(), plot:GetY() )
+			if isActiveType then
+				-- create a strike button for stategic view
+				instance = remove( g_scrapSVStrikeButtons )
+				if instance then
+					instance.Anchor:ChangeParent( Controls.StrategicViewStrikeButtons )
+				else
+					instance = {}
+					ContextPtr:BuildInstanceForControl( "SVRangeStrikeButton", instance, Controls.StrategicViewStrikeButtons )
+					instance.CityRangeStrikeButton:RegisterCallback( Mouse.eLClick, OnCityRangeStrikeButtonClick )
+				end
+				instance.Anchor:SetWorldPositionVal( worldX, worldY, worldZ )
+				instance.CityRangeStrikeButton:SetVoid1( plotIndex )
+				g_svStrikeButtons[ plotIndex ] = instance
+
+				-- create a team type city banner
+				instance = remove( g_scrapTeamBanners )
+				if instance then
+					instance.Anchor:ChangeParent( Controls.CityBanners )
+				else
+					instance = {}
+					ContextPtr:BuildInstanceForControl( "TeamCityBanner", instance, Controls.CityBanners )
+					instance.CityRangeStrikeButton:RegisterCallback( Mouse.eLClick, OnCityRangeStrikeButtonClick )
+					instance.CityIsPuppet:RegisterCallback( Mouse.eLClick, AnnexPopup )
+					InitBannerCallbacks( instance, TeamCityTooltips )
+				end
+				instance.CityIsPuppet:SetVoid1( plotIndex )
+				instance.CityRangeStrikeButton:SetVoid1( plotIndex )
+			else
+				-- create a foreign type city banner
+				instance = remove( g_scrapOtherBanners )
+				if instance then
+					instance.Anchor:ChangeParent( Controls.CityBanners )
+				else
+					instance = {}
+					ContextPtr:BuildInstanceForControl( "OtherCityBanner", instance, Controls.CityBanners )
+					instance.CityQuests:RegisterCallback( Mouse.eLClick, OnQuestInfoClicked )
+					InitBannerCallbacks( instance, OtherCityTooltips )
+				end
+				instance.CityQuests:SetVoid1( plotIndex )
+			end
+
+			instance.CityBannerButton:SetVoid1( plotIndex )
+			instance.Anchor:SetWorldPositionVal( worldX, worldY, worldZ + g_WorldPositionOffsetZ )
+
+			instance[1] = isActiveType
+			g_cityBanners[ plotIndex ] = instance
+		end
+		-- /Create City Banner
+		---------------------
+
+		-- Refresh the damage bar
+		RefreshCityDamage( city, instance, city:GetDamage() )
+
+		-- Colors
+		local color = PrimaryColors[ cityOwnerID ]
+		local backgroundColor = BackgroundColors[ cityOwnerID ]
+
+		-- Update name
+		local cityName = city:GetName()
+		local upperCaseCityName = ToUpper( cityName )
+
+		local originalCityOwnerID = city:GetOriginalOwner()
+		local originalCityOwner = Players[ originalCityOwnerID ]
+		local otherCivID, otherCivAlpha
+		local isRazing = city:IsRazing()
+		local isResistance = city:IsResistance()
+		local isPuppet = city:IsPuppet()
+
+		-- Update capital icon
+		instance.CityIsCapital:SetHide( not city:IsCapital() or cityOwner:IsMinorCiv() )
+		instance.CityIsOriginalCapital:SetHide( city:IsCapital() or not city:IsOriginalCapital() )
+
+		instance.CityName:SetText( upperCaseCityName )
+		instance.CityName:SetColor( color, 0 )			-- #0 = main color
+
+		-- Update strength
+		instance.CityStrength:SetText(floor(city:GetStrengthValue() / 100))
+
+		-- Update population
+		instance.CityPopulationValue:SetText( city:GetPopulation() )
+
+		-- Being Razed ?
+		instance.CityIsRazing:SetHide( not isRazing )
+
+		-- In Resistance ?
+		instance.CityIsResistance:SetHide( not isResistance )
+
+		-- Puppet ?
+		instance.CityIsPuppet:SetHide( not isPuppet )
+
+		-- Occupied ?
+		instance.CityIsOccupied:SetHide( not city:IsOccupied() or city:IsNoOccupiedUnhappiness() )
+
+		-- Blockaded ?
+		instance.CityIsBlockaded:SetHide( not city:IsBlockaded() )
+
+		-- Garrisoned ?
+		instance.GarrisonFrame:SetHide( not ( plot:IsVisible( g_activeTeamID, true ) and city:GetGarrisonedUnit() ) )
+
+		instance.CityBannerBackground:SetColor( backgroundColor )
+		instance.CityBannerRightBackground:SetColor( backgroundColor )
+		instance.CityBannerLeftBackground:SetColor( backgroundColor )
+
+		if isActiveType then
+
+			instance.CityBannerBGLeftHL:SetColor( backgroundColor )
+			instance.CityBannerBGRightHL:SetColor( backgroundColor )
+			instance.CityBannerBackgroundHL:SetColor( backgroundColor )
+
+			-- Update Growth
+			local foodStored100 = city:GetFoodTimes100()
+			local foodThreshold100 = city:GrowthThreshold() * 100
+			local foodPerTurn100 = city:FoodDifferenceTimes100( true )
+			local foodStoredPercent = 0
+			local foodStoredNextTurnPercent = 0
+			if foodThreshold100 > 0 then
+				foodStoredPercent = foodStored100 / foodThreshold100
+				foodStoredNextTurnPercent = ( foodStored100 + foodPerTurn100 ) / foodThreshold100
+				if foodPerTurn100 < 0 then
+					foodStoredPercent, foodStoredNextTurnPercent = foodStoredNextTurnPercent, foodStoredPercent
+				end
+			end
+
+			-- Update Growth Meter
+			instance.GrowthBar:SetPercent( max(min( foodStoredPercent, 1),0))
+			instance.GrowthBarShadow:SetPercent( max(min( foodStoredNextTurnPercent, 1),0))
+			instance.GrowthBarStarve:SetHide( foodPerTurn100 >= 0 )
+
+			-- Update Growth Time
+			local turnsToCityGrowth = city:GetFoodTurnsLeft()
+			local growthText
+
+			if foodPerTurn100 < 0 then
+				turnsToCityGrowth = floor( foodStored100 / -foodPerTurn100 ) + 1
+				growthText = "[COLOR_WARNING_TEXT]" .. turnsToCityGrowth .. "[ENDCOLOR]"
+			elseif city:IsForcedAvoidGrowth() then
+				growthText = "[ICON_LOCKED]"
+			elseif foodPerTurn100 == 0 then
+				growthText = "-"
+			else
+				growthText = min(turnsToCityGrowth,99)
+			end
+
+			instance.CityGrowth:SetText( growthText )
+
+			local productionPerTurn100 = city:GetCurrentProductionDifferenceTimes100(false, false)	-- food = false, overflow = false
+			local productionStored100 = city:GetProductionTimes100() + city:GetCurrentProductionDifferenceTimes100(false, true) - productionPerTurn100
+			local productionNeeded100 = city:GetProductionNeeded() * 100
+			local productionStoredPercent = 0
+			local productionStoredNextTurnPercent = 0
+
+			if productionNeeded100 > 0 then
+				productionStoredPercent = productionStored100 / productionNeeded100
+				productionStoredNextTurnPercent = (productionStored100 + productionPerTurn100) / productionNeeded100
+			end
+
+			instance.ProductionBar:SetPercent( max(min( productionStoredPercent, 1),0))
+			instance.ProductionBarShadow:SetPercent( max(min( productionStoredNextTurnPercent, 1),0))
+
+			-- Update Production Time
+			if city:IsProduction()
+				and not city:IsProductionProcess()
+				and productionPerTurn100 > 0
+			then
+				instance.BuildGrowth:SetText( city:GetProductionTurnsLeft() )
+			else
+				instance.BuildGrowth:SetText( "-" )
+			end
+
+			-- Update Production icon
+			local unitProductionID = city:GetProductionUnit()
+			local buildingProductionID = city:GetProductionBuilding()
+			local projectProductionID = city:GetProductionProject()
+			local processProductionID = city:GetProductionProcess()
+			local portraitIndex, portraitAtlas
+			local item = nil
+
+			if unitProductionID ~= -1 then
+				item = GameInfo.Units[unitProductionID]
+				portraitIndex, portraitAtlas = GetUnitPortraitIcon( (item or {}).ID or -1, cityOwnerID )
+			elseif buildingProductionID ~= -1 then
+				item = GameInfo.Buildings[buildingProductionID]
+			elseif projectProductionID ~= -1 then
+				item = GameInfo.Projects[projectProductionID]
+			elseif processProductionID ~= -1 then
+				item = GameInfo.Processes[processProductionID]
+			end
+			-- really should have an error texture
+
+			instance.CityProduction:SetHide( not( item and
+				IconHookup( portraitIndex or item.PortraitIndex, 45, portraitAtlas or item.IconAtlas, instance.CityProduction )))
+
+			-- Focus?
+			if isRazing or isResistance or isPuppet then
+				instance.CityFocus:SetHide( true )
+			else
+				instance.CityFocus:SetText( g_cityFocusIcons[city:GetFocusType()] )
+				instance.CityFocus:SetHide( false )
+			end
+
+			-- Connected to capital?
+			instance.CityIsConnected:SetHide( city:IsCapital() or not cityOwner:IsCapitalConnectedToCity( city ) )
+
+			-- Demand resource / King day ?
+			local resource = GameInfo.Resources[ city:GetResourceDemanded() ]
+			local weLoveTheKingDayCounter = city:GetWeLoveTheKingDayCounter()
+			-- We love the king
+			if weLoveTheKingDayCounter > 0 then
+				instance.CityQuests:SetText( "[ICON_HAPPINESS_1]" )
+				instance.CityQuests:SetHide( false )
+
+			elseif resource then
+				instance.CityQuests:SetText( resource.IconString )
+				instance.CityQuests:SetHide( false )
+			else
+				instance.CityQuests:SetHide( true )
+			end
+
+			-- update range strike button (if it is the active player's city)
+
+			UpdateRangeIcons( plotIndex, city, instance )
+
+		-- not active team city
+		else
+			local isMinorCiv = cityOwner:IsMinorCiv()
+			if isMinorCiv then
+				-- Update Quests
+				instance.CityQuests:SetText( GetActiveQuestText( g_activePlayerID, cityOwnerID ) )
+				local info = GetCityStateStatusRow( g_activePlayerID, cityOwnerID )
+				instance.StatusIconBG:SetTexture( info and info.StatusIcon )
+				instance.StatusIcon:SetTexture( (GameInfo.MinorCivTraits[ (GameInfo.MinorCivilizations[ cityOwner:GetMinorCivType() ] or {}).MinorCivTrait ] or {}).TraitIcon )
+				-- Update Pledge
+				if gk_mode then
+					local pledge = g_activePlayer:IsProtectingMinor( cityOwnerID )
+					local free = pledge and cityOwner:CanMajorWithdrawProtection( g_activePlayerID )
+					instance.Pledge1:SetHide( not pledge or free )
+					instance.Pledge2:SetHide( not free )
+				end
+				-- Update Allies
+				local allyID = cityOwner:GetAlly()
+				local ally = Players[ allyID ]
+				if ally then
+				-- Set left banner icon to ally flag
+					otherCivAlpha = 1
+					otherCivID = g_activeTeam:IsHasMet( ally:GetTeam() ) and allyID or -1
+				end
+			else
+				CivIconHookup( cityOwnerID, 45, instance.OwnerIcon, instance.OwnerIconBG, instance.OwnerIconShadow, false, true )
+			end
+			instance.CityQuests:SetHide( not isMinorCiv )
+			instance.StatusIconBG:SetHide( not isMinorCiv )
+			instance.OwnerIconBG:SetHide( isMinorCiv )
+		end
+		if not otherCivID and originalCityOwner and (originalCityOwnerID ~= cityOwnerID) then
+		-- Set left banner icon to city state flag
+			if originalCityOwner:IsMinorCiv() then
+				otherCivAlpha = 4 --hack
+				instance.MinorTraitIcon:SetTexture( (GameInfo.MinorCivTraits[ (GameInfo.MinorCivilizations[ originalCityOwner:GetMinorCivType() ] or {}).MinorCivTrait ] or {}).TraitIcon )
+				instance.CityIsOriginalCapital:SetHide( true )
+			else
+				otherCivAlpha = 0.5
+				otherCivID = originalCityOwnerID
+			end
+		end
+		if otherCivID then
+			CivIconHookup( otherCivID, 32, instance.CivIcon, instance.CivIconBG, instance.CivIconShadow, false, true )
+			instance.CivIndicator:SetAlpha( otherCivAlpha )
+		end
+		instance.MinorCivIndicator:SetHide( otherCivAlpha ~= 4 ) -- hack
+		instance.CivIndicator:SetHide( not otherCivID )
+
+		-- Spy & Religion
+		if gk_mode then
+			local spy
+			local x = city:GetX()
+			local y = city:GetY()
+
+			for _, s in ipairs( g_activePlayer:GetEspionageSpies() ) do
+				if s.CityX == x and s.CityY == y then
+					spy = s
+					break
+				end
+			end
+
+			if spy then
+				if spy.IsDiplomat then
+					instance.CityDiplomat:SetVoid1( spy.EstablishedSurveillance and plotIndex or -1 )
+					instance.CityDiplomat:LocalizeAndSetToolTip( "TXT_KEY_CITY_DIPLOMAT_OTHER_CIV_TT", spy.Rank, spy.Name, cityName, spy.Rank, spy.Name, spy.Rank, spy.Name )
+					instance.CityDiplomat:SetHide( false )
+					instance.CitySpy:SetHide( true )
+				else
+					instance.CitySpy:SetHide( false )
+					instance.CitySpy:SetVoid1( spy.EstablishedSurveillance and plotIndex or -1 )
+					if isActivePlayerCity then
+						instance.CitySpy:LocalizeAndSetToolTip( "TXT_KEY_CITY_SPY_YOUR_CITY_TT", spy.Rank, spy.Name, cityName, spy.Rank, spy.Name )
+					elseif cityOwner:IsMinorCiv() then
+						instance.CitySpy:LocalizeAndSetToolTip( "TXT_KEY_CITY_SPY_CITY_STATE_TT", spy.Rank, spy.Name, cityName, spy.Rank, spy.Name)
+					else
+						instance.CitySpy:LocalizeAndSetToolTip( "TXT_KEY_CITY_SPY_OTHER_CIV_TT", spy.Rank, spy.Name, cityName, spy.Rank, spy.Name, spy.Rank, spy.Name)
+					end
+					instance.CityDiplomat:SetHide( true )
+				end
+			else
+				instance.CitySpy:SetHide( true )
+				instance.CityDiplomat:SetHide( true )
+			end
+
+			local religion = GameInfo.Religions[city:GetReligiousMajority()]
+			if religion then
+				IconHookup( religion.PortraitIndex, 32, religion.IconAtlas, instance.CityReligion )
+				IconHookup( religion.PortraitIndex, 32, religion.IconAtlas, instance.ReligiousIconShadow )
+				instance.ReligiousIconContainer:SetHide( false )
+			else
+				instance.ReligiousIconContainer:SetHide( true )
+			end
+		end
+
+		-- Change the width of the banner so it looks good with the length of the city name
+
+		instance.NameStack:CalculateSize()
+		local bannerWidth = instance.NameStack:GetSizeX() - 64
+		instance.CityBannerButton:SetSizeX( bannerWidth + 64 )
+		instance.CityBannerBackground:SetSizeX( bannerWidth )
+		instance.CityBannerBackgroundHL:SetSizeX( bannerWidth )
+		if isActiveType then
+			instance.CityBannerBackgroundIcon:SetSizeX( bannerWidth )
+			instance.CityBannerButtonGlow:SetSizeX( bannerWidth )
+			instance.CityBannerButtonBase:SetSizeX( bannerWidth )
+		else
+			instance.CityBannerBaseFrame:SetSizeX( bannerWidth )
+			instance.CityAtWar:SetSizeX( bannerWidth )
+			instance.CityAtWar:SetHide( not g_activeTeam:IsAtWar( city:GetTeam() ) )
+		end
+
+		instance.CityBannerButton:ReprocessAnchoring()
+		instance.NameStack:ReprocessAnchoring()
+		instance.IconsStack:CalculateSize()
+		instance.IconsStack:ReprocessAnchoring()
+
+		if g_cityHexHighlight == plotIndex then
+
+			if not (InStrategicView and InStrategicView()) then
+				local cityID = city:GetID()
+				local cityTeamID = cityOwner:GetTeam()
+				local plot = GetPlot(0,0)
+				local hexPos
+				local checkFunc = plot.GetCityPurchaseID or plot.GetWorkingCity
+				local checkVal = plot.GetCityPurchaseID and cityID or city
+				for i = 0, city:GetNumCityPlots()-1 do
+					plot = city:GetCityIndexPlot( i )
+					if plot then
+						hexPos = ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }
+						-- Show city limits
+						EventsSerialEventHexHighlight( hexPos, true, nil, "CityLimits" )
+						if plot:GetOwner() == cityOwnerID then
+							local isImproved = true
+							if plot:GetImprovementType()>0 then
+								isImproved  = not plot:IsImprovementPillaged() -- CanHaveImprovement, GetImprovementType, GetRevealedImprovementType, IsImprovementPillaged
+							else
+								for improvement in GameInfo.Improvements() do
+									if plot:CanHaveImprovement( improvement.ID, cityTeamID ) then
+										isImproved = false
+										break
+									end
+								end
+							end
+							if isActiveType and city:IsWorkingPlot( plot ) then
+								-- worked city plots
+								if plot:IsCity() or city:IsForcedWorkingPlot( plot ) then
+									if isImproved then
+										EventsSerialEventHexHighlight( hexPos , true, nil, "WorkedFill" )
+									end
+									EventsSerialEventHexHighlight( hexPos , true, nil, "WorkedOutline" )
+								else
+									if isImproved then
+										EventsSerialEventHexHighlight( hexPos , true, nil, "UnlockedFill" )
+									end
+									EventsSerialEventHexHighlight( hexPos , true, nil, "UnlockedOutline" )
+								end
+							elseif not city:CanWork( plot ) and ( plot:IsWater() and city:IsPlotBlockaded( plot ) or plot:IsVisibleEnemyUnit( cityOwnerID ) ) then
+								-- Blockaded water plot or Enemy Unit standing here
+								EventsSerialEventHexHighlight( hexPos , true, nil, "EnemyFill" )
+								EventsSerialEventHexHighlight( hexPos , true, nil, "EnemyOutline" )
+							elseif checkFunc( plot ) == checkVal then
+								-- city plots that are owned but not worked
+								if isImproved then
+									EventsSerialEventHexHighlight( hexPos , true, nil, "OwnedFill" )
+								end
+								EventsSerialEventHexHighlight( hexPos , true, nil, "OwnedOutline" )
+							end
+						end
+					end
+				end
+			end
+			if isActiveType then
+				-- Show plots that will be acquired by culture
+				local purchasablePlots = {city:GetBuyablePlotList()}
+				for i = 1, #purchasablePlots do
+					local plot = purchasablePlots[i]
+					EventsSerialEventHexHighlight( ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }, true, ColorCulture, "HexContour" )
+				end
+				EventsRequestYieldDisplay( YieldDisplayTypes.AREA, CITY_PLOTS_RADIUS, city:GetX(), city:GetY() )
+			else
+				EventsRequestYieldDisplay( YieldDisplayTypes.CITY_OWNED, city:GetX(), city:GetY() )
+			end
+		end
+	end
+end
+
+local function RefreshCityBannerAtPlot( plot )
+	return plot and RefreshCityBanner( plot:GetPlotCity() )
+end
+
+--==========================================================
+-- Register Events
+--==========================================================
+
+------------------
+-- On City Created
+Events.SerialEventCityCreated.Add( function( hexPos, cityOwnerID, cityID, cultureType, eraType, continent, populationSize, size, fowState )
+	-- fowState 0 is invisible
+	if fowState ~= 0 then
+		return RefreshCityBannerAtPlot( GetPlot( ToGridFromHex( hexPos.x, hexPos.y ) ) )
+	end
+end)
+
+------------------
+-- On City Updated
+Events.SerialEventCityInfoDirty.Add( function()
+	-- Don't know which city, so update all visible city banners
+	for plotIndex in pairs( g_cityBanners ) do
+		RefreshCityBannerAtPlot( GetPlotByIndex( plotIndex ) )
+	end
+end)
+
+--------------------
+-- On City Destroyed
+Events.SerialEventCityDestroyed.Add(
+function( hexPos ) --, cityOwnerID, cityID, newPlayerID )
+	local plot = GetPlot( ToGridFromHex( hexPos.x, hexPos.y ) )
+	if plot then
+		local plotIndex = plot:GetPlotIndex()
+		return DestroyCityBanner( plotIndex, g_cityBanners[ plotIndex ] )
+	end
+end)
+
+---------------------
+-- On City Set Damage
+Events.SerialEventCitySetDamage.Add( function( cityOwnerID, cityID, cityDamage, previousDamage )
+	local cityOwner = Players[ cityOwnerID ]
+	if cityOwner then
+		local city = cityOwner:GetCityByID( cityID )
+		if city then
+			return RefreshCityDamage( city, g_cityBanners[ city:Plot():GetPlotIndex() ], cityDamage )
+		end
+	end
+end)
+
+---------------------------
+-- On Specific City changed
+Events.SpecificCityInfoDirty.Add( function( cityOwnerID, cityID, updateType )
+	local cityOwner = Players[ cityOwnerID ]
+	if cityOwner then
+		local city = cityOwner:GetCityByID( cityID )
+		if city then
+			local plotIndex = city:Plot():GetPlotIndex()
+			local instance = g_cityBanners[ plotIndex ]
+			if instance then
+				if updateType == CityUpdateTypes.CITY_UPDATE_TYPE_ENEMY_IN_RANGE then
+					return UpdateRangeIcons( plotIndex, city, instance )
+				elseif updateType == CityUpdateTypes.CITY_UPDATE_TYPE_BANNER or updateType == CityUpdateTypes.CITY_UPDATE_TYPE_GARRISON then
+					return RefreshCityBanner( city )
+				end
+			end
+		end
+	end
+end)
+
+-------------------------
+-- On Improvement Created
+Events.SerialEventImprovementCreated.Add( function( hexX, hexY, cultureID, continentID, playerID )--, improvementID, rawResourceID, improvementEra, improvementState )
+	if playerID == g_activePlayerID then
+		local plot = GetPlot( ToGridFromHex( hexX, hexY ) )
+		if plot then
+			return RefreshCityBanner( plot:GetWorkingCity() )
+		end
+	end
+end)
+
+---------------------------
+-- On Road/Railroad Created
+Events.SerialEventRoadCreated.Add( function( hexX, hexY, playerID, roadID )
+	if playerID == g_activePlayerID then
+		for city in g_activePlayer:Cities() do
+			RefreshCityBanner( city )
+		end
+	end
+end)
+
+--[[
+-----------------------
+-- On city range strike
+Events.InitCityRangeStrike.Add( function( cityOwnerID, cityID )
+	if cityOwnerID == g_activePlayerID then
+		local city = g_activePlayer:GetCityByID( cityID )
+		if city and city == UI.GetHeadSelectedCity() then
+			UI.SetInterfaceMode( InterfaceModeTypes.INTERFACEMODE_CITY_RANGE_ATTACK )
+		end
+	end
+end)
+--]]
+
+-------------------
+-- On Unit Garrison
+Events.UnitGarrison.Add( function( unitOwnerID, unitID, isGarrisoned )
+	if isGarrisoned then
+		local unitOwner = Players[ unitOwnerID ]
+		if unitOwner then
+			local unit = unitOwner:GetUnitByID( unitID )
+			if unit then
+				local city = unit:GetGarrisonedCity()
+				if city then
+					return HideGarrisonFrame( g_cityBanners[ city:Plot():GetPlotIndex() ], UnitMoving( unitOwnerID, unitID ) )
+				end
+			end
+		end
+	end
+end)
+
+-----------------------------
+-- On Unit Move Queue Changed
+Events.UnitMoveQueueChanged.Add( function( unitOwnerID, unitID, hasRemainingMoves )
+	local unitOwner = Players[ unitOwnerID ]
+	if unitOwner then
+		local unit = unitOwner:GetUnitByID( unitID )
+		if unit then
+			local city = unit:GetGarrisonedCity()
+			if city then
+				return HideGarrisonFrame( g_cityBanners[ city:Plot():GetPlotIndex() ], not hasRemainingMoves )
+			end
+		end
+	end
+end)
+
+--[[
+---------------------------
+-- On interface mode change
+Events.InterfaceModeChanged.Add( function( oldInterfaceMode, newInterfaceMode )
+	local disableBanners = newInterfaceMode ~= InterfaceModeTypes.INTERFACEMODE_SELECTION
+	for _, instance in pairs( g_cityBanners ) do
+		instance.CityBannerButton:SetDisabled( disableBanners )
+		instance.CityBannerButton:EnableToolTip( not disableBanners )
+	end
+end)
+--]]
+
+---------------------------
+-- On strategic view change
+Events.StrategicViewStateChanged.Add( function(isStrategicView, showCityBanners)
+	local showBanners = showCityBanners or not isStrategicView
+	Controls.CityBanners:SetHide( not showBanners )
+	return Controls.StrategicViewStrikeButtons:SetHide( showBanners )
+end)
+
+-----------------------
+-- On fog of war change
+Events.HexFOWStateChanged.Add( function( hexPos, fowType, isWholeMap )
+	if isWholeMap then
+		 -- fowState 0 is invisible
+		if fowType == 0 then
+			for plotIndex, instance in pairs( g_cityBanners ) do
+				DestroyCityBanner( plotIndex, instance )
+			end
+		else
+			for playerID = 0, #Players do
+				local player = Players[ playerID ]
+				if player and player:IsAlive() then
+					for city in player:Cities() do
+						RefreshCityBanner( city )
+					end
+				end
+			end
+		end
+	else
+		local plot = GetPlot( ToGridFromHex( hexPos.x, hexPos.y ) )
+		if plot then
+			-- fowType 0 is invisible
+			if fowType == 0 then
+				local plotIndex = plot:GetPlotIndex()
+				return DestroyCityBanner( plotIndex, g_cityBanners[ plotIndex ] )
+			else
+				return RefreshCityBannerAtPlot( plot )
+			end
+		end
+	end
+end)
+
+---------------------------
+-- On War Declared
+Events.WarStateChanged.Add(
+function( teamID1, teamID2, isAtWar )
+	if teamID1 == g_activeTeamID then
+		teamID1 = teamID2
+	elseif teamID2 ~= g_activeTeamID then
+		return
+	end
+	for playerID = 0, #Players do
+		local player = Players[playerID]
+		if player and player:IsAlive() and player:GetTeam() == teamID1 then
+			for city in player:Cities() do
+				if city:Plot():IsRevealed( g_activeTeamID, true ) then
+					RefreshCityBanner( city )
+				end
+			end
+		end
+	end
+end)
+
+--==========================================================
+-- 'Active' (local human) player has changed:
+-- Check for City Banner Active Type change
+--==========================================================
+Events.GameplaySetActivePlayer.Add( function( activePlayerID, previousActivePlayerID )
+	-- update globals
+
+	g_activePlayerID = Game.GetActivePlayer()
+	g_activePlayer = Players[ g_activePlayerID ]
+	g_activeTeamID = Game.GetActiveTeam()
+	g_activeTeam = Teams[ g_activeTeamID ]
+	ClearHexHighlights()
+	local isDebug = Game.IsDebugMode() or g_activePlayer:IsObserver()
+	-- Update all city banners
+	for playerID = 0, #Players do
+		local player = Players[ playerID ]
+		if player and player:IsAlive() then
+			for city in player:Cities() do
+				local plot = city:Plot()
+				local plotIndex = plot:GetPlotIndex()
+				local instance = g_cityBanners[ plotIndex ]
+
+				if plot:IsRevealed( g_activeTeamID, isDebug ) then
+					RefreshCityBanner( city )
+				-- If city banner is hidden, destroy the banner
+				elseif instance then
+					DestroyCityBanner( plotIndex, instance )
+				end
+			end
+		end
+	end
+end)
+
+--==========================================================
+-- Hide Garrisson Ring during Animated Combat
+--==========================================================
+if gk_mode then
+
+	local function HideGarrisonRing( x, y, hideGarrisonRing )
+
+		local plot = GetPlot( x, y )
+		local city = plot and plot:GetPlotCity()
+		local instance = city and g_cityBanners[ plot:GetPlotIndex() ]
+		return instance and HideGarrisonFrame( instance, hideGarrisonRing or not city:GetGarrisonedUnit() )
+	end
+
+	Events.RunCombatSim.Add( function(
+				attackerPlayerID,
+				attackerUnitID,
+				attackerUnitDamage,
+				attackerFinalUnitDamage,
+				attackerMaxHitPoints,
+				defenderPlayerID,
+				defenderUnitID,
+				defenderUnitDamage,
+				defenderFinalUnitDamage,
+				defenderMaxHitPoints,
+				attackerX,
+				attackerY,
+				defenderX,
+				defenderY,
+				bContinuation)
+--print( "CityBanner CombatBegin", attackerX, attackerY, defenderX, defenderY )
+
+		HideGarrisonRing(attackerX, attackerY, true)
+		HideGarrisonRing(defenderX, defenderY, true)
+	end)
+
+	Events.EndCombatSim.Add( function(
+				attackerPlayerID,
+				attackerUnitID,
+				attackerUnitDamage,
+				attackerFinalUnitDamage,
+				attackerMaxHitPoints,
+				defenderPlayerID,
+				defenderUnitID,
+				defenderUnitDamage,
+				defenderFinalUnitDamage,
+				defenderMaxHitPoints,
+				attackerX,
+				attackerY,
+				defenderX,
+				defenderY )
+
+--print( "CityBanner CombatEnd", attackerX, attackerY, defenderX, defenderY )
+
+		HideGarrisonRing(attackerX, attackerY, false)
+		HideGarrisonRing(defenderX, defenderY, false)
+	end)
+
+end -- gk_mode
+
+--==========================================================
+-- The active player's turn has begun, make sure their range strike icons are correct
+--==========================================================
+Events.ActivePlayerTurnStart.Add( function()
+	for plotIndex, instance in pairs( g_cityBanners ) do
+		UpdateRangeIcons( plotIndex, GetPlotByIndex( plotIndex ):GetPlotCity(), instance )
+	end
+end)
+
+Events.SerialEventUnitDestroyed.Add( function( unitOwnerID, unitID )
+	local unitOwner = Players[ unitOwnerID ]
+	if unitOwner and g_activeTeam:IsAtWar( unitOwner:GetTeam() ) then
+		for city in g_activePlayer:Cities() do
+			local plotIndex = city:Plot():GetPlotIndex()
+			UpdateRangeIcons( plotIndex, city, g_cityBanners[ plotIndex ] )
+		end
+	end
+end)
+
+--==========================================================
+-- Initialize all Visible City Banners
+--==========================================================
+for playerID = 0, #Players do
+	local player = Players[ playerID ]
+	if player and player:IsEverAlive() then
+		for city in player:Cities() do
+			if city:Plot():IsRevealed( g_activeTeamID, true ) then
+				RefreshCityBanner( city )
+			end
+		end
+	end
+end
+
+end)
diff --git a/CityView.lua.cuc b/CityView.lua.cuc
new file mode 100644
index 0000000000000000000000000000000000000000..9411c02ae8d3c98a32a3c44d4891f18fcde2a453
--- /dev/null
+++ b/CityView.lua.cuc
@@ -0,0 +1,2246 @@
+--==========================================================
+-- City View
+-- Re-written by bc1 using Notepad++
+-- code is common using switches
+-- compatible with Gazebo's City-State Diplomacy Mod (CSD) for Brave New World v21
+-- compatible with JFD's Piety & Prestige for Brave New World
+-- compatible with GameInfo.Yields() iterator broken by Communitas
+--todo: upper left corner
+--todo: selection list with all buildable items
+--todo: mod case where several buildings are allowed
+--==========================================================
+
+Events.SequenceGameInitComplete.Add(function()
+
+local IsCiv5 = InStrategicView ~= nil
+local IsCivBE = not IsCiv5
+local IsCiv5vanilla = IsCiv5 and not Game.GetReligionName
+local IsCiv5BNW = IsCiv5 and Game.GetActiveLeague ~= nil
+
+include "UserInterfaceSettings"
+local UserInterfaceSettings = UserInterfaceSettings
+
+include "GameInfoCache" -- warning! booleans are true, not 1, and use iterator ONLY with table field conditions, NOT string SQL query
+local GameInfo = GameInfoCache
+
+include "IconHookup"
+local IconHookup = IconHookup
+local CivIconHookup = CivIconHookup
+local ColorCulture = Color( 1, 0, 1, 1 )
+
+include "StackInstanceManager"
+local StackInstanceManager = StackInstanceManager
+
+include "ShowProgress"
+local ShowProgress = ShowProgress
+
+include "SupportFunctions"
+local TruncateString = TruncateString
+
+if IsCiv5BNW then
+	include "GreatPeopleIcons"
+end
+local GreatPeopleIcons = GreatPeopleIcons
+
+if IsCivBE then
+	include "IntrigueHelper"
+end
+
+--==========================================================
+-- Minor lua optimizations
+--==========================================================
+
+local ipairs = ipairs
+local abs = math.abs
+local ceil = math.ceil
+local floor = math.floor
+local max = math.max
+local min = math.min
+local sqrt = math.sqrt
+local pairs = pairs
+local tonumber = tonumber
+local tostring = tostring
+local unpack = unpack
+local concat = table.concat
+local insert = table.insert
+local sort = table.sort
+
+local ButtonPopupTypes = ButtonPopupTypes
+local CityAIFocusTypes = CityAIFocusTypes
+local CityUpdateTypes = CityUpdateTypes
+local ContextPtr = ContextPtr
+local Controls = Controls
+local Events = Events
+local EventsClearHexHighlightStyle = Events.ClearHexHighlightStyle.Call
+local EventsRequestYieldDisplay = Events.RequestYieldDisplay.Call
+local EventsSerialEventHexHighlight = Events.SerialEventHexHighlight.Call
+local Game = Game
+local GameDefines = GameDefines
+local GameInfoTypes = GameInfoTypes
+local GameMessageTypes = GameMessageTypes
+local GameOptionTypes = GameOptionTypes
+local HexToWorld = HexToWorld
+local InStrategicView = InStrategicView or function() return false end
+local KeyEvents = KeyEvents
+local Keys = Keys
+local L = Locale.ConvertTextKey
+local Locale = Locale
+local eLClick = Mouse.eLClick
+local eRClick = Mouse.eRClick
+local eMouseEnter = Mouse.eMouseEnter
+local Network = Network
+local NotificationTypes = NotificationTypes
+local OptionsManager = OptionsManager
+local Players = Players
+local PopupPriority = PopupPriority
+local TaskTypes = TaskTypes
+local ToHexFromGrid = ToHexFromGrid
+local UI = UI
+local GetHeadSelectedCity = UI.GetHeadSelectedCity
+local GetUnitPortraitIcon = UI.GetUnitPortraitIcon
+local YieldDisplayTypesAREA = YieldDisplayTypes.AREA
+local YieldTypes = YieldTypes
+
+local ORDER_TRAIN = OrderTypes.ORDER_TRAIN
+local ORDER_CONSTRUCT = OrderTypes.ORDER_CONSTRUCT
+local ORDER_CREATE = OrderTypes.ORDER_CREATE
+local ORDER_MAINTAIN = OrderTypes.ORDER_MAINTAIN
+
+--==========================================================
+-- Globals
+--==========================================================
+
+local g_currencyIcon = IsCiv5 and "[ICON_GOLD]" or "[ICON_ENERGY]"
+local g_maintenanceCurrency = IsCiv5 and "GoldMaintenance" or "EnergyMaintenance"
+local g_yieldCurrency = IsCiv5 and YieldTypes.YIELD_GOLD or YieldTypes.YIELD_ENERGY
+
+local g_isAdvisor = true
+
+local g_activePlayerID = Game.GetActivePlayer()
+local g_activePlayer = Players[ g_activePlayerID ]
+local g_finishedItems = {}
+
+local g_workerHeadingOpen = OptionsManager.IsNoCitizenWarning()
+
+local g_worldPositionOffset = { x = 0, y = 0, z = 30 }
+local g_worldPositionOffset2 = { x = 0, y = 35, z = 0 }
+local g_portraitSize = Controls.PQportrait:GetSizeX()
+local g_screenHeight = select(2, UIManager.GetScreenSizeVal() )
+local g_leftStackHeigth = g_screenHeight - 40 - Controls.CityInfoBG:GetOffsetY() - Controls.CityInfoBG:GetSizeY()
+
+local g_PlotButtonIM	= StackInstanceManager( "PlotButtonInstance", "PlotButtonAnchor", Controls.PlotButtonContainer )
+local g_BuyPlotButtonIM	= StackInstanceManager( "BuyPlotButtonInstance", "BuyPlotButtonAnchor", Controls.PlotButtonContainer )
+local g_GreatWorksIM	= StackInstanceManager( "Work", "Button", false )
+local g_SpecialistsIM	= StackInstanceManager( "Slot", "Button", false )
+
+local g_ProdQueueIM, g_SpecialBuildingsIM, g_GreatWorkIM, g_WondersIM, g_BuildingsIM, g_GreatPeopleIM, g_SlackerIM, g_UnitSelectIM, g_BuildingSelectIM, g_WonderSelectIM, g_ProcessSelectIM, g_FocusSelectIM
+local g_queuedItemNumber, g_isDebugMode, g_BuyPlotMode, g_previousCity, g_isButtonPopupChooseProduction, g_isScreenAutoClose, g_isResetCityPlotPurchase
+
+local g_citySpecialists = {}
+
+local g_isViewingMode = true
+
+local g_slotTexture = {
+	SPECIALIST_CITIZEN = "CitizenUnemployed.dds",
+	SPECIALIST_SCIENTIST = "CitizenScientist.dds",
+	SPECIALIST_MERCHANT = "CitizenMerchant.dds",
+	SPECIALIST_ARTIST = "CitizenArtist.dds",
+	SPECIALIST_MUSICIAN = "CitizenArtist.dds",
+	SPECIALIST_WRITER = "CitizenArtist.dds",
+	SPECIALIST_ENGINEER = "CitizenEngineer.dds",
+	SPECIALIST_CIVIL_SERVANT = "CitizenCivilServant.dds",	-- Compatibility with Gazebo's City-State Diplomacy Mod (CSD) for Brave New World
+	SPECIALIST_JFD_MONK = "CitizenMonk.dds", -- Compatibility with JFD's Piety & Prestige for Brave New World
+	SPECIALIST_PMMM_ENTERTAINER = "PMMMEntertainmentSpecialist.dds", --Compatibility with Vicevirtuoso's Madoka Magica: Wish for the World for Brave New World
+}
+for specialist in GameInfo.Specialists() do
+	if specialist.SlotTexture then
+		g_slotTexture[ specialist.Type ] = specialist.SlotTexture
+	end
+end
+
+local g_slackerTexture = IsCivBE and "UnemployedIndicator.dds" or g_slotTexture[ (GameInfo.Specialists[GameDefines.DEFAULT_SPECIALIST] or {}).Type ]
+
+local g_gameInfo = {
+[ORDER_TRAIN] = GameInfo.Units,
+[ORDER_CONSTRUCT] = GameInfo.Buildings,
+[ORDER_CREATE] = GameInfo.Projects,
+[ORDER_MAINTAIN] = GameInfo.Processes,
+}
+local g_avisorRecommended = {
+[ORDER_TRAIN] = Game.IsUnitRecommended,
+[ORDER_CONSTRUCT] = Game.IsBuildingRecommended,
+[ORDER_CREATE] = Game.IsProjectRecommended,
+}
+local g_advisors = {
+[AdvisorTypes.ADVISOR_ECONOMIC] = "EconomicRecommendation",
+[AdvisorTypes.ADVISOR_MILITARY] = "MilitaryRecommendation",
+[AdvisorTypes.ADVISOR_SCIENCE] = "ScienceRecommendation",
+[AdvisorTypes.ADVISOR_FOREIGN] = "ForeignRecommendation",
+}
+
+local function GetSelectedCity()
+	return ( not Game.IsNetworkMultiPlayer() or g_activePlayer:IsTurnActive() ) and GetHeadSelectedCity()
+end
+
+local function GetSelectedModifiableCity()
+	return not g_isViewingMode and GetSelectedCity()
+end
+
+----------------
+-- Citizen Focus
+local g_cityFocusButtons = {
+		a = Controls.AvoidGrowthButton,
+		b = Controls.ResetButton,
+		c = Controls.BoxOSlackers,
+		d = Controls.EditButton,
+}
+do
+	local g_cityFocusControls = {
+		[ Controls.BalancedFocusButton or -1 ] =  CityAIFocusTypes.NO_CITY_AI_FOCUS_TYPE or false,
+		[ Controls.FoodFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FOOD or false,
+		[ Controls.ProductionFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_PRODUCTION or false,
+		[ Controls.GoldFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GOLD or CityAIFocusTypes.CITY_AI_FOCUS_TYPE_ENERGY or false,
+		[ Controls.ResearchFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_SCIENCE or false,
+		[ Controls.CultureFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_CULTURE or false,
+		[ Controls.GPFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GREAT_PEOPLE or false,
+		[ Controls.FaithFocusButton or -1 ] =  CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FAITH or false,
+	} g_cityFocusControls[-1] = nil
+	local function FocusButtonBehavior( focus )
+		local city = GetSelectedModifiableCity()
+		if city then
+			Network.SendSetCityAIFocus( city:GetID(), focus )
+			return Network.SendUpdateCityCitizens( city:GetID() )
+		end
+	end
+	for control, focus in pairs( g_cityFocusControls ) do
+		if focus then
+			g_cityFocusButtons[ focus ] = control
+			control:SetVoid1( focus )
+			control:RegisterCallback( eLClick, FocusButtonBehavior )
+		else
+			control:SetHide( true )
+		end
+	end
+end
+
+local function HexRadius( a )
+	return ( sqrt( 1 + 4*a/3 ) - 1 ) / 2
+end
+
+local function SetupCallbacks( controls, toolTips, tootTipType, callBacks )
+	local control
+	-- Setup Tootips
+	for name, callback in pairs( toolTips ) do
+		control = controls[name]
+		if control then
+			control:SetToolTipCallback( callback )
+			control:SetToolTipType( tootTipType )
+		end
+	end
+	-- Setup Callbacks
+	for name, eventCallbacks in pairs( callBacks ) do
+		control = controls[name]
+		if control then
+			for event, callback in pairs( eventCallbacks ) do
+				control:RegisterCallback( event, callback )
+			end
+		end
+	end
+end
+
+local function ResizeProdQueue()
+	local selectionPanelHeight = 0
+	local queuePanelHeight = min( 190, Controls.QueueStack:IsHidden() and 0 or Controls.QueueStack:GetSizeY() )	-- 190 = 5 x 38=instance height
+	if not Controls.SelectionScrollPanel:IsHidden() then
+		Controls.SelectionStacks:CalculateSize()
+		selectionPanelHeight = max( min( g_leftStackHeigth - queuePanelHeight, Controls.SelectionStacks:GetSizeY() ), 64 )
+--		Controls.SelectionBackground:SetSizeY( selectionPanelHeight + 85 )
+		Controls.SelectionScrollPanel:SetSizeY( selectionPanelHeight )
+		Controls.SelectionScrollPanel:CalculateInternalSize()
+		Controls.SelectionScrollPanel:ReprocessAnchoring()
+	end
+	Controls.QueueSlider:SetSizeY( queuePanelHeight + 38 )				-- 38 = Controls.PQbox:GetSizeY()
+	Controls.QueueScrollPanel:SetSizeY( queuePanelHeight )
+	Controls.QueueScrollPanel:CalculateInternalSize()
+	Controls.QueueBackground:SetSizeY( queuePanelHeight + selectionPanelHeight + 152 )	-- 125 = 38=Controls.PQbox:GetSizeY() + 87 + 27
+	return Controls.QueueBackground:ReprocessAnchoring()
+end
+
+local function ResizeRightStack()
+	Controls.BoxOSlackers:SetHide( Controls.SlackerStack:IsHidden() )
+	Controls.BoxOSlackers:SetSizeY( Controls.SlackerStack:GetSizeY() )
+	Controls.WorkerManagementBox:CalculateSize()
+	Controls.WorkerManagementBox:ReprocessAnchoring()
+	Controls.RightStack:CalculateSize()
+	local rightStackHeight = Controls.RightStack:GetSizeY() + 85
+	Controls.BuildingListBackground:SetSizeY( max( min( g_screenHeight + 48, rightStackHeight ), 160 ) )
+	Controls.RightScrollPanel:SetSizeY( min( g_screenHeight - 38, rightStackHeight ) )
+	Controls.RightScrollPanel:CalculateInternalSize()
+	return Controls.RightScrollPanel:ReprocessAnchoring()
+end
+
+local cityIsCanPurchase
+if IsCiv5vanilla then
+	function cityIsCanPurchase( city, bTestPurchaseCost, bTestTrainable, unitID, buildingID, projectID, yieldID )
+		if yieldID == g_yieldCurrency then
+			return city:IsCanPurchase( not bTestPurchaseCost, unitID, buildingID, projectID )
+							-- bOnlyTestVisible
+		else
+			return false
+		end
+	end
+else
+	function cityIsCanPurchase( city, ... )
+		return city:IsCanPurchase( ... )
+	end
+end
+
+--==========================================================
+-- Clear out the UI so that when a player changes
+-- the next update doesn't show the previous player's
+-- values for a frame
+--==========================================================
+local function ClearCityUIInfo()
+	g_ProdQueueIM:ResetInstances()
+	g_ProdQueueIM.Commit()
+	Controls.PQremove:SetHide( true )
+	Controls.PQrank:SetText()
+	Controls.PQname:SetText()
+	Controls.PQturns:SetText()
+	return Controls.ProductionPortraitButton:SetHide(true)
+end
+
+--==========================================================
+-- Selling Buildings
+--==========================================================
+local function SellBuilding( buildingID )
+
+	local city = GetSelectedModifiableCity()
+	local building = GameInfo.Buildings[ buildingID ]
+	-- Can this building be sold?
+	if building and city and city:IsBuildingSellable( buildingID ) then
+		Controls.YesButton:SetVoids( city:GetID(), buildingID )
+		Controls.SellBuildingTitle:SetText( building._Name:upper() )
+		Controls.SellBuildingText:LocalizeAndSetText( "TXT_KEY_SELL_BUILDING_INFO", city:GetSellBuildingRefund(buildingID), building[g_maintenanceCurrency] or 0 )
+		Controls.SellBuildingImage:SetHide( not IconHookup( building.PortraitIndex, 256, building.IconAtlas, Controls.SellBuildingImage ) )
+		Controls.SellBuildingStack:CalculateSize()
+		Controls.SellBuildingFrame:DoAutoSize()
+--todo energy
+		return Controls.SellBuildingConfirm:SetHide( false )
+	end
+end
+
+local function CancelBuildingSale()
+	Controls.SellBuildingConfirm:SetHide(true)
+	return Controls.YesButton:SetVoids( -1, -1 )
+end
+
+local function CleanupCityScreen()
+	-- clear any rogue leftover tooltip
+	g_isButtonPopupChooseProduction = false
+	Controls.RightScrollPanel:SetScrollValue(0)
+	return CancelBuildingSale()
+end
+
+local function GotoNextCity()
+	if not g_isViewingMode then
+		CleanupCityScreen()
+		return Game.DoControl( GameInfoTypes.CONTROL_NEXTCITY )
+	end
+end
+
+local function GotoPrevCity()
+	if not g_isViewingMode then
+		CleanupCityScreen()
+		return Game.DoControl( GameInfoTypes.CONTROL_PREVCITY )
+	end
+end
+
+local function ExitCityScreen()
+--print("request exit city screen")
+--	CleanupCityScreen()
+	return Events.SerialEventExitCityScreen()
+end
+
+--==========================================================
+-- Key Down Processing
+--==========================================================
+do
+	local VK_RETURN = Keys.VK_RETURN
+	local VK_ESCAPE = Keys.VK_ESCAPE
+	local VK_LEFT  = Keys.VK_LEFT
+	local VK_RIGHT = Keys.VK_RIGHT
+	local KeyDown = KeyEvents.KeyDown
+	ContextPtr:SetInputHandler( function ( uiMsg, wParam )
+		if uiMsg == KeyDown then
+			if wParam == VK_ESCAPE or wParam == VK_RETURN then
+				if Controls.SellBuildingConfirm:IsHidden() then
+					ExitCityScreen()
+				else
+					CancelBuildingSale()
+				end
+				return true
+			elseif wParam == VK_LEFT then
+				GotoPrevCity()
+				return true
+			elseif wParam == VK_RIGHT then
+				GotoNextCity()
+				return true
+			end
+		end
+	end)
+end
+
+--==========================================================
+-- Pedia
+--==========================================================
+local SearchForPediaEntry = Events.SearchForPediaEntry.Call
+local function Pedia( row )
+	return SearchForPediaEntry( row and row._Name )
+end
+	
+local function UnitClassPedia( unitClassID )
+	return Pedia( GameInfo.UnitClasses[ unitClassID ] )
+end
+
+local function BuildingPedia( buildingID )
+	return Pedia( GameInfo.Buildings[ buildingID ] )
+end
+
+local function SpecialistPedia( buildingID )
+	return Pedia( GameInfo.Specialists[ GameInfoTypes[(GameInfo.Buildings[buildingID]or{}).SpecialistType] or GameDefines.DEFAULT_SPECIALIST ] )
+end
+
+local function SelectionPedia( orderID, itemID )
+	return Pedia( (g_gameInfo[ orderID ]or{})[ itemID ] )
+end
+
+local function ProductionPedia( queuedItemNumber )
+	local city = GetHeadSelectedCity()
+	if city and queuedItemNumber then
+		return SelectionPedia( city:GetOrderFromQueue( queuedItemNumber ) )
+	end
+end
+
+--==========================================================
+-- Tooltips
+--==========================================================
+
+local GreatPeopleIcon = GreatPeopleIcons and function (k)
+	return GreatPeopleIcons[k] 
+end or function()
+	return "[ICON_GREAT_PEOPLE]"
+end
+
+local function GetSpecialistYields( city, specialist )
+	local yieldTips = {}
+	if city and specialist then
+		local specialistYield, specialistYieldModifier, yieldInfo
+		local specialistID = specialist.ID
+		local cityOwner = Players[ city:GetOwner() ]
+		-- Culture
+		local cultureFromSpecialist = city:GetCultureFromSpecialist( specialistID )
+		local specialistCultureModifier = city:GetCultureRateModifier() + ( cityOwner and ( cityOwner:GetCultureCityModifier() + ( city:GetNumWorldWonders() > 0 and cityOwner:GetCultureWonderMultiplier() or 0 ) or 0 ) )
+		-- Yield
+		for yieldID = 0, YieldTypes.NUM_YIELD_TYPES-1 do
+			yieldInfo = GameInfo.Yields[yieldID]
+			if yieldInfo then
+				specialistYield = city:GetSpecialistYield( specialistID, yieldID )
+				specialistYieldModifier = city:GetBaseYieldRateModifier( yieldID )
+				if yieldID == YieldTypes.YIELD_CULTURE then
+					specialistYield = specialistYield + cultureFromSpecialist
+					specialistYieldModifier = specialistYieldModifier + specialistCultureModifier
+					cultureFromSpecialist = 0
+				end
+				if specialistYield ~= 0 then
+					insert( yieldTips, specialistYield * specialistYieldModifier / 100 .. yieldInfo.IconString )
+				end
+			end
+		end
+		if cultureFromSpecialist ~= 0 then
+			insert( yieldTips, cultureFromSpecialist .. "[ICON_CULTURE]" )
+		end
+		if IsCiv5 and (specialist.GreatPeopleRateChange or 0) ~= 0 then
+			insert( yieldTips, specialist.GreatPeopleRateChange .. GreatPeopleIcon( specialist.Type ) )
+		end
+	end
+	return concat( yieldTips, " " )
+end
+
+local ShowTextToolTipAndPicture = LuaEvents.ShowTextToolTipAndPicture.Call
+local BuildingToolTip = LuaEvents.CityViewBuildingToolTip.Call
+local CityOrderItemTooltip = LuaEvents.CityOrderItemTooltip.Call
+
+local function SpecialistTooltip( control )
+	local buildingID = control:GetVoid1()
+	local building = GameInfo.Buildings[ buildingID ]
+	local specialistType = building and building.SpecialistType
+	local specialistID = specialistType and GameInfoTypes[specialistType] or GameDefines.DEFAULT_SPECIALIST
+	local specialist = GameInfo.Specialists[ specialistID ]
+	local tip = specialist._Name .. " " .. GetSpecialistYields( GetHeadSelectedCity(), specialist )
+	local slotTable = building and g_citySpecialists[buildingID]
+	if slotTable and not slotTable[control:GetVoid2()] then
+		tip = L"TXT_KEY_CITYVIEW_EMPTY_SLOT".."[NEWLINE]("..tip..")"
+	end
+	return ShowTextToolTipAndPicture( tip, specialist.PortraitIndex, specialist.IconAtlas )
+end
+
+local function ProductionToolTip( control )
+	local city = GetHeadSelectedCity()
+	local queuedItemNumber = control:GetVoid1()
+	if city and not Controls.QueueSlider:IsTrackingLeftMouseButton() then
+		return CityOrderItemTooltip( city, false, false, city:GetOrderFromQueue( queuedItemNumber ) )
+	end
+end
+
+--==========================================================
+-- Specialist Managemeent
+--==========================================================
+
+local function OnSlackersSelected( buildingID, slotID )
+	local city = GetSelectedModifiableCity()
+	if city then
+		for _=1, slotID<=0 and city:GetSpecialistCount( GameDefines.DEFAULT_SPECIALIST ) or 1 do
+			Network.SendDoTask( city:GetID(), TaskTypes.TASK_REMOVE_SLACKER, 0, -1, false )
+		end
+	end
+end
+
+local function ToggleSpecialist( buildingID, slotID )
+	local city = buildingID and slotID and GetSelectedModifiableCity()
+	if city then
+
+		-- If Specialists are automated then you can't change things with them
+		if IsCiv5 and not city:IsNoAutoAssignSpecialists() then
+			Game.SelectedCitiesGameNetMessage(GameMessageTypes.GAMEMESSAGE_DO_TASK, TaskTypes.TASK_NO_AUTO_ASSIGN_SPECIALISTS, -1, -1, true)
+			Controls.NoAutoSpecialistCheckbox:SetCheck(true)
+			if IsCiv5BNW then
+				Controls.NoAutoSpecialistCheckbox2:SetCheck(true)
+			end
+		end
+
+		local specialistID = GameInfoTypes[(GameInfo.Buildings[ buildingID ] or {}).SpecialistType] or -1
+		local specialistTable = g_citySpecialists[buildingID]
+		if specialistTable[slotID] then
+			if city:GetNumSpecialistsInBuilding(buildingID) > 0 then
+				specialistTable[slotID] = false
+				specialistTable.n = specialistTable.n - 1
+				return Game.SelectedCitiesGameNetMessage( GameMessageTypes.GAMEMESSAGE_DO_TASK, TaskTypes.TASK_REMOVE_SPECIALIST, specialistID, buildingID )
+			end
+		elseif city:IsCanAddSpecialistToBuilding(buildingID) then
+			specialistTable[slotID] = true
+			specialistTable.n = specialistTable.n + 1
+			return Game.SelectedCitiesGameNetMessage( GameMessageTypes.GAMEMESSAGE_DO_TASK, TaskTypes.TASK_ADD_SPECIALIST, specialistID, buildingID )
+		end
+	end
+end
+
+--==========================================================
+-- Great Work Managemeent
+--==========================================================
+
+local function GreatWorkPopup( greatWorkID )
+	local greatWork = GameInfo.GreatWorks[ Game.GetGreatWorkType( greatWorkID or -1 ) or -1 ]
+
+	if greatWork and greatWork.GreatWorkClassType ~= "GREAT_WORK_ARTIFACT" then
+		return Events.SerialEventGameMessagePopup{
+			Type = ButtonPopupTypes.BUTTONPOPUP_GREAT_WORK_COMPLETED_ACTIVE_PLAYER,
+			Data1 = greatWorkID,
+			Priority = PopupPriority.Current
+			}
+	end
+end
+
+local function YourCulturePopup( greatWorkID )
+	return Events.SerialEventGameMessagePopup{
+		Type = ButtonPopupTypes.BUTTONPOPUP_CULTURE_OVERVIEW,
+		Data1 = 1,
+		Data2 = 1,
+		}
+end
+
+local function ThemingTooltip( buildingClassID, _, control )
+	control:SetToolTipString( GetHeadSelectedCity():GetThemingTooltip( buildingClassID ) )
+end
+
+local function GreatWorkTooltip( greatWorkID, greatWorkSlotID, slot )
+	if greatWorkID >= 0 then
+		return slot:SetToolTipString( Game.GetGreatWorkTooltip( greatWorkID, GetHeadSelectedCity():GetOwner() ) )
+	else
+		return slot:LocalizeAndSetToolTip( tostring(( GameInfo.GreatWorkSlots[ greatWorkSlotID ] or {}).EmptyToolTipText) )
+	end
+end
+
+--==========================================================
+-- City Buildings List
+--==========================================================
+
+local function sortBuildings(a,b)
+	if a and b then
+		if a[4] ~= b[4] then
+			return a[4] < b[4]
+		elseif a[3] ~= b[3] then
+			return a[3] > b[3]
+		end
+		return a[2] < b[2]
+	end
+end
+
+local function SetupBuildingList( city, buildings, buildingIM )
+	buildingIM:ResetInstances()
+	sort( buildings, sortBuildings )
+	local cityOwnerID = city:GetOwner()
+	local cityOwner = Players[ cityOwnerID ]
+	local isNotResistance = not city:IsResistance()
+	-- Get the active perk types for civ BE
+	local cityOwnerPerks = IsCivBE and cityOwner:GetAllActivePlayerPerkTypes()
+	local building, buildingID, buildingClassID, buildingName, greatWorkCount, greatWorkID, slotStack, slot, new, instance, buildingButton, sellButton, textButton
+
+	for i = 1, #buildings do
+
+		building, buildingName, greatWorkCount = unpack(buildings[i])
+		buildingID = building.ID
+		buildingClassID = GameInfoTypes[ building.BuildingClass ] or -1
+		instance, new = buildingIM:GetInstance()
+		slotStack = instance.SlotStack
+		buildingButton = instance.Button
+		sellButton = instance.SellButton
+		textButton = instance.TextButton
+		textButton:SetHide( true )
+
+		if new then
+			buildingButton:RegisterCallback( eRClick, BuildingPedia )
+			buildingButton:SetToolTipCallback( BuildingToolTip )
+			sellButton:RegisterCallback( eLClick, SellBuilding )
+			textButton:RegisterCallback( eLClick, YourCulturePopup )
+			textButton:RegisterCallback( eMouseEnter, ThemingTooltip )
+		end
+		buildingButton:SetVoid1( buildingID )
+
+		-- Can we sell this building?
+		if not g_isViewingMode and city:IsBuildingSellable( buildingID ) then
+			sellButton:SetText( city:GetSellBuildingRefund( buildingID ) .. g_currencyIcon )
+			sellButton:SetHide( false )
+			sellButton:SetVoid1( buildingID )
+		else
+			sellButton:SetHide( true )
+		end
+
+
+--!!!BE portrait size is bigger
+
+		instance.Portrait:SetHide( not IconHookup( building.PortraitIndex, 64, building.IconAtlas, instance.Portrait ) )
+
+		-------------------
+		-- Great Work Slots
+		if greatWorkCount > 0 then
+			local buildingGreatWorkSlotType = building.GreatWorkSlotType
+			if buildingGreatWorkSlotType then
+				local buildingGreatWorkSlot = GameInfo.GreatWorkSlots[ buildingGreatWorkSlotType ]
+				if city:IsThemingBonusPossible( buildingClassID ) then
+					textButton:SetText( " +" .. city:GetThemingBonus( buildingClassID ) )
+					textButton:SetVoid1( buildingClassID )
+					textButton:SetHide( false )
+				end
+
+				for i = 0, greatWorkCount - 1 do
+					slot, new = g_GreatWorksIM:GetInstance( slotStack )
+					slot = slot.Button
+					if new then
+						slot:RegisterCallback( eLClick, YourCulturePopup )
+						slot:RegisterCallback( eMouseEnter, GreatWorkTooltip )
+					end
+					greatWorkID = city:GetBuildingGreatWork( buildingClassID, i )
+					slot:SetVoids( greatWorkID, buildingGreatWorkSlot.ID )
+					if greatWorkID >= 0 then
+						slot:SetTexture( buildingGreatWorkSlot.FilledIcon )
+						slot:RegisterCallback( eRClick, GreatWorkPopup )
+					else
+						slot:SetTexture( buildingGreatWorkSlot.EmptyIcon )
+						slot:ClearCallback( eRClick )
+					end
+				end
+			end
+		end
+
+		-------------------
+		-- Specialist Slots
+		local numSpecialistsInBuilding = city:GetNumSpecialistsInBuilding( buildingID )
+		local specialistTable = g_citySpecialists[buildingID] or {}
+		if specialistTable.n ~= numSpecialistsInBuilding then
+			specialistTable = { n = numSpecialistsInBuilding }
+			for i = 1, numSpecialistsInBuilding do
+				specialistTable[i] = true
+			end
+			g_citySpecialists[buildingID] = specialistTable
+		end
+		local specialistType = building.SpecialistType
+		local specialist = GameInfo.Specialists[specialistType]
+		if specialist then
+			for slotID = 1, city:GetNumSpecialistsAllowedByBuilding( buildingID ) do
+				slot, new = g_SpecialistsIM:GetInstance( slotStack )
+				slot = slot.Button
+				if new then
+					slot:RegisterCallback( eRClick, SpecialistPedia )
+					slot:SetToolTipCallback( SpecialistTooltip )
+				end
+				if IsCiv5 then
+					slot:SetTexture( specialistTable[ slotID ] and g_slotTexture[ specialistType ] or "CitizenEmpty.dds" )
+				else
+					-- todo (does not look right)
+					IconHookup( specialist.PortraitIndex, 45, specialist.IconAtlas, slot )
+					slot:SetHide( not specialistTable[ slotID ] )
+				end
+				slot:SetVoids( buildingID, slotID )
+				if g_isViewingMode then
+					slot:ClearCallback( eLClick )
+				else
+					slot:RegisterCallback( eLClick, ToggleSpecialist )
+				end
+			end -- Specialist Slots
+		end
+
+		-- Building stats/bonuses
+		local maintenanceCost = tonumber(building[g_maintenanceCurrency]) or 0
+		local defenseChange = tonumber(building.Defense) or 0
+		local hitPointChange = tonumber(building.ExtraCityHitPoints) or 0
+		local buildingCultureRate = (IsCiv5vanilla and tonumber(building.Culture) or 0) + (specialist and city:GetCultureFromSpecialist( specialist.ID ) or 0) * numSpecialistsInBuilding
+		local buildingCultureModifier = tonumber(building.CultureRateModifier) or 0
+		local cityCultureRateModifier = cityOwner:GetCultureCityModifier() + city:GetCultureRateModifier() + (city:GetNumWorldWonders() > 0 and cityOwner and cityOwner:GetCultureWonderMultiplier() or 0)
+		local cityCultureRate
+		local population = city:GetPopulation()
+		local tips = {}
+		local thisBuildingAndYieldTypes = { BuildingType = building.Type }
+		if IsCiv5 then
+			cityCultureRate = city:GetBaseJONSCulturePerTurn()
+			-- Happiness
+			local happinessChange = (tonumber(building.Happiness) or 0) + (tonumber(building.UnmoddedHappiness) or 0)
+						+ cityOwner:GetExtraBuildingHappinessFromPolicies( buildingID )
+						+ (cityOwner:IsHalfSpecialistUnhappiness() and GameDefines.UNHAPPINESS_PER_POPULATION * numSpecialistsInBuilding * ((city:IsCapital() and cityOwner:GetCapitalUnhappinessMod() or 0)+100) * (cityOwner:GetUnhappinessMod() + 100) * (cityOwner:GetTraitPopUnhappinessMod() + 100) / 2e6 or 0) -- missing getHandicapInfo().getPopulationUnhappinessMod()
+			if happinessChange ~=0 then
+				insert( tips, happinessChange .. "[ICON_HAPPINESS_1]" )
+			end
+		else -- IsCivBE
+			cityCultureRate = city:GetBaseCulturePerTurn()
+			-- Health
+			local healthChange = (tonumber(building.Health) or 0) + (tonumber(building.UnmoddedHealth) or 0) + cityOwner:GetExtraBuildingHealthFromPolicies( buildingID )
+			local healthModifier = tonumber(building.HealthModifier) or 0
+			-- Effect of player perks
+			for _, perkID in ipairs(cityOwnerPerks) do
+				healthChange = healthChange + Game.GetPlayerPerkBuildingClassPercentHealthChange( perkID, buildingClassID )
+				healthModifier = healthModifier + Game.GetPlayerPerkBuildingClassPercentHealthChange( perkID, buildingClassID )
+				defenseChange = defenseChange + Game.GetPlayerPerkBuildingClassCityStrengthChange( perkID, buildingClassID )
+				hitPointChange = hitPointChange + Game.GetPlayerPerkBuildingClassCityHPChange( perkID, buildingClassID )
+				maintenanceCost = maintenanceCost + Game.GetPlayerPerkBuildingClassEnergyMaintenanceChange( perkID, buildingClassID )
+			end
+			if healthChange ~=0 then
+				insert( tips, healthChange .. "[ICON_HEALTH_1]" )
+			end
+--			if healthModifier~=0 then insert( tips, L( "TXT_KEY_STAT_POSITIVE_YIELD_MOD", "[ICON_HEALTH_1]", healthModifier ) ) end
+		end
+		local buildingYieldRate, buildingYieldPerPop, buildingYieldModifier, cityYieldRate, cityYieldRateModifier, isProducing, yieldInfo
+		for yieldID = 0, YieldTypes.NUM_YIELD_TYPES-1 do
+			yieldInfo = GameInfo.Yields[yieldID]
+			if yieldInfo then
+				isProducing = isNotResistance
+				thisBuildingAndYieldTypes.YieldType = yieldInfo.Type or -1
+				-- Yield changes from the building
+				buildingYieldRate = Game.GetBuildingYieldChange( buildingID, yieldID )
+							+ (not IsCiv5vanilla and cityOwner:GetPlayerBuildingClassYieldChange( buildingClassID, yieldID )
+								+ city:GetReligionBuildingClassYieldChange( buildingClassID, yieldID ) or 0)
+							+ (IsCiv5BNW and city:GetLeagueBuildingClassYieldChange( buildingClassID, yieldID ) or 0)
+				-- Yield modifiers from the building
+				buildingYieldModifier = Game.GetBuildingYieldModifier( buildingID, yieldID )
+							+ cityOwner:GetPolicyBuildingClassYieldModifier( buildingClassID, yieldID )
+				-- Effect of player perks
+				if IsCivBE then
+					for _, perkID in ipairs(cityOwnerPerks) do
+						buildingYieldRate = buildingYieldRate + Game.GetPlayerPerkBuildingClassFlatYieldChange( perkID, buildingClassID, yieldID )
+						buildingYieldModifier = buildingYieldModifier + Game.GetPlayerPerkBuildingClassPercentYieldChange( perkID, buildingClassID, yieldID )
+					end
+				end
+				-- Specialists yield
+				if specialist then
+					buildingYieldRate = buildingYieldRate + numSpecialistsInBuilding * city:GetSpecialistYield( specialist.ID, yieldID )
+				end
+				cityYieldRateModifier = city:GetBaseYieldRateModifier( yieldID )
+				cityYieldRate = city:GetYieldPerPopTimes100( yieldID ) * population / 100 + city:GetBaseYieldRate( yieldID )
+				-- Special culture case
+				if yieldID == YieldTypes.YIELD_CULTURE then
+					buildingYieldRate = buildingYieldRate + buildingCultureRate
+					buildingYieldModifier = buildingYieldModifier + buildingCultureModifier
+					cityYieldRateModifier = cityYieldRateModifier + cityCultureRateModifier
+					cityYieldRate = cityYieldRate + cityCultureRate
+					buildingCultureRate = 0
+					buildingCultureModifier = 0
+				elseif yieldID == YieldTypes.YIELD_FOOD then
+					local foodPerPop = GameDefines.FOOD_CONSUMPTION_PER_POPULATION
+					local foodConsumed = city:FoodConsumption()
+					buildingYieldRate = buildingYieldRate + (foodConsumed < foodPerPop * population and foodPerPop * numSpecialistsInBuilding / 2 or 0)
+					buildingYieldModifier = buildingYieldModifier + (tonumber(building.FoodKept) or 0)
+					cityYieldRate = city:FoodDifferenceTimes100() / 100 -- cityYieldRate - foodConsumed
+					cityYieldRateModifier = cityYieldRateModifier + city:GetMaxFoodKeptPercent()
+					isProducing = true
+				end
+				-- Population yield
+				buildingYieldPerPop = 0
+				for row in GameInfo.Building_YieldChangesPerPop( thisBuildingAndYieldTypes ) do
+					buildingYieldPerPop = buildingYieldPerPop + (row.Yield or 0)
+				end
+				buildingYieldRate = buildingYieldRate + buildingYieldPerPop * population / 100
+
+				buildingYieldRate = buildingYieldRate * cityYieldRateModifier + ( cityYieldRate - buildingYieldRate ) * buildingYieldModifier
+				if isProducing and buildingYieldRate ~= 0 then
+					insert( tips, buildingYieldRate / 100 .. yieldInfo.IconString )
+				end
+			end
+		end
+
+		-- Culture leftovers
+		buildingCultureRate = buildingCultureRate * (100+cityCultureRateModifier) + ( cityCultureRate - buildingCultureRate ) * buildingCultureModifier
+		if isNotResistance and buildingCultureRate ~=0 then
+			insert( tips, buildingCultureRate / 100 .. "[ICON_CULTURE]" )
+		end
+
+-- TODO TOURISM
+		if IsCiv5BNW then
+			local tourism = ( ( (building.FaithCost or 0) > 0
+					and building.UnlockedByBelief
+					and building.Cost == -1
+					and city and city:GetFaithBuildingTourism()
+					) or 0 )
+--			local enhancedYieldTechID = GameInfoTypes[ building.EnhancedYieldTech ]
+			tourism = tourism + (tonumber(building.TechEnhancedTourism) or 0)
+			if tourism ~= 0 then
+				insert( tips, tourism.."[ICON_TOURISM]" )
+			end
+		end
+
+		if IsCiv5 and building.IsReligious then
+			buildingName = L( "TXT_KEY_RELIGIOUS_BUILDING", buildingName, Players[city:GetOwner()]:GetStateReligionKey() )
+		end
+		if city:GetNumFreeBuilding( buildingID ) > 0 then
+			buildingName = buildingName .. " (" .. L"TXT_KEY_FREE" .. ")"
+		elseif maintenanceCost ~=0 then
+			insert( tips, -maintenanceCost .. g_currencyIcon )
+		end
+		instance.Name:SetText( buildingName )
+
+		if defenseChange ~=0 then
+			insert( tips, defenseChange / 100 .. "[ICON_STRENGTH]" )
+		end
+		if hitPointChange ~= 0 then
+			insert( tips, L( "TXT_KEY_PEDIA_DEFENSE_HITPOINTS", hitPointChange ) )
+		end
+
+		instance.Label:ChangeParent( instance.Stack )
+		instance.Label:SetText( concat( tips, " ") )
+		slotStack:CalculateSize()
+		if slotStack:GetSizeX() + instance.Label:GetSizeX() < 254 then
+			instance.Label:ChangeParent( slotStack )
+		end
+		instance.Stack:CalculateSize()
+--		slotStack:ReprocessAnchoring()
+--		instance.Stack:ReprocessAnchoring()
+		buildingButton:SetSizeY( max(64, instance.Stack:GetSizeY() + 16) )
+	end
+	return buildingIM.Commit()
+end
+
+--==========================================================
+-- Production Selection List Management
+--==========================================================
+
+local function SelectionPurchase( orderID, itemID, yieldID, soundKey )
+	local city = GetHeadSelectedCity()
+	if city then
+		local cityOwnerID = city:GetOwner()
+		if cityOwnerID == g_activePlayerID
+			and ( not city:IsPuppet() or ( IsCiv5BNW and g_activePlayer:MayNotAnnex() ) )
+							----------- Venice exception -----------
+		then
+			local cityID = city:GetID()
+			local isPurchase
+			if orderID == ORDER_TRAIN then
+				if cityIsCanPurchase( city, true, true, itemID, -1, -1, yieldID ) then
+					Game.CityPurchaseUnit( city, itemID, yieldID )
+					isPurchase = true
+				end
+			elseif orderID == ORDER_CONSTRUCT then
+				if cityIsCanPurchase( city, true, true, -1, itemID, -1, yieldID ) then
+					Game.CityPurchaseBuilding( city, itemID, yieldID )
+					Network.SendUpdateCityCitizens( cityID )
+					isPurchase = true
+				end
+			elseif orderID == ORDER_CREATE then
+				if cityIsCanPurchase( city, true, true, -1, -1, itemID, yieldID ) then
+					Game.CityPurchaseProject( city, itemID, yieldID )
+					isPurchase = true
+				end
+			end
+			if isPurchase then
+				Events.SpecificCityInfoDirty( cityOwnerID, cityID, CityUpdateTypes.CITY_UPDATE_TYPE_BANNER )
+				Events.SpecificCityInfoDirty( cityOwnerID, cityID, CityUpdateTypes.CITY_UPDATE_TYPE_PRODUCTION )
+				if soundKey then
+					Events.AudioPlay2DSound( soundKey )
+				end
+			end
+		end
+	end
+end
+
+local function AddSelectionItem( city, item,
+				selectionList,
+				orderID,
+				cityCanProduce,
+				unitID, buildingID, projectID,
+				cityGetProductionTurnsLeft,
+				cityGetGoldCost,
+				cityGetFaithCost )
+
+	local itemID = item.ID
+	local name = item._Name
+	local turnsLeft = not g_isViewingMode and cityCanProduce( city, itemID, 0, 1 )	-- 0 = /continue, 1 = testvisible, nil = /ignore cost
+	local canProduce = not g_isViewingMode and cityCanProduce( city, itemID )	-- nil = /continue, nil = /testvisible, nil = /ignore cost
+	local canBuyWithGold, goldCost, canBuyWithFaith, faithCost
+	if unitID then
+		if IsCivBE then
+			local bestUpgradeInfo = GameInfo.UnitUpgrades[ g_activePlayer:GetBestUnitUpgrade(unitID) ]
+			name = bestUpgradeInfo and bestUpgradeInfo._Name or name
+		end
+		if cityGetGoldCost then
+			canBuyWithGold = cityIsCanPurchase( city, true, true, unitID, buildingID, projectID, g_yieldCurrency )
+			goldCost = cityIsCanPurchase( city, false, false, unitID, buildingID, projectID, g_yieldCurrency )
+					and cityGetGoldCost( city, itemID )
+		end
+		if cityGetFaithCost then
+			canBuyWithFaith = cityIsCanPurchase( city, true, true, unitID, buildingID, projectID, YieldTypes.YIELD_FAITH )
+			faithCost = cityIsCanPurchase( city, false, false, unitID, buildingID, projectID, YieldTypes.YIELD_FAITH )
+					and cityGetFaithCost( city, itemID, true )
+		end
+	end
+	if turnsLeft or goldCost or faithCost then --or (g_isDebugMode and not city:IsHasBuilding(buildingID)) then
+		turnsLeft = turnsLeft and ( cityGetProductionTurnsLeft and cityGetProductionTurnsLeft( city, itemID ) or -1 )
+		return insert( selectionList, { item, orderID, name, turnsLeft, canProduce, goldCost, canBuyWithGold, faithCost, canBuyWithFaith } )
+	end
+end
+
+local function SortSelectionList(a,b)
+	return a[3]<b[3]
+end
+
+local g_SelectionListCallBacks = {
+	Button = {
+		[eLClick] = function( orderID, itemID )
+			local city = GetSelectedModifiableCity()
+			if city then
+				local cityOwnerID = city:GetOwner()
+				if cityOwnerID == g_activePlayerID and not city:IsPuppet() then
+					-- cityPushOrder( city, orderID, itemID, bAlt, bShift, bCtrl )
+					-- cityPushOrder( city, orderID, itemID, repeatBuild, replaceQueue, bottomOfQueue )
+					Game.CityPushOrder( city, orderID, itemID, UI.AltKeyDown(), UI.ShiftKeyDown(), not UI.CtrlKeyDown() )
+					Events.SpecificCityInfoDirty( cityOwnerID, city:GetID(), CityUpdateTypes.CITY_UPDATE_TYPE_BANNER )
+					Events.SpecificCityInfoDirty( cityOwnerID, city:GetID(), CityUpdateTypes.CITY_UPDATE_TYPE_PRODUCTION )
+					if g_isButtonPopupChooseProduction then
+						-- is there another city without production order ?
+						for cityX in g_activePlayer:Cities() do
+							if cityX ~= city and not cityX:IsPuppet() and cityX:GetOrderQueueLength() < 1 then
+								UI.SelectCity( cityX )
+								return UI.LookAtSelectionPlot()
+							end
+						end
+						-- all cities are producing...
+						return ExitCityScreen()
+					end
+				end
+			end
+		end,
+		[eRClick] = SelectionPedia,
+	},
+	GoldButton = {
+		[eLClick] = function( orderID, itemID )
+			return SelectionPurchase( orderID, itemID, g_yieldCurrency, "AS2D_INTERFACE_CITY_SCREEN_PURCHASE" )
+		end,
+	},
+	FaithButton = {
+		[eLClick] = function( orderID, itemID )
+			return SelectionPurchase( orderID, itemID, YieldTypes.YIELD_FAITH, "AS2D_INTERFACE_FAITH_PURCHASE" )
+		end,
+	},
+}
+local g_SelectionListTooltips = {
+	Button = function( control )
+		return CityOrderItemTooltip( GetHeadSelectedCity(), true, false, control:GetVoid1(), control:GetVoid2() )
+	end,
+	GoldButton = function( control )
+		return CityOrderItemTooltip( GetHeadSelectedCity(), control:IsDisabled(), g_yieldCurrency, control:GetVoid1(), control:GetVoid2() )
+	end,
+	FaithButton = function( control )
+		return CityOrderItemTooltip( GetHeadSelectedCity(), control:IsDisabled(), YieldTypes.YIELD_FAITH, control:GetVoid1(), control:GetVoid2() )
+	end,
+}
+
+local function SetupSelectionList( itemList, selectionIM, cityOwnerID, getUnitPortraitIcon )
+	sort( itemList, SortSelectionList )
+	selectionIM:ResetInstances()
+	local cash = g_activePlayer:GetGold()
+	local faith = not IsCiv5vanilla and g_activePlayer:GetFaith() or 0
+	for i = 1, #itemList do
+		local item, orderID, itemDescription, turnsLeft, canProduce, goldCost, canBuyWithGold, faithCost, canBuyWithFaith = unpack( itemList[i] )
+		local itemID = item.ID
+		local avisorRecommended = g_isAdvisor and g_avisorRecommended[ orderID ]
+
+		local instance, new = selectionIM:GetInstance()
+		if new then
+			SetupCallbacks( instance, g_SelectionListTooltips, "EUI_ItemTooltip", g_SelectionListCallBacks )
+		end
+		instance.DisabledProduction:SetHide( canProduce or not(canBuyWithGold or canBuyWithFaith) )
+		instance.Disabled:SetHide( canProduce or canBuyWithGold or canBuyWithFaith )
+		if getUnitPortraitIcon then
+			local iconIndex, iconAtlas = getUnitPortraitIcon( itemID, cityOwnerID )
+			IconHookup( iconIndex, 45, iconAtlas, instance.Portrait )
+		else
+			IconHookup( item.PortraitIndex, 45, item.IconAtlas, instance.Portrait )
+		end
+		instance.Name:SetText( itemDescription )
+		if not turnsLeft then
+		elseif turnsLeft > -1 and turnsLeft <= 999 then
+			instance.Turns:LocalizeAndSetText( "TXT_KEY_STR_TURNS", turnsLeft )
+		else
+			instance.Turns:LocalizeAndSetText( "TXT_KEY_PRODUCTION_HELP_INFINITE_TURNS" )
+		end
+		instance.Turns:SetHide( not turnsLeft )
+		instance.Button:SetVoids( orderID, itemID )
+
+		instance.GoldButton:SetHide( not goldCost )
+		if goldCost then
+			instance.GoldButton:SetDisabled( not canBuyWithGold )
+			instance.GoldButton:SetAlpha( canBuyWithGold and 1 or 0.5 )
+			instance.GoldButton:SetVoids( orderID, itemID )
+			instance.GoldButton:SetText( (cash>=goldCost and goldCost or "[COLOR_WARNING_TEXT]"..(goldCost-cash).."[ENDCOLOR]") .. g_currencyIcon )
+		end
+		instance.FaithButton:SetHide( not faithCost )
+		if faithCost then
+			instance.FaithButton:SetDisabled( not canBuyWithFaith )
+			instance.FaithButton:SetAlpha( canBuyWithFaith and 1 or 0.5 )
+			instance.FaithButton:SetVoids( orderID, itemID )
+			instance.FaithButton:SetText( (faith>=faithCost and faithCost or "[COLOR_WARNING_TEXT]"..(faithCost-faith).."[ENDCOLOR]") .. "[ICON_PEACE]" )
+		end
+
+		for advisorID, advisorName in pairs(g_advisors) do
+			local advisorControl = instance[ advisorName ]
+			if advisorControl then
+				advisorControl:SetHide( not (avisorRecommended and avisorRecommended( itemID, advisorID )) )
+			end
+		end
+	end
+	return selectionIM.Commit()
+end
+
+--==========================================================
+-- Production Queue Managemeent
+--==========================================================
+local function RemoveQueueItem( queuedItemNumber )
+	local city = GetSelectedModifiableCity()
+	if city then
+		local queueLength = city:GetOrderQueueLength()
+		if city:GetOwner() == g_activePlayerID and queueLength > queuedItemNumber then
+			Game.SelectedCitiesGameNetMessage( GameMessageTypes.GAMEMESSAGE_POP_ORDER, queuedItemNumber )
+			if queueLength < 2 then
+				local strTooltip = L( "TXT_KEY_NOTIFICATION_NEW_CONSTRUCTION", city:GetNameKey() )
+				g_activePlayer:AddNotification( NotificationTypes.NOTIFICATION_PRODUCTION, strTooltip, strTooltip, city:GetX(), city:GetY(), -1, -1 )
+			end
+		end
+	end
+end
+
+local function SwapQueueItem( queuedItemNumber )
+	if g_queuedItemNumber and Controls.QueueSlider:IsTrackingLeftMouseButton() then
+		local a = g_queuedItemNumber
+		local b = queuedItemNumber
+		if a>b then a, b = b, a end
+		for i=a, b-1 do
+			Game.SelectedCitiesGameNetMessage( GameMessageTypes.GAMEMESSAGE_SWAP_ORDER, i )
+		end
+		for i=b-2, a, -1 do
+			Game.SelectedCitiesGameNetMessage( GameMessageTypes.GAMEMESSAGE_SWAP_ORDER, i )
+		end
+	end
+	g_queuedItemNumber = queuedItemNumber or g_queuedItemNumber
+end
+
+Controls.AutomateProduction:RegisterCheckHandler( function( isChecked )
+	Game.SelectedCitiesGameNetMessage( GameMessageTypes.GAMEMESSAGE_DO_TASK, TaskTypes.TASK_SET_AUTOMATED_PRODUCTION, -1, -1, isChecked, false )
+--	Network.SendDoTask( city:GetID(), TaskTypes.TASK_SET_AUTOMATED_PRODUCTION, -1, -1, isChecked, false )
+end)
+
+--==========================================================
+-- Update Production Queue
+--==========================================================
+local function UpdateCityProductionQueue( city, cityID, cityOwnerID, isVeniceException )
+	local queueLength = city:GetOrderQueueLength()
+	local currentProductionPerTurnTimes100 = city:GetCurrentProductionDifferenceTimes100( false, false )	-- bIgnoreFood, bOverflow
+	local isGeneratingProduction = ( currentProductionPerTurnTimes100 > 0 ) and not ( city.IsProductionProcess and city:IsProductionProcess() )
+	local isMaintain = false
+	local isQueueEmpty = queueLength < 1
+	Controls.ProductionFinished:SetHide( true )
+
+	-- Progress info for meter
+	ShowProgress( g_portraitSize, Controls.PQlossMeter, Controls.PQprogressMeter, Controls.PQline1, Controls.PQlabel1, Controls.PQline2, Controls.PQlabel2,
+		isGeneratingProduction and not isQueueEmpty and city:GetProductionTurnsLeft(),
+		city:GetProductionNeeded() * 100,
+		city:GetProductionTimes100() + city:GetCurrentProductionDifferenceTimes100( false, true ) - currentProductionPerTurnTimes100,	-- bIgnoreFood, bOverflow
+		currentProductionPerTurnTimes100 )
+
+	Controls.ProdPerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_PERTURN_TEXT", currentProductionPerTurnTimes100 / 100 )
+
+	Controls.ProductionPortraitButton:SetHide( false )
+
+	g_ProdQueueIM:ResetInstances()
+	local queueItems = {}
+
+	for queuedItemNumber = 0, max( queueLength-1, 0 ) do
+
+		local orderID, itemID, _, isRepeat, isReallyRepeat
+		if isQueueEmpty then
+			local item = g_finishedItems[ cityID ]
+			if item then
+				orderID, itemID = unpack( item )
+				Controls.ProductionFinished:SetHide( false )
+			end
+		else
+			orderID, itemID, _, isRepeat = city:GetOrderFromQueue( queuedItemNumber )
+			queueItems[ orderID / 64 + itemID ] = true
+		end
+		local instance, portraitSize
+		if queuedItemNumber == 0 then
+			instance = Controls
+			portraitSize = g_portraitSize
+		else
+			portraitSize = 45
+			instance = g_ProdQueueIM:GetInstance()
+			instance.PQdisabled:SetHide( not isMaintain )
+		end
+		instance.PQbox:SetVoid1( queuedItemNumber )
+		instance.PQbox:RegisterCallback( eMouseEnter, SwapQueueItem )
+		instance.PQbox:RegisterCallback( eRClick, ProductionPedia )
+		instance.PQbox:SetToolTipCallback( ProductionToolTip )
+
+		instance.PQremove:SetHide( isQueueEmpty or g_isViewingMode )
+		instance.PQremove:SetVoid1( queuedItemNumber )
+		instance.PQremove:RegisterCallback( eLClick, RemoveQueueItem )
+
+		local itemInfo, turnsRemaining, portraitOffset, portraitAtlas
+
+		if orderID == ORDER_TRAIN then
+			itemInfo = GameInfo.Units
+			turnsRemaining = city:GetUnitProductionTurnsLeft( itemID, queuedItemNumber )
+			portraitOffset, portraitAtlas = GetUnitPortraitIcon( itemID, cityOwnerID )
+			isReallyRepeat = isRepeat
+		elseif orderID == ORDER_CONSTRUCT then
+			itemInfo = GameInfo.Buildings
+			turnsRemaining = city:GetBuildingProductionTurnsLeft( itemID, queuedItemNumber )
+		elseif orderID == ORDER_CREATE then
+			itemInfo = GameInfo.Projects
+			turnsRemaining = city:GetProjectProductionTurnsLeft( itemID, queuedItemNumber )
+		elseif orderID == ORDER_MAINTAIN then
+			itemInfo = GameInfo.Processes
+			isMaintain = true
+			isReallyRepeat = true
+		end
+		if itemInfo then
+			local item = itemInfo[itemID]
+			itemInfo = IconHookup( portraitOffset or item.PortraitIndex, portraitSize, portraitAtlas or item.IconAtlas, instance.PQportrait )
+			instance.PQname:SetText( item._Name )
+			if isMaintain or isQueueEmpty then
+			elseif isGeneratingProduction then
+				instance.PQturns:LocalizeAndSetText( "TXT_KEY_PRODUCTION_HELP_NUM_TURNS", turnsRemaining )
+			else
+				instance.PQturns:LocalizeAndSetText( "TXT_KEY_PRODUCTION_HELP_INFINITE_TURNS" )
+			end
+		else
+			instance.PQname:LocalizeAndSetText( "TXT_KEY_PRODUCTION_NO_PRODUCTION" )
+		end
+		instance.PQturns:SetHide( isMaintain or isQueueEmpty or not itemInfo )
+		instance.PQportrait:SetHide( not itemInfo )
+		if isReallyRepeat then
+			isMaintain = true
+			instance.PQrank:SetText( "[ICON_TURNS_REMAINING]" )
+		else
+			instance.PQrank:SetText( not isMaintain and queueLength > 1 and (queuedItemNumber+1).."." )
+		end
+	end
+
+	g_ProdQueueIM.Commit()
+
+	-------------------------------------------
+	-- Update Selection List
+	-------------------------------------------
+
+	local isSelectionList = not g_isViewingMode or isVeniceException or g_isDebugMode
+	Controls.SelectionScrollPanel:SetHide( not isSelectionList )
+	if isSelectionList then
+		local unitSelectList = {}
+		local buildingSelectList = {}
+		local wonderSelectList = {}
+		local processSelectList = {}
+
+		if g_isAdvisor then
+			Game.SetAdvisorRecommenderCity( city )
+		end
+		-- Buildings & Wonders
+		local orderID = ORDER_CONSTRUCT
+		local code = orderID / 64
+		for item in GameInfo.Buildings() do
+			local buildingClass = GameInfo.BuildingClasses[item.BuildingClass]
+			local isWonder = buildingClass and (buildingClass.MaxGlobalInstances > 0 or buildingClass.MaxPlayerInstances == 1 or buildingClass.MaxTeamInstances > 0)
+			if not queueItems[ code + item.ID ] then
+				AddSelectionItem( city, item,
+						isWonder and wonderSelectList or buildingSelectList,
+						orderID,
+						city.CanConstruct,
+						-1, item.ID, -1,
+						city.GetBuildingProductionTurnsLeft,
+						city.GetBuildingPurchaseCost,
+						city.GetBuildingFaithPurchaseCost )
+			end
+		end
+
+		-- Units
+		orderID = ORDER_TRAIN
+		for item in GameInfo.Units() do
+			AddSelectionItem( city, item,
+						unitSelectList,
+						orderID,
+						city.CanTrain,
+						item.ID, -1, -1,
+						city.GetUnitProductionTurnsLeft,
+						city.GetUnitPurchaseCost,
+						city.GetUnitFaithPurchaseCost )
+		end
+		-- Projects
+		orderID = ORDER_CREATE
+		code = orderID / 64
+		for item in GameInfo.Projects() do
+			if not queueItems[ code + item.ID ] then
+				AddSelectionItem( city, item,
+						wonderSelectList,
+						orderID,
+						city.CanCreate,
+						-1, -1, item.ID,
+						city.GetProjectProductionTurnsLeft,
+						city.GetProjectPurchaseCost,
+						city.GetProjectFaithPurchaseCost )	-- nil
+			end
+		end
+		-- Processes
+		orderID = ORDER_MAINTAIN
+		code = orderID / 64
+		for item in GameInfo.Processes() do
+			if not queueItems[ code + item.ID ] then
+				AddSelectionItem( city, item,
+						processSelectList,
+						orderID,
+						city.CanMaintain )
+			end
+		end
+
+		SetupSelectionList( unitSelectList, g_UnitSelectIM, cityOwnerID, GetUnitPortraitIcon )
+		SetupSelectionList( buildingSelectList, g_BuildingSelectIM )
+		SetupSelectionList( wonderSelectList, g_WonderSelectIM )
+		SetupSelectionList( processSelectList, g_ProcessSelectIM )
+
+	end
+	return ResizeProdQueue()
+end
+
+--==========================================================
+-- City Hex Clicking & Mousing
+--==========================================================
+
+local function PlotButtonClicked( plotIndex )
+	local city = GetSelectedModifiableCity()
+	local plot = city and city:GetCityIndexPlot( plotIndex )
+	if plot then
+		local outside = city ~= plot:GetWorkingCity()
+		-- calling this with the city center (0 in the third param) causes it to reset all forced tiles
+		Network.SendDoTask( city:GetID(), TaskTypes.TASK_CHANGE_WORKING_PLOT, plotIndex, -1, false )
+		if outside then
+			return Network.SendUpdateCityCitizens( city:GetID() )
+		end
+	end
+end
+
+local function BuyPlotAnchorButtonClicked( plotIndex )
+	local city = GetSelectedModifiableCity()
+	if city then
+		local plot = city:GetCityIndexPlot( plotIndex )
+		local plotX = plot:GetX()
+		local plotY = plot:GetY()
+		Network.SendCityBuyPlot(city:GetID(), plotX, plotY)
+		Network.SendUpdateCityCitizens( city:GetID() )
+		UI.UpdateCityScreen()
+		Events.AudioPlay2DSound("AS2D_INTERFACE_BUY_TILE")
+	end
+	return true
+end
+
+--==========================================================
+-- Update City Hexes
+--==========================================================
+
+local function UpdateCityWorkingHexes( city )
+
+	EventsClearHexHighlightStyle( "HexContour" )
+	EventsClearHexHighlightStyle( "WorkedFill" )
+	EventsClearHexHighlightStyle( "WorkedOutline" )
+	EventsClearHexHighlightStyle( "UnlockedFill" )
+	EventsClearHexHighlightStyle( "UnlockedOutline" )
+	EventsClearHexHighlightStyle( "OverlapFill" )
+	EventsClearHexHighlightStyle( "OverlapOutline" )
+	EventsClearHexHighlightStyle( "VacantFill" )
+	EventsClearHexHighlightStyle( "VacantOutline" )
+	EventsClearHexHighlightStyle( "EnemyFill" )
+	EventsClearHexHighlightStyle( "EnemyOutline" )
+	EventsClearHexHighlightStyle( "BuyFill" )
+	EventsClearHexHighlightStyle( "BuyOutline" )
+
+	g_PlotButtonIM:ResetInstances()
+	g_BuyPlotButtonIM:ResetInstances()
+
+	-- Show plots that will be acquired by culture
+	local purchasablePlots = {city:GetBuyablePlotList()}
+	for i = 1, #purchasablePlots do
+		local plot = purchasablePlots[i]
+		EventsSerialEventHexHighlight( ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }, true, ColorCulture, "HexContour" )
+		purchasablePlots[ plot ] = true
+	end
+
+
+	local cityArea = city:GetNumCityPlots() - 1
+	EventsRequestYieldDisplay( YieldDisplayTypesAREA, HexRadius( cityArea ), city:GetX(), city:GetY() )
+
+	-- display worked plots buttons
+	local cityOwnerID = city:GetOwner()
+	local notInStrategicView = not InStrategicView()
+	local showButtons = g_workerHeadingOpen and not g_isViewingMode
+
+	for cityPlotIndex = 0, cityArea do
+		local plot = city:GetCityIndexPlot( cityPlotIndex )
+
+		if plot and plot:GetOwner() == cityOwnerID then
+
+			local hexPos = ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }
+			local worldPos = HexToWorld( hexPos )
+			local iconID, tipKey
+			if city:IsWorkingPlot( plot ) then
+
+				-- The city itself
+				if cityPlotIndex == 0 then
+					iconID = 11
+					tipKey = "TXT_KEY_CITYVIEW_CITY_CENTER"
+
+				-- FORCED worked plot
+				elseif city:IsForcedWorkingPlot( plot ) then
+					iconID = 10
+					tipKey = "TXT_KEY_CITYVIEW_FORCED_WORK_TILE"
+
+				-- AI-picked worked plot
+				else
+					iconID = 0
+					tipKey = "TXT_KEY_CITYVIEW_GUVNA_WORK_TILE"
+				end
+				if notInStrategicView then
+					if plot:IsCity() or city:IsForcedWorkingPlot( plot ) then
+						EventsSerialEventHexHighlight( hexPos , true, nil, "WorkedFill" )
+						EventsSerialEventHexHighlight( hexPos , true, nil, "WorkedOutline" )
+					else
+						EventsSerialEventHexHighlight( hexPos , true, nil, "UnlockedFill") 
+                        EventsSerialEventHexHighlight( hexPos , true, nil, "UnlockedOutline")
+					end
+				end
+			else
+				local workingCity = plot:GetWorkingCity()
+				-- worked by another one of our Cities
+				if workingCity:IsWorkingPlot( plot ) then
+					iconID = 12
+					tipKey = "TXT_KEY_CITYVIEW_NUTHA_CITY_TILE"
+
+				-- Workable plot
+				elseif workingCity:CanWork( plot ) then
+					iconID = 9
+					tipKey = "TXT_KEY_CITYVIEW_UNWORKED_CITY_TILE"
+
+				-- Blockaded water plot
+				elseif plot:IsWater() and city:IsPlotBlockaded( plot ) then
+					iconID = 13
+					tipKey = "TXT_KEY_CITYVIEW_BLOCKADED_CITY_TILE"
+					cityPlotIndex = nil
+
+				-- Enemy Unit standing here
+				elseif plot:IsVisibleEnemyUnit( cityOwnerID ) then
+					iconID = 13
+					tipKey = "TXT_KEY_CITYVIEW_ENEMY_UNIT_CITY_TILE"
+					cityPlotIndex = nil
+				end
+				if notInStrategicView then
+					if workingCity ~= city then
+						EventsSerialEventHexHighlight( hexPos , true, nil, "OverlapFill" )
+						EventsSerialEventHexHighlight( hexPos , true, nil, "OverlapOutline" )
+					elseif cityPlotIndex then
+						EventsSerialEventHexHighlight( hexPos , true, nil, "VacantFill" )
+						EventsSerialEventHexHighlight( hexPos , true, nil, "VacantOutline" )
+					else
+						EventsSerialEventHexHighlight( hexPos , true, nil, "EnemyFill" )
+						EventsSerialEventHexHighlight( hexPos , true, nil, "EnemyOutline" )
+					end
+				end
+			end
+			if iconID and showButtons then
+				local instance = g_PlotButtonIM:GetInstance()
+				instance.PlotButtonAnchor:SetWorldPositionVal( worldPos.x + g_worldPositionOffset.x, worldPos.y + g_worldPositionOffset.y, worldPos.z + g_worldPositionOffset.z ) --todo: improve code
+				instance.PlotButtonImage:LocalizeAndSetToolTip( tipKey )
+				IconHookup( iconID, 45, "CITIZEN_ATLAS", instance.PlotButtonImage )
+				local button = instance.PlotButtonImage
+				if not cityPlotIndex or g_isViewingMode then
+					button:ClearCallback( eLClick )
+				else
+					button:SetVoid1( cityPlotIndex )
+					button:RegisterCallback( eLClick, PlotButtonClicked )
+				end
+			end
+		end
+	end --loop
+
+	-- display buy plot buttons
+	if g_BuyPlotMode and not g_isViewingMode then
+		local cash = g_activePlayer:GetGold()
+		for cityPlotIndex = 0, cityArea do
+			local plot = city:GetCityIndexPlot( cityPlotIndex )
+			if plot then
+				local x = plot:GetX()
+				local y = plot:GetY()
+				local hexPos = ToHexFromGrid{ x=x, y=y }
+				local worldPos = HexToWorld( hexPos )
+				if city:CanBuyPlotAt( x, y, true ) then
+					local instance = g_BuyPlotButtonIM:GetInstance()
+					local button = instance.BuyPlotAnchoredButton
+					instance.BuyPlotButtonAnchor:SetWorldPositionVal( worldPos.x + g_worldPositionOffset2.x, worldPos.y + g_worldPositionOffset2.y, worldPos.z + g_worldPositionOffset2.z ) --todo: improve code
+					local plotCost = city:GetBuyPlotCost( x, y )
+					local tip, txt, alpha
+					local canBuy = city:CanBuyPlotAt( x, y, false )
+					if canBuy then
+						tip = L( "TXT_KEY_CITYVIEW_CLAIM_NEW_LAND", plotCost )
+						txt = plotCost
+						alpha = 1
+						button:SetVoid1( cityPlotIndex )
+						button:RegisterCallback( eLClick, BuyPlotAnchorButtonClicked )
+						if notInStrategicView then
+							EventsSerialEventHexHighlight( hexPos , true, nil, "BuyFill" )
+							if not purchasablePlots[ plot ] then
+								EventsSerialEventHexHighlight( hexPos , true, nil, "BuyOutline" )
+							end
+						end
+					else
+						tip = L( "TXT_KEY_CITYVIEW_NEED_MONEY_BUY_TILE", plotCost )
+						txt = "[COLOR_WARNING_TEXT]"..(plotCost-cash).."[ENDCOLOR]"
+						alpha = 0.5
+					end
+					button:SetDisabled( not canBuy )
+					instance.BuyPlotButtonAnchor:SetAlpha( alpha )
+--todo
+					button:SetToolTipString( tip )
+					instance.BuyPlotAnchoredButtonLabel:SetText( txt )
+				end
+			end
+		end --loop
+	end
+end
+
+local function UpdateWorkingHexes()
+	local city = GetHeadSelectedCity()
+	if city and UI.IsCityScreenUp() then
+		return UpdateCityWorkingHexes( city )
+	end
+end
+
+--==========================================================
+-- Update City View
+--==========================================================
+
+local function UpdateCityView()
+
+	local city = GetHeadSelectedCity()
+
+	if city and UI.IsCityScreenUp() then
+
+		if g_citySpecialists.city ~= city then
+			g_citySpecialists = { city = city }
+		end
+--[[
+		if g_previousCity ~= city then
+			g_previousCity = city
+			EventsClearHexHighlightStyle("CityLimits")
+			if not InStrategicView() then
+				for cityPlotIndex = 0, city:GetNumCityPlots() - 1 do
+					local plot = city:GetCityIndexPlot( cityPlotIndex )
+					if plot then
+						local hexPos = ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }
+						EventsSerialEventHexHighlight( hexPos , true, nil, "CityLimits" )
+					end
+				end
+			end
+		end
+--]]
+		local cityID = city:GetID()
+		local cityOwnerID = city:GetOwner()
+		local cityOwner = Players[cityOwnerID]
+		local isActivePlayerCity = cityOwnerID == Game.GetActivePlayer()
+		local isCityCaptureViewingMode = UI.IsPopupTypeOpen(ButtonPopupTypes.BUTTONPOPUP_CITY_CAPTURED)
+		g_isDebugMode = Game.IsDebugMode()
+		g_isViewingMode = city:IsPuppet() or not isActivePlayerCity or isCityCaptureViewingMode
+
+		if IsCiv5 then
+			-- Auto Specialist checkbox
+			local isNoAutoAssignSpecialists = city:IsNoAutoAssignSpecialists()
+			Controls.NoAutoSpecialistCheckbox:SetCheck( isNoAutoAssignSpecialists )
+			Controls.NoAutoSpecialistCheckbox:SetDisabled( g_isViewingMode )
+			if IsCiv5BNW then
+				Controls.TourismPerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_PERTURN_TEXT", city:GetBaseTourism() )
+				Controls.NoAutoSpecialistCheckbox2:SetCheck( isNoAutoAssignSpecialists )
+				Controls.NoAutoSpecialistCheckbox2:SetDisabled( g_isViewingMode )
+			end
+		end
+		Controls.AutomateProduction:SetCheck( city:IsProductionAutomated() )
+		Controls.AutomateProduction:SetDisabled( g_isViewingMode )
+
+		-------------------------------------------
+		-- City Banner
+		-------------------------------------------
+
+		local isCapital = city:IsCapital()
+		Controls.CityCapitalIcon:SetHide( not isCapital )
+
+		Controls.CityIsConnected:SetHide( isCapital or city:IsBlockaded() or not cityOwner:IsCapitalConnectedToCity(city) or city:GetTeam() ~= Game.GetActiveTeam() )
+
+		Controls.CityIsBlockaded:SetHide( not city:IsBlockaded() )
+
+		if city:IsRazing() then
+			Controls.CityIsRazing:SetHide(false)
+			Controls.CityIsRazing:LocalizeAndSetToolTip("TXT_KEY_CITY_BURNING", city:GetRazingTurns())
+		else
+			Controls.CityIsRazing:SetHide(true)
+		end
+
+		Controls.CityIsPuppet:SetHide( not city:IsPuppet() )
+
+		if city:IsResistance() then
+			Controls.CityIsResistance:SetHide(false)
+			Controls.CityIsResistance:LocalizeAndSetToolTip("TXT_KEY_CITY_RESISTANCE", city:GetResistanceTurns())
+		else
+			Controls.CityIsResistance:SetHide(true)
+		end
+
+--todo BE
+		Controls.CityIsOccupied:SetHide( not( IsCiv5 and city:IsOccupied() and not city:IsNoOccupiedUnhappiness() ) )
+
+		local cityName = Locale.ToUpper( city:GetName() )
+
+		if city:IsRazing() then
+			cityName = cityName .. " (" .. L"TXT_KEY_BURNING" .. ")"
+		end
+
+		local size = isCapital and Controls.CityCapitalIcon:GetSizeX() or 0
+		Controls.CityNameTitleBarLabel:SetOffsetX( size / 2 )
+		TruncateString( Controls.CityNameTitleBarLabel, abs(Controls.NextCityButton:GetOffsetX()) * 2 - Controls.NextCityButton:GetSizeX() - size, cityName )
+
+		Controls.Defense:SetText( floor( city:GetStrengthValue() / 100 ) )
+
+ 		CivIconHookup( cityOwnerID, 64, Controls.CivIcon, Controls.CivIconBG, Controls.CivIconShadow, false, true )
+
+		-------------------------------------------
+		-- City Damage
+		-------------------------------------------
+
+		local cityDamage = city:GetDamage()
+		if cityDamage > 0 then
+			local cityHealthPercent = 1 - cityDamage / ( not IsCiv5vanilla and city:GetMaxHitPoints() or GameDefines.MAX_CITY_HIT_POINTS )
+
+			Controls.HealthMeter:SetPercent( cityHealthPercent )
+			if cityHealthPercent > 0.66 then
+				Controls.HealthMeter:SetTexture("CityNamePanelHealthBarGreen.dds")
+			elseif cityHealthPercent > 0.33 then
+				Controls.HealthMeter:SetTexture("CityNamePanelHealthBarYellow.dds")
+			else
+				Controls.HealthMeter:SetTexture("CityNamePanelHealthBarRed.dds")
+			end
+			Controls.HealthFrame:SetHide( false )
+		else
+			Controls.HealthFrame:SetHide( true )
+		end
+
+		-------------------------------------------
+		-- Growth Meter
+		-------------------------------------------
+
+		local cityPopulation = floor( city:GetPopulation() )
+		Controls.CityPopulationLabel:SetText( cityPopulation )
+		Controls.PeopleMeter:SetPercent( city:GetFood() / city:GrowthThreshold() )
+
+		--Update suffix to use correct plurality.
+		Controls.CityPopulationLabelSuffix:LocalizeAndSetText( "TXT_KEY_CITYVIEW_CITIZENS_TEXT", cityPopulation )
+
+
+		-------------------------------------------
+		-- Citizen Focus & Slackers
+		-------------------------------------------
+
+		Controls.AvoidGrowthButton:SetCheck( city:IsForcedAvoidGrowth() )
+
+		local slackerCount = city:GetSpecialistCount( GameDefines.DEFAULT_SPECIALIST )
+
+		local focusButton = g_cityFocusButtons[ city:GetFocusType() ]
+		if focusButton then
+			focusButton:SetCheck( true )
+		end
+
+		local doHide = city:GetNumForcedWorkingPlots() < 1 and slackerCount < 1
+		Controls.ResetButton:SetHide( doHide )
+		Controls.ResetFooter:SetHide( doHide )
+
+		g_SlackerIM:ResetInstances()
+		for i = 1, slackerCount do
+			local instance = g_SlackerIM:GetInstance()
+			local slot = instance.Button
+			slot:SetVoids( -1, i )
+			slot:SetToolTipCallback( SpecialistTooltip )
+			slot:SetTexture( g_slackerTexture )
+			if g_isViewingMode then
+				slot:ClearCallback( eLClick )
+			else
+				slot:RegisterCallback( eLClick, OnSlackersSelected )
+			end
+			slot:RegisterCallback( eRClick, SpecialistPedia )
+		end
+		g_SlackerIM.Commit()
+
+		-------------------------------------------
+		-- Great Person Meters
+		-------------------------------------------
+		if IsCiv5 then
+			g_GreatPeopleIM:ResetInstances()
+			for specialist in GameInfo.Specialists() do
+
+				local gpuClass = specialist.GreatPeopleUnitClass	-- nil / UNITCLASS_ARTIST / UNITCLASS_SCIENTIST / UNITCLASS_MERCHANT / UNITCLASS_ENGINEER ...
+				local unitClass = GameInfo.UnitClasses[ gpuClass or -1 ]
+				if unitClass then
+					local gpThreshold = city:GetSpecialistUpgradeThreshold(unitClass.ID)
+					local gpProgress = city:GetSpecialistGreatPersonProgressTimes100(specialist.ID) / 100
+					local gpChange = specialist.GreatPeopleRateChange * city:GetSpecialistCount( specialist.ID )
+					for building in GameInfo.Buildings{SpecialistType = specialist.Type} do
+						if city:IsHasBuilding(building.ID) then
+							gpChange = gpChange + building.GreatPeopleRateChange
+						end
+					end
+
+					local gpChangePlayerMod = cityOwner:GetGreatPeopleRateModifier()
+					local gpChangeCityMod = city:GetGreatPeopleRateModifier()
+					local gpChangePolicyMod = 0
+					local gpChangeWorldCongressMod = 0
+					local gpChangeGoldenAgeMod = 0
+					local isGoldenAge = cityOwner:GetGoldenAgeTurns() > 0
+
+					if IsCiv5BNW then
+						-- Generic GP mods
+
+						gpChangePolicyMod = cityOwner:GetPolicyGreatPeopleRateModifier()
+
+						local worldCongress = (Game.GetNumActiveLeagues() > 0) and Game.GetActiveLeague()
+
+						-- GP mods by type
+						if specialist.GreatPeopleUnitClass == "UNITCLASS_WRITER" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatWriterRateModifier()
+							gpChangePolicyMod = gpChangePolicyMod + cityOwner:GetPolicyGreatWriterRateModifier()
+							if worldCongress then
+								gpChangeWorldCongressMod = gpChangeWorldCongressMod + worldCongress:GetArtsyGreatPersonRateModifier()
+							end
+							if isGoldenAge and cityOwner:GetGoldenAgeGreatWriterRateModifier() > 0 then
+								gpChangeGoldenAgeMod = gpChangeGoldenAgeMod + cityOwner:GetGoldenAgeGreatWriterRateModifier()
+							end
+						elseif specialist.GreatPeopleUnitClass == "UNITCLASS_ARTIST" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatArtistRateModifier()
+							gpChangePolicyMod = gpChangePolicyMod + cityOwner:GetPolicyGreatArtistRateModifier()
+							if worldCongress then
+								gpChangeWorldCongressMod = gpChangeWorldCongressMod + worldCongress:GetArtsyGreatPersonRateModifier()
+							end
+							if isGoldenAge and cityOwner:GetGoldenAgeGreatArtistRateModifier() > 0 then
+								gpChangeGoldenAgeMod = gpChangeGoldenAgeMod + cityOwner:GetGoldenAgeGreatArtistRateModifier()
+							end
+						elseif specialist.GreatPeopleUnitClass == "UNITCLASS_MUSICIAN" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatMusicianRateModifier()
+							gpChangePolicyMod = gpChangePolicyMod + cityOwner:GetPolicyGreatMusicianRateModifier()
+							if worldCongress then
+								gpChangeWorldCongressMod = gpChangeWorldCongressMod + worldCongress:GetArtsyGreatPersonRateModifier()
+							end
+							if isGoldenAge and cityOwner:GetGoldenAgeGreatMusicianRateModifier() > 0 then
+								gpChangeGoldenAgeMod = gpChangeGoldenAgeMod + cityOwner:GetGoldenAgeGreatMusicianRateModifier()
+							end
+						elseif specialist.GreatPeopleUnitClass == "UNITCLASS_SCIENTIST" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatScientistRateModifier()
+							gpChangePolicyMod = gpChangePolicyMod + cityOwner:GetPolicyGreatScientistRateModifier()
+							if worldCongress then
+								gpChangeWorldCongressMod = gpChangeWorldCongressMod + worldCongress:GetScienceyGreatPersonRateModifier()
+							end
+						elseif specialist.GreatPeopleUnitClass == "UNITCLASS_MERCHANT" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatMerchantRateModifier()
+							gpChangePolicyMod = gpChangePolicyMod + cityOwner:GetPolicyGreatMerchantRateModifier()
+							if worldCongress then
+								gpChangeWorldCongressMod = gpChangeWorldCongressMod + worldCongress:GetScienceyGreatPersonRateModifier()
+							end
+						elseif specialist.GreatPeopleUnitClass == "UNITCLASS_ENGINEER" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatEngineerRateModifier()
+							gpChangePolicyMod = gpChangePolicyMod + cityOwner:GetPolicyGreatEngineerRateModifier()
+							if worldCongress then
+								gpChangeWorldCongressMod = gpChangeWorldCongressMod + worldCongress:GetScienceyGreatPersonRateModifier()
+							end
+						-- Compatibility with Gazebo's City-State Diplomacy Mod (CSD) for Brave New World
+						elseif cityOwner.GetGreatDiplomatRateModifier and specialist.GreatPeopleUnitClass == "UNITCLASS_GREAT_DIPLOMAT" then
+							gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetGreatDiplomatRateModifier()
+						end
+
+						-- Player mod actually includes policy mod and World Congress mod, so separate them for tooltip
+
+						gpChangePlayerMod = gpChangePlayerMod - gpChangePolicyMod - gpChangeWorldCongressMod
+
+					elseif gpuClass == "UNITCLASS_SCIENTIST" then
+
+						gpChangePlayerMod = gpChangePlayerMod + cityOwner:GetTraitGreatScientistRateModifier()
+
+					end
+
+					local gpChangeMod = gpChangePlayerMod + gpChangePolicyMod + gpChangeWorldCongressMod + gpChangeCityMod + gpChangeGoldenAgeMod
+					gpChange = (gpChangeMod / 100 + 1) * gpChange
+
+					if gpProgress > 0 or gpChange > 0 then
+						local instance = g_GreatPeopleIM:GetInstance()
+						local percent = gpProgress / gpThreshold
+						instance.GPMeter:SetPercent( percent )
+						local labelText = unitClass._Name
+						local icon = GreatPeopleIcon(gpuClass)
+						local tips = { "[COLOR_YIELD_FOOD]" .. Locale.ToUpper( labelText ) .. "[ENDCOLOR]" .. " " .. gpProgress .. icon .." / " .. gpThreshold .. icon }
+	--					insert( tips, L( "TXT_KEY_PROGRESS_TOWARDS", "[COLOR_YIELD_FOOD]" .. Locale.ToUpper( labelText ) .. "[ENDCOLOR]" ) )
+						if gpChange > 0 then
+							local gpTurns = ceil( (gpThreshold - gpProgress) / gpChange )
+							insert( tips, "[COLOR_YIELD_FOOD]" .. Locale.ToUpper( L( "TXT_KEY_STR_TURNS", gpTurns ) ) .. "[ENDCOLOR]  "
+										 .. gpChange .. icon .. " " .. L"TXT_KEY_GOLD_PERTURN_HEADING4_TITLE" )
+							labelText = labelText .. ": " .. Locale.ToLower( L( "TXT_KEY_STR_TURNS", gpTurns ) )
+						end
+						instance.GreatPersonLabel:SetText( icon .. labelText )
+						if IsCiv5vanilla then
+							if gpChangeMod ~= 0 then
+								insert( tips, "[ICON_BULLET] "..gpChangeMod..icon )
+							end
+						else
+							if gpChangePlayerMod ~= 0 then
+								insert( tips, L( "TXT_KEY_PLAYER_GP_MOD", gpChangePlayerMod ) )
+							end
+							if gpChangePolicyMod ~= 0 then
+								insert( tips, L( "TXT_KEY_POLICY_GP_MOD", gpChangePolicyMod ) )
+							end
+							if gpChangeCityMod ~= 0 then
+								insert( tips, L( "TXT_KEY_CITY_GP_MOD", gpChangeCityMod ) )
+							end
+							if gpChangeGoldenAgeMod ~= 0 then
+								insert( tips, L( "TXT_KEY_GOLDENAGE_GP_MOD", gpChangeGoldenAgeMod ) )
+							end
+							if gpChangeWorldCongressMod < 0 then
+								insert( tips, L( "TXT_KEY_WORLD_CONGRESS_NEGATIVE_GP_MOD", gpChangeWorldCongressMod ) )
+							elseif gpChangeWorldCongressMod > 0 then
+								insert( tips, L( "TXT_KEY_WORLD_CONGRESS_POSITIVE_GP_MOD", gpChangeWorldCongressMod ) )
+							end
+						end
+						instance.GPBox:SetToolTipString( concat( tips, "[NEWLINE]") )
+						instance.GPBox:SetVoid1( unitClass.ID )
+						instance.GPBox:RegisterCallback( eRClick, UnitClassPedia )
+
+						local portraitOffset, portraitAtlas = GetUnitPortraitIcon( GameInfoTypes[ unitClass.DefaultUnit ], cityOwnerID )
+						instance.GPImage:SetHide(not IconHookup( portraitOffset, 64, portraitAtlas, instance.GPImage ) )
+					end
+				end
+			end
+			g_GreatPeopleIM.Commit()
+		end
+
+		-------------------------------------------
+		-- Buildings
+		-------------------------------------------
+
+		local greatWorkBuildings = {}
+		local specialistBuildings = {}
+		local wonders = {}
+		local otherBuildings = {}
+		local noWondersWithSpecialistInThisCity = true
+
+		for building in GameInfo.Buildings() do
+			local buildingID = building.ID
+			if city:IsHasBuilding(buildingID) then
+				local buildingClass = GameInfo.BuildingClasses[ building.BuildingClass ]
+				local buildings
+				local greatWorkCount = IsCiv5BNW and building.GreatWorkCount or 0
+				local areSpecialistsAllowedByBuilding = city:GetNumSpecialistsAllowedByBuilding(buildingID) > 0
+
+				if buildingClass 
+					and ( buildingClass.MaxGlobalInstances > 0
+						or buildingClass.MaxTeamInstances > 0
+						or ( buildingClass.MaxPlayerInstances == 1 and not areSpecialistsAllowedByBuilding ) )
+				then
+					buildings = wonders
+					if areSpecialistsAllowedByBuilding then
+						noWondersWithSpecialistInThisCity = false
+					end
+				elseif areSpecialistsAllowedByBuilding then
+					buildings = specialistBuildings
+				elseif greatWorkCount > 0 then
+					buildings = greatWorkBuildings
+				elseif greatWorkCount == 0 then		-- compatibility with Firaxis code exploit for invisibility
+					buildings = otherBuildings
+				end
+				if buildings then
+					insert( buildings, { building, building._Name, greatWorkCount, areSpecialistsAllowedByBuilding and GameInfoTypes[building.SpecialistType] or 999 } )
+				end
+			end
+		end
+		local strMaintenanceTT = L( "TXT_KEY_BUILDING_MAINTENANCE_TT", city:GetTotalBaseBuildingMaintenance() )
+		Controls.SpecialBuildingsHeader:SetToolTipString(strMaintenanceTT)
+		Controls.BuildingsHeader:SetToolTipString(strMaintenanceTT)
+		Controls.GreatWorkHeader:SetToolTipString(strMaintenanceTT)
+		Controls.SpecialistControlBox:SetHide( #specialistBuildings < 1 )
+		Controls.SpecialistControlBox2:SetHide( noWondersWithSpecialistInThisCity )
+		g_GreatWorksIM:ResetInstances()
+		g_SpecialistsIM:ResetInstances()
+		SetupBuildingList( city, specialistBuildings, g_SpecialBuildingsIM )
+		SetupBuildingList( city, wonders, g_WondersIM )
+		SetupBuildingList( city, greatWorkBuildings, g_GreatWorkIM )
+		SetupBuildingList( city, otherBuildings, g_BuildingsIM )
+		ResizeRightStack()
+
+		-------------------------------------------
+		-- Buying Plots
+		-------------------------------------------
+--		szText = L"TXT_KEY_CITYVIEW_BUY_TILE"
+--		Controls.BuyPlotButton:LocalizeAndSetToolTip( "TXT_KEY_CITYVIEW_BUY_TILE_TT" )
+--		Controls.BuyPlotText:SetText(szText)
+--		Controls.BuyPlotButton:SetDisabled( g_isViewingMode or (GameDefines.BUY_PLOTS_DISABLED ~= 0 and city:CanBuyAnyPlot()) )
+
+		-------------------------------------------
+		-- Resource Demanded
+		-------------------------------------------
+
+		if city:GetResourceDemanded(true) ~= -1 then
+			local resourceInfo = GameInfo.Resources[ city:GetResourceDemanded() ]
+			local weLoveTheKingDayCounter = city:GetWeLoveTheKingDayCounter()
+			if weLoveTheKingDayCounter > 0 then
+				Controls.ResourceDemandedString:LocalizeAndSetText( "TXT_KEY_CITYVIEW_WLTKD_COUNTER", weLoveTheKingDayCounter )
+				Controls.ResourceDemandedBox:LocalizeAndSetToolTip( "TXT_KEY_CITYVIEW_RESOURCE_FULFILLED_TT" )
+			else
+				Controls.ResourceDemandedString:LocalizeAndSetText( "TXT_KEY_CITYVIEW_RESOURCE_DEMANDED", (resourceInfo.IconString or"") .. " " .. resourceInfo._Name )
+				Controls.ResourceDemandedBox:LocalizeAndSetToolTip( "TXT_KEY_CITYVIEW_RESOURCE_DEMANDED_TT" )
+			end
+
+			Controls.ResourceDemandedBox:SetSizeX(Controls.ResourceDemandedString:GetSizeX() + 10)
+			Controls.ResourceDemandedBox:SetHide(false)
+		else
+			Controls.ResourceDemandedBox:SetHide(true)
+		end
+
+		Controls.IconsStack:CalculateSize()
+		Controls.IconsStack:ReprocessAnchoring()
+
+		Controls.NotificationStack:CalculateSize()
+		Controls.NotificationStack:ReprocessAnchoring()
+
+		-------------------------------------------
+		-- Raze / Unraze / Annex City Buttons
+		-------------------------------------------
+
+		local buttonToolTip, buttonLabel, taskID
+		if isActivePlayerCity then
+
+			if city:IsRazing() then
+
+				-- We can unraze this city
+				taskID = TaskTypes.TASK_UNRAZE
+				buttonLabel = L"TXT_KEY_CITYVIEW_UNRAZE_BUTTON_TEXT"
+				buttonToolTip = L"TXT_KEY_CITYVIEW_UNRAZE_BUTTON_TT"
+
+			elseif city:IsPuppet() and not(IsCiv5BNW and cityOwner:MayNotAnnex()) then
+
+				-- We can annex this city
+				taskID = TaskTypes.TASK_ANNEX_PUPPET
+				buttonLabel = L"TXT_KEY_POPUP_ANNEX_CITY"
+-- todo
+				if IsCiv5 then
+					buttonToolTip = L( "TXT_KEY_POPUP_CITY_CAPTURE_INFO_ANNEX", cityOwner:GetUnhappinessForecast(city) - cityOwner:GetUnhappiness() )
+				end
+			elseif not g_isViewingMode and cityOwner:CanRaze( city, true ) then
+				buttonLabel = L"TXT_KEY_CITYVIEW_RAZE_BUTTON_TEXT"
+
+				if cityOwner:CanRaze( city, false ) then
+
+					-- We can actually raze this city
+					taskID = TaskTypes.TASK_RAZE
+					buttonToolTip = L"TXT_KEY_CITYVIEW_RAZE_BUTTON_TT"
+				else
+					-- We COULD raze this city if it weren't a capital
+					buttonToolTip = L"TXT_KEY_CITYVIEW_RAZE_BUTTON_DISABLED_BECAUSE_CAPITAL_TT"
+				end
+			end
+		end
+		local CityTaskButton = Controls.CityTaskButton
+		CityTaskButton:SetText( buttonLabel )
+		CityTaskButton:SetVoids( cityID, taskID or -1 )
+		CityTaskButton:SetToolTipString( buttonToolTip )
+		CityTaskButton:SetDisabled( not taskID )
+		CityTaskButton:SetHide( not buttonLabel )
+--Controls.ReturnToMapButton:SetToolTipString( concat( {"g_isViewingMode:", tostring(g_isViewingMode), "Can raze:", tostring(cityOwner:CanRaze( city, true )), "Can actually raze:", tostring(cityOwner:CanRaze( city, false )), "taskID:", tostring(taskID) }, " " ) )
+
+		UpdateCityWorkingHexes( city )
+
+		UpdateCityProductionQueue( city, cityID, cityOwnerID, isActivePlayerCity and not isCityCaptureViewingMode and IsCiv5BNW and cityOwner:MayNotAnnex() and city:IsPuppet() )
+
+		-- display gold income
+		local iGoldPerTurn = city:GetYieldRateTimes100( g_yieldCurrency ) / 100
+		Controls.GoldPerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_PERTURN_TEXT", iGoldPerTurn )
+
+		-- display science income
+		if Game.IsOption(GameOptionTypes.GAMEOPTION_NO_SCIENCE) then
+			Controls.SciencePerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_OFF" )
+		else
+			local iSciencePerTurn = city:GetYieldRateTimes100(YieldTypes.YIELD_SCIENCE) / 100
+			Controls.SciencePerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_PERTURN_TEXT", iSciencePerTurn )
+		end
+
+		local culturePerTurn, cultureStored, cultureNext
+		-- thanks for Firaxis Cleverness !
+		if IsCiv5 then
+			culturePerTurn = city:GetJONSCulturePerTurn()
+			cultureStored = city:GetJONSCultureStored()
+			cultureNext = city:GetJONSCultureThreshold()
+		else
+			culturePerTurn = city:GetCulturePerTurn()
+			cultureStored = city:GetCultureStored()
+			cultureNext = city:GetCultureThreshold()
+		end
+		Controls.CulturePerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_PERTURN_TEXT", culturePerTurn )
+		local cultureDiff = cultureNext - cultureStored
+		if culturePerTurn > 0 then
+			local cultureTurns = max( ceil(cultureDiff / culturePerTurn), 1 )
+			Controls.CultureTimeTillGrowthLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_TURNS_TILL_TILE_TEXT", cultureTurns )
+			Controls.CultureTimeTillGrowthLabel:SetHide( false )
+		else
+			Controls.CultureTimeTillGrowthLabel:SetHide( true )
+		end
+		local percentComplete = cultureStored / cultureNext
+		Controls.CultureMeter:SetPercent( percentComplete )
+
+		if not IsCiv5vanilla then
+			if Game.IsOption(GameOptionTypes.GAMEOPTION_NO_RELIGION) then
+				Controls.FaithPerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_OFF" )
+			else
+				Controls.FaithPerTurnLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_PERTURN_TEXT", city:GetFaithPerTurn() )
+			end
+			Controls.FaithFocusButton:SetDisabled( g_isViewingMode )
+		end
+
+		local cityGrowth = city:GetFoodTurnsLeft()
+		local foodPerTurnTimes100 = city:FoodDifferenceTimes100()
+		if city:IsFoodProduction() or foodPerTurnTimes100 == 0 then
+			Controls.CityGrowthLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_STAGNATION_TEXT" )
+		elseif foodPerTurnTimes100 < 0 then
+			Controls.CityGrowthLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_STARVATION_TEXT" )
+		else
+			Controls.CityGrowthLabel:LocalizeAndSetText( "TXT_KEY_CITYVIEW_TURNS_TILL_CITIZEN_TEXT", cityGrowth )
+		end
+
+		Controls.FoodPerTurnLabel:LocalizeAndSetText( foodPerTurnTimes100 >= 0 and "TXT_KEY_CITYVIEW_PERTURN_TEXT" or "TXT_KEY_CITYVIEW_PERTURN_TEXT_NEGATIVE", foodPerTurnTimes100 / 100 )
+
+		-------------------------------------------
+		-- Disable Buttons as Appropriate
+		-------------------------------------------
+		local bIsLock = g_isViewingMode or (cityOwner:GetNumCities() <= 1)
+		Controls.PrevCityButton:SetDisabled( bIsLock )
+		Controls.NextCityButton:SetDisabled( bIsLock )
+
+		for _, control in pairs( g_cityFocusButtons ) do
+			control:SetDisabled( g_isViewingMode )
+		end
+
+	end
+end
+
+local function UpdateOptionsAndCityView()
+	g_isAdvisor = UserInterfaceSettings.CityAdvisor ~= 0
+	g_isScreenAutoClose = UserInterfaceSettings.ScreenAutoClose ~= 0
+	g_isResetCityPlotPurchase = UserInterfaceSettings.ResetCityPlotPurchase ~= 0
+	g_FocusSelectIM.Collapse( not OptionsManager.IsNoCitizenWarning() )
+	return UpdateCityView()
+end
+
+g_SpecialBuildingsIM	= StackInstanceManager( "BuildingInstance", "Button", Controls.SpecialBuildingsStack, Controls.SpecialBuildingsHeader, ResizeRightStack )
+g_GreatWorkIM		= StackInstanceManager( "BuildingInstance", "Button", Controls.GreatWorkStack, Controls.GreatWorkHeader, ResizeRightStack )
+g_WondersIM		= StackInstanceManager( "BuildingInstance", "Button", Controls.WondersStack, Controls.WondersHeader, ResizeRightStack )
+g_BuildingsIM		= StackInstanceManager( "BuildingInstance", "Button", Controls.BuildingsStack, Controls.BuildingsHeader, ResizeRightStack )
+g_GreatPeopleIM		= StackInstanceManager( "GPInstance", "GPBox", Controls.GPStack, Controls.GPHeader, ResizeRightStack )
+g_SlackerIM		= StackInstanceManager( "Slot", "Button", Controls.SlackerStack, Controls.SlackerHeader, ResizeRightStack )
+g_ProdQueueIM		= StackInstanceManager( "ProductionInstance", "PQbox", Controls.QueueStack, Controls.ProdBox, ResizeProdQueue, true )
+g_UnitSelectIM		= StackInstanceManager( "SelectionInstance", "Button", Controls.UnitButtonStack, Controls.UnitButton, ResizeProdQueue )
+g_BuildingSelectIM	= StackInstanceManager( "SelectionInstance", "Button", Controls.BuildingButtonStack, Controls.BuildingsButton, ResizeProdQueue )
+g_WonderSelectIM	= StackInstanceManager( "SelectionInstance", "Button", Controls.WonderButtonStack, Controls.WondersButton, ResizeProdQueue )
+g_ProcessSelectIM	= StackInstanceManager( "SelectionInstance", "Button", Controls.OtherButtonStack, Controls.OtherButton, ResizeProdQueue )
+g_FocusSelectIM		= StackInstanceManager( "", "", Controls.WorkerManagementBox, Controls.WorkerHeader, function(collapsed) g_workerHeadingOpen = not collapsed ResizeRightStack() UpdateWorkingHexes() end, true, not g_workerHeadingOpen )
+
+--------------
+-- Rename City
+local function RenameCity()
+	local city = GetHeadSelectedCity()
+	if city then
+		return Events.SerialEventGameMessagePopup{
+				Type = ButtonPopupTypes.BUTTONPOPUP_RENAME_CITY,
+				Data1 = city:GetID(),
+				Data2 = -1,
+				Data3 = -1,
+				Option1 = false,
+				Option2 = false
+				}
+	end
+end
+
+local NoAutoSpecialistCheckbox = { [eLClick] = function()
+			return Game.SelectedCitiesGameNetMessage(GameMessageTypes.GAMEMESSAGE_DO_TASK, TaskTypes.TASK_NO_AUTO_ASSIGN_SPECIALISTS, -1, -1, not GetHeadSelectedCity():IsNoAutoAssignSpecialists() )
+		end }
+
+--==========================================================
+-- Register Events
+--==========================================================
+
+SetupCallbacks( Controls, 
+{
+	ProdBox = LuaEvents.CityViewToolTips.Call,
+	FoodBox = LuaEvents.CityViewToolTips.Call,
+	GoldBox = LuaEvents.CityViewToolTips.Call,
+	ScienceBox = LuaEvents.CityViewToolTips.Call,
+	CultureBox = LuaEvents.CityViewToolTips.Call,
+	FaithBox = LuaEvents.CityViewToolTips.Call,
+	TourismBox = LuaEvents.CityViewToolTips.Call,
+	ProductionPortraitButton = LuaEvents.CityViewToolTips.Call,
+	PopulationBox = LuaEvents.CityViewToolTips.Call,
+},
+"EUI_ItemTooltip",
+{
+	AvoidGrowthButton = {
+		[eLClick] = function()
+			local city = GetSelectedModifiableCity()
+			if city then
+				Network.SendSetCityAvoidGrowth( city:GetID(), not city:IsForcedAvoidGrowth() )
+				return Network.SendUpdateCityCitizens( city:GetID() )
+			end
+		end,
+	},
+	CityTaskButton = {
+		[eLClick] = function( cityID, taskID, button )
+			local city = GetSelectedCity()
+			if city and city:GetID() == cityID then
+				return Events.SerialEventGameMessagePopup{
+					Type = ButtonPopupTypes.BUTTONPOPUP_CONFIRM_CITY_TASK,
+					Data1 = cityID,
+					Data2 = taskID,
+					Text = button:GetToolTipString()
+					}
+			end
+		end,
+	},
+	YesButton = {
+		[eLClick] = function( cityID, buildingID )
+			Controls.SellBuildingConfirm:SetHide( true )
+			if cityID and buildingID and buildingID > 0 and GetSelectedModifiableCity() then
+				Network.SendSellBuilding( cityID, buildingID )
+				Network.SendUpdateCityCitizens( cityID )
+			end
+			return Controls.YesButton:SetVoids( -1, -1 )
+		end,
+	},
+	NoButton = { [eLClick] = CancelBuildingSale },
+	NextCityButton = { [eLClick] = GotoNextCity },
+	PrevCityButton = { [eLClick] = GotoPrevCity },
+	ReturnToMapButton = { [eLClick] = ExitCityScreen },
+	ProductionPortraitButton = { [eRClick] = ProductionPedia },
+	BoxOSlackers = { [eLClick] = OnSlackersSelected },
+	ResetButton = { [eLClick] = PlotButtonClicked },
+	NoAutoSpecialistCheckbox = NoAutoSpecialistCheckbox,
+	NoAutoSpecialistCheckbox2 = NoAutoSpecialistCheckbox,
+	EditButton = { [eLClick] = RenameCity },
+	TitlePanel = { [eRClick] = RenameCity },
+})
+
+--Controls.ResetButton:SetVoid1( 0 )	-- calling with 0 = city center causes reset of all forced tiles
+--Controls.BoxOSlackers:SetVoids(-1,-1)
+--Controls.ProductionPortraitButton:SetVoid1( 0 )
+
+Controls.FaithBox:SetHide( IsCivBE or IsCiv5vanilla )
+Controls.TourismBox:SetHide( not IsCiv5BNW )
+
+
+Controls.BuyPlotCheckBox:RegisterCheckHandler( function( isChecked ) -- Void1, Void2, control )
+	g_BuyPlotMode = isChecked
+	return UpdateCityView()
+end)
+
+Events.GameOptionsChanged.Add( UpdateOptionsAndCityView )
+UpdateOptionsAndCityView()
+
+--------------------------
+-- Enter City Screen Event
+Events.SerialEventEnterCityScreen.Add( function()
+
+--print("enter city screen", GetHeadSelectedCity())
+	LuaEvents.TryQueueTutorial("CITY_SCREEN", true)
+
+	Events.SerialEventCityScreenDirty.Add( UpdateCityView )
+	Events.SerialEventCityInfoDirty.Add( UpdateCityView )
+	Events.SerialEventCityHexHighlightDirty.Add( UpdateWorkingHexes )
+	g_queuedItemNumber = nil
+	g_previousCity = nil
+	if g_isResetCityPlotPurchase then
+		g_BuyPlotMode = false
+		Controls.BuyPlotCheckBox:SetCheck( false )
+	end
+	Controls.RightScrollPanel:SetScrollValue(0)
+--TODO other scroll panels
+	return UpdateCityView()
+end)
+
+-------------------------
+-- Exit City Screen Event
+Events.SerialEventExitCityScreen.Add( function()
+--print("exit city screen")
+	if UI.IsCityScreenUp() then
+		Events.SerialEventCityScreenDirty.RemoveAll()
+		Events.SerialEventCityInfoDirty.RemoveAll()
+		Events.SerialEventCityHexHighlightDirty.RemoveAll()
+		local city = UI.GetHeadSelectedCity()
+		local plot = city and city:Plot()
+
+		CleanupCityScreen()
+		Events.ClearHexHighlights()
+
+		-- We may get here after a player change, clear the UI if this is not the active player's city
+		if not city or city:GetOwner() ~= g_activePlayerID then
+			ClearCityUIInfo()
+		end
+		-- required for game engine to display proper hex shading
+		UI.ClearSelectedCities()
+		LuaEvents.TryDismissTutorial("CITY_SCREEN")
+		-- Try and re-select the last unit selected
+		if not UI.GetHeadSelectedUnit() and UI.GetLastSelectedUnit() then
+			UI.SelectUnit( UI.GetLastSelectedUnit() )
+		end
+		if plot then
+			UI.LookAt( plot, 2 ) -- 1 = CAMERALOOKAT_CITY_ZOOM_IN, 2 = CAMERALOOKAT_NORMAL (player zoom)
+		else
+			UI.LookAtSelectionPlot()
+		end
+		g_isViewingMode = true
+	end
+end)
+
+--==========================================================
+-- Strategic View State Change Event
+--==========================================================
+
+if IsCiv5 then
+	local NormalWorldPositionOffset = g_worldPositionOffset
+	local NormalWorldPositionOffset2 = g_worldPositionOffset2
+	local StrategicViewWorldPositionOffset = { x = 0, y = 20, z = 0 }
+	Events.StrategicViewStateChanged.Add( function( bStrategicView )
+		if bStrategicView then
+			g_worldPositionOffset = StrategicViewWorldPositionOffset
+			g_worldPositionOffset2 = StrategicViewWorldPositionOffset
+		else
+			g_worldPositionOffset = NormalWorldPositionOffset
+			g_worldPositionOffset2 = NormalWorldPositionOffset2
+		end
+		g_previousCity = false
+		return UpdateCityView()
+	end)
+end
+--==========================================================
+-- 'Active' (local human) player has changed
+--==========================================================
+
+Events.GameplaySetActivePlayer.Add( function( activePlayerID )--, previousActivePlayerID )
+	g_activePlayerID = activePlayerID
+	g_activePlayer = Players[ g_activePlayerID ]
+	g_finishedItems = {}
+	ClearCityUIInfo()
+	if UI.IsCityScreenUp() then
+		return ExitCityScreen()
+	end
+end)
+
+Events.ActivePlayerTurnEnd.Add( function()
+	g_finishedItems = {}
+end)
+
+AddSerialEventGameMessagePopup( function( popupInfo )
+	if popupInfo.Type == ButtonPopupTypes.BUTTONPOPUP_CHOOSEPRODUCTION then
+		Events.SerialEventGameMessagePopupProcessed.CallImmediate(ButtonPopupTypes.BUTTONPOPUP_CHOOSEPRODUCTION, 0)
+		Events.SerialEventGameMessagePopupShown( popupInfo )
+
+		local cityID = popupInfo.Data1		-- city id
+		local orderID = popupInfo.Data2		-- finished order id
+		local itemID = popupInfo.Data3		-- finished item id
+		local city = cityID and g_activePlayer:GetCityByID( cityID )
+
+		if city and not UI.IsCityScreenUp() then
+			if orderID >= 0 and itemID >= 0 then
+				g_finishedItems[ cityID ] = { orderID, itemID }
+			end
+			g_isButtonPopupChooseProduction = g_isScreenAutoClose
+			return UI.DoSelectCityAtPlot( city:Plot() )	-- open city screen
+		end
+	end
+end, ButtonPopupTypes.BUTTONPOPUP_CHOOSEPRODUCTION )
+
+Events.NotificationAdded.Add( function( notificationID, notificationType, toolTip, strSummary, data1, data2, playerID )
+	if notificationType == NotificationTypes.NOTIFICATION_PRODUCTION and playerID == g_activePlayerID then
+		-- Hack to find city
+		for city in g_activePlayer:Cities() do
+			if strSummary == L( "TXT_KEY_NOTIFICATION_NEW_CONSTRUCTION", city:GetNameKey() ) then
+				if data1 >= 0 and data2 >=0 then
+					g_finishedItems[ city:GetID() ] = { data1, data2 }
+				end
+				if city:GetGameTurnFounded() == Game.GetGameTurn() and not UI.IsCityScreenUp() then
+					return UI.DoSelectCityAtPlot( city:Plot() )	-- open city screen
+				end
+				return
+			end
+		end
+	end
+end)
+end)
+
+--==========================================================
+-- Support for Modded Add-in UI's
+--==========================================================
+do
+	g_uiAddins = {}
+	local Modding = Modding
+	local uiAddins = g_uiAddins
+	for addin in Modding.GetActivatedModEntryPoints("CityViewUIAddin") do
+		local addinFile = Modding.GetEvaluatedFilePath(addin.ModID, addin.Version, addin.File)
+		if addinFile then
+			print( "Loading MOD CityViewUIAddin\n", Modding.GetModProperty(addin.ModID, addin.Version, "Name"), addin.File )
+			table.insert( uiAddins, ContextPtr:LoadNewContext( addinFile.EvaluatedPath:match("(.*)%..*") ) )
+		end
+	end
+end
diff --git a/EUI_Converter/Changes.txt b/EUI_Converter/Changes.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5a00f91c2ef7929c60919c5d146cd66d4f81f043
--- /dev/null
+++ b/EUI_Converter/Changes.txt
@@ -0,0 +1,17 @@
+Add UI_bc1 folder to MP_MODSPACK
+Replace:
+	IGE-Mod: IGE_Window.lua --> EUI compatible
+	EUI: UnitPanel.lua --> mod compatible
+	EUI: CityBannerManager.lua, CityView.lua & Highlights.xml --> Unlocked recolored
+Add "ContextPtr:LoadNewContext"-Parts from old UI files to UI_bc1 in:
+	CityView.lua
+	InGame.lua
+	LeaderHeadRoot.lua
+	UnitPanel.lua (EvilSpiritsMission, THTanukiMission)
+Delete:
+	Old UI folder
+	BNW Mass Effect (v 7)\Dummy Building Folder\CivilopediaScreen.lua
+	BNW Mass Effect (v 7)\Dummy Building Folder\CityView.lua
+	Touhou - Evil Spirits (v 2)\Lua\UnitPanel.lua
+	Touhou - Probability Space Hypervessel (v 1)\Lua\TechTree.lua
+	Touhou - Probability Space Hypervessel (v 1)\Lua\TechButtonInclude.lua
\ No newline at end of file
diff --git a/EUI_Converter/EUI_Converter.py b/EUI_Converter/EUI_Converter.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0f65c779797f54a18bd78b456c269d9bc297f8a
--- /dev/null
+++ b/EUI_Converter/EUI_Converter.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+#Imports
+import os
+from os.path import join as j
+from glob import glob as g
+import subprocess
+import shutil
+import re
+
+#Change to base DLC directory
+os.chdir("../..")
+
+##Global Values
+print("Configuring variables...")
+#Names
+modpack_folder_name = "MP_MODSPACK"
+modded_eui_zip_name = "EUI_CUC.7z"
+eui_cu_file_names = ["CityBannerManager.lua",
+                     "CityView.lua",
+                     "Highlights.xml"]
+load_tag = "ContextPtr:LoadNewContext"
+unit_panel_file_name = "UnitPanel.lua"
+ige_compat_file_name = "IGE_Window.lua"
+delete_file_names = ["CivilopediaScreen.lua",
+                     "CityView.lua",
+                     "TechTree.lua",
+                     "TechButtonInclude.lua",
+                     unit_panel_file_name]
+unit_panel_modcompat_file_names = ["EvilSpiritsMission.lua",
+                                   "THTanukiMission.lua"]
+#Paths
+base_path = os.getcwd()
+modsave_path = j(base_path, "zzz_Modsaves")
+modpack_path = j(base_path, modpack_folder_name)
+vanilla_packs_path =  j(base_path, "zz_Vanilla_Versions")
+ui_path = j(modpack_path, "UI")
+eui_path = j(base_path, "UI_bc1")
+szip = r"C:\Program Files\7-Zip\7z.exe"
+#Files
+mod_files = j(modpack_path, "Mods", "**", "*.lua")
+ui_files = j(ui_path, "*.lua")
+eui_files = j(eui_path, "*", "*.lua")
+base_eui_zip = j(vanilla_packs_path, "0EUI.7z")
+modded_eui_zip_path = j(base_path, modded_eui_zip_name)
+
+#Global Variables
+load_tags = {}
+unit_panel_modcompat_needed = False
+null = open(os.devnull, 'w')
+
+
+#Get modpack zip
+while True:
+    modpack_name = input("\nWhich pack should be converted?\n")
+    modpack_zips = g(j(vanilla_packs_path, modpack_name + ".*"))
+    if len(modpack_zips) > 0:
+        modpack_zip = modpack_zips[0]
+        break
+    print("This file doesn't exist, try again.")
+
+
+#Remove previous modpack
+print("Removing previous modpack leftovers...")
+if os.path.isdir(modpack_path):
+    shutil.rmtree(modpack_path)
+if os.path.isdir(eui_path):
+    shutil.rmtree(eui_path)
+
+#Compile EUI with colored unlocked citizens
+if not os.path.isfile(modded_eui_zip_path):
+    print("Creating colored unlocked Citizens EUI...")
+    subprocess.run([szip, 'x', base_eui_zip], stdout=null, stderr=null)
+    #shutil.move(j(vanilla_packs_path, eui_file_name), eui_path)
+    for eui_cu_file_name in eui_cu_file_names:
+        eui_cu_file = g(j(modsave_path, eui_cu_file_name + "*"))[0]
+        orig_eui_file = g(j(eui_path, "*", eui_cu_file_name))[0]
+        shutil.move(orig_eui_file, orig_eui_file + ".orig")
+        shutil.copyfile(eui_cu_file, orig_eui_file)
+    subprocess.run([szip, 'a', modded_eui_zip_name, eui_path], stdout=null, stderr=null)
+else:
+    #Unzip EUI
+    print("Unzipping EUI...")
+    subprocess.run([szip, 'x', modded_eui_zip_path], stdout=null, stderr=null)
+
+
+#Unzip modpack zip
+print("Unzipping Modpack...")
+subprocess.run([szip, 'x', j(vanilla_packs_path, modpack_zip)], stdout=null, stderr=null)
+
+
+#Manage mod files
+for mod_file in g(mod_files, recursive = True):
+    mod_file_path = mod_file.split(os.sep)
+    mod_file_name = mod_file_path[len(mod_file_path) - 1]
+
+    #IGE UI compat file
+    if mod_file_name == ige_compat_file_name:
+        print("Providing IGE-EUI-compat...")
+        shutil.move(mod_file, mod_file + ".orig")
+        shutil.copyfile(g(j(modsave_path, ige_compat_file_name + "*"))[0], mod_file)
+
+    #Delete UI overwrite duplicates
+    if mod_file_name in delete_file_names:
+        print("Removing overwriting file " + mod_file_name + "...")
+        os.remove(mod_file)
+
+    #Find out if modcompat unit panel needed
+    if mod_file_name in unit_panel_modcompat_file_names:
+        print("UnitPanel modcompat need detected...")
+        unit_panel_modcompat_needed = True
+
+#Delete useless desktop.ini (Thanks True...)
+ini_files = re.sub(r"\.\w+$", ".ini", mod_files)
+for ini_file in g(ini_files, recursive = True):
+    ini_file_path = ini_file.split(os.sep)
+    ini_file_name = ini_file_path[len(ini_file_path) - 1]
+    if ini_file_name == "desktop.ini":
+        print("Removing useless desktop.ini (Thanks True)...")
+        os.remove(mod_file)
+
+
+#Get stuff from UI files
+for ui_file in g(ui_files):
+    with open(ui_file, 'r') as file:
+        lines = file.readlines()
+        
+    ui_file_path = ui_file.split(os.sep)
+    ui_file_name = ui_file_path[len(ui_file_path) - 1]
+
+    print("Getting tags from " + ui_file_name + "...")
+
+    load_tags[ui_file_name] = []
+
+    for line in lines:
+        if line.startswith(load_tag):
+            load_tags[ui_file_name].append(line)
+
+#Insert stuff into EUI files
+for eui_file in g(eui_files):
+    eui_file_path = eui_file.split(os.sep)
+    eui_file_name = eui_file_path[len(eui_file_path) - 1]
+
+    #Base UI files
+    if eui_file_name in load_tags.keys():
+        print("Writing tags to " + eui_file_name + "...")
+        with open(eui_file, 'a') as file:
+            file.write('\n')
+            for load_tag in load_tags[eui_file_name]:
+                file.write(load_tag)
+    #Modcompat unit panel
+    elif eui_file_name == unit_panel_file_name and unit_panel_modcompat_needed:
+        print("Providing EUI-UnitPanel-Modcompat...")
+        shutil.move(eui_file, eui_file + ".orig")
+        shutil.copyfile(g(j(modsave_path, unit_panel_file_name + "*"))[0], eui_file)
+
+
+#Move EUI folder
+print("Moving EUI folder...")
+shutil.move(eui_path, modpack_path)
+print("Removing UI folder...")
+shutil.rmtree(ui_path)
+
+#Zip modpack folder
+print("Zipping Modpack...")
+subprocess.run([szip, 'a', modpack_name + "_EUI.7z", modpack_path], stdout=null, stderr=null)
+
+##Move modpack folder
+#print("Moving Modspack folder")
+#shutil.move(modpack_path, j(base_path, modpack_folder_name))
+
+null.close()
+print("Done.\n")
\ No newline at end of file
diff --git a/EUI_Converter/EUI_Converter.pyproj b/EUI_Converter/EUI_Converter.pyproj
new file mode 100644
index 0000000000000000000000000000000000000000..eb302b261ac87743cb9969a21ffc0132c4aee410
--- /dev/null
+++ b/EUI_Converter/EUI_Converter.pyproj
@@ -0,0 +1,35 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>ea28d07f-cd43-45d1-b01d-1eeeb360d215</ProjectGuid>
+    <ProjectHome>.</ProjectHome>
+    <StartupFile>EUI_Converter.py</StartupFile>
+    <SearchPath>
+    </SearchPath>
+    <WorkingDirectory>.</WorkingDirectory>
+    <OutputPath>.</OutputPath>
+    <Name>EUI_Converter</Name>
+    <RootNamespace>EUI_Converter</RootNamespace>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <DebugSymbols>true</DebugSymbols>
+    <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+    <DebugSymbols>true</DebugSymbols>
+    <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="EUI_Converter.py" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
+  <!-- Uncomment the CoreCompile target to enable the Build command in
+       Visual Studio and specify your pre- and post-build commands in
+       the BeforeBuild and AfterBuild targets below. -->
+  <!--<Target Name="CoreCompile" />-->
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+</Project>
\ No newline at end of file
diff --git a/EUI_Converter/EUI_Converter.sln b/EUI_Converter/EUI_Converter.sln
new file mode 100644
index 0000000000000000000000000000000000000000..607c2680c4ef84eaf5c63155899a80d9184503cd
--- /dev/null
+++ b/EUI_Converter/EUI_Converter.sln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31019.35
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "EUI_Converter", "EUI_Converter.pyproj", "{EA28D07F-CD43-45D1-B01D-1EEEB360D215}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{081253F6-C547-4C81-B928-0D7472199986}"
+	ProjectSection(SolutionItems) = preProject
+		..\.gitattributes = ..\.gitattributes
+		..\.gitignore = ..\.gitignore
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{EA28D07F-CD43-45D1-B01D-1EEEB360D215}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EA28D07F-CD43-45D1-B01D-1EEEB360D215}.Release|Any CPU.ActiveCfg = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {D4E8032E-FBFB-40D5-8FEC-B7EF67F27664}
+	EndGlobalSection
+EndGlobal
diff --git a/Highlights.xml.cuc b/Highlights.xml.cuc
new file mode 100644
index 0000000000000000000000000000000000000000..efcb992d049ee7a2e6d31263cdc9f287b86f61a2
--- /dev/null
+++ b/Highlights.xml.cuc
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Highlights>
+	<style name="" type="HexContour" width=".2" texture="hex_contour2.dds"/>
+	<style name="SampleFilledHex" type="FilledHex" width="1" color="0,0,0,128"/>
+	<style name="SampleStaticTexture" type="StaticTexture" texture="static_texture_highlight.dds"/>
+	<style name="SampleHexModel" type="HexModel" model="SelectedAniOneTurn.fxsxml"/>
+	<style name="EditorHexStyle1" type="HexContour" width=".15" texture="Solid_Contour.dds"/>
+	<style name="EditorHexStyle2" type="HexContour" width=".08" texture="Solid_Contour.dds"/>
+	<style name="EditorHexStyle3" type="HexContour" width=".03" texture="Solid_Contour.dds"/>
+	<style name="TempBorder" type="HexContour" width=".2" texture="hex_contour2.dds"/>
+	<style name="GroupBorder" type="SplineBorder" width ="6" texture="spline_border_contour2.dds" color="255,128,0,200"/>
+	<style name="ValidFireTargetBorder" type="StaticTexture" texture="static_texture_highlight.dds"/>
+	<style name="FireRangeBorder" type="SplineBorder" width="6" texture="spline_border_contour2.dds" color="255,0,0,200"/>
+	<style name="MovementRangeBorder" type="SplineBorder" width="4" texture="spline_border_contour2.dds" color="100,185,245,240"/>
+	<style name="AMRBorder" type="StaticTexture" texture="static_texture_highlight.dds"/>
+	<style name="TradeRoute" type="HexContour" width=".2" texture="hex_contour2.dds"/>
+	<style name="GUHB" type="HexContour" width=".2" texture="hex_contour2.dds"/>
+
+	<!-- EUI styles -->
+
+	<style name="HexContour" type="HexContour" width=".2" texture="hex_contour2.dds" />
+
+	<style name="CityOverlap" type="FilledHex" width ="1" color="255,140,0,64" />
+
+	<style name="OverlapFill" type="FilledHex" width ="1" color="0,0,255,50" />
+	<style name="OverlapOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="0,0,255,164" />
+
+	<style name="WorkedFill" type="FilledHex" width ="1" color="0,255,0,50" />
+	<style name="WorkedOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="0,255,0,164" />
+	
+	<style name="UnlockedFill" type="FilledHex" width ="1" color="7,107,0,50" />
+	<style name="UnlockedOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="7,107,0,164" />
+
+	<style name="OwnedFill" type="FilledHex" width ="1" color="0,140,255,50" />
+	<style name="OwnedOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="0,140,255,164" />
+
+	<style name="BuyFill" type="FilledHex" width ="1" color="255,215,0,50" />
+	<style name="BuyOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="255,215,0,192" />
+
+	<style name="VacantFill" type="FilledHex" width ="1" color="0,140,255,50" />
+	<style name="VacantOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="0,140,255,164" />
+
+	<style name="EnemyFill" type="FilledHex" width ="1" color="255,0,0,64" />
+	<style name="EnemyOutline" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="255,0,0,200" />
+
+	<style name="CityLimits" type="SplineBorder" width ="7" texture="hex_contour1.dds" color="0,0,0,164" />
+
+</Highlights>
diff --git a/IGE_Window.lua.euicompat b/IGE_Window.lua.euicompat
new file mode 100644
index 0000000000000000000000000000000000000000..b910f9a89e74d524cf4f55b48a71b2eaceea60c5
--- /dev/null
+++ b/IGE_Window.lua.euicompat
@@ -0,0 +1,682 @@
+-- Released under GPL v3
+--------------------------------------------------------------
+include("IGE_API_All");
+print("IGE_Window");
+
+IGE = nil;
+local initialized = false;
+local currentPlot = nil;
+local oldCurrentPlot = nil;
+local mouseHandlers = {};
+local mouseMode = 0;
+--local busy = false;
+
+
+--===============================================================================================
+-- INIT-SHOW-HIDE
+--===============================================================================================
+local function OnSharingGlobalAndOptions(_IGE)
+	IGE = _IGE;
+	OnUpdatedOptions(_IGE);
+end
+LuaEvents.IGE_SharingGlobalAndOptions.Add(OnSharingGlobalAndOptions);
+
+-------------------------------------------------------------------------------------------------
+function OnInitialize()
+	Resize(Controls.MainGrid);
+	local sizeX, sizeY = UIManager:GetScreenSizeVal();
+	local offset = (sizeX - 320) - Controls.MainGrid:GetSizeX();
+	Controls.MainGrid:SetOffsetX(offset > 0 and offset * 0.5 or -10);
+
+	if sizeY < 1000 then
+		Controls.PanelsContainer:SetOffsetY(47);
+		LowerSizeY(Controls.MainGrid, 21);
+	end
+
+	if not UI.CompareFileTime then 
+		print("Pirate version, autosave disabled.");
+		Controls.ReloadButton:SetHide(true);
+		Controls.SaveButton:SetHide(true);
+		Controls.AutoSave:SetHide(true);
+	end
+end
+LuaEvents.IGE_Initialize.Add(OnInitialize);
+
+-------------------------------------------------------------------------------------------------
+local function IsVisible()
+	return not Controls.Container:IsHidden();
+end
+
+-------------------------------------------------------------------------------------------------
+local function SetBusy(flag, loading)
+	busy = flag;
+	UI.SetBusy(flag);--[[
+	Controls.MainButton:SetDisabled(busy);
+	UIManager:SetUICursor(busy and 1 or 0);]]
+end
+
+-------------------------------------------------------------------------------------------------
+local function ClosePopups()
+	LookUpControl("InGame/WorldView/InfoCorner/TechPanel"):SetHide(true);
+	LookUpControl("InGame/TechAwardPopup"):SetHide(true);
+	LookUpControl("InGame/ProductionPopup"):SetHide(true);
+	Controls.ChooseReligionPopup:SetHide(true);
+	Controls.ChoosePantheonPopup:SetHide(true);
+	Controls.ProductionPopup:SetHide(true);
+	Controls.OptionsPanel:SetHide(true);
+	Controls.WonderPopup:SetHide(true);
+end
+
+-------------------------------------------------------------------------------------------------
+local function OpenCore()
+	if not initialized then
+		LuaEvents.IGE_Initialize();
+		initialized = true;
+		print("Initialization completed");
+	end
+
+	IGE.revealMap = false;
+	OnUpdateUI()
+	LuaEvents.IGE_Showing();
+	UpdateMouse();
+
+	ClosePopups();
+	UI.SetInterfaceMode(InterfaceModeTypes.INTERFACEMODE_SELECTION);
+	print("OpenCore - step1");
+    Events.SystemUpdateUI.CallImmediate(SystemUpdateUIType.BulkHideUI);
+	Events.SerialEventMouseOverHex.Add(OnMouseMoveOverHex);
+	print("OpenCore - step2");
+	Events.CameraViewChanged.Add(UpdateMouse);
+	Controls.Container:SetHide(false);
+	print("OpenCore - done");
+end
+
+-------------------------------------------------------------------------------------------------
+local reportedError = false;
+local function OnInitializationError(err)
+	if reportedError then return end
+	reportedError = true;
+
+	print("Failed to open IGE:");
+	err = FormatError(err, 1);
+
+	-- Show popup to user
+	local str = L("TXT_KEY_IGE_LOADING_ERROR").."[NEWLINE][ICON_PIRATE] "..err;
+	Events.SerialEventGameMessagePopup( { Type = ButtonPopupTypes.BUTTONPOPUP_TEXT, Data1 = 800, Option1 = true, Text = str } );
+
+	-- Restore things up
+	Events.SystemUpdateUI.CallImmediate(SystemUpdateUIType.BulkShowUI);
+	Events.ClearHexHighlights();
+	LuaEvents.IGE_Show_Fail();
+	return false
+end
+
+-------------------------------------------------------------------------------------------------
+local function Open()
+	if not IsVisible() and not busy then
+		SetBusy(true);
+
+		-- More than one version installed?
+		local pingData = { count = 0 };
+		LuaEvents.IGE_PingAllVersions(pingData);
+		if (pingData.count > 1) then
+			local str = L("TXT_KEY_IGE_MORE_THAN_ONE_VERSION_ERROR");
+			Events.SerialEventGameMessagePopup( { Type = ButtonPopupTypes.BUTTONPOPUP_TEXT, Data1 = 800, Option1 = true, Text = str } );
+			SetBusy(false);
+			return;
+		end
+
+		-- Try to init data
+		reportedError = false;
+		local status, err = xpcall(OpenCore, OnInitializationError);
+		if not status then 
+			SetBusy(false);
+			return;
+		end
+
+		-- Autosave
+		print("SaveFile - begin");
+		if IGE.autoSave then
+			SaveFile(IGE.cleanUpFiles);
+		end
+		print("SaveFile - done");
+
+		-- Restore current plot
+		if oldCurrentPlot then
+			SetCurrentPlot(oldCurrentPlot);
+		end
+
+		SetBusy(false);
+		print("SetBusy - done");
+		LuaEvents.IGE_Update();
+	end
+end
+
+-------------------------------------------------------------------------------------------------
+local function Close(keepBulkUIHidden, takingSeat)
+	if IsVisible() and not busy then
+		SetBusy(true);
+		if not takingSeat then 
+			if Game.GetActivePlayer() ~= IGE.initialPlayerID then
+				Game.SetActivePlayer(IGE.initialPlayerID);
+			end
+		end
+
+		leftButtonDown = false;
+		rightButtonDown = false;
+		oldCurrentPlot = currentPlot;
+		SetCurrentPlot(nil);
+		LuaEvents.IGE_Closing(takingSeat);
+
+		if not keepBulkUIHidden then
+			Events.SystemUpdateUI.CallImmediate(SystemUpdateUIType.BulkShowUI);
+		end
+		ClosePopups();
+
+		Events.SerialEventMouseOverHex.Remove(OnMouseMoveOverHex);
+		Events.CameraViewChanged.Remove(UpdateMouse);
+
+		Controls.Container:SetHide(true);
+		UI.SetInterfaceMode(InterfaceModeTypes.INTERFACEMODE_SELECTION);
+		SetMouseMode(IGE_MODE_NONE);
+		Map.RecalculateAreas();
+
+		SetBusy(false);
+	end
+end
+Controls.CloseButton:RegisterCallback(Mouse.eLClick, function() Close(false, false) end);
+
+-------------------------------------------------------------------------------------------------
+local function OnForceQuit(takingSeat)
+	Close(false, takingSeat);
+end
+LuaEvents.IGE_ForceQuit.Add(OnForceQuit);
+
+-------------------------------------------------------------------------------------------------
+local function CloseAndKeepUIHidden()
+	Close(true, false);
+end
+Events.SearchForPediaEntry.Add(CloseAndKeepUIHidden);
+Events.GoToPediaHomePage.Add(CloseAndKeepUIHidden);
+
+
+--===============================================================================================
+-- MOUSE HOVER
+--===============================================================================================
+local selectedNewPlot = false;
+local highlightedPlots = {};
+function SetCurrentPlot(plot)
+	selectedNewPlot = (plot ~= currentPlot);
+	currentPlot = plot;
+
+	LuaEvents.IGE_SelectedPlot(plot);
+	LuaEvents.IGE_Update();
+end
+
+-------------------------------------------------------------------------------------------------
+local function HighlightPlot(plot, color)
+	if plot then
+		Events.SerialEventHexHighlight(ToHexFromGrid(Vector2(plot:GetX(), plot:GetY())), true, color);
+	end
+end
+
+-------------------------------------------------------------------------------------------------
+function UpdateMouse(mouseOver, gridX, gridY)
+	if gridX == nil then
+		gridX, gridY = UI.GetMouseOverHex();
+	end
+	if mouseOver == nil then
+		mouseOver = Controls.Background:HasMouseOver();
+	end
+
+	local shift = UIManager:GetShift();
+	local plot = mouseOver and Map.GetPlot(gridX, gridY) or nil;
+	Events.ClearHexHighlights();
+
+	if mouseMode == IGE_MODE_PAINT then
+		if plot then
+			local color = rightButtonDown and Vector4(1.0, 0.0, 0.0, 1) or Vector4(0, 1.0, 0, 1);
+			HighlightPlot(plot, color);
+
+			if shift then
+				for neighbor in Neighbors(plot) do
+					HighlightPlot(neighbor, color);
+				end
+			end
+
+			if rightButtonDown then
+				LuaEvents.IGE_PaintPlot(2, plot, shift);
+			end
+		end
+
+	elseif mouseMode == IGE_MODE_PLOP then
+		if plot then
+			local color = rightButtonDown and Vector4(1, 0, 0, 1) or Vector4(0, 1, 0, 1);
+			HighlightPlot(plot, color);
+		end
+
+	elseif mouseMode == IGE_MODE_EDIT_AND_PLOP then
+		if currentPlot then
+			local rightClickCurrentPlot = (rightButtonDown and plot == currentPlot);
+			local color = rightClickCurrentPlot and Vector4(0, 1, 0, 1) or Vector4(1, 0, 0, 1);
+			HighlightPlot(currentPlot, color);
+		end
+
+	elseif mouseMode == IGE_MODE_EDIT then
+		if currentPlot then
+			HighlightPlot(currentPlot, Vector4(1, 0, 0, 1));
+		end
+	end
+
+	-- Plots that have been undone
+	for i, v in ipairs(highlightedPlots) do
+		HighlightPlot(v, Vector4(0, 0, 1, 1));
+	end
+
+	LuaEvents.IGE_BroadcastingMouseState(mouseOver, gridX, gridY, plot, shift);
+end
+
+-------------------------------------------------------------------------------------------------
+function OnMouseMoveOverHex(hexX, hexY)
+	UpdateMouse(nil, hexX, hexY);
+end
+
+-------------------------------------------------------------------------------------------------
+function OnBackgroundMouseEnter()
+	if IsVisible() then
+		leftButtonDown = false;
+		rightButtonDown = false;
+		UpdateCursor(true);
+		UpdateMouse(true);
+	end
+end
+Controls.Background:RegisterCallback(Mouse.eMouseEnter, OnBackgroundMouseEnter);
+
+-------------------------------------------------------------------------------------------------
+function OnBackgroundMouseExit()
+	if IsVisible() then
+		leftButtonDown = false;
+		rightButtonDown = false;
+		UpdateCursor(false);
+		UpdateMouse(false);
+	end
+end
+Controls.Background:RegisterCallback(Mouse.eMouseExit, OnBackgroundMouseExit);
+
+-------------------------------------------------------------------------------------------------
+function UpdateCursor(hasMouseOver)
+	local cursor = 0;
+	if hasMouseOver then
+		if mouseMode > IGE_MODE_EDIT_AND_PLOP then
+			cursor = 8;
+		elseif mouseMode > 0 then
+			cursor = 3;
+		end
+	end
+	UIManager:SetUICursor(cursor);
+end
+
+-------------------------------------------------------------------------------------------------
+function SetMouseMode(mode)
+	if mode == mouseMode then return end
+	leftButtonDown = false;
+	rightButtonDown = false;
+	mouseMode = mode;
+	UpdateCursor();
+	UpdateMouse();
+end
+LuaEvents.IGE_SetMouseMode.Add(SetMouseMode);
+
+-------------------------------------------------------------------------------------------------
+function OnFlashPlot(plot)
+	table.insert(highlightedPlots, plot);
+	UpdateMouse();
+
+	LuaEvents.IGE_Schedule(nil, 1.0, function()
+		table.remove(highlightedPlots, 1);
+		UpdateMouse();
+	end);
+end
+LuaEvents.IGE_FlashPlot.Add(OnFlashPlot);
+
+
+--===============================================================================================
+-- INPUTS
+--===============================================================================================
+mouseHandlers[IGE_MODE_NONE] = function(uiMsg)
+end
+
+-------------------------------------------------------------------------------------------------
+mouseHandlers[IGE_MODE_EDIT] = function(uiMsg)
+	if uiMsg == MouseEvents.RButtonDown then
+		SetCurrentPlot(nil);
+		UpdateMouse();
+	elseif uiMsg == MouseEvents.LButtonDown then
+		SetCurrentPlot(Map.GetPlot(UI.GetMouseOverHex()));
+		UpdateMouse();
+	end
+end
+
+-------------------------------------------------------------------------------------------------
+mouseHandlers[IGE_MODE_EDIT_AND_PLOP] = function(uiMsg)
+	if uiMsg == MouseEvents.RButtonDown then
+		SetCurrentPlot(nil);
+		UpdateMouse();
+	elseif uiMsg == MouseEvents.LButtonDown then
+		SetCurrentPlot(Map.GetPlot(UI.GetMouseOverHex()));
+		UpdateMouse();
+
+		if UIManager:GetShift() then
+			LuaEvents.IGE_Plop(1, Map.GetPlot(UI.GetMouseOverHex()), true);
+		end
+	end
+end
+
+-------------------------------------------------------------------------------------------------
+mouseHandlers[IGE_MODE_PAINT] = function(uiMsg)
+	if uiMsg == MouseEvents.RButtonUp then
+		UpdateMouse();
+	elseif uiMsg == MouseEvents.RButtonDown then
+		LuaEvents.IGE_BeginPaint();
+		UpdateMouse();
+	end
+end
+
+-------------------------------------------------------------------------------------------------
+mouseHandlers[IGE_MODE_PLOP] = function(uiMsg)
+	if uiMsg == MouseEvents.RButtonUp then
+		UpdateMouse();
+	elseif uiMsg == MouseEvents.RButtonDown then
+		UpdateMouse();
+		LuaEvents.IGE_Plop(2, Map.GetPlot(UI.GetMouseOverHex()), UIManager:GetShift());
+	end
+end
+
+-------------------------------------------------------------------------------------------------
+function InputHandler(uiMsg, wParam, lParam)
+	if IsVisible() then
+		if uiMsg == MouseEvents.LButtonDown then
+			selectedNewPlot = false;
+			leftButtonDown = true;
+		elseif uiMsg == MouseEvents.LButtonUp then
+			leftButtonDown = false;
+		elseif uiMsg == MouseEvents.RButtonDown then
+			rightButtonDown = true;
+		elseif uiMsg == MouseEvents.RButtonUp then
+			rightButtonDown = false;
+		end
+
+		if Controls.Background:HasMouseOver() then
+			local func = mouseHandlers[mouseMode];
+			func(uiMsg);
+		end
+		
+		if uiMsg == MouseEvents.LButtonDown then
+			return false;
+		elseif uiMsg == MouseEvents.LButtonUp then
+			return selectedNewPlot;
+		elseif uiMsg == MouseEvents.RButtonDown then
+			return true;
+		elseif uiMsg == MouseEvents.RButtonUp then
+			return true;
+		elseif uiMsg == KeyEvents.KeyUp then
+			UpdateMouse();
+			if wParam == Keys.Z and UIManager:GetControl() then
+				return true;
+			end
+
+		-- Shortcuts
+		elseif uiMsg == KeyEvents.KeyDown then
+			if ProcessShortcuts(wParam) then
+				UpdateMouse();
+				return true;
+			else
+				UpdateMouse();
+			end
+		end
+	end
+
+	-- Open IGE
+	if uiMsg == KeyEvents.KeyDown and wParam == Keys.I and UIManager:GetControl() then
+		Toggle();
+		return true;
+	end
+
+	return false;
+end
+ContextPtr:SetInputHandler(InputHandler);
+
+-------------------------------------------------------------------------------------------------
+function ProcessShortcuts(key)
+	-- Escape = quit
+	if key == Keys.VK_ESCAPE then
+		Close(false, false);
+		return true;
+	end
+
+	-- Ctrl + Z = undo
+	if key == Keys.Z and UIManager:GetControl() then
+		rightButtonDown = false;
+		leftButtonDown = false;
+		LuaEvents.IGE_Undo();
+		return true;
+	end
+	-- Ctrl + Y = redo
+	if key == Keys.Y and UIManager:GetControl() then
+		rightButtonDown = false;
+		leftButtonDown = false;
+		LuaEvents.IGE_Redo();
+		return true;
+	end
+
+	-- F5 = quicksave
+	-- Ctrl + F5 = save and reload
+	if key == Keys.VK_F5 then
+		if UIManager:GetControl() then
+			OnReloadButtonClick();
+		else
+			OnSaveButtonClick();
+		end
+		return true;
+	end
+
+	-- F1-F4 and F6-F8 : panels
+	if key == Keys.VK_F1 then
+		LuaEvents.IGE_SetTab("TERRAIN_EDITION");
+		return true;
+	end
+	if key == Keys.VK_F2 then
+		LuaEvents.IGE_SetTab("CITIES_AND_UNITS");
+		return true;
+	end
+	if key == Keys.VK_F3 then
+		LuaEvents.IGE_SetTab("TERRAIN_PAINTING");
+		return true;
+	end
+	if key == Keys.VK_F4 then
+		LuaEvents.IGE_SetTab("UNITS");
+		return true;
+	end
+	if key == Keys.VK_F6 then
+		LuaEvents.IGE_SetTab("PLAYERS");
+		return true;
+	end
+	if key == Keys.VK_F7 then
+		LuaEvents.IGE_SetTab("TECHS");
+		return true;
+	end
+	if key == Keys.VK_F8 then
+		LuaEvents.IGE_SetTab("POLICIES");
+		return true;
+	end
+end
+
+
+--===============================================================================================
+-- OPTIONS AND CONTROLS
+--===============================================================================================
+function OnUpdatedOptions(IGE)
+	Controls.AutoSave:SetCheck(IGE.autoSave);
+	Controls.ShowResources:SetCheck(IGE.showResources);
+	Controls.ShowUnknownResources:SetCheck(IGE.showUnknownResources);
+	Controls.DisableStrategicView:SetCheck(IGE.disableStrategicView);
+	Controls.CleanUpFiles:SetCheck(IGE.cleanUpFiles);
+	Controls.ShowYields:SetCheck(IGE.showYields);
+	Controls.SafeMode:SetCheck(IGE.safeMode);
+end
+LuaEvents.IGE_UpdatedOptions.Add(OnUpdatedOptions);
+
+-------------------------------------------------------------------------------------------------
+function OnOptionControlChanged()
+	local options = {};
+	options.autoSave = Controls.AutoSave:IsChecked();
+	options.cleanUpFiles = Controls.CleanUpFiles:IsChecked();
+	options.disableStrategicView = Controls.DisableStrategicView:IsChecked();
+	options.showUnknownResources = Controls.ShowUnknownResources:IsChecked();
+	options.showResources = Controls.ShowResources:IsChecked();
+	options.showYields = Controls.ShowYields:IsChecked();
+	options.safeMode = Controls.SafeMode:IsChecked();
+	LuaEvents.IGE_UpdateOptions(options);
+end
+Controls.CleanUpFiles:RegisterCheckHandler(OnOptionControlChanged);
+Controls.DisableStrategicView:RegisterCheckHandler(OnOptionControlChanged);
+Controls.ShowUnknownResources:RegisterCheckHandler(OnOptionControlChanged);
+Controls.ShowResources:RegisterCheckHandler(OnOptionControlChanged);
+Controls.ShowYields:RegisterCheckHandler(OnOptionControlChanged);
+Controls.SafeMode:RegisterCheckHandler(OnOptionControlChanged);
+Controls.AutoSave:RegisterCheckHandler(OnOptionControlChanged);
+
+-------------------------------------------------------------------------------------------------
+function OnSaveButtonClick()
+	SaveFile(IGE.cleanUpFiles);
+end
+Controls.SaveButton:RegisterCallback(Mouse.eLClick, OnSaveButtonClick);
+
+-------------------------------------------------------------------------------------------------
+function OnReloadButtonClick()
+	LuaEvents.IGE_ConfirmPopup(L("TXT_KEY_IGE_CONFIRM_SAVE_AND_RELOAD"), function()
+		local fileName = "IGE - reload";
+		if IGE.cleanUpFiles then
+			DeleteFiles(fileName);
+		end
+		UI.SaveGame(fileName, true);
+		LoadFile(fileName);
+	end);
+end
+Controls.ReloadButton:RegisterCallback(Mouse.eLClick, OnReloadButtonClick);
+
+-------------------------------------------------------------------------------------------------
+local function OnRevealMapButtonClick()
+	IGE.revealMap = true;
+	LuaEvents.IGE_ToggleRevealMap(IGE.revealMap);
+end
+Controls.RevealMapButton:RegisterCallback(Mouse.eLClick, OnRevealMapButtonClick);
+
+-------------------------------------------------------------------------------------------------
+local function OnCoverMapButtonClick()
+	IGE.revealMap = false;
+	LuaEvents.IGE_ToggleRevealMap(IGE.revealMap);
+end
+Controls.CoverMapButton:RegisterCallback(Mouse.eLClick, OnCoverMapButtonClick);
+
+-------------------------------------------------------------------------------------------------
+function OnUpdateUI()
+	local notHuman = not Players[Game.GetActivePlayer()]:IsHuman()
+	Controls.CoverMapButton:SetHide(notHuman or not IGE.revealMap);
+	Controls.RevealMapButton:SetHide(notHuman or IGE.revealMap);
+	Controls.IGECameraButton:SetHide(notHuman)
+end
+LuaEvents.IGE_Update.Add(OnUpdateUI)
+LuaEvents.IGE_ToggleRevealMap.Add(OnUpdateUI);
+
+-------------------------------------------------------------------------------------------------
+local function OnIGECameraHome()
+	local plot = IGE.currentPlayer:GetStartingPlot()
+	if plot then 
+		print("Go home")
+		UI.LookAt(plot) 
+	end
+end
+Controls.IGECameraButton:RegisterCallback(Mouse.eLClick, OnIGECameraHome);
+
+-------------------------------------------------------------------------------------------------
+local function ToggleOptions() 
+	local hidden = Controls.OptionsPanel:IsHidden();
+	Controls.OptionsPanel:SetHide(not hidden);
+end 
+Controls.MainButton:RegisterCallback(Mouse.eRClick, ToggleOptions);
+
+-------------------------------------------------------------------------------------------------
+function Toggle() 
+	if IsVisible() then
+		Close();
+	else
+		Open();
+	end
+end 
+Controls.MainButton:RegisterCallback(Mouse.eLClick, Toggle);
+
+-------------------------------------------------------------------------------------------------
+local TOP    = 0;
+local BOTTOM = 1;
+local LEFT   = 2;
+local RIGHT  = 3;
+
+local function ScrollMouseEnter(which)
+    if which == TOP then
+		Events.SerialEventCameraStartMovingForward();
+    elseif which == BOTTOM then
+		Events.SerialEventCameraStartMovingBack();
+    elseif which == LEFT then
+		Events.SerialEventCameraStartMovingLeft();
+    else
+		Events.SerialEventCameraStartMovingRight();
+    end
+end
+
+local function ScrollMouseExit(which)
+    if which == TOP then
+		Events.SerialEventCameraStopMovingForward();
+    elseif which == BOTTOM then
+		Events.SerialEventCameraStopMovingBack();
+    elseif which == LEFT then
+		Events.SerialEventCameraStopMovingLeft();
+    else
+		Events.SerialEventCameraStopMovingRight();
+    end
+end
+Controls.ScrollTop:RegisterCallback( Mouse.eMouseEnter, ScrollMouseEnter);
+Controls.ScrollTop:RegisterCallback( Mouse.eMouseExit, ScrollMouseExit);
+Controls.ScrollTop:SetVoid1(TOP);
+
+Controls.ScrollBottom:RegisterCallback( Mouse.eMouseEnter, ScrollMouseEnter);
+Controls.ScrollBottom:RegisterCallback( Mouse.eMouseExit, ScrollMouseExit);
+Controls.ScrollBottom:SetVoid1(BOTTOM);
+
+Controls.ScrollLeft:RegisterCallback( Mouse.eMouseEnter, ScrollMouseEnter);
+Controls.ScrollLeft:RegisterCallback( Mouse.eMouseExit, ScrollMouseExit);
+Controls.ScrollLeft:SetVoid1(LEFT);
+
+Controls.ScrollRight:RegisterCallback( Mouse.eMouseEnter, ScrollMouseEnter);
+Controls.ScrollRight:RegisterCallback( Mouse.eMouseExit, ScrollMouseExit);
+Controls.ScrollRight:SetVoid1(RIGHT);
+
+LuaEvents.IGE_ShareGlobalAndOptions();
+print("IGE loaded");
+
+-----------------------------------------------------------------------------------------
+-- Changes by bc1 to hook up IGE's Main Button to the rigth of the Top Panel's left stack
+-----------------------------------------------------------------------------------------
+
+ContextPtr:SetUpdate( function()
+	ContextPtr:ClearUpdate();
+	local topPanelInfoStack = LookUpControl( "/InGame/TopPanel/TopPanelInfoStack" );
+	if topPanelInfoStack then
+		Controls.MainButton:ChangeParent( topPanelInfoStack );
+		Controls.MainButton:SetOffsetVal( 0, 0 );
+		topPanelInfoStack:ReprocessAnchoring();
+		print( "IGE main button moved successfully." );
+	else
+		print( "IGE main button move attempt failed." );
+	end
+end);
\ No newline at end of file
diff --git a/UnitPanel.lua.eui.modcompat b/UnitPanel.lua.eui.modcompat
new file mode 100644
index 0000000000000000000000000000000000000000..cbff06968cb61bbdce3a6dad72a628e1d74d3ef4
--- /dev/null
+++ b/UnitPanel.lua.eui.modcompat
@@ -0,0 +1,1470 @@
+--==========================================================
+-- Unit Panel
+-- Re-written by bc1 using Notepad++
+-- code is common using gk_mode and bnw_mode switches
+--==========================================================
+
+Events.SequenceGameInitComplete.Add(function()
+
+include "UserInterfaceSettings"
+local UserInterfaceSettings = UserInterfaceSettings
+
+include "GameInfoCache" -- warning! booleans are true, not 1, and use iterator ONLY with table field conditions, NOT string SQL query
+local GameInfo = GameInfoCache
+
+include "IconHookup"
+local IconHookup = IconHookup
+local Color = Color
+local PrimaryColors = PrimaryColors
+local BackgroundColors = BackgroundColors
+
+include "GetUnitBuildProgressData"
+local GetUnitBuildProgressData = GetUnitBuildProgressData
+
+--==========================================================
+-- Minor lua optimizations
+--==========================================================
+
+local gk_mode = Game.GetReligionName ~= nil
+local bnw_mode = Game.GetActiveLeague ~= nil
+--local debug_print = print
+
+local ceil = math.ceil
+local floor = math.floor
+local max = math.max
+local pairs = pairs
+--local print = print
+local format = string.format
+local concat = table.concat
+local insert = table.insert
+local remove = table.remove
+local tostring = tostring
+
+local ActionSubTypes = ActionSubTypes
+local ActivityTypes = ActivityTypes
+local ContextPtr = ContextPtr
+local Controls = Controls
+local DomainTypes = DomainTypes
+local Events = Events
+local Game = Game
+local GameDefines = GameDefines
+local GameInfoActions = GameInfoActions
+local GameInfo_Automates = GameInfo.Automates
+local GameInfo_Builds = GameInfo.Builds
+local GameInfo_Missions = GameInfo.Missions
+local GameInfo_Units = GameInfo.Units
+local L = Locale.ConvertTextKey
+local ToUpper = Locale.ToUpper
+--local PlotDistance = Map.PlotDistance
+local Mouse = Mouse
+local OrderTypes = OrderTypes
+local Players = Players
+local Teams = Teams
+local ToHexFromGrid = ToHexFromGrid
+local DoSelectCityAtPlot = UI.DoSelectCityAtPlot
+local GetHeadSelectedCity = UI.GetHeadSelectedCity
+local GetHeadSelectedUnit = UI.GetHeadSelectedUnit
+--local GetSelectedUnitID = UI.GetSelectedUnitID
+local GetUnitFlagIcon = UI.GetUnitFlagIcon
+local GetUnitPortraitIcon = UI.GetUnitPortraitIcon
+local LookAt = UI.LookAt
+local SelectUnit = UI.SelectUnit
+
+include "InstanceManager"
+local actionIM = InstanceManager:new("UnitAction", "UnitActionButton", Controls.ActionStack)
+
+--==========================================================
+-- Globals
+--==========================================================
+
+local g_activePlayerID, g_activePlayer, g_activeTeamID, g_activeTeam, g_activeTechs
+local g_AllPlayerOptions = { UnitTypes = {}, UnitsInRibbon = {} }
+
+local g_ActivePlayerUnitTypes
+local g_ActivePlayerUnitsInRibbon = {}
+local g_isHideCityList, g_isHideUnitList, g_isHideUnitTypes
+
+--[[ 
+ _   _       _ _          ___      ____ _ _   _             ____  _ _     _                 
+| | | |_ __ (_) |_ ___   ( _ )    / ___(_) |_(_) ___  ___  |  _ \(_) |__ | |__   ___  _ __  
+| | | | '_ \| | __/ __|  / _ \/\ | |   | | __| |/ _ \/ __| | |_) | | '_ \| '_ \ / _ \| '_ \ 
+| |_| | | | | | |_\__ \ | (_>  < | |___| | |_| |  __/\__ \ |  _ <| | |_) | |_) | (_) | | | |
+ \___/|_| |_|_|\__|___/  \___/\/  \____|_|\__|_|\___||___/ |_| \_\_|_.__/|_.__/ \___/|_| |_|
+]]
+
+-- NO_ACTIVITY, ACTIVITY_AWAKE, ACTIVITY_HOLD, ACTIVITY_SLEEP, ACTIVITY_HEAL, ACTIVITY_SENTRY, ACTIVITY_INTERCEPT, ACTIVITY_MISSION
+local g_activityMissions = {
+--[ActivityTypes.ACTIVITY_AWAKE or -1] = nil,
+[ActivityTypes.ACTIVITY_HOLD or -1] = false, --GameInfo_Missions.MISSION_SKIP, -- only when moves left > 0
+--[ActivityTypes.ACTIVITY_SLEEP or -1] = GameInfo_Missions.MISSION_SLEEP, -- can be sleep or fortify
+[ActivityTypes.ACTIVITY_HEAL or -1] = GameInfo_Missions.MISSION_HEAL,
+[ActivityTypes.ACTIVITY_SENTRY or -1] = GameInfo_Missions.MISSION_ALERT,
+[ActivityTypes.ACTIVITY_INTERCEPT or -1] = GameInfo_Missions.MISSION_AIRPATROL,
+--[ActivityTypes.ACTIVITY_MISSION or -1] = GameInfo_Missions.MISSION_MOVE_TO,
+[-1] = nil }
+
+local MAX_HIT_POINTS = GameDefines.MAX_HIT_POINTS or 100
+local AIR_UNIT_REBASE_RANGE_MULTIPLIER = GameDefines.AIR_UNIT_REBASE_RANGE_MULTIPLIER
+local RELIGION_MISSIONARY_PRESSURE_MULTIPLIER = GameDefines.RELIGION_MISSIONARY_PRESSURE_MULTIPLIER or 1
+local MOVE_DENOMINATOR = GameDefines.MOVE_DENOMINATOR
+
+local g_unitsIM, g_citiesIM, g_unitTypesIM, g_units, g_cities, g_unitTypes
+
+local g_cityFocusIcons = {
+--[CityAIFocusTypes.NO_CITY_AI_FOCUS_TYPE or -1] = "",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FOOD or -1] = "[ICON_FOOD]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_PRODUCTION or -1] = "[ICON_PRODUCTION]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GOLD or -1] = "[ICON_GOLD]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_SCIENCE or -1] = "[ICON_RESEARCH]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_CULTURE or -1] = "[ICON_CULTURE]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_GREAT_PEOPLE or -1] = "[ICON_GREAT_PEOPLE]",
+[CityAIFocusTypes.CITY_AI_FOCUS_TYPE_FAITH or -1] = "[ICON_PEACE]",
+[-1] = nil }
+
+local g_UnitTypeOrder = {}
+do
+	local i = 1
+	for unit in GameInfo.Units() do
+		g_UnitTypeOrder[ unit.ID ] = i
+		i = i + 1
+	end
+end
+
+local function SortByVoid2( a, b )
+	return a and b and a:GetVoid2() > b:GetVoid2()
+end
+
+--==========================================================
+-- Mod Panels
+--==========================================================
+
+local addinActions = {}
+local addinBuilds = {}
+
+function OnUnitPanelActionAddin(action)
+  print(string.format("Adding UnitPanel action %s (%s)", Locale.ConvertTextKey(action.Title), action.Name))
+  table.insert(addinActions, action)
+end
+LuaEvents.UnitPanelActionAddin.Add(OnUnitPanelActionAddin)
+
+-- function OnUnitPanelBuildAddin(build)
+  -- print(string.format("Adding UnitPanel build %s (%s)", Locale.ConvertTextKey(build.Title), build.Name))
+  -- table.insert(addinBuilds, build)
+-- end
+-- LuaEvents.UnitPanelBuildAddin.Add(OnUnitPanelBuildAddin)
+
+do
+	g_uiAddins = {}
+	local Modding = Modding
+	local insert = table.insert
+	local uiAddins = g_uiAddins
+	for addin in Modding.GetActivatedModEntryPoints("UnitPanelAddin") do
+		local addinFile = Modding.GetEvaluatedFilePath(addin.ModID, addin.Version, addin.File)
+		if addinFile then
+			print("Loading MOD UnitPanelUIAddin\n", Modding.GetModProperty(addin.ModID, addin.Version, "Name"), addin.File )
+			insert(uiAddins,  ContextPtr:LoadNewContext( addinFile.EvaluatedPath:match("(.*)%..*")))
+		end
+	end
+end
+ContextPtr:LoadNewContext("EvilSpiritsMission")
+ContextPtr:LoadNewContext("THTanukiMission")
+
+--==========================================================
+-- Tooltip Utilities
+--==========================================================
+
+local function lookAtPlot( plot )
+	local hex = ToHexFromGrid{ x=plot:GetX(), y=plot:GetY() }
+	Events.GameplayFX( hex.x, hex.y, -1 )
+	return LookAt( plot )
+end
+
+local function lookAtUnit( unit )
+	if unit then
+		return lookAtPlot( unit:GetPlot() )
+	end
+end
+
+--==========================================================
+-- Ribbon Manager
+--==========================================================
+
+local function g_RibbonManager( name, stack, scrap, createAllItems, initItem, callbacks, tooltips, closure, toolTipCallback )
+	local index = {}
+	local spares = {}
+
+	local function Create( item, itemID, itemOrder )
+		if item then
+			local instance = remove( spares )
+			local button
+			if instance then
+--debug_print("Recycle from scrap", name, instance, "item", itemID, item and item:GetName() )
+				button = instance.Button
+				button:ChangeParent( stack )
+			else
+				instance = { m_PromotionIcons={} }
+--debug_print("Create new ", name, instance, "item", itemID, item and item:GetName() )
+				ContextPtr:BuildInstanceForControl( name, instance, stack )
+				-- Setup Tootip Callbacks
+				for controlID, toolTipType in pairs( tooltips ) do
+					instance[ controlID ]:SetToolTipCallback( function( control )
+						control:SetToolTipCallback( function( control ) return toolTipCallback( control, closure( button ) ) end )
+						control:SetToolTipType( toolTipType )
+					end)
+				end
+				-- Setup action Callbacks
+				button = instance.Button
+				for event, callback in pairs( callbacks ) do
+					button:RegisterCallback( event, callback )
+				end
+			end
+			index[ itemID ] = instance
+			button:SetVoids( itemID, itemOrder )
+			return initItem( item, instance )
+--else print( "Failed attempt to add an item to the list", itemID )
+		end
+	end
+
+return{
+	Create = Create,
+
+	Destroy = function( itemID )
+		local instance = index[ itemID ]
+--debug_print( "Remove item from list", name, "item", itemID, instance )
+		if instance then
+			index[ itemID ] = nil
+			insert( spares, instance )
+			instance.Button:ChangeParent( scrap )
+		end
+	end,
+
+	Initialize = function( isHidden )
+--debug_print("Initializing ", name, " stack", "hidden ?", isHidden )
+		for itemID, instance in pairs( index ) do
+			insert( spares, instance )
+			instance.Button:ChangeParent( scrap )
+			index[ itemID ] = nil
+		end
+		if not isHidden then
+--debug_print("Initializing ", name, " stack contents" )
+			createAllItems( Create )
+		end
+	end,
+
+	}, index
+end
+
+--==========================================================
+-- Item Functions
+--==========================================================
+
+local function UpdateCity( city, instance )
+	if city and instance then
+		local itemInfo, portraitOffset, portraitAtlas, buildPercent
+		local turnsRemaining = city:GetProductionTurnsLeft()
+		local productionNeeded = city:GetProductionNeeded()
+		local storedProduction = city:GetProduction() + city:GetOverflowProduction() + city:GetFeatureProduction()
+		local orderID, itemID = city:GetOrderFromQueue()
+		if orderID == OrderTypes.ORDER_TRAIN then
+			itemInfo = GameInfo.Units
+			portraitOffset, portraitAtlas = GetUnitPortraitIcon( itemID, g_activePlayerID )
+		elseif orderID == OrderTypes.ORDER_CONSTRUCT then
+			itemInfo = GameInfo.Buildings
+		elseif orderID == OrderTypes.ORDER_CREATE then
+			itemInfo = GameInfo.Projects
+		elseif orderID == OrderTypes.ORDER_MAINTAIN then
+			itemInfo = GameInfo.Processes
+			turnsRemaining = nil
+			productionNeeded = 0
+		end
+		if itemInfo then
+			itemInfo = itemInfo[ itemID ]or{}
+			itemInfo = IconHookup( portraitOffset or itemInfo.PortraitIndex, 64, portraitAtlas or itemInfo.IconAtlas, instance.CityProduction )
+			if productionNeeded > 0 then
+				buildPercent = 1 - max( 0, storedProduction/productionNeeded )
+			else
+				buildPercent = 0
+			end
+			instance.BuildMeter:SetPercents( 0, buildPercent )
+		else
+			turnsRemaining = nil
+		end
+		instance.CityProduction:SetHide( not itemInfo )
+		instance.BuildGrowth:SetString( turnsRemaining )
+		instance.CityPopulation:SetString( city:GetPopulation() )
+		local foodPerTurnTimes100 = city:FoodDifferenceTimes100()
+		if foodPerTurnTimes100 < 0 then
+			instance.CityGrowth:SetString( " [COLOR_RED]" .. (floor( city:GetFoodTimes100() / -foodPerTurnTimes100 ) + 1) .. "[ENDCOLOR] " )
+		elseif city:IsForcedAvoidGrowth() then
+			instance.CityGrowth:SetString( "[ICON_LOCKED]" )
+		elseif city:IsFoodProduction() or foodPerTurnTimes100 == 0 then
+			instance.CityGrowth:SetString()
+		else
+			instance.CityGrowth:SetString( " "..city:GetFoodTurnsLeft().." " )
+		end
+
+		local isNotPuppet = not city:IsPuppet()
+		local isNotRazing = not city:IsRazing()
+		local isNotResistance = not city:IsResistance()
+		local isCapital = city:IsCapital()
+
+		instance.CityIsCapital:SetHide( not isCapital )
+		instance.CityIsPuppet:SetHide( isNotPuppet )
+		instance.CityFocus:SetText( isNotRazing and isNotPuppet and g_cityFocusIcons[city:GetFocusType()] )
+		instance.CityQuests:SetText( city:GetWeLoveTheKingDayCounter() > 0 and "[ICON_HAPPINESS_1]" or (GameInfo.Resources[city:GetResourceDemanded()] or {}).IconString )
+		instance.CityIsRazing:SetHide( isNotRazing )
+		instance.CityIsResistance:SetHide( isNotResistance )
+		instance.CityIsConnected:SetHide( isCapital or not g_activePlayer:IsCapitalConnectedToCity( city ) )
+		instance.CityIsBlockaded:SetHide( not city:IsBlockaded() )
+		instance.CityIsOccupied:SetHide( not city:IsOccupied() or city:IsNoOccupiedUnhappiness() )
+		instance.Name:SetString( city:GetName() )
+
+		local culturePerTurn = city:GetJONSCulturePerTurn()
+		instance.BorderGrowth:SetString( culturePerTurn > 0 and ceil( (city:GetJONSCultureThreshold() - city:GetJONSCultureStored()) / culturePerTurn ) )
+
+		local percent = 1 - city:GetDamage() / ( gk_mode and city:GetMaxHitPoints() or GameDefines.MAX_CITY_HIT_POINTS )
+		instance.Button:SetColor( Color( 1, percent, percent, 1 ) )
+	end
+end
+
+local function UpdateUnit( unit, instance, nextInstance )
+	if unit and instance then
+		local unitMovesLeft = unit:MovesLeft()
+		local pip
+		if unitMovesLeft >= unit:MaxMoves() then
+			pip = 0 -- cyan (green)
+		elseif unitMovesLeft > 0 then
+			if unit:IsCombatUnit() and unit:IsOutOfAttacks() then
+				pip = 96 -- orange (gray)
+			else
+				pip = 32 -- green (yellow)
+			end
+		else
+			pip = 64 -- red
+		end
+		local damage = unit:GetDamage()
+		local percent
+		if damage <= 0 then
+			percent = 1
+		elseif instance == Controls then
+			percent = 1 - damage / MAX_HIT_POINTS / 3
+		else
+			percent = 1 - damage / MAX_HIT_POINTS
+		end
+
+		local info
+		local text
+		local buildID = unit:GetBuildType()
+
+		if buildID ~= -1 then -- unit is actively building something
+			info = GameInfo_Builds[buildID]
+			text = GetUnitBuildProgressData( unit:GetPlot(), buildID, unit )
+			if text > 99 then text = nil end
+
+		elseif unit:IsEmbarked() then
+			info = GameInfo_Missions.MISSION_EMBARK
+
+		elseif unit:IsReadyToMove() then
+
+		elseif unit:IsAutomated() then
+			if unit:IsWork() then
+				info = GameInfo_Automates.AUTOMATE_BUILD
+			elseif bnw_mode and unit:IsTrade() then
+				info = GameInfo_Missions.MISSION_ESTABLISH_TRADE_ROUTE
+			else
+				info = GameInfo_Automates.AUTOMATE_EXPLORE
+			end
+
+		elseif unit:LastMissionPlot() ~= unit:GetPlot() then
+			info = GameInfo_Missions.MISSION_MOVE_TO
+
+		elseif unit:IsWaiting() then
+			local activityType = unit:GetActivityType()
+			info = g_activityMissions[ activityType ]
+			if not info and unitMovesLeft > 0 then
+--print( "ACTIVITY_MISSION", unit:GetName(), unit:GetX(), unit:GetY(), unit:GetPlot() ~= unit:LastMissionPlot() )
+				if info == false then
+					info = GameInfo_Missions.MISSION_SKIP
+				elseif unit:IsGarrisoned() then
+					info = GameInfo_Missions.MISSION_GARRISON
+				elseif unit:IsEverFortifyable() then
+					info = GameInfo_Missions.MISSION_FORTIFY
+				else
+					info = GameInfo_Missions.MISSION_SLEEP
+				end
+			end
+
+		elseif unitMovesLeft > 0 then
+			info = GameInfo_Missions.MISSION_MOVE_TO
+		end
+
+		repeat
+			instance.Button:SetColor( Color( 1, percent, percent, 1 ) )
+			instance.MovementPip:SetTextureOffsetVal( 0, pip )
+			instance.Mission:SetHide( not( info and IconHookup( info.IconIndex, 45, info.IconAtlas, instance.Mission ) ) )
+			instance.MissionText:SetText( text )
+			if nextInstance then
+				instance, nextInstance = nextInstance
+				instance.MovementPip:Play()
+			else
+				break
+			end
+		until false
+	end
+end
+
+local function FilterUnit( unit )
+	return unit and g_ActivePlayerUnitsInRibbon[ unit:GetUnitType() ]
+end
+
+--==========================================================
+-- Unit Ribbon "Object"
+--==========================================================
+local CallFlagManagerUpdateUnitPromotions = LuaEvents.CallFlagManagerUpdateUnitPromotions.Call
+
+g_unitsIM, g_units = g_RibbonManager( "UnitInstance", Controls.UnitStack, Controls.Scrap,
+	function( Create ) -- createAllItems( Create )
+		local unitID
+		for unit in g_activePlayer:Units() do
+			if FilterUnit( unit ) then
+				unitID = unit:GetID()
+				Create( unit, unitID, g_UnitTypeOrder[unit:GetUnitType()] * 65536 + unitID % 65536 )
+			end
+		end
+		Controls.UnitStack:SortChildren( SortByVoid2 )
+	end,
+	function( unit, instance ) -- initItem( item, instance )
+		local portraitOffset, portraitAtlas = GetUnitPortraitIcon( unit )
+		IconHookup( portraitOffset, 64, portraitAtlas, instance.Portrait )
+		if unit == GetHeadSelectedUnit() then
+			instance.MovementPip:Play()
+		else
+			instance.MovementPip:SetToBeginning()
+		end
+		UpdateUnit( unit, instance )
+		return CallFlagManagerUpdateUnitPromotions( unit )
+	end,
+	{-- the callback function table names need to match associated instance control ID defined in xml
+		[Mouse.eLClick] = function( unitID )
+			local unit = g_activePlayer:GetUnitByID( unitID )
+			SelectUnit( unit )
+			lookAtUnit( unit )
+		end,
+		[Mouse.eRClick] = function( unitID )
+			lookAtUnit( g_activePlayer:GetUnitByID( unitID ) )
+		end,
+	},--/unit callbacks
+	{
+		Button = "EUI_UnitTooltip",
+		MovementPip = "EUI_ItemTooltip",
+		Mission = "EUI_ItemTooltip",
+	},
+	function( button )
+		return g_activePlayer:GetUnitByID( button:GetVoid1() )
+	end,
+	LuaEvents.UnitToolTips.Call
+)--/unit ribbon object
+--==========================================================
+
+LuaEvents.EUI_UnitRibbonTable( g_units )
+
+--==========================================================
+-- City Ribbon "Object"
+--==========================================================
+g_citiesIM, g_cities = g_RibbonManager( "CityInstance", Controls.CityStack, Controls.Scrap,
+	function( Create ) -- createAllItems( Create )
+		for city in g_activePlayer:Cities() do
+			Create( city, city:GetID() )
+		end
+	end,
+	UpdateCity, -- initItem( item, instance )
+	{-- the callback function table names need to match associated instance control ID defined in xml
+		[Mouse.eLClick] = function( cityID )
+			local city = g_activePlayer:GetCityByID( cityID )
+			if city then
+				DoSelectCityAtPlot( city:Plot() )
+			end
+		end,
+		[Mouse.eRClick] = function( cityID )
+			local city = g_activePlayer:GetCityByID( cityID )
+			if city then
+				lookAtPlot( city:Plot() )
+			end
+		end,
+	},--/city callbacks
+	{
+		Button = "EUI_CityProductionTooltip",
+		CityPopulation = "EUI_CityGrowthTooltip",
+--		CityProduction = "EUI_CityProductionTooltip",
+--		BuildMeter = "EUI_ItemTooltip",
+--		GrowthMeter = "EUI_ItemTooltip",
+		CityIsCapital = "EUI_ItemTooltip",
+		CityIsPuppet = "EUI_ItemTooltip",
+		CityIsOccupied = "EUI_ItemTooltip",
+		CityIsResistance = "EUI_ItemTooltip",
+		CityIsRazing = "EUI_ItemTooltip",
+		CityIsConnected = "EUI_ItemTooltip",
+		CityIsBlockaded = "EUI_ItemTooltip",
+		CityFocus = "EUI_ItemTooltip",
+		CityGrowth = "EUI_ItemTooltip",
+		CityQuests = "EUI_ItemTooltip",
+		BuildGrowth = "EUI_ItemTooltip",
+		BorderGrowth = "EUI_ItemTooltip",
+	},
+	function( button )
+		return g_activePlayer:GetCityByID( button:GetVoid1() )
+	end,
+	LuaEvents.CityToolTips.Call
+)--/city ribbon object
+--==========================================================
+
+--[[ 
+ _   _       _ _     ____                  _ 
+| | | |_ __ (_) |_  |  _ \ __ _ _ __   ___| |
+| | | | '_ \| | __| | |_) / _` | '_ \ / _ \ |
+| |_| | | | | | |_  |  __/ (_| | | | |  __/ |
+ \___/|_| |_|_|\__| |_|   \__,_|_| |_|\___|_|
+]]
+
+local g_screenWidth , g_screenHeight = UIManager:GetScreenSizeVal()
+local g_topOffset0 = Controls.CityPanel:GetOffsetY()
+local g_topOffset = g_topOffset0
+local g_bottomOffset0 = Controls.UnitPanel:GetOffsetY()
+local g_bottomOffset = g_bottomOffset0
+
+local g_Actions = {}
+local g_Promotions = {}
+local g_UnusedControls = Controls.Scrap
+
+local g_lastUnit		-- Used to determine if a different unit has been selected.
+local g_isWorkerActionPanelOpen = false
+
+local g_unitPortraitSize = Controls.UnitPortrait:GetSizeX()
+
+local g_actionButtonSpacing = OptionsManager.GetSmallUIAssets() and 42 or 58
+
+--[[
+local g_actionIconSize = OptionsManager.GetSmallUIAssets() and 36 or 50
+local g_recommendedActionButton = {}
+ContextPtr:BuildInstanceForControlAtIndex( "UnitAction", g_recommendedActionButton, Controls.WorkerActionStack, 1 )
+--Controls.RecommendedActionLabel:ChangeParent( g_recommendedActionButton.UnitActionButton )
+local g_existingBuild = {}
+ContextPtr:BuildInstanceForControl( "UnitAction", g_existingBuild, Controls.WorkerActionStack )
+g_existingBuild.WorkerProgressBar:SetPercent( 1 )
+g_existingBuild.UnitActionButton:SetDisabled( true )
+g_existingBuild.UnitActionButton:SetAlpha( 0.8 )
+--]]
+
+--==========================================================
+-- Event Handlers
+--==========================================================
+
+local function OnUnitActionClicked( actionID )
+	local action = GameInfoActions[actionID]
+	if action and g_activePlayer:IsTurnActive() then
+		Game.HandleAction( actionID )
+		if action.SubType == ActionSubTypes.ACTIONSUBTYPE_PROMOTION then
+			Events.AudioPlay2DSound("AS2D_INTERFACE_UNIT_PROMOTION")
+		end
+	end
+end
+
+Controls.CycleLeft:RegisterCallback( Mouse.eLClick,
+function()
+	-- Cycle to next selection.
+	Game.CycleUnits(true, true, false)
+end)
+
+Controls.CycleRight:RegisterCallback( Mouse.eLClick,
+function()
+	-- Cycle to previous selection.
+	Game.CycleUnits(true, false, false)
+end)
+
+local function OnUnitNameClicked()
+	-- go to this unit
+	lookAtUnit( GetHeadSelectedUnit() )
+end
+Controls.UnitNameButton:RegisterCallback( Mouse.eLClick, OnUnitNameClicked )
+
+do
+	local UnitToolTipCall = LuaEvents.UnitToolTip.Call
+	Controls.UnitPortraitButton:SetToolTipCallback( function( control )
+		control:SetToolTipCallback( function() return UnitToolTipCall( GetHeadSelectedUnit(), L"TXT_KEY_CURRENTLY_SELECTED_UNIT", "----------------" ) end )
+		control:SetToolTipType( "EUI_UnitTooltip" )
+	end)
+end
+Controls.UnitPortraitButton:RegisterCallback( Mouse.eLClick, OnUnitNameClicked )
+
+Controls.UnitPortraitButton:RegisterCallback( Mouse.eRClick,
+function()
+	local unit = GetHeadSelectedUnit()
+	Events.SearchForPediaEntry( unit and unit:GetNameKey() )
+end)
+
+local function OnEditNameClick()
+
+	if GetHeadSelectedUnit() then
+		Events.SerialEventGameMessagePopup{
+				Type = ButtonPopupTypes.BUTTONPOPUP_RENAME_UNIT,
+				Data1 = GetHeadSelectedUnit():GetID(),
+				Data2 = -1,
+				Data3 = -1,
+				Option1 = false,
+				Option2 = false
+			}
+	end
+end
+Controls.EditButton:RegisterCallback( Mouse.eLClick, OnEditNameClick )
+Controls.UnitNameButton:RegisterCallback( Mouse.eRClick, OnEditNameClick )
+
+--==========================================================
+-- Utilities
+--==========================================================
+local function ResizeCityUnitRibbons()
+--debug_print("ResizeCityUnitRibbons" )
+	local maxTotalStackHeight = g_screenHeight - g_topOffset - g_bottomOffset
+	local halfTotalStackHeight = floor(maxTotalStackHeight / 2)
+
+	Controls.CityStack:CalculateSize()
+	local cityStackHeight = Controls.CityStack:GetSizeY()
+	Controls.UnitStack:CalculateSize()
+	local unitStackHeight = Controls.UnitStack:GetSizeY()
+
+	if unitStackHeight + cityStackHeight <= maxTotalStackHeight then
+		unitStackHeight = false
+		halfTotalStackHeight = false
+	elseif cityStackHeight <= halfTotalStackHeight then
+		unitStackHeight = maxTotalStackHeight - cityStackHeight
+		halfTotalStackHeight = false
+	elseif unitStackHeight <= halfTotalStackHeight then
+		cityStackHeight = maxTotalStackHeight - unitStackHeight
+		unitStackHeight = false
+	else
+		cityStackHeight = halfTotalStackHeight
+		unitStackHeight = halfTotalStackHeight
+	end
+
+	Controls.CityScrollPanel:SetHide( not halfTotalStackHeight )
+	if halfTotalStackHeight then
+		Controls.CityStack:ChangeParent( Controls.CityScrollPanel )
+		Controls.CityScrollPanel:SetSizeY( cityStackHeight )
+		Controls.CityScrollPanel:CalculateInternalSize()
+	else
+		Controls.CityStack:ChangeParent( Controls.CityPanel )
+	end
+	Controls.CityPanel:ReprocessAnchoring()
+--		Controls.CityPanel:SetSizeY( cityStackHeight )
+
+	Controls.UnitScrollPanel:SetHide( not unitStackHeight )
+	if unitStackHeight then
+		Controls.UnitStack:ChangeParent( Controls.UnitScrollPanel )
+		Controls.UnitScrollPanel:SetSizeY( unitStackHeight )
+		Controls.UnitScrollPanel:CalculateInternalSize()
+	else
+		Controls.UnitStack:ChangeParent( Controls.UnitPanel )
+	end
+	Controls.UnitPanel:ReprocessAnchoring()
+end
+
+local function UpdateUnits()
+	local activePlayer = g_activePlayer
+	for unitID, instance in pairs( g_units ) do
+		UpdateUnit( activePlayer:GetUnitByID( unitID ), instance )
+	end
+end
+
+local function UpdateCities()
+	local activePlayer = g_activePlayer
+	for cityID, instance in pairs( g_cities ) do
+		UpdateCity( activePlayer:GetCityByID( cityID ), instance )
+	end
+end
+
+local function UpdateSpecificCity( playerID, cityID )
+	if playerID == g_activePlayerID then
+		UpdateCity( g_activePlayer:GetCityByID( cityID ), g_cities[cityID] )
+	end
+end
+
+local function SelectUnitType( isChecked, unitTypeID ) -- Void2, control )
+	g_ActivePlayerUnitsInRibbon[ unitTypeID ] = isChecked
+	g_unitsIM.Initialize( g_isHideUnitList )
+	ResizeCityUnitRibbons()
+	-- only save player 0 preferences, not other hotseat player's
+	if g_activePlayerID == 0 then
+		UserInterfaceSettings[ "RIBBON_"..GameInfo_Units[ unitTypeID ].Type] = isChecked and 1 or 0
+	end
+end
+local function ResizeUnitTypesPanel()
+--		if not g_isHideUnitTypes then
+	local n = Controls.UnitTypesStack:GetNumChildren()
+	Controls.UnitTypesStack:SetWrapWidth( ceil( n / ceil( n / 5 ) ) * 64 )
+	Controls.UnitTypesStack:CalculateSize()
+	local x, y = Controls.UnitTypesStack:GetSizeVal()
+	if y<64 then y=64 elseif y>320 then y=320 end
+	Controls.UnitTypesPanel:SetSizeVal( x+40, y+85 )
+	Controls.UnitTypesScrollPanel:SetSizeVal( x, y )
+	Controls.UnitTypesScrollPanel:CalculateInternalSize()
+	Controls.UnitTypesScrollPanel:ReprocessAnchoring()
+end
+local function AddUnitType( unit, unitID )
+	local unitTypeID = unit:GetUnitType()
+	g_ActivePlayerUnitTypes[ unitID or unit:GetID() ] = unitTypeID
+	local instance = g_unitTypes[ unitTypeID ]
+	if instance then
+--debug_print( "Add unit:", unit:GetID(), unit:GetName(), "type:", instance, unitTypeID, "count:", n )
+		return instance.Count:SetText( instance.Count:GetText()+1 )
+	else
+--debug_print( "Add unit:", unit:GetID(), unit:GetName(), "new type:", unitTypeID )
+		g_unitTypesIM.Create( unit, unitTypeID, -g_UnitTypeOrder[unitTypeID] )
+		if unitID then
+			Controls.UnitTypesStack:SortChildren( SortByVoid2 )
+			return ResizeUnitTypesPanel()
+		end
+	end
+end
+--==========================================================
+-- Unit Options "Object"
+--==========================================================
+g_unitTypesIM, g_unitTypes = g_RibbonManager( "UnitTypeInstance", Controls.UnitTypesStack, Controls.Scrap,
+	function() -- createAllItems
+		g_ActivePlayerUnitTypes = {}
+		for unit in g_activePlayer:Units() do
+			AddUnitType( unit )
+		end
+		Controls.UnitTypesStack:SortChildren( SortByVoid2 )
+		return ResizeUnitTypesPanel()
+	end,
+	function( unit, instance ) -- initItem( item, instance )
+		local portrait = instance.Portrait
+		local portraitOffset, portraitAtlas = GetUnitPortraitIcon( unit )
+		portrait:SetHide(not ( portraitOffset and portraitAtlas and IconHookup( portraitOffset, portrait:GetSizeX(), portraitAtlas, portrait ) ) )
+		instance.CheckBox:RegisterCheckHandler( SelectUnitType )
+		local unitTypeID = unit:GetUnitType()
+		instance.CheckBox:SetCheck( g_ActivePlayerUnitsInRibbon[ unitTypeID ] )
+		instance.CheckBox:SetVoid1( unitTypeID )
+		instance.Count:SetText("1")
+	end,
+	{-- the callback function table names need to match associated instance control ID defined in xml
+		[Mouse.eRClick] = function( unitTypeID )
+			local unit = GameInfo.Units[ unitTypeID ]
+			if unit then
+				Events.SearchForPediaEntry( unit.Description )
+			end
+		end,
+	},--/unit options callbacks
+	{-- the tooltip function names need to match associated instance control ID defined in xml
+		Button = "EUI_ItemTooltip",
+	},--/units options tooltips
+	function( button )
+		return button:GetVoid1()
+	end,
+	LuaEvents.UnitPanelItemTooltip.Call
+)--/unit options object
+
+local function CreateUnit( playerID, unitID ) --hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible )
+	if playerID == g_activePlayerID then
+		local unit = g_activePlayer:GetUnitByID( unitID )
+--debug_print("Create unit", unitID, unit and unit:GetName() )
+		if unit then
+			AddUnitType( unit, unitID )
+			if FilterUnit( unit ) then
+				g_unitsIM.Create( unit, unitID, g_UnitTypeOrder[unit:GetUnitType()] + unitID / 65536 )
+				Controls.UnitStack:SortChildren( SortByVoid2 )
+				return ResizeCityUnitRibbons()
+			end
+		end
+	end
+end
+
+local function CreateCity( hexPos, playerID, cityID ) --, cultureType, eraType, continent, populationSize, size, fowState )
+	if playerID == g_activePlayerID then
+		g_citiesIM.Create( g_activePlayer:GetCityByID( cityID ), cityID )
+		return ResizeCityUnitRibbons()
+	end
+end
+
+local function DestroyUnit( playerID, unitID )
+	if playerID == g_activePlayerID then
+		g_unitsIM.Destroy( unitID )
+		local unitTypeID = g_ActivePlayerUnitTypes[ unitID ]
+		local instance = g_unitTypes[ unitTypeID ]
+--debug_print( "Destroy unit", unitID, "type:", g_ActivePlayerUnitTypes[ unitID ], instance, "previous count:", instance.Count )
+		g_ActivePlayerUnitTypes[ unitID ] = nil
+		if instance then
+			local n = instance.Count:GetText() - 1
+			if n <= 0 then
+				g_unitTypesIM.Destroy( unitTypeID )
+				ResizeUnitTypesPanel()
+			else
+				instance.Count:SetText( n )
+			end
+		end
+		return ResizeCityUnitRibbons()
+	end
+end
+
+local function DestroyCity( hexPos, playerID, cityID )
+	if playerID == g_activePlayerID then
+		g_citiesIM.Destroy( cityID )
+		return ResizeCityUnitRibbons()
+	end
+end
+
+
+local function SetHide( ... )
+	for _, control in pairs{...} do
+		control:SetHide( true )
+	end
+end
+
+local function SetShow( ... )
+	for _, control in pairs{...} do
+		control:SetHide( false )
+	end
+end
+
+local function SetTextAndFontSize( control, text, x )
+	control:SetText( text )
+	for i = 24, 14, -2 do
+		control:SetFontByName( "TwCenMT"..i )
+		if control:GetSizeVal() <= x then
+			break
+		end
+	end
+end
+
+local function DeselectLastUnit( unit )
+	if g_lastUnit then
+		local lastUnitID = g_lastUnit:GetID()
+		Events.UnitSelectionChanged( g_lastUnit:GetOwner(), lastUnitID, 0, 0, 0, false, false )
+		local instance = g_units[ lastUnitID ]
+		if instance then
+			instance.MovementPip:SetToBeginning()
+			UpdateUnit( g_lastUnit, instance )
+		end
+	end
+	g_lastUnit = unit
+end
+
+local g_infoSource = {
+		[ ActionSubTypes.ACTIONSUBTYPE_PROMOTION or -1 ] = GameInfo.UnitPromotions,
+		[ ActionSubTypes.ACTIONSUBTYPE_INTERFACEMODE or -1 ] = GameInfo.InterfaceModes,
+		[ ActionSubTypes.ACTIONSUBTYPE_MISSION or -1 ] = GameInfo.Missions,
+		[ ActionSubTypes.ACTIONSUBTYPE_COMMAND or -1 ] = GameInfo.Commands,
+		[ ActionSubTypes.ACTIONSUBTYPE_AUTOMATE or -1 ] = GameInfo.Automates,
+		[ ActionSubTypes.ACTIONSUBTYPE_BUILD or -1 ] = GameInfo.Builds,
+		[ ActionSubTypes.ACTIONSUBTYPE_CONTROL or -1 ] = GameInfo.Controls,
+		[-1] = nil
+}
+
+local UnitActionToolTipCall = LuaEvents.UnitActionToolTip.Call
+local function UnitActionToolTip( button )
+	button:SetToolTipCallback( UnitActionToolTipCall )
+	button:SetToolTipType( "EUI_UnitAction" )
+end
+
+local function UpdateUnitPanel()
+	actionIM:ResetInstances()
+	-- Retrieve the currently selected unit.
+	local unit = GetHeadSelectedUnit()
+-- Events.GameplayAlertMessage( "SerialEventUnitInfoDirty, GetHeadSelectedUnit=".. tostring(unit and unit:GetName())..", last unit="..tostring(g_lastUnit and g_lastUnit:GetName()) )
+--debug_print( "UpdateUnitPanel", "GetHeadSelectedCity", GetHeadSelectedCity() and  GetHeadSelectedCity():GetName(), "GetHeadSelectedUnit", GetHeadSelectedUnit()and GetHeadSelectedUnit():GetName(), "Last unit", g_lastUnit and g_lastUnit:GetName() )
+	if unit then
+		local unitID = unit:GetID()
+		-- Selected Unit
+		if unit ~= g_lastUnit then
+			DeselectLastUnit( unit )
+			local hexPosition = ToHexFromGrid{ x = unit:GetX(), y = unit:GetY() }
+			Events.UnitSelectionChanged( unit:GetOwner(), unitID, hexPosition.x, hexPosition.y, 0, true, false )
+		end
+		local unitMovesLeft = unit:MovesLeft() / MOVE_DENOMINATOR
+		local unitPlot = unit:GetPlot()
+
+		-- Unit Name
+		SetTextAndFontSize( Controls.UnitName, ToUpper( L( unit:IsGreatPerson() and unit:HasName() and unit:GetNameNoDesc() or unit:GetName() ) ), Controls.UnitNameButton:GetSizeVal()-50 )
+
+		-- Unit Actions
+		local canPromote = unit:IsPromotionReady()
+		local GameCanHandleAction = Game.CanHandleAction
+		local numBuildActions = 0
+		local action, instance, button, buildTurnsLeft, buildProgress, buildTime, canBuild, isBuildRecommended
+
+		--==========================================================
+		--Mod Actions
+		--==========================================================
+		local function SetToolTip(sTitle, sToolTip)
+			print(string.format("Setting Tooltip: Title: %s - Tooltip: %s", sTitle, sToolTip))
+			local ttTable = {}
+			TTManager:GetTypeControlTable( "EUI_UnitAction", ttTable )
+
+			ttTable.UnitActionHelp:SetText(string.format("[NEWLINE]%s", sToolTip))
+			ttTable.UnitActionText:SetText(string.format("[COLOR_POSITIVE_TEXT]%s[ENDCOLOR]", Locale.ConvertTextKey(sTitle)))
+			ttTable.UnitActionHotKey:SetText("")
+
+			ttTable.UnitActionMouseover:DoAutoSize()
+			local mouseoverSize = ttTable.UnitActionMouseover:GetSize();
+			if (mouseoverSize.x < 350) then
+				ttTable.UnitActionMouseover:SetSizeX(350)
+			end
+		end
+		  
+		for _, action in pairs(addinActions) do
+			print(string.format("Processing UnitPanel action %s (%s)", Locale.ConvertTextKey(action.Title), action.Name))
+			if (action.Condition == nil or
+			(type(action.Condition) == "function" and action.Condition(action, unit)) or
+			(type(action.Condition) ~= "function" and action.Condition)) then
+				local instance = actionIM:GetInstance()
+
+				if ((type(action.Disabled) == "function" and action.Disabled(action, unit)) or
+				(type(action.Disabled) ~= "function" and action.Disabled)) then
+					instance.UnitActionButton:SetAlpha(0.4)
+					instance.UnitActionButton:SetDisabled(true)
+				else
+					instance.UnitActionButton:SetAlpha(1.0)
+					instance.UnitActionButton:SetDisabled(false)
+				end
+
+				IconHookup(action.PortraitIndex, actionIconSize, action.IconAtlas, instance.UnitActionIcon)
+				local sTitle
+				if (type(action.Title) == "function") then
+					sTitle = action.Title(action, unit)
+				else
+					sTitle = action.Title
+				end
+
+				local sToolTip
+				if (type(action.ToolTip) == "function") then
+					sToolTip = action.ToolTip(action, unit)
+				else
+					sToolTip = action.ToolTip
+				end
+				instance.UnitActionButton:SetToolTipCallback(function() SetToolTip(sTitle, sToolTip) end)
+
+				if (type(action.Action) == "function") then
+					instance.UnitActionButton:RegisterCallback(Mouse.eLClick, function() action.Action(action, unit, Mouse.eLClick) end)
+					instance.UnitActionButton:RegisterCallback(Mouse.eRClick, function() action.Action(action, unit, Mouse.eRClick) end)
+				else
+					instance.UnitActionButton:RegisterCallback(Mouse.eLClick, function() end)
+					instance.UnitActionButton:RegisterCallback(Mouse.eRClick, function() end)
+				end
+			end
+		end
+
+		-- for _, build in pairs(addinBuilds) do
+			-- if (build.Condition == nil or
+		  -- (type(build.Condition) == "function" and build.Condition(build, unit)) or
+		  -- (type(build.Condition) ~= "function" and build.Condition)) then
+				-- local instance = g_BuildIM:GetInstance()
+				-- instance.UnitActionButton:SetAnchor( "L,B" )
+				-- instance.UnitActionButton:SetOffsetVal((numBuildActions % numberOfButtonsPerRow) * buttonSize + buttonPadding + buttonOffsetX, math.floor(numBuildActions / numberOfButtonsPerRow) * buttonSize + buttonPadding + buttonOffsetY)
+				-- numBuildActions = numBuildActions + 1
+
+				-- if ((type(build.Disabled) == "function" and build.Disabled(build, unit)) or
+				-- (type(build.Disabled) ~= "function" and build.Disabled)) then
+					-- instance.UnitActionButton:SetAlpha(0.4)
+					-- instance.UnitActionButton:SetDisabled(true)
+				-- else
+					-- instance.UnitActionButton:SetAlpha(1.0)
+					-- instance.UnitActionButton:SetDisabled(false)
+				-- end
+
+				-- IconHookup(build.PortraitIndex, actionIconSize, build.IconAtlas, instance.UnitActionIcon)
+				-- local sToolTip
+				-- if (type(build.ToolTip) == "function") then
+					-- sToolTip = build.ToolTip(build, unit)
+				-- else
+					-- sToolTip = build.ToolTip
+				-- end
+				-- instance.UnitActionButton:SetToolTipCallback(function() SetToolTip(build.Title, sToolTip) end)
+
+				-- if (type(build.Build) == "function") then
+					-- instance.UnitActionButton:RegisterCallback(Mouse.eLClick, function() build.Build(build, unit, Mouse.eLClick) end)
+					-- instance.UnitActionButton:RegisterCallback(Mouse.eRClick, function() build.Build(build, unit, Mouse.eRClick) end)
+				-- else
+					-- instance.UnitActionButton:RegisterCallback(Mouse.eLClick, function() end)
+					-- instance.UnitActionButton:RegisterCallback(Mouse.eRClick, function() end)
+				-- end
+
+				-- if (recommendedBuild == nil and 
+				-- ((type(build.Recommended) == "function" and build.Recommended(build, unit)) or
+				-- (type(build.Recommended) ~= "function" and build.Recommended))) then
+					-- recommendedBuild = build;
+					-- IconHookup(build.PortraitIndex, actionIconSize, build.IconAtlas, Controls.RecommendedActionImage)
+
+					-- if (type(build.Build) == "function") then
+						-- Controls.RecommendedActionButton:RegisterCallback(Mouse.eLClick, function() build.Build(build, unit, Mouse.eLClick) end)
+						-- Controls.RecommendedActionButton:RegisterCallback(Mouse.eRClick, function() build.Build(build, unit, Mouse.eRClick) end)
+					-- else
+						-- Controls.RecommendedActionButton:RegisterCallback(Mouse.eLClick, function() end)
+						-- Controls.RecommendedActionButton:RegisterCallback(Mouse.eRClick, function() end)
+					-- end
+
+					-- Controls.RecommendedActionButton:SetToolTipCallback(function() SetToolTip(build.Title, sToolTip) end)
+					-- Controls.RecommendedActionLabel:SetText(Locale.ConvertTextKey(build.Title))
+				-- end
+			-- end
+		-- end
+		
+		--==========================================================
+
+		for actionID = 0, #GameInfoActions do
+			action = GameInfoActions[ actionID ]
+			if action and action.Visible ~= false then
+				instance = g_Actions[ actionID ]
+				if GameCanHandleAction( actionID, unitPlot, true ) then
+					if instance then
+						button = instance.UnitActionButton
+					else
+						instance = {}
+						instance.isBuild = action.SubType == ActionSubTypes.ACTIONSUBTYPE_BUILD
+						instance.isBuildType = instance.isBuild or action.Type == "INTERFACEMODE_ROUTE_TO" or action.Type == "AUTOMATE_BUILD"
+						instance.isPromotion = action.SubType == ActionSubTypes.ACTIONSUBTYPE_PROMOTION
+						instance.isException = instance.isPromotion or action.Type == "COMMAND_CANCEL" or action.Type == "COMMAND_STOP_AUTOMATION"
+						instance.recommendation = (bnw_mode and (L"TXT_KEY_UPANEL_RECOMMENDED" .. "[NEWLINE]") or "") .. L( tostring( action.TextKey or action.Type ) )
+						if action.Type == "MISSION_FOUND" then
+							instance.UnitActionButton = Controls.BuildCityButton
+						else
+							ContextPtr:BuildInstanceForControl( "UnitAction", instance, g_UnusedControls )
+							instance.WorkerProgressBar:SetHide( not instance.isBuild )
+							local info = ( g_infoSource[ action.SubType ] or {} )[ action.Type ]
+							if info then
+								instance.IconIndex = info.IconIndex or info.PortraitIndex
+								instance.IconAtlas = info.IconAtlas
+								IconHookup( instance.IconIndex, instance.UnitActionIcon:GetSizeX(), instance.IconAtlas, instance.UnitActionIcon )
+							end
+						end
+						button = instance.UnitActionButton
+						button:RegisterCallback( Mouse.eLClick, OnUnitActionClicked )
+						button:SetVoid1( actionID )
+						button:SetToolTipCallback( UnitActionToolTip )
+						instance.ID = actionID
+						g_Actions[ actionID ] = instance
+					end
+					if unitMovesLeft > 0 or instance.isException then
+						if instance.isPromotion then
+							numBuildActions = numBuildActions + 1
+							button:ChangeParent( Controls.WorkerActionStack )
+
+						elseif instance.isBuildType and not canPromote then
+							numBuildActions = numBuildActions + 1
+							if unitMovesLeft > 0 and not isBuildRecommended and unit:IsActionRecommended( actionID ) then
+								isBuildRecommended = true
+								button:ChangeParent( Controls.RecommendedActionIcon )
+								Controls.RecommendedActionLabel:SetText( instance.recommendation )
+							else
+								button:ChangeParent( Controls.WorkerActionStack )
+							end
+							if instance.isBuild then
+								canBuild = true
+								buildTurnsLeft, buildProgress, buildTime = GetUnitBuildProgressData( unitPlot, action.MissionData, unit )
+								instance.WorkerProgressBar:SetPercent( buildProgress / buildTime )
+								instance.UnitActionText:SetText( buildTurnsLeft > 0 and buildTurnsLeft or nil )
+							end
+						else
+							button:ChangeParent( Controls.ActionStack )
+						end
+						-- test w/o visible flag (ie can train right now)
+						if GameCanHandleAction( actionID, unitPlot, false ) then
+							button:SetAlpha( 1.0 )
+							button:SetDisabled( false )
+						else
+							button:SetAlpha( 0.6 )
+							button:SetDisabled( true )
+						end
+						instance.isVisible = true
+					elseif instance.isVisible then
+						button:ChangeParent( g_UnusedControls )
+						instance.isVisible = false
+					end
+				elseif instance and instance.isVisible then
+					instance.UnitActionButton:ChangeParent( g_UnusedControls )
+					instance.isVisible = false
+				end
+			end -- action.Visible
+		end -- GameInfoActions loop
+
+		if numBuildActions > 0 or canPromote then
+			Controls.WorkerActionPanel:SetHide( false )
+			g_isWorkerActionPanelOpen = true
+			Controls.RecommendedAction:SetHide( not isBuildRecommended )
+--[[
+			local improvement = canBuild and not canPromote and GameInfo.Improvements[ unitPlot:GetImprovementType() ]
+			local build = improvement and GameInfo_Builds{ ImprovementType = improvement.Type }()
+			if build then
+				numBuildActions = numBuildActions + 1
+				IconHookup( build.IconIndex, g_actionIconSize, build.IconAtlas, g_existingBuild.UnitActionIcon )
+			end
+			g_existingBuild.UnitActionButton:SetHide( not build )
+--]]
+			Controls.WorkerText:SetHide( canPromote )
+			Controls.PromotionText:SetHide( not canPromote )
+			Controls.PromotionAnimation:SetHide( not canPromote )
+			Controls.EditButton:SetHide( not canPromote )
+			Controls.WorkerActionStack:SetWrapWidth( isBuildRecommended and 232 or ceil( numBuildActions / ceil( numBuildActions / 5 ) ) * g_actionButtonSpacing )
+			Controls.WorkerActionStack:CalculateSize()
+			local x, y = Controls.WorkerActionStack:GetSizeVal()
+			Controls.WorkerActionPanel:SetSizeVal( max( x, 200 ) + 50, y + 96 )
+			Controls.WorkerActionStack:ReprocessAnchoring()
+		else
+			Controls.WorkerActionPanel:SetHide( true )
+			g_isWorkerActionPanelOpen = false
+		end
+
+		-- Unit XP
+		if unit:IsCombatUnit() or unit:GetDomainType() == DomainTypes.DOMAIN_AIR then
+			local iLevel = unit:GetLevel()
+			local iExperience = unit:GetExperience()
+			local iExperienceNeeded = unit:ExperienceNeeded()
+			Controls.XPMeter:LocalizeAndSetToolTip( "TXT_KEY_UNIT_EXPERIENCE_INFO", iLevel, iExperience, iExperienceNeeded )
+			Controls.XPMeter:SetPercent( iExperience / iExperienceNeeded )
+			Controls.XPFrame:SetHide( false )
+		else
+			Controls.XPFrame:SetHide( true )
+		end
+
+		-- Unit Flag
+		local flagOffset, flagAtlas = GetUnitFlagIcon( unit )
+		IconHookup( flagOffset, 32, flagAtlas, Controls.UnitIcon )
+		IconHookup( flagOffset, 32, flagAtlas, Controls.UnitIconShadow )
+
+		-- Unit Portrait
+		local portraitOffset, portraitAtlas = GetUnitPortraitIcon( unit )
+		IconHookup( portraitOffset, g_unitPortraitSize, portraitAtlas, Controls.UnitPortrait )
+
+		-- Unit Promotions
+		for promotion in GameInfo.UnitPromotions() do
+			if promotion.ShowInUnitPanel ~= false then
+				instance = g_Promotions[ promotion.ID ]
+				if unit:IsHasPromotion( promotion.ID ) then
+					if instance then
+						instance.EarnedPromotion:ChangeParent( Controls.EarnedPromotionStack )
+					else
+						instance = {}
+						ContextPtr:BuildInstanceForControl( "EarnedPromotionInstance", instance, Controls.EarnedPromotionStack )
+						IconHookup( promotion.PortraitIndex, 32, promotion.IconAtlas, instance.UnitPromotionImage )
+						instance.EarnedPromotion:SetToolTipString( ( promotion._Name or "???" ) .. "[NEWLINE][NEWLINE]" .. L(promotion.Help or "???") )
+						g_Promotions[ promotion.ID ] = instance
+					end
+					instance.isVisible = true
+				elseif instance and instance.isVisible then
+					instance.EarnedPromotion:ChangeParent( g_UnusedControls )
+					instance.isVisible = false
+				end
+			end
+		end
+
+		-- Unit Movement
+		if unit:GetDomainType() == DomainTypes.DOMAIN_AIR then
+			local unitRange = unit:Range()
+			Controls.UnitStatMovement:SetText( unitRange .. "[ICON_MOVES]" )
+			Controls.UnitStatMovement:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_UNIT_MAY_STRIKE_REBASE", unitRange, unitRange * AIR_UNIT_REBASE_RANGE_MULTIPLIER / 100 )
+		else
+			local text = format(" %.3g/%g[ICON_MOVES]", unitMovesLeft, unit:MaxMoves() / MOVE_DENOMINATOR )
+			Controls.UnitStatMovement:SetText( text )
+			Controls.UnitStatMovement:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_UNIT_MAY_MOVE", text )
+		end
+
+		-- Unit Strength
+		local strength = ( unit:GetDomainType() == DomainTypes.DOMAIN_AIR and unit:GetBaseRangedCombatStrength() )
+						or ( not unit:IsEmbarked() and unit:GetBaseCombatStrength() ) or 0
+		if strength > 0 then
+			Controls.UnitStatStrength:SetText( strength .. "[ICON_STRENGTH]" )
+			Controls.UnitStatStrength:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_STRENGTH_TT" )
+		elseif gk_mode and unit:GetSpreadsLeft() > 0 then
+			-- Religious units
+			Controls.UnitStatStrength:SetText( floor(unit:GetConversionStrength()/RELIGION_MISSIONARY_PRESSURE_MULTIPLIER) .. "[ICON_PEACE]" )
+			Controls.UnitStatStrength:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_RELIGIOUS_STRENGTH_TT" )
+		elseif bnw_mode and unit:GetTourismBlastStrength() > 0 then
+			Controls.UnitStatStrength:SetText( unit:GetTourismBlastStrength() .. "[ICON_TOURISM]" )
+			Controls.UnitStatStrength:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_TOURISM_STRENGTH_TT" )
+		else
+			Controls.UnitStatStrength:SetText()
+		end
+
+		-- Ranged Strength
+		local rangedStrength = unit:GetDomainType() ~= DomainTypes.DOMAIN_AIR and unit:GetBaseRangedCombatStrength() or 0
+		if rangedStrength > 0 then
+			Controls.UnitStatRangedAttack:SetText( rangedStrength .. "[ICON_RANGE_STRENGTH]" .. unit:Range() )
+			Controls.UnitStatRangedAttack:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_RANGED_ATTACK_TT" )
+		elseif gk_mode and unit:GetSpreadsLeft() > 0 then
+			-- Religious units
+			local unitReligion = unit:GetReligion()
+			local icon = (GameInfo.Religions[unitReligion] or {}).IconString
+			Controls.UnitStatRangedAttack:SetText( icon and (unit:GetSpreadsLeft()..icon) )
+			Controls.UnitStatRangedAttack:SetToolTipString( L(Game.GetReligionName(unitReligion))..": "..L"TXT_KEY_UPANEL_SPREAD_RELIGION_USES_TT" )
+--		elseif gk_mode and GameInfo_Units[unit:GetUnitType()].RemoveHeresy then
+--			Controls.UnitStatRangedAttack:LocalizeAndSetText( "TXT_KEY_UPANEL_REMOVE_HERESY_USES" )
+--			Controls.UnitStatRangedAttack:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_REMOVE_HERESY_USES_TT" )
+		elseif bnw_mode and unit:CargoSpace() > 0 then
+			Controls.UnitStatRangedAttack:SetText( L"TXT_KEY_UPANEL_CARGO_CAPACITY" .. " " .. unit:CargoSpace() )
+			Controls.UnitStatRangedAttack:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_CARGO_CAPACITY_TT", unit:GetName() )
+		else
+			Controls.UnitStatRangedAttack:SetText()
+		end
+		Controls.UnitStats:CalculateSize()
+		Controls.UnitStats:ReprocessAnchoring()
+
+		-- Unit Health Bar
+		local damage = unit:GetDamage()
+		if damage ~= 0 then
+			local healthPercent = 1.0 - (damage / MAX_HIT_POINTS)
+			local barSize = 123 * healthPercent
+			if healthPercent <= .33 then
+				Controls.RedBar:SetSizeY(barSize)
+				Controls.RedAnim:SetSizeY(barSize)
+				Controls.GreenBar:SetHide(true)
+				Controls.YellowBar:SetHide(true)
+				Controls.RedBar:SetHide(false)
+			elseif healthPercent <= .66 then
+				Controls.YellowBar:SetSizeY(barSize)
+				Controls.GreenBar:SetHide(true)
+				Controls.YellowBar:SetHide(false)
+				Controls.RedBar:SetHide(true)
+			else
+				Controls.GreenBar:SetSizeY(barSize)
+				Controls.GreenBar:SetHide(false)
+				Controls.YellowBar:SetHide(true)
+				Controls.RedBar:SetHide(true)
+			end
+			Controls.HealthBar:LocalizeAndSetToolTip( "TXT_KEY_UPANEL_SET_HITPOINTS_TT", MAX_HIT_POINTS-damage, MAX_HIT_POINTS )
+			Controls.HealthBar:SetHide(false)
+		else
+			Controls.HealthBar:SetHide(true)
+		end
+
+		-- Unit Stats
+		UpdateUnit( unit, Controls, g_units[ unitID ] )
+
+		Controls.UnitStatBox:SetHide( bnw_mode and unit:IsTrade() )
+
+		-- These controls need to be shown since potentially hidden depending on previous selection
+		SetShow( Controls.EarnedPromotionStack, Controls.UnitTypeFrame, Controls.CycleLeft, Controls.CycleRight, Controls.ActionStack )
+		Controls.ActionStack:CalculateSize()
+		Controls.ActionStack:ReprocessAnchoring()
+
+	else
+		-- Deselect last unit, if any
+		DeselectLastUnit()
+		-- Attempt to show currently selected city
+		unit = GetHeadSelectedCity()
+		if unit then
+			-- City Name
+			SetTextAndFontSize( Controls.UnitName, ToUpper( L(unit:GetName()) ), Controls.UnitNameButton:GetSizeVal()-50 )
+
+			-- City Portrait
+			IconHookup( 0, g_unitPortraitSize, "CITY_ATLAS", Controls.UnitPortrait )
+
+			-- Hide various aspects of Unit Panel since they don't apply to the city.
+			SetHide( Controls.EarnedPromotionStack, Controls.UnitTypeFrame, Controls.CycleLeft, Controls.CycleRight, Controls.XPFrame, Controls.UnitStatBox, Controls.WorkerActionPanel, Controls.ActionStack )
+			g_isWorkerActionPanelOpen = false
+		end
+	end
+	if (not unit) ~= Controls.Panel:IsHidden() then
+		if unit then
+			g_bottomOffset = g_bottomOffset0
+			Controls.UnitTypesPanel:SetOffsetVal( g_unitPortraitSize * 0.625, 120 )
+		else
+			g_bottomOffset = 35
+			Controls.UnitTypesPanel:SetOffsetVal( 80, -40 )
+		end
+		Controls.Panel:SetHide( not unit )
+		Controls.Actions:SetHide( not unit )
+		Controls.UnitPanel:SetOffsetY( g_bottomOffset )
+		ResizeCityUnitRibbons()
+	end
+end
+
+local function UpdateOptions()
+
+	local option = UserInterfaceSettings.UnitTypes == 0
+	if g_isHideUnitTypes ~= option then
+		g_unitTypesIM.Initialize( option )
+		ResizeUnitTypesPanel()
+		g_isHideUnitTypes = option
+		Controls.UnitTypesOpen:SetHide( option )
+		Controls.UnitTypesClose:SetHide( not option )
+	end
+
+	option = UserInterfaceSettings.UnitRibbon == 0
+	if g_isHideUnitList ~= option then
+		g_isHideUnitList = option
+		local AddOrRemove = option and "Remove" or "Add"
+		Events.SerialEventUnitCreated[ AddOrRemove ]( CreateUnit )
+		Events.SerialEventUnitDestroyed[ AddOrRemove ]( DestroyUnit )
+		Events.ActivePlayerTurnStart[ AddOrRemove ]( UpdateUnits )
+	end
+	g_unitsIM.Initialize( option )
+	Controls.UnitPanel:SetHide( option )
+	Controls.UnitTypesPanel:SetHide( option or g_isHideUnitTypes )
+
+	option = UserInterfaceSettings.CityRibbon == 0
+	if g_isHideCityList ~= option then
+		g_isHideCityList = option
+		local AddOrRemove = option and "Remove" or "Add"
+		Events.SerialEventCityCreated[ AddOrRemove ]( CreateCity )
+		Events.SerialEventCityDestroyed[ AddOrRemove ]( DestroyCity )
+		Events.SerialEventCityCaptured[ AddOrRemove ]( DestroyCity )
+		Events.SerialEventCityInfoDirty[ AddOrRemove ]( UpdateCities )
+		Events.SerialEventCitySetDamage[ AddOrRemove ]( UpdateSpecificCity )
+		Events.SpecificCityInfoDirty[ AddOrRemove ]( UpdateSpecificCity )
+	end
+	g_citiesIM.Initialize( option )
+	Controls.CityPanel:SetHide( option )
+
+	UpdateUnitPanel()
+	ResizeCityUnitRibbons()
+end
+
+Controls.UnitTypesButton:RegisterCallback( Mouse.eLClick,
+function()
+	UserInterfaceSettings.UnitTypes = g_isHideUnitTypes and 1 or 0
+	return UpdateOptions()
+end)
+
+local function SetActivePlayer()-- activePlayerID, prevActivePlayerID )
+	-- initialize globals
+	if g_activePlayerID then
+		g_AllPlayerOptions.UnitTypes[ g_activePlayerID ] = g_ActivePlayerUnitTypes
+		g_AllPlayerOptions.UnitsInRibbon[ g_activePlayerID ] = g_ActivePlayerUnitsInRibbon
+	end
+	g_activePlayerID = Game.GetActivePlayer()
+	g_activePlayer = Players[ g_activePlayerID ]
+	g_activeTeamID = g_activePlayer:GetTeam()
+	g_activeTeam = Teams[ g_activeTeamID ]
+	g_activeTechs = g_activeTeam:GetTeamTechs()
+	g_ActivePlayerUnitTypes = g_AllPlayerOptions.UnitTypes[ g_activePlayerID ] or {}
+	g_ActivePlayerUnitsInRibbon = g_AllPlayerOptions.UnitsInRibbon[ g_activePlayerID ]
+	if not g_ActivePlayerUnitsInRibbon then
+		g_ActivePlayerUnitsInRibbon = {}
+		for row in GameInfo.Units() do
+			g_ActivePlayerUnitsInRibbon[ row.ID ] = UserInterfaceSettings[ "RIBBON_"..row.Type ] ~= 0
+		end
+	end
+
+	-- set civilization icon and color
+	local civInfo = GameInfo.Civilizations[ g_activePlayer:GetCivilizationType() ] or {}
+	IconHookup( civInfo.PortraitIndex, 128, civInfo.IconAtlas, Controls.BackgroundCivSymbol )
+	Controls.UnitIcon:SetColor( PrimaryColors[ g_activePlayerID ] )
+	Controls.UnitIconBackground:SetColor( BackgroundColors[ g_activePlayerID ] )
+
+	return UpdateOptions()
+end
+
+SetActivePlayer()
+Events.GameplaySetActivePlayer.Add( SetActivePlayer )
+Events.GameOptionsChanged.Add( UpdateOptions )
+Events.SerialEventUnitInfoDirty.Add( UpdateUnitPanel )
+--[[
+Events.UnitActionChanged.Add(
+function( playerID, unitID )
+	if playerID == g_activePlayerID then
+		local instance = g_units[ unitID ]
+		if instance then
+			return UpdateUnit( g_activePlayer:GetUnitByID( unitID ), instance )
+		end
+	end
+end)
+--]]
+Events.SerialEventEnterCityScreen.Add(
+function()
+	DeselectLastUnit()
+end)
+
+local g_infoCornerYmax = {
+[InfoCornerID.None or -1] = g_topOffset0,
+[InfoCornerID.Tech or -1] = OptionsManager.GetSmallUIAssets() and 150 or 225,
+[-1] = nil }
+
+Events.OpenInfoCorner.Add( function( infoCornerID )
+	g_topOffset = g_infoCornerYmax[infoCornerID] or 380
+	Controls.CityPanel:SetOffsetY( g_topOffset )
+	return UpdateOptions()
+end)
+
+--[[
+Events.EndCombatSim.Add( function(
+			attackerPlayerID,
+			attackerUnitID,
+			attackerUnitDamage,
+			attackerFinalUnitDamage,
+			attackerMaxHitPoints,
+			defenderPlayerID,
+			defenderUnitID,
+			defenderUnitDamage,
+			defenderFinalUnitDamage,
+			defenderMaxHitPoints )
+	if attackerPlayerID == g_activePlayerID then
+		local instance = g_units[ attackerUnitID ]
+		if instance then
+			local toolTip = instance.Button:GetToolTipString()
+			if toolTip then
+				toolTip = toolTip .. "[NEWLINE]"
+			else
+				toolTip = ""
+			end
+			toolTip = toolTip
+				.."Attack: "
+				.. " / " .. tostring( attackerPlayerID )
+				.. " / " .. tostring( attackerUnitID )
+				.. " / " .. tostring( attackerUnitDamage )
+				.. " / " .. tostring( attackerFinalUnitDamage )
+				.. " / " .. tostring( attackerMaxHitPoints )
+				.. " / " .. tostring( defenderPlayerID )
+				.. " / " .. tostring( defenderUnitID )
+				.. " / " .. tostring( defenderUnitDamage )
+				.. " / " .. tostring( defenderFinalUnitDamage )
+				.. " / " .. tostring( defenderMaxHitPoints )
+			instance.Button:SetToolTipString( toolTip )
+		end
+	elseif defenderPlayerID == g_activePlayerID then
+		local instance = g_units[ defenderUnitID ]
+		if instance then
+			local toolTip = instance.Button:GetToolTipString()
+			if toolTip then
+				toolTip = toolTip .. "[NEWLINE]"
+			else
+				toolTip = ""
+			end
+			toolTip = toolTip
+				.."Defense: "
+				.. " / " .. tostring( attackerPlayerID )
+				.. " / " .. tostring( attackerUnitID )
+				.. " / " .. tostring( attackerUnitDamage )
+				.. " / " .. tostring( attackerFinalUnitDamage )
+				.. " / " .. tostring( attackerMaxHitPoints )
+				.. " / " .. tostring( defenderPlayerID )
+				.. " / " .. tostring( defenderUnitID )
+				.. " / " .. tostring( defenderUnitDamage )
+				.. " / " .. tostring( defenderFinalUnitDamage )
+				.. " / " .. tostring( defenderMaxHitPoints )
+			instance.Button:SetToolTipString( toolTip )
+		end
+	end
+end)
+--]]
+-- Process request to hide enemy panel
+LuaEvents.EnemyPanelHide.Add(
+	function( isEnemyPanelHide )
+		if g_isWorkerActionPanelOpen then
+			Controls.WorkerActionPanel:SetHide( not isEnemyPanelHide )
+		end
+		if not g_isHideUnitTypes and not g_isHideUnitList then
+			Controls.UnitTypesPanel:SetHide( not isEnemyPanelHide )
+		end
+	end)
+local EnemyUnitPanel = LookUpControl( "/InGame/WorldView/EnemyUnitPanel" )
+local isHidden = ContextPtr:IsHidden()
+ContextPtr:SetShowHideHandler(
+	function( isHide, isInit )
+		if not isInit and isHidden ~= isHide then
+			isHidden = isHide
+			if isHide and EnemyUnitPanel then
+				EnemyUnitPanel:SetHide( true )
+			end
+		end
+	end)
+ContextPtr:SetHide( false )
+
+end)